mirror of
https://github.com/JuliusBrussee/cavekit.git
synced 2026-09-14 16:32:40 +08:00
499205d2e0
Introduces the machinery that turns /ck:make into a hands-off autonomous
loop without changing the Hunt methodology. Nothing in existing commands,
skills, or agents breaks; the new layer is additive and opt-in.
New runtime (scripts/):
- cavekit-tools.cjs — state machine, lock with heartbeat, token ledger,
task registry, routing, capability discovery, backprop directive,
status-block dashboard. Zero runtime deps.
- cavekit-router.cjs — five-axis task scoring mapped to haiku/sonnet/opus
tiers with role baselines and budget-pressure demotion.
New hook system (hooks/):
- stop-hook.sh — Stop-event driver; reads .cavekit/state, routes the next
prompt, returns {decision:"block",reason:...} to keep the session going
until <promise>CAVEKIT_COMPLETE</promise> is emitted or a budget trips.
- token-monitor.sh — per-task budget (80% warn, 100% halt).
- tool-cache.js / tool-cache-store.js — 120s TTL cache for read-only tools.
- test-output-filter.js — condense test output around failure lines.
- auto-backprop.js — write flag file on test failure; stop-hook prepends
a backpropagation directive on the next iteration.
- progress-tracker.js — zero-stdout snapshot for /ck:watch.
- hooks.json — registers all of the above (Stop + PreToolUse + PostToolUse).
New skills (skills/):
- karpathy-guardrails — four behavioral rules (think-before-code, simplicity,
surgical, goal-driven) enforced by reviewer and task-builder.
- caveman-internal — three intensity modes (lite/full/ultra) for
machine-to-machine artifacts, with budget-pressure auto-selection and a
verbose-regeneration fallback. Separate from the user-facing /caveman skill.
- backpropagation — six-step bug-to-kit trace; auto-triggered by the
auto-backprop hook or manually via /ck:backprop.
- complexity-detection — five-axis scoring rubric; depth mapping
(0-6 quick / 7-13 standard / 14+ thorough).
- autonomous-loop — end-to-end loop reference (state files, sentinels,
lock protocol, debugging).
- capability-discovery — MCP + plugin + CLI detection, writes
.cavekit/capabilities.json.
- graphify-integration — optional knowledge-graph queries; degrades to grep
when graphify-out/graph.json is absent.
New commands (commands/):
- /ck:watch — live dashboard.
- /ck:resume — recover from crash, lock conflict, or interrupt.
- /ck:backprop — manual entry to the backpropagation skill.
- /ck:review-branch — two-pass (kit + code) branch review, optional Codex.
- /ck:setup-tools — run capability discovery and summarize.
New agents (agents/):
- complexity — haiku-only classifier, returns JSON score.
- verifier — goal-backward verification with stub detection.
- researcher — multi-source research brief with citations.
Templates (templates/):
- state.md, config.json, task-status.json, spec-kit.md — seed files that
commands copy into .cavekit/ or context/ on init.
Config (scripts/bp-config.sh):
- Adds session_budget, max_iterations, task_budget_{quick,standard,thorough},
auto_backprop, tool_cache(+_ttl_ms), test_filter, progress_tracker,
parallelism_max_{agents,per_repo}, model_routing, graphify_enabled.
- Extends caveman_phases validator to accept review and verify.
Plugin manifest (.claude-plugin/):
- plugin.json + marketplace.json for the marketplace discovery path.
Existing root plugin.json bumped to 2.1.0; install.sh version strings
bumped to match.
Tests (tests/):
- 34 zero-dep Node.js tests covering frontmatter round-trip, state
merging, lock acquire/heartbeat/steal/release, per-task + session
budget, task registry with dependency ordering, routing sentinels,
router tier bands, preset matrix, and deep-merged config loading.
- Runner: `node tests/run-tests.cjs`.
No changes to existing /ck:sketch, /ck:map, /ck:make, /ck:check, or any
existing skill/agent — this commit strictly adds the runtime layer beneath
them.
https://claude.ai/code/session_018edLvsv8JE9947oFiBXyHS
71 lines
2.3 KiB
JavaScript
71 lines
2.3 KiB
JavaScript
"use strict";
|
|
const assert = require("assert");
|
|
const path = require("path");
|
|
const fs = require("fs");
|
|
const { acquireLock, heartbeat, releaseLock, detectStaleLock } = require("../scripts/cavekit-tools.cjs");
|
|
const { cavekitDir, rmrf } = require("./helpers.cjs");
|
|
|
|
function acquires_lock_when_free() {
|
|
const ck = cavekitDir("lock-free");
|
|
const r = acquireLock(ck, "session:alpha");
|
|
assert.strictEqual(r.ok, true);
|
|
assert.strictEqual(r.lock.owner, "session:alpha");
|
|
rmrf(path.dirname(ck));
|
|
}
|
|
|
|
function refuses_same_lock_to_other_owner() {
|
|
const ck = cavekitDir("lock-conflict");
|
|
const a = acquireLock(ck, "session:alpha");
|
|
assert.strictEqual(a.ok, true);
|
|
const b = acquireLock(ck, "session:beta");
|
|
assert.strictEqual(b.ok, false);
|
|
assert.strictEqual(b.reason, "held");
|
|
rmrf(path.dirname(ck));
|
|
}
|
|
|
|
function heartbeat_refreshes_owner_lock() {
|
|
const ck = cavekitDir("lock-hb");
|
|
acquireLock(ck, "session:alpha");
|
|
const before = JSON.parse(fs.readFileSync(path.join(ck, ".loop.lock"), "utf8"));
|
|
// Force a later timestamp.
|
|
const sleepUntil = Date.now() + 20;
|
|
while (Date.now() < sleepUntil) { /* spin briefly */ }
|
|
const r = heartbeat(ck, "session:alpha");
|
|
assert.strictEqual(r.ok, true);
|
|
const after = JSON.parse(fs.readFileSync(path.join(ck, ".loop.lock"), "utf8"));
|
|
assert.ok(after.heartbeat_at >= before.heartbeat_at);
|
|
rmrf(path.dirname(ck));
|
|
}
|
|
|
|
function steals_stale_lock() {
|
|
const ck = cavekitDir("lock-steal");
|
|
acquireLock(ck, "session:alpha");
|
|
// Force stale.
|
|
const file = path.join(ck, ".loop.lock");
|
|
const existing = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
existing.heartbeat_at = Date.now() - 10 * 60_000;
|
|
fs.writeFileSync(file, JSON.stringify(existing));
|
|
assert.strictEqual(detectStaleLock(ck), true);
|
|
const r = heartbeat(ck, "session:beta");
|
|
assert.strictEqual(r.ok, true);
|
|
assert.strictEqual(r.lock.owner, "session:beta");
|
|
rmrf(path.dirname(ck));
|
|
}
|
|
|
|
function release_removes_file() {
|
|
const ck = cavekitDir("lock-rel");
|
|
acquireLock(ck, "session:alpha");
|
|
const r = releaseLock(ck, "session:alpha");
|
|
assert.strictEqual(r.ok, true);
|
|
assert.strictEqual(fs.existsSync(path.join(ck, ".loop.lock")), false);
|
|
rmrf(path.dirname(ck));
|
|
}
|
|
|
|
module.exports = {
|
|
acquires_lock_when_free,
|
|
refuses_same_lock_to_other_owner,
|
|
heartbeat_refreshes_owner_lock,
|
|
steals_stale_lock,
|
|
release_removes_file,
|
|
};
|