@rafinery/cli 0.2.0 → 0.2.1
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 +18 -1
- package/blueprint/.claude/commands/rafa.md +51 -2
- package/blueprint/.rafa/capabilities/leverage.md +10 -4
- package/lib/leverage.mjs +3 -1
- package/lib/migrate.mjs +7 -1
- package/lib/migrations/index.mjs +8 -0
- package/lib/releases.mjs +19 -4
- package/lib/stamp.mjs +9 -1
- package/lib/update.mjs +40 -25
- package/lib/version-check.mjs +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,9 +4,26 @@ The machine-readable version of this lives in [`lib/releases.mjs`](lib/releases.
|
|
|
4
4
|
CLI reads it to tell a client, on `rafa update`, exactly what an upgrade requires (nothing,
|
|
5
5
|
a re-scan, or a `rafa migrate`).
|
|
6
6
|
|
|
7
|
+
## 0.2.1
|
|
8
|
+
|
|
9
|
+
**Adopt with:** `npx @rafinery/cli@latest update` (or `/rafa update` in Claude Code) — no re-scan,
|
|
10
|
+
no migration. Recommended: `/rafa leverage` to tune your toolbox.
|
|
11
|
+
|
|
12
|
+
- **Intelligence where it belongs.** `leverage` and `migrate` are now `/rafa` commands: the CLI
|
|
13
|
+
*detects* (deterministic), the LLM *acts* — `/rafa leverage` edits your config exactly (merges
|
|
14
|
+
settings, wires the MCP, scaffolds a skill, on approval); `/rafa migrate` rewrites plans to a new
|
|
15
|
+
schema preserving meaning, then compile-gates them.
|
|
16
|
+
- **Two-layer update — one job each, no double work.**
|
|
17
|
+
- `rafa update` (CLI) = the **blueprint side**: pulls the latest blueprint into your repo and runs
|
|
18
|
+
the blueprint/mechanical migrations. It prints the **release notes** since your version and
|
|
19
|
+
**exactly what the brain needs** next (computed from the contract/plans schema deltas).
|
|
20
|
+
- `/rafa update` (Claude Code) = the **brain side**: runs the migrations that need intelligence
|
|
21
|
+
(re-scan a stale brain, rewrite plans to a new shape). It does **not** re-run the CLI.
|
|
22
|
+
- `/rafa` redirects the terminal-only `compile` instead of erroring on it.
|
|
23
|
+
|
|
7
24
|
## 0.2.0
|
|
8
25
|
|
|
9
|
-
**Adopt with:** `
|
|
26
|
+
**Adopt with:** `npx @rafinery/cli@latest update` — no re-scan, no migration.
|
|
10
27
|
Your brain data is still valid (contract v1, plans v1 unchanged).
|
|
11
28
|
|
|
12
29
|
- **Non-destructive vendoring.** `init`/`update` never clobber tuned files: owned files
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: rafa — init
|
|
2
|
+
description: rafa — init · scan · improve · push · leverage · migrate · update. Usage: /rafa <init|scan|improve|push|leverage|migrate|update> [--brain-only]
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
You are the **conductor** of the rafa agent loop, running in the **main session**.
|
|
@@ -56,8 +56,57 @@ high-leverage P0/P1s — don't nag; the dev triages.**
|
|
|
56
56
|
`node .rafa/bin/rafa-push.mjs` — commit `.rafa/` and push to `origin` (the brain repo) with the
|
|
57
57
|
dev's own git auth. Use after a scan, or to re-sync a brain that changed.
|
|
58
58
|
|
|
59
|
+
## `leverage` — tune the dev's toolbox (rafa's 3rd mission; intelligence + exact edits)
|
|
60
|
+
Detection is cheap and deterministic; **acting needs intelligence — that's why this is a
|
|
61
|
+
`/rafa` command, not just the `rafa leverage` CLI.** The CLI *reports*; you *fix*.
|
|
62
|
+
1. **Inspect** the committed toolbox: `.claude/settings.json`, `.mcp.json`, `.claude/skills/`,
|
|
63
|
+
`.claude/commands/`, and the stack (`package.json` / dirs). (`rafa leverage` gives the fast
|
|
64
|
+
deterministic signal; here you reason over the real files.)
|
|
65
|
+
2. **Reason** per [`.rafa/capabilities/leverage.md`](../../.rafa/capabilities/leverage.md): what's
|
|
66
|
+
missing / misconfigured / unused — permission gaps, an MCP the stack needs but isn't wired, a
|
|
67
|
+
repeatable flow worth capturing as a skill, the best-fit *existing* skill for THIS repo. P1→P3.
|
|
68
|
+
3. **Act — on the dev's approval, edit exactly.** Merge the precise permission entries, write the
|
|
69
|
+
correct `.mcp.json` server block, scaffold the skill. **Merge, never clobber** the dev's config;
|
|
70
|
+
show the diff. Propose, don't force. Never open `.env*` or read a secret value (scan's guardrail).
|
|
71
|
+
|
|
72
|
+
## `migrate` — bring structured files to the current schema (the intelligent half)
|
|
73
|
+
Two channels. **Mechanical** changes (field rename, path move) run deterministically via the
|
|
74
|
+
terminal `rafa migrate`. **Semantic** changes — a plan's *shape* changed and each file must be
|
|
75
|
+
understood to be rewritten correctly — need intelligence, and that's this command.
|
|
76
|
+
1. Read the target schema in [`.rafa/contract.md`](../../.rafa/contract.md) (§6/§7 for plans) and
|
|
77
|
+
the `from` versions in `rafa.json`.
|
|
78
|
+
2. For each affected file under `.rafa/plans/` (and any other structured dir), **rewrite it to
|
|
79
|
+
conform** — preserve meaning, remap fields, fill new required fields sensibly.
|
|
80
|
+
3. Run `node .rafa/bin/rafa-compile.mjs` until it **exits 0** (validate-and-correct loop), surface
|
|
81
|
+
the diff for approval, then offer `rafa push` if this is a connected repo.
|
|
82
|
+
|
|
83
|
+
## `update` — the BRAIN-side of an upgrade (the CLI already did the blueprint side)
|
|
84
|
+
**Two layers, two actors — don't re-run the CLI here.**
|
|
85
|
+
- The terminal **`npx @rafinery/cli@latest update`** already pulled the latest blueprint and ran
|
|
86
|
+
the **blueprint-side** (mechanical/structural) migrations. It also *reported* what the brain needs.
|
|
87
|
+
- **`/rafa update` (this command)** does the **brain-side** migrations — the part that needs
|
|
88
|
+
intelligence — against the blueprint the CLI just installed. It does **not** pull the blueprint
|
|
89
|
+
or re-run the CLI.
|
|
90
|
+
1. **Find the gap.** The brain/plans conform to a *data* version; the blueprint just moved to a
|
|
91
|
+
newer one. Compare `.rafa/manifest.json`'s `schemaVersion` (and `rafa.json`'s `contract`/`plans`)
|
|
92
|
+
to the current `.rafa/contract.md`. That gap is exactly what the CLI update flagged. (If the CLI
|
|
93
|
+
update hasn't run yet, tell the dev to run `npx @rafinery/cli@latest update` first, then return.)
|
|
94
|
+
2. **Migrate the brain side:**
|
|
95
|
+
- **Contract bumped → the brain is a stale cache:** re-scan (`## scan`) to regenerate it, or for
|
|
96
|
+
a targeted change, intelligently rewrite the affected notes to the new schema and re-validate.
|
|
97
|
+
- **Plans shape changed:** rewrite each plan per `## migrate` (preserve meaning), compile to exit 0.
|
|
98
|
+
3. **Record + summarize.** Run `node .rafa/bin/rafa-compile.mjs` to exit 0, then advance the data
|
|
99
|
+
version in `rafa.json` (`contract`/`plans` → the versions you migrated to). Summarize what changed;
|
|
100
|
+
on the dev's approval, `rafa push` if this is a connected repo.
|
|
101
|
+
Never hand-edit around a migration or discard tuned files.
|
|
102
|
+
|
|
59
103
|
## (no argument, or unrecognized)
|
|
60
|
-
|
|
104
|
+
`/rafa` runs the in-editor passes: `init` · `scan [--brain-only]` · `improve` · `push` ·
|
|
105
|
+
`leverage` · `migrate` · `update`.
|
|
106
|
+
|
|
107
|
+
`compile` is a **terminal** command on the `rafa` CLI (`@rafinery/cli`) — not a slash command.
|
|
108
|
+
If the dev typed it, don't error out; point them to the shell (`rafa compile`) and stop. For
|
|
109
|
+
anything else unrecognized, print this usage line and stop.
|
|
61
110
|
|
|
62
111
|
---
|
|
63
112
|
Token discipline: glob/grep/AST before reading; scoped reads; deterministic extraction
|
|
@@ -6,8 +6,13 @@ config layer (permissions, skills, MCP servers, hooks, commands) that decides ho
|
|
|
6
6
|
the AI engineer actually is. It surfaces what's missing, misconfigured, or unused, and points
|
|
7
7
|
at the fix. This is platform DNA: **leverage what's present; never reinvent it.**
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
ranks, and reports
|
|
9
|
+
**Two layers — detection is deterministic, action needs intelligence:**
|
|
10
|
+
- **`rafa leverage`** (CLI) — the cheap **detector**. Reads the repo, ranks, and *reports*; it
|
|
11
|
+
**changes nothing**. Good for a notify/CI signal, and as structured input to the LLM.
|
|
12
|
+
- **`/rafa leverage`** (in Claude Code) — the **actor**. rafa reasons over the real config and,
|
|
13
|
+
on the dev's approval, **applies the fix exactly** — merges the precise permission entries,
|
|
14
|
+
writes the correct `.mcp.json` block, scaffolds the skill. The CLI can't do this; only the
|
|
15
|
+
intelligence can edit correctly. The detector finds the gap; the LLM closes it.
|
|
11
16
|
|
|
12
17
|
---
|
|
13
18
|
|
|
@@ -21,8 +26,9 @@ ranks, and reports; it **changes nothing**.
|
|
|
21
26
|
advisor. Few, relevant, dismissible.
|
|
22
27
|
3. **Leverage-ranked** — `P1` security/correctness → `P2` friction/leverage → `P3` nice-to-have.
|
|
23
28
|
Lead with impact × ease. Never dump an unranked wall.
|
|
24
|
-
4. **
|
|
25
|
-
|
|
29
|
+
4. **Approval-gated, never authoritarian** — the dev owns their setup. `rafa leverage` (CLI) only
|
|
30
|
+
proposes. `/rafa leverage` *applies*, but **only on the dev's approval and always as a merge** —
|
|
31
|
+
show the diff, never clobber, propose don't force.
|
|
26
32
|
5. **Tool-agnostic by construction** — the advice engine knows nothing about any specific tool;
|
|
27
33
|
all tool knowledge lives in an **adapter**. This is what makes "leverage what's available"
|
|
28
34
|
true for Codex, Cursor, or any other toolchain, not just Claude Code.
|
package/lib/leverage.mjs
CHANGED
|
@@ -31,6 +31,8 @@ export default async function leverage() {
|
|
|
31
31
|
console.log("");
|
|
32
32
|
}
|
|
33
33
|
console.log(
|
|
34
|
-
`${tips.length} suggestion(s), most impactful first.
|
|
34
|
+
`${tips.length} suggestion(s), most impactful first. This is the detector — it changed nothing.\n` +
|
|
35
|
+
"To have rafa apply these exactly (merge settings, wire the MCP, scaffold a skill), run\n" +
|
|
36
|
+
" /rafa leverage in Claude Code — it edits with your approval.",
|
|
35
37
|
);
|
|
36
38
|
}
|
package/lib/migrate.mjs
CHANGED
|
@@ -14,9 +14,15 @@ export default async function migrate() {
|
|
|
14
14
|
|
|
15
15
|
if (pending.length === 0) {
|
|
16
16
|
console.log(
|
|
17
|
-
`✓ Nothing to migrate — plans v${from.plans}, contract v${from.contract} ` +
|
|
17
|
+
`✓ Nothing mechanical to migrate — plans v${from.plans}, contract v${from.contract} ` +
|
|
18
18
|
`already match ${CURRENT.version}.`,
|
|
19
19
|
);
|
|
20
|
+
if (CURRENT.plans > from.plans || CURRENT.contract > from.contract) {
|
|
21
|
+
console.log(
|
|
22
|
+
" ↳ but the schema version advanced. If a plan/brain SHAPE changed and files need\n" +
|
|
23
|
+
" rewriting, that's a semantic migration — run /rafa migrate in Claude Code.",
|
|
24
|
+
);
|
|
25
|
+
}
|
|
20
26
|
// Still advance the stamp so the recorded CLI version reflects reality.
|
|
21
27
|
stampCurrent(cwd);
|
|
22
28
|
return;
|
package/lib/migrations/index.mjs
CHANGED
|
@@ -6,10 +6,18 @@
|
|
|
6
6
|
// Contract:
|
|
7
7
|
// id string stable identifier, shown in the log
|
|
8
8
|
// kind "plans" | "contract" | "config"
|
|
9
|
+
// mode "mechanical" | "assisted" default "mechanical"
|
|
9
10
|
// summary string one line: what it changes + why
|
|
10
11
|
// appliesTo(from, to) => boolean from = stampedVersions(cwd), to = CURRENT
|
|
11
12
|
// run(cwd) => { changed: string[], notes: string[] } idempotent; report what it touched
|
|
12
13
|
//
|
|
14
|
+
// mode distinguishes the two migration channels:
|
|
15
|
+
// "mechanical" — a deterministic transform (field rename, path move, add-default). `run` does
|
|
16
|
+
// it here in the CLI, no LLM needed.
|
|
17
|
+
// "assisted" — the shape changed and each file must be UNDERSTOOD to be rewritten. The CLI
|
|
18
|
+
// can't do this correctly; `run` is a no-op that flags it, and `/rafa migrate`
|
|
19
|
+
// (the LLM) rewrites the files against the contract. Intelligence lives there.
|
|
20
|
+
//
|
|
13
21
|
// Empty by design until a release actually changes a shape — the SEAM is what matters,
|
|
14
22
|
// so the first breaking change ships its migration here and `rafa migrate` just works.
|
|
15
23
|
// Example (kept as a reference, not registered):
|
package/lib/releases.mjs
CHANGED
|
@@ -3,11 +3,14 @@
|
|
|
3
3
|
// package.json). It declares the contract + plans schema versions that release ships and
|
|
4
4
|
// the strongest action a client must take to adopt it:
|
|
5
5
|
//
|
|
6
|
-
// requires:
|
|
7
|
-
//
|
|
8
|
-
//
|
|
6
|
+
// requires: "update" | "migrate" | "rescan" the strongest structural action
|
|
7
|
+
// ("update" → just re-sync files; "migrate"/"rescan" are also computed
|
|
8
|
+
// deterministically from the contract/plans version deltas below).
|
|
9
|
+
// recommends: string[] soft follow-ups this release suggests (e.g. ["leverage"]) — shown
|
|
10
|
+
// by `update` as recommended, not required.
|
|
9
11
|
//
|
|
10
|
-
// `update` reads this to tell the client — transparently — what an upgrade means for them
|
|
12
|
+
// `update` reads this to tell the client — transparently — what an upgrade means for them:
|
|
13
|
+
// the release notes since their version, and exactly which actions the upgrade needs.
|
|
11
14
|
|
|
12
15
|
import { readFileSync } from "node:fs";
|
|
13
16
|
import { dirname, join } from "node:path";
|
|
@@ -31,6 +34,18 @@ export const RELEASES = [
|
|
|
31
34
|
"Non-destructive vendoring (merge settings, never clobber tuned agents); secrets " +
|
|
32
35
|
"guardrail in scan; `rafa leverage` toolbox advisor; version-aware updates + `rafa migrate`.",
|
|
33
36
|
},
|
|
37
|
+
{
|
|
38
|
+
version: "0.2.1",
|
|
39
|
+
contract: 1,
|
|
40
|
+
plans: 1,
|
|
41
|
+
requires: "update",
|
|
42
|
+
recommends: ["leverage"],
|
|
43
|
+
summary:
|
|
44
|
+
"leverage & migrate are now intelligent `/rafa` commands — the LLM applies toolbox fixes " +
|
|
45
|
+
"and rewrites plans exactly. Two-layer update: `rafa update` (CLI) does the blueprint side + " +
|
|
46
|
+
"blueprint migrations and reports what the brain needs; new `/rafa update` does the brain-side " +
|
|
47
|
+
"migrations (re-scan / rewrite). `rafa update` now prints release notes + required actions.",
|
|
48
|
+
},
|
|
34
49
|
];
|
|
35
50
|
|
|
36
51
|
// The release this CLI build ships (last entry).
|
package/lib/stamp.mjs
CHANGED
|
@@ -46,10 +46,18 @@ export function stampedVersions(cwd) {
|
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
// Re-stamp to the versions this CLI build ships (called after init
|
|
49
|
+
// Re-stamp to the versions this CLI build ships (called after init, or after update when NO
|
|
50
|
+
// brain-side migration remains — i.e. blueprint AND data are current together).
|
|
50
51
|
export function stampCurrent(cwd, extra = {}) {
|
|
51
52
|
return writeStamp(cwd, {
|
|
52
53
|
...extra,
|
|
53
54
|
rafa: { cli: CLI_VERSION, contract: CURRENT.contract, plans: CURRENT.plans },
|
|
54
55
|
});
|
|
55
56
|
}
|
|
57
|
+
|
|
58
|
+
// Advance ONLY the blueprint/CLI version — the data (brain/plans) version is left where it is,
|
|
59
|
+
// so `/rafa update` can still see the gap and run the brain-side migrations. Use after
|
|
60
|
+
// `rafa update` when brain-side work remains.
|
|
61
|
+
export function stampCli(cwd) {
|
|
62
|
+
return writeStamp(cwd, { rafa: { cli: CLI_VERSION } });
|
|
63
|
+
}
|
package/lib/update.mjs
CHANGED
|
@@ -9,8 +9,8 @@ import { existsSync } from "node:fs";
|
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
import { blueprintSource, copyBlueprint } from "./blueprint.mjs";
|
|
11
11
|
import { reportBlueprint, reportSettings } from "./init.mjs";
|
|
12
|
-
import { CURRENT, releasesAfter } from "./releases.mjs";
|
|
13
|
-
import { stampedVersions, stampCurrent } from "./stamp.mjs";
|
|
12
|
+
import { CURRENT, CLI_VERSION, releasesAfter } from "./releases.mjs";
|
|
13
|
+
import { stampedVersions, stampCurrent, stampCli } from "./stamp.mjs";
|
|
14
14
|
import { pendingMigrations } from "./migrations/index.mjs";
|
|
15
15
|
import { notifyIfBehind } from "./version-check.mjs";
|
|
16
16
|
|
|
@@ -30,39 +30,54 @@ export default async function update() {
|
|
|
30
30
|
reportBlueprint(copyBlueprint(blueprintSource(), TARGET));
|
|
31
31
|
reportSettings(TARGET);
|
|
32
32
|
|
|
33
|
-
//
|
|
33
|
+
// ── Blueprint-side migrations: mechanical/deterministic, run here in the CLI. ──
|
|
34
|
+
// (Anything needing intelligence is left for the brain-side pass — /rafa update.)
|
|
35
|
+
const pending = pendingMigrations(from, CURRENT);
|
|
36
|
+
const mechanical = pending.filter((m) => (m.mode ?? "mechanical") !== "assisted");
|
|
37
|
+
for (const m of mechanical) {
|
|
38
|
+
try {
|
|
39
|
+
const r = m.run(TARGET);
|
|
40
|
+
console.log(` ✓ migrated ${m.id}: ${r.changed.length} file(s)`);
|
|
41
|
+
} catch (e) {
|
|
42
|
+
console.log(` ! migration ${m.id} failed: ${e instanceof Error ? e.message : e}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ── Release notes: what changed since the client's version. ──
|
|
34
47
|
const news = releasesAfter(from.cli);
|
|
35
48
|
if (news.length) {
|
|
36
|
-
console.log(`\n
|
|
37
|
-
for (const r of news) console.log(`
|
|
49
|
+
console.log(`\n Release notes (${from.cli} → ${CLI_VERSION}):`);
|
|
50
|
+
for (const r of news) console.log(` ${r.version} — ${r.summary}`);
|
|
38
51
|
}
|
|
39
52
|
|
|
40
|
-
//
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
` ⚠ Brain contract v${from.contract} → v${CURRENT.contract}: your brain is now stale.\n` +
|
|
48
|
-
" Run /rafa scan to regenerate it, then rafa push.",
|
|
53
|
+
// ── Brain-side follow-ups: the CLI can't do these — they need intelligence. Route to /rafa update. ──
|
|
54
|
+
// The gap is measured against the DATA version (from.contract/plans), which only advances when the
|
|
55
|
+
// brain/plans are actually regenerated — not by this blueprint sync.
|
|
56
|
+
const brain = [];
|
|
57
|
+
if (CURRENT.contract > from.contract) {
|
|
58
|
+
brain.push(
|
|
59
|
+
`Re-scan the brain (contract v${from.contract} → v${CURRENT.contract}; the brain is a stale cache)`,
|
|
49
60
|
);
|
|
50
61
|
}
|
|
51
|
-
if (
|
|
52
|
-
|
|
53
|
-
` ⚠ Plan/structure schema changed (plans v${from.plans} → v${CURRENT.plans}).\n` +
|
|
54
|
-
" Run rafa migrate to bring your files forward.",
|
|
55
|
-
);
|
|
62
|
+
if (CURRENT.plans > from.plans || pending.some((m) => m.mode === "assisted")) {
|
|
63
|
+
brain.push(`Rewrite plans to the new shape (plans v${from.plans} → v${CURRENT.plans})`);
|
|
56
64
|
}
|
|
57
|
-
|
|
58
|
-
|
|
65
|
+
|
|
66
|
+
console.log("");
|
|
67
|
+
if (brain.length) {
|
|
68
|
+
console.log(" Brain-side follow-ups — run /rafa update in Claude Code (needs intelligence):");
|
|
69
|
+
for (const b of brain) console.log(` • ${b}`);
|
|
70
|
+
// Blueprint is current, but the data still lags — keep the gap visible for /rafa update.
|
|
71
|
+
stampCli(TARGET);
|
|
72
|
+
} else {
|
|
73
|
+
console.log(" ✓ No brain migration needed — your brain data is still valid.");
|
|
74
|
+
stampCurrent(TARGET);
|
|
59
75
|
}
|
|
60
76
|
|
|
61
|
-
|
|
62
|
-
|
|
77
|
+
const recs = new Set(news.flatMap((r) => r.recommends ?? []));
|
|
78
|
+
if (recs.has("leverage")) console.log(" ↳ recommended: /rafa leverage (tune your toolbox).");
|
|
63
79
|
|
|
64
80
|
console.log(
|
|
65
|
-
"\n
|
|
66
|
-
"Review any `.new` files, then restart Claude Code to reload the agents.",
|
|
81
|
+
"\n Owned files you tuned were preserved (review any `.new`). Restart Claude Code to reload the agents.",
|
|
67
82
|
);
|
|
68
83
|
}
|
package/lib/version-check.mjs
CHANGED
|
@@ -27,7 +27,7 @@ export async function notifyIfBehind(installed = CLI_VERSION) {
|
|
|
27
27
|
if (!latest || cmpSemver(latest, installed) <= 0) return latest;
|
|
28
28
|
console.log(
|
|
29
29
|
`\n ⬆ rafinery ${latest} is available (you're on ${installed}).\n` +
|
|
30
|
-
` Update:
|
|
30
|
+
` Update: npx @rafinery/cli@latest update (fetches the latest CLI + vendors it into your repo)\n`,
|
|
31
31
|
);
|
|
32
32
|
return latest;
|
|
33
33
|
}
|
package/package.json
CHANGED