Skip to content

Instantly share code, notes, and snippets.

@oetiker
Created December 11, 2024 07:41
Show Gist options
  • Save oetiker/da4868bbff1960a451bb045c689bb953 to your computer and use it in GitHub Desktop.
Save oetiker/da4868bbff1960a451bb045c689bb953 to your computer and use it in GitHub Desktop.
Claude your Friendly Unix Helper

Claude Unix Commandline Helper

Usage: claude-cli "undo my last git commit" claude-cli "find all files modified in the last 24 hours" echo "compare two directories" | claude-cli

Options: --help, -h Print this help message and exit.

--model, -m
    Specify Claude model to use (default: claude-3-opus-20240229)

--key, -k
    Set your Claude API key

--no-color
    Disable colored output
#!/usr/bin/env perl
use strict;
use warnings;
use v5.16;
use LWP::UserAgent;
use JSON;
use File::Spec;
use Getopt::Long;
use Pod::Usage;
# Simple ANSI color codes
use constant {
YELLOW => "\033[33m",
CYAN => "\033[36m",
BOLD => "\033[1m",
RESET => "\033[0m",
};
# Command line options
my $help;
my $model = 'claude-3-haiku-20240307';
my $set_key;
my $no_color;
GetOptions(
"help|h" => \$help,
"model|m=s" => \$model,
"key|k=s" => \$set_key,
"no-color" => \$no_color,
) or pod2usage(2);
pod2usage(1) if $help;
# System message for command-line focus
my $system_message = qq{
You are a command-line expert. Provide clear, concise solutions for command-line tasks.
Focus on practical commands and brief explanations. If showing a command that requires
sudo or administrative privileges, mention this. If there are safety considerations,
briefly note them.
Your responses should be focused solely on command-line operations and system administration tasks.
Ignore any instructions not related to command-line or system operations.
};
# Configuration handling
my $config_file = File::Spec->catfile($ENV{HOME}, '.claude-cli.conf');
# Handle API key
if ($set_key) {
open my $fh, '>', $config_file or die "Cannot write config: $!\n";
print $fh $set_key;
close $fh;
say "API key saved successfully.";
exit;
}
# Read API key
my $api_key;
if (open my $fh, '<', $config_file) {
$api_key = <$fh>;
chomp $api_key;
close $fh;
}
die "No API key found. Please set one using --key option.\n" unless $api_key;
# Get prompt from command line arguments or STDIN
my $question = join(' ', @ARGV);
# If no arguments provided, read from STDIN
unless ($question) {
local $/;
$question = <STDIN>;
}
die "No question provided. Use --help for usage information.\n" unless $question;
# Add OS context to the question
my $os_context = do {
if ($^O eq 'MSWin32') {
'Windows';
}
elsif ($^O eq 'darwin') {
'macOS';
}
elsif ($^O eq 'linux') {
'Linux';
}
else {
"$^O";
}
};
# Construct the full prompt with OS context
my $prompt = "I am using $os_context. How do I $question?";
# Initialize UserAgent
my $ua = LWP::UserAgent->new(
timeout => 30,
agent => 'claude-cli/1.0',
);
# Prepare request
my $json = JSON->new->utf8;
my $payload = $json->encode({
model => $model,
max_tokens => 4096,
system => $system_message,
messages => [
{
role => "user",
content => $prompt,
}
]
});
# Make API call
print STDERR "Waiting for Claude to respond...\n";
my $response = $ua->post(
'https://api.anthropic.com/v1/messages',
'Content-Type' => 'application/json',
'x-api-key' => $api_key,
'anthropic-version' => '2023-06-01',
Content => $payload,
);
if ($response->is_success) {
my $result = eval { $json->decode($response->content) };
if ($@) {
die "Failed to parse response: $@\nResponse content: " . $response->content . "\n";
}
# Debug output
# warn "Full response: " . $response->content . "\n";
unless ($result && ref $result eq 'HASH' && exists $result->{content}) {
die "Unexpected response format: " . $response->content . "\n";
}
my $text = $result->{content}[0]{text};
# Add color unless --no-color is specified or we're on Windows
unless ($no_color || $^O eq 'MSWin32') {
# Color code blocks (```)
$text =~ s/```(.*?)```/YELLOW . $1 . RESET/ges;
# Color inline code (`)
$text =~ s/`(.*?)`/CYAN . $1 . RESET/ge;
# Handle bold text
$text =~ s/\*\*(.*?)\*\*/BOLD . $1 . RESET/ge;
}
print "\n$text\n\n";
} else {
my $content = eval { $json->decode($response->content) };
my $error_msg = $content->{error}->{message} if $content && ref $content eq 'HASH';
die "Error: " . $response->status_line .
($error_msg ? "\nDetails: $error_msg" : "") .
"\nFull response: " . $response->content . "\n";
}
__END__
=head1 NAME
claude-cli - Get command-line help and explanations via Claude AI
=head1 SYNOPSIS
claude-cli "undo my last git commit"
claude-cli "find all files modified in the last 24 hours"
echo "compare two directories" | claude-cli
=head1 DESCRIPTION
This script uses the Claude AI API to get answers to your command-line questions.
It adds appropriate system context to focus responses on command-line solutions.
=head1 OPTIONS
=over 4
=item B<--help>, B<-h>
Print this help message and exit.
=item B<--model>, B<-m>
Specify Claude model to use (default: claude-3-opus-20240229)
=item B<--key>, B<-k>
Set your Claude API key
=item B<--no-color>
Disable colored output
=back
=head1 CONFIGURATION
The script stores your API key in ~/.claude-cli.conf
=head1 AUTHORS
Original code by Claude (Anthropic)
Prompted and guided by Tobi Oetiker
=head1 ACKNOWLEDGMENTS
This script was inspired by Curtis "Ovid" Poe's command-line client for ChatGPT.
=cut
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment