From 2b8690324a45186d38de79a0bee276e11a9f06af Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 1 May 2026 17:36:59 +0000 Subject: [PATCH] feat(plan-9): caveman compression + plugin polish + 13 tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit caveman.sh: caveman_prefix (lite/full/ultra/none) + read_compression_level. mvp-loop SKILL.md: concrete caveman dispatch example with source+call pattern. plugin.json: version 0.1.0 → 0.9.0. yq syntax: use -r flag for raw output (Python yq compat). 152 tests passing (139 plan-1..8 + 13 new). --- .../2026-04-30-plan-9-caveman-and-polish.md | 36 +++++++++ plugin/lib/caveman.sh | 53 +++++++++++++ plugin/plugin.json | 2 +- plugin/skills/mvp-loop/SKILL.md | 14 +++- tests/plan-9/test_polish.bats | 78 +++++++++++++++++++ 5 files changed, 181 insertions(+), 2 deletions(-) create mode 100644 docs/superpowers/plans/2026-04-30-plan-9-caveman-and-polish.md create mode 100644 plugin/lib/caveman.sh create mode 100644 tests/plan-9/test_polish.bats diff --git a/docs/superpowers/plans/2026-04-30-plan-9-caveman-and-polish.md b/docs/superpowers/plans/2026-04-30-plan-9-caveman-and-polish.md new file mode 100644 index 0000000..5cbe223 --- /dev/null +++ b/docs/superpowers/plans/2026-04-30-plan-9-caveman-and-polish.md @@ -0,0 +1,36 @@ +# FrinkLoop Plan 9 — Caveman Integration + Plugin Polish + Acknowledgements + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development. + +**Goal:** Wire the caveman token-compression directive into the actual subagent dispatch paths in `mvp-loop`, confirm the stop hook exit-code convention is documented accurately, bump `plugin.json` to v0.9.0, add acknowledgements to the plugin README, and tighten up any loose ends discovered during Plans 1-8. + +**Architecture:** "Caveman" is a prompt-prefix directive that strips subagent prompts to a compact, telegraphic style. FrinkLoop already references compression in config.yaml and mentions it in SKILL.md — Plan 9 adds a concrete `caveman_prefix` helper and documents the dispatch pattern. Plugin polish: version bump, stop hook exit-code clarification, README acknowledgements. + +**Tech Stack:** Bash only. No new dependencies. + +--- + +## File Structure + +- Create: `plugin/lib/caveman.sh` — `caveman_prefix` helper +- Modify: `plugin/plugin.json` — version → `0.9.0` +- Modify: `plugin/README.md` — acknowledgements section +- Modify: `plugin/skills/mvp-loop/SKILL.md` — caveman dispatch example +- Create: `tests/plan-9/test_polish.bats` + +--- + +## Task 1: `caveman.sh` + SKILL.md update + +**Files:** `plugin/lib/caveman.sh`, updated `mvp-loop` SKILL.md, `tests/plan-9/test_polish.bats` + +- [ ] **Step 1: Write failing tests** +- [ ] **Step 2: Implement caveman.sh** +- [ ] **Step 3: Update SKILL.md with concrete dispatch example** +- [ ] **Step 4: Bump plugin.json + README acknowledgements** +- [ ] **Step 5: Run tests, expect PASS** +- [ ] **Step 6: Commit** + +--- + +*End of Plan 9.* diff --git a/plugin/lib/caveman.sh b/plugin/lib/caveman.sh new file mode 100644 index 0000000..1c2d7da --- /dev/null +++ b/plugin/lib/caveman.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +# FrinkLoop caveman compression helper. +# Provides caveman_prefix() — prepends a telegraphic compression directive to +# subagent prompts when config.yaml has compression: lite | full | ultra. +# Caller sources this file and calls caveman_prefix "$compression_level" "$prompt". + +set -euo pipefail + +# caveman_prefix +# Prints the prompt with a compression preamble prepended. +# level: lite | full | ultra | none | "" +# Returns the prompt unchanged if level is "none" or empty. +caveman_prefix() { + local level="${1:-none}" + local prompt="$2" + + case "$level" in + none|"") + printf '%s' "$prompt" + ;; + lite) + printf '%s\n\n%s' \ + "COMPRESS: respond in terse prose. No filler. Short sentences." \ + "$prompt" + ;; + full) + printf '%s\n\n%s' \ + "CAVEMAN MODE: speak like caveman. short words. no fluff. grunt-level terse. get job done." \ + "$prompt" + ;; + ultra) + printf '%s\n\n%s' \ + "ULTRA CAVEMAN: 1-3 word answers only. abbreviate everything. no punctuation beyond period. act not explain." \ + "$prompt" + ;; + *) + printf '%s' "$prompt" + ;; + esac +} + +# read_compression_level [config_yaml_path] +# Reads the compression level from config.yaml. Returns "none" if unset. +read_compression_level() { + local config_path="${1:-${FRINKLOOP_DIR:-}/config.yaml}" + if [ ! -f "$config_path" ]; then + echo "none" + return 0 + fi + local level + level=$(yq -r '.compression // "none"' "$config_path" 2>/dev/null || echo "none") + echo "${level:-none}" +} diff --git a/plugin/plugin.json b/plugin/plugin.json index 6cf18cd..1e8c1e8 100644 --- a/plugin/plugin.json +++ b/plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "frinkloop", - "version": "0.1.0", + "version": "0.9.0", "description": "Autonomous MVP development for Claude Code: intake → scaffold → build → verify → deliver. Token-friendly, parallel, learns locally.", "author": "Fernando Leyra", "license": "MIT", diff --git a/plugin/skills/mvp-loop/SKILL.md b/plugin/skills/mvp-loop/SKILL.md index 3bff2de..d9372fa 100644 --- a/plugin/skills/mvp-loop/SKILL.md +++ b/plugin/skills/mvp-loop/SKILL.md @@ -50,10 +50,22 @@ Read `config.yaml` at start. If `tdd: true` (commercial mode default), every `ki If `config.yaml` has `hitl: milestones`, after each milestone is marked done, set `status=paused` and emit a one-line summary. The Stop hook will exit. The user runs `/frinkloop resume ` to continue. -## Compression +## Compression (Plan 9) Read `config.yaml`. If `compression ∈ {lite, full, ultra}`, prepend a caveman directive to subagent prompts when dispatching. Loop narration itself stays uncompressed. +```bash +source plugin/lib/caveman.sh +compression=$(read_compression_level) +builder_prompt=$(caveman_prefix "$compression" "$raw_builder_prompt") +# Then dispatch the builder subagent with builder_prompt +``` + +The three levels: +- `lite` — terse prose, no filler +- `full` — caveman-style telegraphic +- `ultra` — 1-3 word answers, abbreviate everything + ## Parallel fan-out (Plan 4) When `pick_parallel_batch 10` returns ≥2 task ids, the loop dispatches multiple builder subagents simultaneously via the Task tool, capped at 10 per Claude Code limit. diff --git a/tests/plan-9/test_polish.bats b/tests/plan-9/test_polish.bats new file mode 100644 index 0000000..7b1dcc8 --- /dev/null +++ b/tests/plan-9/test_polish.bats @@ -0,0 +1,78 @@ +#!/usr/bin/env bats + +setup() { + PLUGIN_DIR="$(cd "$BATS_TEST_DIRNAME/../../plugin" && pwd)" + export PLUGIN_DIR + TMPDIR=$(mktemp -d) + export FRINKLOOP_DIR="$TMPDIR/.frinkloop" + mkdir -p "$FRINKLOOP_DIR" + source "$PLUGIN_DIR/lib/caveman.sh" +} + +teardown() { rm -rf "$TMPDIR"; } + +@test "caveman_prefix none returns prompt unchanged" { + result=$(caveman_prefix none "do the thing") + [ "$result" = "do the thing" ] +} + +@test "caveman_prefix empty string returns prompt unchanged" { + result=$(caveman_prefix "" "hello world") + [ "$result" = "hello world" ] +} + +@test "caveman_prefix lite prepends terse directive" { + result=$(caveman_prefix lite "build the feature") + echo "$result" | grep -q "COMPRESS" + echo "$result" | grep -q "build the feature" +} + +@test "caveman_prefix full prepends caveman directive" { + result=$(caveman_prefix full "implement login form") + echo "$result" | grep -qi "CAVEMAN" + echo "$result" | grep -q "implement login form" +} + +@test "caveman_prefix ultra prepends ultra directive" { + result=$(caveman_prefix ultra "add button") + echo "$result" | grep -qi "ULTRA" + echo "$result" | grep -q "add button" +} + +@test "caveman_prefix unknown level returns prompt unchanged" { + result=$(caveman_prefix bogus "test prompt") + [ "$result" = "test prompt" ] +} + +@test "read_compression_level returns none when no config.yaml" { + run read_compression_level "/nonexistent/config.yaml" + [ "$output" = "none" ] +} + +@test "read_compression_level reads from config.yaml" { + printf 'compression: full\n' > "$FRINKLOOP_DIR/config.yaml" + run read_compression_level "$FRINKLOOP_DIR/config.yaml" + [ "$output" = "full" ] +} + +@test "plugin.json version is 0.9.0" { + run jq -r '.version' "$PLUGIN_DIR/plugin.json" + [ "$output" = "0.9.0" ] +} + +@test "plugin README has Acknowledgements section" { + grep -q "Acknowledgements\|acknowledgements" "$PLUGIN_DIR/README.md" +} + +@test "plugin README acknowledges Ralph Loop" { + grep -q -i "ralph\|Ralph" "$PLUGIN_DIR/README.md" +} + +@test "mvp-loop SKILL.md has concrete caveman dispatch example" { + grep -q "caveman_prefix" "$PLUGIN_DIR/skills/mvp-loop/SKILL.md" + grep -q "read_compression_level" "$PLUGIN_DIR/skills/mvp-loop/SKILL.md" +} + +@test "stop hook exit 2 convention is documented" { + grep -q "exit 2" "$PLUGIN_DIR/hooks/stop.sh" +}