fix repl-block, check-tangle, commands: READMEs, exit codes, path resolution

repl-block:
- Listing mode exits 0, not 1 (listing is not an error)
- Dead lines parameter removed from find_by_function
- Block listing goes to stderr (not stdout) so piped use works
- Added README.org

check-tangle:
- Fixed tangle tool resolution: prefer local projects/tangle-tool/tangle
- Fixed path resolution for relative and absolute tangle paths
- Removed 2>/dev/null suppression so tangle errors are visible
- Added README.org

Commands:
- Rewrote all three .opencode/commands/*.md with proper prompts
This commit is contained in:
2026-05-13 12:57:06 -04:00
parent c9cc874e53
commit a2a7b4ca08
7 changed files with 93 additions and 16 deletions

View File

@@ -3,6 +3,9 @@ description: Check paren balance in lisp blocks of .org files
--- ---
Run `projects/check-parens/check-parens` on the given .org files to verify all Run `projects/check-parens/check-parens` on the given .org files to verify all
`#+begin_src lisp` blocks have balanced parentheses. `#+begin_src lisp` blocks have balanced parentheses. Uses SBCL's reader for
100% accuracy — no false positives from string literals or character literals.
Exit 0 if all blocks balanced, 1 if any issues found.
Usage: /check-parens <file.org> [<file.org> ...] Usage: /check-parens <file.org> [<file.org> ...]

View File

@@ -3,6 +3,13 @@ description: Tangle an org file and compile the result
--- ---
Tangle an .org file to .lisp then compile with SBCL. Reports the first Tangle an .org file to .lisp then compile with SBCL. Reports the first
compile error. Exit 0 = clean, exit 1 = compilation error. compile error with line numbers. Exit 0 = clean compile, exit 1 = error.
Prepares code for commit by ensuring the tangled .lisp file is syntactically
valid. Catches missing symbols, undefined functions, and type errors before
they reach the running daemon.
Usage: /check-tangle <file.org> Usage: /check-tangle <file.org>
Example:
/check-tangle projects/passepartout/org/channel-tui-main.org

View File

@@ -2,12 +2,16 @@
description: Send a lisp block from an org file to the REPL description: Send a lisp block from an org file to the REPL
--- ---
Extract a `#+begin_src lisp` block from an .org file and send it to the Extract a `#+begin_src lisp` block from an .org file and pipe it to the
running daemon REPL. running daemon REPL. Identify the block by function name or index.
The `--package` flag wraps the block in `(in-package ...)` so it evaluates
in the right namespace — essential when the block references symbols from
a specific package without the package prefix.
Usage: /repl-block <file.org> --function <name> Usage: /repl-block <file.org> --function <name>
/repl-block <file.org> --block <number>
/repl-block <file.org> --function <name> --package <pkg> /repl-block <file.org> --function <name> --package <pkg>
/repl-block <file.org> --block <number>
The --package flag wraps the block in an (in-package ...) form. Example:
Use --block to identify by 1-based index, --function to find by defun name. /repl-block projects/passepartout/org/channel-tui-view.org --function view-status --package :passepartout.channel-tui

View File

@@ -0,0 +1,22 @@
#+TITLE: check-tangle
Tangle an .org file and compile the resulting .lisp with SBCL in one step.
== Usage
#+begin_src shell
check-tangle org/file.org
#+end_src
Exit 0 if compilation succeeds, 1 if tangling or compilation fails.
== What it checks
1. Reads the ~:tangle~ header from the org file to find the target .lisp path
2. Runs ~tangle~ to generate the .lisp from the .org source
3. Runs ~sbcl compile-file~ on the result
4. Reports the first compile error if any
== Requires
Emacs (for tangling), SBCL (for compilation).

View File

@@ -28,13 +28,26 @@ fi
# Resolve relative tangle path # Resolve relative tangle path
ORG_DIR=$(dirname "$ORG_FILE") ORG_DIR=$(dirname "$ORG_FILE")
LISP_FILE=$(cd "$ORG_DIR" && realpath -m "$TANGLE" 2>/dev/null || echo "$ORG_DIR/$TANGLE") if [ "$TANGLE" = "${TANGLE#/}" ]; then
# Relative path
LISP_FILE=$(cd "$ORG_DIR" && realpath -m "$TANGLE" 2>/dev/null || echo "$ORG_DIR/$TANGLE")
else
# Absolute path
LISP_FILE="$TANGLE"
fi
echo "Tangling: $ORG_FILE → $LISP_FILE" >&2 echo "Tangling: $ORG_FILE → $LISP_FILE" >&2
# Tangle using opencode's tangle tool # Prefer the memex's own tangle tool, then fall back to PATH
TANGLE_CMD=$(command -v tangle 2>/dev/null || echo "/home/user/.opencode/bin/tangle") if [ -x "projects/tangle-tool/tangle" ]; then
if ! "$TANGLE_CMD" "$ORG_FILE" 2>/dev/null; then TANGLE_CMD="projects/tangle-tool/tangle"
elif command -v tangle &>/dev/null; then
TANGLE_CMD="tangle"
else
TANGLE_CMD="/home/user/.opencode/bin/tangle"
fi
if ! "$TANGLE_CMD" "$ORG_FILE"; then
echo "FAIL: Tangling $ORG_FILE failed" >&2 echo "FAIL: Tangling $ORG_FILE failed" >&2
exit 1 exit 1
fi fi

View File

@@ -0,0 +1,28 @@
#+TITLE: repl-block
Extract a ~#+begin_src lisp~ block from an .org file and output it to stdout,
ready to pipe to ~repl~ or inspect.
== Usage
#+begin_src shell
repl-block org/file.org --function foo | repl
repl-block org/file.org --block 3
repl-block org/file.org --function foo --package :my-package
repl-block org/file.org # list all blocks
#+end_src
== Identifying blocks
By function name (--function): scans all blocks for (defun <name> ...) and
outputs the containing block. By index (--block): 1-based position in file.
The --package flag prepends an ~(in-package ...)~ form so the REPL evaluates
in the right namespace.
Combine with ~repl~ to send a block directly to the daemon:
repl-block org/channel-tui-view.org --function view-status --package :passepartout.channel-tui | repl
== Requires
Python 3. No dependencies.

View File

@@ -41,7 +41,7 @@ def extract_blocks(lines):
return blocks return blocks
def find_by_function(blocks, name, lines): def find_by_function(blocks, name):
for line_no, body in blocks: for line_no, body in blocks:
for bline in body: for bline in body:
if re.match(rf"\(def(un|macro|method|var|parameter|class|struct|package)\s+{re.escape(name)}\b", bline): if re.match(rf"\(def(un|macro|method|var|parameter|class|struct|package)\s+{re.escape(name)}\b", bline):
@@ -76,7 +76,7 @@ def main():
blocks = extract_blocks(lines) blocks = extract_blocks(lines)
if args.function: if args.function:
line_no, body = find_by_function(blocks, args.function, lines) line_no, body = find_by_function(blocks, args.function)
if body is None: if body is None:
print(f"No block found containing function '{args.function}'", file=sys.stderr) print(f"No block found containing function '{args.function}'", file=sys.stderr)
return 1 return 1
@@ -86,12 +86,12 @@ def main():
print(f"Block {args.block} not found (file has {len(blocks)} blocks)", file=sys.stderr) print(f"Block {args.block} not found (file has {len(blocks)} blocks)", file=sys.stderr)
return 1 return 1
else: else:
# Print listing # Print listing to stderr so piping still works
for idx, (line_no, body) in enumerate(blocks, 1): for idx, (line_no, body) in enumerate(blocks, 1):
first = (body or [""])[0][:60] first = (body or [""])[0][:60]
print(f" {idx}: line {line_no}: {first}") print(f" {idx}: line {line_no}: {first}", file=sys.stderr)
print(f"\n{len(blocks)} total blocks", file=sys.stderr) print(f"\n{len(blocks)} total blocks", file=sys.stderr)
return 1 return 0
if args.package: if args.package:
pkg = args.package pkg = args.package