Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions docs/superpowers/plans/2026-04-30-plan-9-caveman-and-polish.md
Original file line number Diff line number Diff line change
@@ -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.*
53 changes: 53 additions & 0 deletions plugin/lib/caveman.sh
Original file line number Diff line number Diff line change
@@ -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 <level> <prompt>
# 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}"
}
2 changes: 1 addition & 1 deletion plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
14 changes: 13 additions & 1 deletion plugin/skills/mvp-loop/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <project>` 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.
Expand Down
78 changes: 78 additions & 0 deletions tests/plan-9/test_polish.bats
Original file line number Diff line number Diff line change
@@ -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"
}