@rafinery/cli 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +79 -0
- package/README.md +17 -7
- package/bin/rafa.mjs +68 -20
- package/blueprint/.claude/agents/atlas.md +28 -20
- package/blueprint/.claude/agents/bloom.md +15 -8
- package/blueprint/.claude/agents/compass.md +46 -0
- package/blueprint/.claude/agents/prism.md +42 -25
- package/blueprint/.claude/commands/rafa.md +151 -26
- package/blueprint/{.rafa → .claude/rafa}/contract.md +108 -22
- package/blueprint/.claude/skills/rafa-build/SKILL.md +76 -0
- package/blueprint/.claude/skills/rafa-distill/SKILL.md +87 -0
- package/blueprint/{.rafa/capabilities/improve.md → .claude/skills/rafa-improve/SKILL.md} +10 -5
- package/blueprint/.claude/skills/rafa-insights/SKILL.md +71 -0
- package/blueprint/{.rafa/capabilities/leverage.md → .claude/skills/rafa-leverage/SKILL.md} +9 -4
- package/blueprint/{.rafa/capabilities/plan.md → .claude/skills/rafa-plan/SKILL.md} +30 -3
- package/blueprint/{.rafa/capabilities/scan.md → .claude/skills/rafa-scan/SKILL.md} +14 -9
- package/blueprint/{.rafa/capabilities/validate.md → .claude/skills/rafa-validate/SKILL.md} +14 -5
- package/lib/blueprint.mjs +22 -12
- package/lib/brain-repo.mjs +100 -0
- package/lib/checkpoint.mjs +138 -0
- package/lib/ci-setup.mjs +109 -0
- package/lib/claude-config.mjs +5 -5
- package/lib/compile.mjs +6 -21
- package/lib/distill.mjs +237 -0
- package/lib/fold.mjs +53 -0
- package/lib/gate/compile.mjs +549 -0
- package/lib/gate/verify-citations.mjs +156 -0
- package/lib/hydrate.mjs +96 -0
- package/lib/init.mjs +15 -30
- package/lib/leverage/adapters/claude-code.mjs +5 -4
- package/lib/mcp-client.mjs +97 -0
- package/lib/mcp-config.mjs +1 -1
- package/lib/migrations/index.mjs +39 -3
- package/lib/pull.mjs +129 -0
- package/lib/push.mjs +93 -14
- package/lib/releases.mjs +23 -0
- package/lib/verify-citations.mjs +9 -0
- package/lib/working-set.mjs +113 -0
- package/package.json +2 -2
- package/blueprint/.rafa/bin/rafa-compile.mjs +0 -478
- package/blueprint/.rafa/bin/rafa-push.mjs +0 -75
- package/blueprint/.rafa/bin/verify-citations.mjs +0 -153
- package/blueprint/.rafa/capabilities/build.md +0 -39
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Deterministic citation checker for the atlas scan output. Three gates:
|
|
4
|
-
*
|
|
5
|
-
* RESOLUTION (B1) — every cite `- <file>:<line>[-<end>] :: <token>` points at a line
|
|
6
|
-
* that actually contains <token>. Catches off-by-N / wrong-file.
|
|
7
|
-
* COMPLETENESS (B2) — a note may declare `anchor: <token>`; the checker greps it repo-wide
|
|
8
|
-
* (code only, docs excluded) and asserts EVERY hit is a cited site.
|
|
9
|
-
* Catches the "3-place vs 5-place" omission bug.
|
|
10
|
-
* POLICY — every `type: contract` MUST declare `anchor:` — a greppable token,
|
|
11
|
-
* or `anchor: none` for composition/ordering contracts that don't grep
|
|
12
|
-
* as one token. So completeness can't be silently skipped (closes F1:
|
|
13
|
-
* the guarantee is mandatory, not opt-in; exemptions are explicit).
|
|
14
|
-
*
|
|
15
|
-
* Writes a generated report to .rafa/brain/citation-check.md (don't hand-paste it).
|
|
16
|
-
* Exits 1 on any failure. Run from repo root: node .rafa/bin/verify-citations.mjs
|
|
17
|
-
*/
|
|
18
|
-
import { readFileSync, writeFileSync, readdirSync, statSync, existsSync, mkdtempSync, rmSync } from "node:fs";
|
|
19
|
-
import { execSync } from "node:child_process";
|
|
20
|
-
import { join } from "node:path";
|
|
21
|
-
import { tmpdir } from "node:os";
|
|
22
|
-
|
|
23
|
-
const arg = (k, d) => { const m = process.argv.find((a) => a.startsWith(`--${k}=`)); return m ? m.slice(k.length + 3) : d; };
|
|
24
|
-
const ROOT = arg("root", ".rafa/brain"); // --root=.rafa/improve for the improve ledger
|
|
25
|
-
const NOTE_DIRS = arg("dirs", "rules,playbooks").split(",").filter(Boolean); // --dirs=improvements
|
|
26
|
-
const CITE = /^\s*-\s+(.+?):(\d+)(?:-(\d+))?\s*::\s*(.+?)\s*$/; // - file:start[-end] :: token
|
|
27
|
-
const ANCHOR = /^anchor:\s*(.+?)\s*$/;
|
|
28
|
-
const TYPE = /^type:\s*(.+?)\s*$/;
|
|
29
|
-
|
|
30
|
-
function walk(dir) {
|
|
31
|
-
if (!existsSync(dir)) return [];
|
|
32
|
-
return readdirSync(dir).flatMap((e) => {
|
|
33
|
-
const p = join(dir, e);
|
|
34
|
-
return statSync(p).isDirectory() ? walk(p) : (e.endsWith(".md") && !e.startsWith("_")) ? [p] : []; // ignore _*.md scratch
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function gitGrep(token) {
|
|
39
|
-
try {
|
|
40
|
-
const out = execSync(`git grep -nF -e ${JSON.stringify(token)}`, { encoding: "utf8" });
|
|
41
|
-
return out.split("\n").filter(Boolean).map((l) => {
|
|
42
|
-
const m = l.match(/^(.+?):(\d+):/);
|
|
43
|
-
return m ? { file: m[1], line: +m[2] } : null;
|
|
44
|
-
}).filter(Boolean);
|
|
45
|
-
} catch (e) {
|
|
46
|
-
if (e.status === 1) return []; // git grep: no matches
|
|
47
|
-
throw e;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function citeResolves(file, start, end, token) {
|
|
52
|
-
if (!existsSync(file)) return { ok: false, reason: "file missing" };
|
|
53
|
-
const src = readFileSync(file, "utf8").split("\n");
|
|
54
|
-
if (start < 1 || end > src.length) return { ok: false, reason: `lines ${start}-${end} out of range (${src.length})` };
|
|
55
|
-
if (src.slice(start - 1, end).some((l) => l.includes(token))) return { ok: true, reason: "" };
|
|
56
|
-
return { ok: false, reason: `token not on ${start}${end !== start ? "-" + end : ""}` };
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// --selftest: prove the resolution logic fails on a bad cite and passes a good one, on a
|
|
60
|
-
// throwaway temp file (no brain/repo pollution). prism runs this as its mutation probe —
|
|
61
|
-
// re-running the checker is not proof it still works; this is.
|
|
62
|
-
if (process.argv.includes("--selftest")) {
|
|
63
|
-
const dir = mkdtempSync(join(tmpdir(), "vc-selftest-"));
|
|
64
|
-
const f = join(dir, "sample.txt");
|
|
65
|
-
writeFileSync(f, "alpha\nTOKEN beta\ngamma\n");
|
|
66
|
-
const good = citeResolves(f, 2, 2, "TOKEN").ok; // expect true
|
|
67
|
-
const bad = citeResolves(f, 1, 1, "TOKEN").ok; // expect false (off-by-one)
|
|
68
|
-
rmSync(dir, { recursive: true, force: true });
|
|
69
|
-
const sane = good === true && bad === false;
|
|
70
|
-
console.log(`checker self-test: ${sane ? "PASS" : "FAIL"} — good cite=${good} (expect true), bad cite=${bad} (expect false)`);
|
|
71
|
-
process.exit(sane ? 0 : 1);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const notes = NOTE_DIRS.flatMap((d) => walk(join(ROOT, d)));
|
|
75
|
-
if (notes.length === 0) {
|
|
76
|
-
console.log(`No notes under ${ROOT}/{${NOTE_DIRS.join(",")}}/ — run a scan first.`);
|
|
77
|
-
process.exit(0);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const resolution = []; // { note, loc, token, ok, reason }
|
|
81
|
-
const completeness = []; // { note, anchor, site, ok, reason }
|
|
82
|
-
const policy = []; // { note, ok, reason }
|
|
83
|
-
|
|
84
|
-
for (const note of notes) {
|
|
85
|
-
const rel = note.replace(ROOT + "/", "");
|
|
86
|
-
const lines = readFileSync(note, "utf8").split("\n");
|
|
87
|
-
const cites = [];
|
|
88
|
-
const anchors = [];
|
|
89
|
-
let noteType = "";
|
|
90
|
-
let fence = 0;
|
|
91
|
-
for (const line of lines) {
|
|
92
|
-
if (line.trim() === "---") { fence++; if (fence >= 2) break; continue; }
|
|
93
|
-
if (fence !== 1) continue;
|
|
94
|
-
const c = line.match(CITE);
|
|
95
|
-
if (c) { cites.push({ file: c[1], start: +c[2], end: c[3] ? +c[3] : +c[2], token: c[4] }); continue; }
|
|
96
|
-
const a = line.match(ANCHOR);
|
|
97
|
-
if (a) { anchors.push(a[1].replace(/\s+#.*$/, "").replace(/^["']|["']$/g, "").trim()); continue; }
|
|
98
|
-
const t = line.match(TYPE);
|
|
99
|
-
if (t) noteType = t[1].replace(/\s+#.*$/, "").trim();
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// POLICY — every contract must declare an anchor (token or explicit `none`)
|
|
103
|
-
if (noteType === "contract") {
|
|
104
|
-
const ok = anchors.length > 0;
|
|
105
|
-
policy.push({ note: rel, ok, reason: ok ? "" : "type: contract must declare `anchor: <token>` (or `anchor: none` for composition/ordering contracts)" });
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// RESOLUTION
|
|
109
|
-
for (const ct of cites) {
|
|
110
|
-
const { ok, reason } = citeResolves(ct.file, ct.start, ct.end, ct.token);
|
|
111
|
-
resolution.push({ note: rel, loc: `${ct.file}:${ct.start}${ct.end !== ct.start ? "-" + ct.end : ""}`, token: ct.token, ok, reason });
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// COMPLETENESS
|
|
115
|
-
for (const anchor of anchors) {
|
|
116
|
-
if (anchor.toLowerCase() === "none") continue; // explicit, visible exemption
|
|
117
|
-
const hits = gitGrep(anchor).filter((h) => !h.file.endsWith(".md")); // docs excluded
|
|
118
|
-
if (hits.length === 0) {
|
|
119
|
-
completeness.push({ note: rel, anchor, site: "(none)", ok: false, reason: "anchor found nowhere in code — wrong token?" });
|
|
120
|
-
continue;
|
|
121
|
-
}
|
|
122
|
-
for (const h of hits) {
|
|
123
|
-
const covered = cites.some((ct) => ct.file === h.file && h.line >= ct.start && h.line <= ct.end);
|
|
124
|
-
completeness.push({ note: rel, anchor, site: `${h.file}:${h.line}`, ok: covered, reason: covered ? "" : "grep hit not cited — site omitted" });
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const rFail = resolution.filter((r) => !r.ok);
|
|
130
|
-
const cFail = completeness.filter((r) => !r.ok);
|
|
131
|
-
const pFail = policy.filter((r) => !r.ok);
|
|
132
|
-
const fail = rFail.length + cFail.length + pFail.length;
|
|
133
|
-
|
|
134
|
-
const md = [
|
|
135
|
-
"# Citation check (generated — do not hand-edit)",
|
|
136
|
-
"",
|
|
137
|
-
`## Resolution (B1): ${resolution.length - rFail.length}/${resolution.length} ✓`,
|
|
138
|
-
...resolution.map((r) => `${r.ok ? "✓" : "✗"} ${r.loc} :: ${r.token}${r.ok ? "" : " — " + r.reason + " [" + r.note + "]"}`),
|
|
139
|
-
"",
|
|
140
|
-
`## Completeness (B2): ${completeness.length - cFail.length}/${completeness.length} ✓ (${new Set(completeness.map((c) => c.anchor)).size} anchors)`,
|
|
141
|
-
...completeness.map((r) => `${r.ok ? "✓" : "✗"} anchor '${r.anchor}' → ${r.site}${r.ok ? "" : " — " + r.reason + " [" + r.note + "]"}`),
|
|
142
|
-
"",
|
|
143
|
-
`## Policy (contract → anchor declared): ${policy.length - pFail.length}/${policy.length} ✓`,
|
|
144
|
-
...policy.map((r) => `${r.ok ? "✓" : "✗"} ${r.note}${r.ok ? "" : " — " + r.reason}`),
|
|
145
|
-
"",
|
|
146
|
-
fail ? `**${fail} FAILED.**` : "**All pass.**",
|
|
147
|
-
"",
|
|
148
|
-
].join("\n");
|
|
149
|
-
writeFileSync(join(ROOT, "citation-check.md"), md);
|
|
150
|
-
|
|
151
|
-
console.log(md);
|
|
152
|
-
console.log(`resolution ${resolution.length - rFail.length}/${resolution.length} · completeness ${completeness.length - cFail.length}/${completeness.length} · policy ${policy.length - pFail.length}/${policy.length}` + (fail ? ` · ${fail} FAILED` : " · all pass"));
|
|
153
|
-
process.exit(fail ? 1 : 0);
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
# build — execute the plan, trio-choreographed, compounding (capability #4)
|
|
2
|
-
|
|
3
|
-
> Status: **active.** The work-time loop where recall + validation + improvement all
|
|
4
|
-
> fire — the mission payoff. Depends on: plan (#3), brain (#1), ledger (#2).
|
|
5
|
-
> Invoke via `/rafa build`.
|
|
6
|
-
|
|
7
|
-
Execute the approved plan with all three agents in the loop, knowledge served by the
|
|
8
|
-
platform MCP (one read path — the same surface any third-party agent uses).
|
|
9
|
-
|
|
10
|
-
## The trio at build time
|
|
11
|
-
|
|
12
|
-
| Role | Agent | Job per task |
|
|
13
|
-
|---|---|---|
|
|
14
|
-
| **Executor** | atlas | RECALL the task's brain slice via MCP (`search_knowledge` + `get_rule`/`get_playbook`; honor non-exemplars) → implement, convention-adherent |
|
|
15
|
-
| **Validator** | prism | validate the execution against the child's `## Done-check` — strict, unbiased, against code + brain, never against atlas's claims. **`status: done` only on prism PASS**; FAIL → atlas corrects (validate-and-correct at work time). This gate is conductor-flow SOP — deterministic enforcement is the deferred capture-engine's job |
|
|
16
|
-
| **Improver** | bloom | **push**: new improvement opportunities spotted during execution → new ledger files. **close**: improvements fixed in passing → `status: fixed`. **nudge**: top-leverage open item in the task's blast radius — opt-in, never blocking |
|
|
17
|
-
|
|
18
|
-
## Procedure
|
|
19
|
-
|
|
20
|
-
1. **Resume** — `get_active_plan` (platform) or local `active.md`; staleness check
|
|
21
|
-
(envelope `brainForSha` vs local stamp → prompt `rafa push` if behind).
|
|
22
|
-
2. **Per task:** atlas recalls → implements → prism validates vs `## Done-check` →
|
|
23
|
-
bloom sweeps (push new / close fixed / nudge) → update the child file's `status`
|
|
24
|
-
→ periodic `rafa compile` + `rafa push` so the platform (and every MCP consumer)
|
|
25
|
-
reflects live progress.
|
|
26
|
-
3. **Brain changes mid-build** — execution may invalidate rules or teach new ones.
|
|
27
|
-
v1: run a full `/rafa scan` (regenerate → prism → compile → push); the whole
|
|
28
|
-
brain re-stamps at the new sha, so `brain = f(code@sha)` stays exact. NEVER
|
|
29
|
-
hand-edit brain files around the gate.
|
|
30
|
-
4. **Verify** (prism-style) before declaring the plan done; final compile + push.
|
|
31
|
-
|
|
32
|
-
## Deferred / open
|
|
33
|
-
- **The capture engine** — `Stop`/`PostToolUse` hooks making the gates + capture
|
|
34
|
-
automatic rather than conductor-driven SOP (deterministic-enforcement lesson).
|
|
35
|
-
- **Incremental re-scan** — cite-graph invalidation (diff → invalidate notes citing
|
|
36
|
-
changed files → re-verify/regenerate only those). Needs: partial-brain cache-key
|
|
37
|
-
semantics + the seam-neighbor scope rule. Designed (see
|
|
38
|
-
.fable/sessions/2026-07-07-brain-versioning-and-incremental.md); first post-loop item.
|
|
39
|
-
- Show-thinking + pivot protocol from the atlas character.
|