tools: add repl-block, check-tangle; move existing tools into memex
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
This commit is contained in:
74
projects/repl-tool/repl
Executable file
74
projects/repl-tool/repl
Executable file
@@ -0,0 +1,74 @@
|
||||
#!/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();
|
||||
Reference in New Issue
Block a user