New tools (projects/<tool>/ — standalone, git-committed): - repl-block: extract and pipe lisp blocks from org files to the REPL - check-tangle: tangle + compile in one step, reports errors Existing tools moved from ~/.opencode/bin/ into memex (survives reinstalls): - repl, tangle, org-eval, verify-repl AGENTS.md updated: - Tool reference table with all 7 tools - Package reference table for passepartout and cl-tty - Updated tangle command to use project-local tools .opencode/commands/ added: check-parens, repl-block, check-tangle commands
75 lines
1.9 KiB
Perl
Executable File
75 lines
1.9 KiB
Perl
Executable File
#!/usr/bin/env perl
|
|
# repl — evaluate Lisp forms against the running Passepartout daemon
|
|
# Usage: repl <lisp-form>
|
|
# or: echo '<lisp-form>' | repl
|
|
#
|
|
# Connects to the daemon on 127.0.0.1:9105, sends a repl-eval request
|
|
# via the framed TCP protocol, reads and prints the response.
|
|
|
|
use strict;
|
|
use warnings;
|
|
use IO::Socket::INET;
|
|
|
|
my $HOST = $ENV{PASSEPARTOUT_HOST} || "127.0.0.1";
|
|
my $PORT = $ENV{PASSEPARTOUT_PORT} || "9105";
|
|
my $TIMEOUT = $ENV{PASSEPARTOUT_REPL_TIMEOUT} || 10;
|
|
|
|
my $expr = join(" ", @ARGV);
|
|
if (!$expr) {
|
|
$expr = do { local $/; <STDIN> };
|
|
}
|
|
chomp($expr);
|
|
# Quote the expression for embedding in a Lisp string
|
|
$expr =~ s/\\/\\\\/g; # backslash → doubled
|
|
$expr =~ s/"/\\"/g; # " → \"
|
|
if (!$expr) {
|
|
die "Usage: repl <lisp-form>\n or: echo '(+ 1 2)' | repl\n";
|
|
}
|
|
|
|
my $sock = IO::Socket::INET->new(
|
|
PeerHost => $HOST,
|
|
PeerPort => $PORT,
|
|
Proto => "tcp",
|
|
Timeout => 2
|
|
) or die "Cannot connect to $HOST:$PORT: $!\n";
|
|
|
|
sub read_frame {
|
|
my ($sock) = @_;
|
|
my $hex;
|
|
$sock->read($hex, 6) or return undef;
|
|
my $len = hex($hex);
|
|
my $content;
|
|
$sock->read($content, $len);
|
|
return $content;
|
|
}
|
|
|
|
sub write_frame {
|
|
my ($sock, $content) = @_;
|
|
my $len = sprintf("%06X", length($content));
|
|
$sock->send($len . $content);
|
|
}
|
|
|
|
# Read handshake (discard)
|
|
my $handshake = read_frame($sock);
|
|
|
|
# Build framed message
|
|
my $msg = '(:TYPE :EVENT :PAYLOAD (:SENSOR :repl-eval :CODE "' . $expr . '"))';
|
|
write_frame($sock, $msg);
|
|
|
|
# Read response
|
|
my $response = read_frame($sock);
|
|
if ($response) {
|
|
if ($response =~ /:VALUE "([^"]*)"/s) {
|
|
print "$1\n";
|
|
} elsif ($response =~ /:message "([^"]*)"/s) {
|
|
print STDERR "$1\n";
|
|
exit 1;
|
|
} else {
|
|
print "Response: $response\n";
|
|
}
|
|
} else {
|
|
print STDERR "No response from daemon (is it running on $HOST:$PORT?)\n";
|
|
exit 1;
|
|
}
|
|
$sock->close();
|