@rafinery/cli 0.8.4 → 0.8.5
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/blueprint/.claude/rafa/hooks/brain-commit.mjs +61 -1
- package/blueprint/.claude/rafa/hooks/post-checkout +0 -0
- package/blueprint/.claude/rafa/hooks/post-commit +0 -0
- package/blueprint/.claude/rafa/hooks/post-rewrite +0 -0
- package/blueprint/.claude/skills/rafa-scan/SKILL.md +10 -3
- package/lib/checkpoint.mjs +2 -2
- package/lib/distiller/doctrine.mjs +32 -0
- package/lib/distiller.mjs +22 -1
- package/lib/gather.mjs +64 -0
- package/lib/push.mjs +17 -0
- package/lib/releases.mjs +14 -0
- package/lib/working-set.mjs +10 -0
- package/package.json +10 -9
- package/LICENSE +0 -21
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
// non-blocking always — a brain problem must never block a code commit.
|
|
8
8
|
|
|
9
9
|
import { execSync } from "node:child_process";
|
|
10
|
-
import {
|
|
10
|
+
import { createHash } from "node:crypto";
|
|
11
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
11
12
|
import { join } from "node:path";
|
|
12
13
|
|
|
13
14
|
const ROOT = process.cwd();
|
|
@@ -21,6 +22,31 @@ try {
|
|
|
21
22
|
const rafa = join(ROOT, ".rafa");
|
|
22
23
|
const shR = (cmd) => sh(cmd, rafa);
|
|
23
24
|
|
|
25
|
+
// DISPOSAL (MIRRORS working-set.isDisposableHydration — the ONE rule, both planes):
|
|
26
|
+
// an UNEDITED hydration must never enter a brain commit, or we re-push org content as a
|
|
27
|
+
// branch delta → stale-override at reconcile. After staging, unstage every unedited
|
|
28
|
+
// hydration (it stays in the working tree as disposable cache) so only real deltas commit.
|
|
29
|
+
const disposeHydrations = (br) => {
|
|
30
|
+
try {
|
|
31
|
+
const sc = JSON.parse(readFileSync(join(rafa, "hydration.json"), "utf8"));
|
|
32
|
+
const synced = (sc.sync ?? {})[br] ?? {};
|
|
33
|
+
for (const [p, rec] of Object.entries(sc.files ?? {})) {
|
|
34
|
+
const abs = join(rafa, p);
|
|
35
|
+
if (!existsSync(abs) || !/^[A-Za-z0-9._/-]+$/.test(p)) continue;
|
|
36
|
+
const hash = createHash("sha256").update(readFileSync(abs, "utf8")).digest("hex");
|
|
37
|
+
if (!synced[p] && rec?.hash === hash) {
|
|
38
|
+
try {
|
|
39
|
+
shR(`git reset -q -- "${p}"`);
|
|
40
|
+
} catch {
|
|
41
|
+
/* not staged (never tracked) — already excluded */
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
} catch {
|
|
46
|
+
/* no/corrupt sidecar → nothing hydrated to dispose */
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
24
50
|
const branch = sh("git rev-parse --abbrev-ref HEAD");
|
|
25
51
|
// Trunk commits never mirror — the brain default branch has one writer, the
|
|
26
52
|
// distiller. Conservative charset guard: a refname git allows but a shell
|
|
@@ -34,6 +60,7 @@ try {
|
|
|
34
60
|
if (cur !== branch) {
|
|
35
61
|
try {
|
|
36
62
|
shR("git add -A");
|
|
63
|
+
disposeHydrations(cur);
|
|
37
64
|
shR(`git commit -q -m "brain(switch-carryover)" -m "switch-carryover-from: ${cur}"`);
|
|
38
65
|
} catch {
|
|
39
66
|
/* nothing dirty */
|
|
@@ -46,6 +73,38 @@ try {
|
|
|
46
73
|
}
|
|
47
74
|
}
|
|
48
75
|
|
|
76
|
+
// Merge-union (spec r2 §2.2): a LOCAL merge of branch X into this branch Y ⇒ union
|
|
77
|
+
// brain/X into brain/Y, the merged branch winning per path (fold doctrine on the git
|
|
78
|
+
// plane, `-X theirs`). TRUNK is excluded — merging main into a branch is a resync, not
|
|
79
|
+
// a knowledge transport (unioning main's brain would re-pull org content = stale-
|
|
80
|
+
// override). Best-effort: an unrecoverable branch name just leaves the 1-1 commit, and
|
|
81
|
+
// the reconciler's ancestry sweep still catches the merged commits.
|
|
82
|
+
try {
|
|
83
|
+
const parents = sh("git rev-list --parents -n 1 HEAD").split(/\s+/).slice(1);
|
|
84
|
+
if (parents.length >= 2) {
|
|
85
|
+
const m = sh("git log -1 --pretty=%B").match(
|
|
86
|
+
/Merge branch '([^']+)'|Merge branch "([^"]+)"|Merge remote-tracking branch '[^/]+\/([^']+)'/,
|
|
87
|
+
);
|
|
88
|
+
const merged = m ? m[1] || m[2] || m[3] : null;
|
|
89
|
+
if (
|
|
90
|
+
merged &&
|
|
91
|
+
merged !== branch &&
|
|
92
|
+
merged !== "main" &&
|
|
93
|
+
merged !== "master" &&
|
|
94
|
+
/^[A-Za-z0-9._/-]+$/.test(merged)
|
|
95
|
+
) {
|
|
96
|
+
try {
|
|
97
|
+
shR(`git rev-parse --verify -q "refs/heads/${merged}"`);
|
|
98
|
+
shR(`git merge -q -m "brain(merge): ${merged} -> ${branch}" -X theirs "${merged}"`);
|
|
99
|
+
} catch {
|
|
100
|
+
/* no brain branch for the merged code branch, or already up to date */
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
} catch {
|
|
105
|
+
/* not a merge / rev-list unavailable — normal 1-1 path */
|
|
106
|
+
}
|
|
107
|
+
|
|
49
108
|
// The intent record — the commit's end-to-end intent, mechanically joined.
|
|
50
109
|
// Minimal here (sha · subject · files); the P3 capture worker enriches.
|
|
51
110
|
const fullSha = sh("git rev-parse HEAD");
|
|
@@ -71,6 +130,7 @@ try {
|
|
|
71
130
|
);
|
|
72
131
|
|
|
73
132
|
shR("git add -A");
|
|
133
|
+
disposeHydrations(branch);
|
|
74
134
|
shR(
|
|
75
135
|
`git commit --allow-empty -q -m "brain(${branch}): ${subject}" ` +
|
|
76
136
|
`-m "code-commit: ${fullSha}" -m "code-branch: ${branch}"`,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -253,9 +253,16 @@ pipeline; **`--brain-only`** stops after the brain is validated (step 5 PASS)
|
|
|
253
253
|
6. **Improve** *(skip if `--brain-only`)* — run the improve pass ([rafa-improve](../rafa-improve/SKILL.md)):
|
|
254
254
|
spawn `bloom` → `.rafa/improve/`. It reads the *validated* brain as its index, so it only
|
|
255
255
|
runs after PASS.
|
|
256
|
-
7. **
|
|
257
|
-
dev's explicit approval**,
|
|
258
|
-
|
|
256
|
+
7. **Land it** — present the full summary (brain verdict/score + top improvements). **On the
|
|
257
|
+
dev's explicit approval**, land the brain the way EVERY brain change lands — through the
|
|
258
|
+
commit hooks, **never a direct trunk push** (the reconciler is the org brain's only writer).
|
|
259
|
+
The dev MUST be on a feature branch (the post-commit hook no-ops on `main`). Then:
|
|
260
|
+
`git add -A && git commit` → **post-commit** mirrors the validated `.rafa/` brain 1-1 onto
|
|
261
|
+
the matching brain branch (stamped `code-commit: <sha>`); `git push` → **pre-push**
|
|
262
|
+
checkpoints the working set to the platform; then land it on main (open a PR **or** a plain
|
|
263
|
+
`git merge` + push — both work) → detection enqueues and the **reconciler** authors the org
|
|
264
|
+
brain at the merge sha, minting the node (brain ↔ code). Do **not** run `rafa push` (retired:
|
|
265
|
+
it wrote the trunk directly, bypassing the reconciler). Never land without approval.
|
|
259
266
|
8. **The coach offer (founding scan only)** — if this was the repo's FIRST scan and the dev's
|
|
260
267
|
user brain is empty (`list_dev_insights` → none), offer ONCE: *"the code side is mapped —
|
|
261
268
|
want me to bootstrap YOUR insights from your usage report?"* Accepted = run `## insights`
|
package/lib/checkpoint.mjs
CHANGED
|
@@ -20,7 +20,7 @@ import { execSync } from "node:child_process";
|
|
|
20
20
|
import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
21
21
|
import { join } from "node:path";
|
|
22
22
|
import { callTool } from "./mcp-client.mjs";
|
|
23
|
-
import { hashOf, readSidecar, scanWorkingFiles, writeSidecar } from "./working-set.mjs";
|
|
23
|
+
import { hashOf, isDisposableHydration, readSidecar, scanWorkingFiles, writeSidecar } from "./working-set.mjs";
|
|
24
24
|
|
|
25
25
|
const die = (m) => {
|
|
26
26
|
console.error(`✗ ${m}`);
|
|
@@ -85,7 +85,7 @@ export default async function checkpoint() {
|
|
|
85
85
|
const synced = syncBranch[path];
|
|
86
86
|
|
|
87
87
|
if (synced?.hash === hash && synced.conflictSeen === undefined) continue; // already checkpointed
|
|
88
|
-
if (
|
|
88
|
+
if (isDisposableHydration({ synced, hydrated, hash })) continue; // unedited hydration = disposable cache
|
|
89
89
|
|
|
90
90
|
candidates.push({
|
|
91
91
|
path,
|
|
@@ -131,6 +131,38 @@ export async function arbitrateCandidate(candidate, cwd, { judge }) {
|
|
|
131
131
|
return { outcome: "banked", noteId, cites: grounded, regroundings, detail: `banked as ${noteId}` };
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
+
// ── mechanical grounding for the brain-grounded agent path (agent.mjs) ──────────
|
|
135
|
+
// The agent owns claim-level JUDGMENT (does this hold? redundant? contradicted?);
|
|
136
|
+
// grounding stays MECHANICAL here — it is grep, not judgment, so an LLM must never
|
|
137
|
+
// decide it. groundCandidate is the PRE-PASS: it resolves a candidate's cites against
|
|
138
|
+
// merged main exactly as arbitrateCandidate's mechanical half does, but WITHOUT the
|
|
139
|
+
// judge, and returns the raw grounding so the agent path can (a) prune a candidate
|
|
140
|
+
// citing deleted code without spending a token and (b) hand the agent only grounded /
|
|
141
|
+
// re-grounded claims to judge. `deleted` non-empty ⇒ a mechanical prune (deletion is
|
|
142
|
+
// ground truth). Same one implementation of "does the token still live" as the gate.
|
|
143
|
+
export function groundCandidate(candidate, cwd = process.cwd()) {
|
|
144
|
+
const cites = citesOf(candidate.content);
|
|
145
|
+
const noteId = idOf(candidate.content, candidate.path);
|
|
146
|
+
const perCite = cites.map((c) => arbitrateCite(c, cwd));
|
|
147
|
+
const deleted = perCite.filter((d) => d.status === "deleted");
|
|
148
|
+
const regroundings = perCite.filter((d) => d.status === "regrounded");
|
|
149
|
+
const grounded = perCite.map((d) => (d.status === "regrounded" ? d.to : d.cite));
|
|
150
|
+
return { cites, noteId, deleted, regroundings, grounded };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// citesGround is the POST-VERIFY GUARDRAIL: whatever content the agent proposes to
|
|
154
|
+
// author, EVERY cite in it must resolve AS AUTHORED against merged main (the agent is
|
|
155
|
+
// responsible for having re-grounded any moved cite itself in the content it returns).
|
|
156
|
+
// An unresolved cite means the agent hallucinated or mis-grounded — the loop downgrades
|
|
157
|
+
// that ONE disposition to needs-adjudication rather than banking ungrounded knowledge.
|
|
158
|
+
// This sits in FRONT of the verify-citations gate: it fails a single note to
|
|
159
|
+
// adjudication instead of letting the whole run's gate reject. Returns { ok, unresolved }.
|
|
160
|
+
export function citesGround(content, cwd = process.cwd()) {
|
|
161
|
+
const cites = citesOf(content);
|
|
162
|
+
const unresolved = cites.filter((c) => arbitrateCite(c, cwd).status !== "resolved");
|
|
163
|
+
return { ok: unresolved.length === 0, unresolved };
|
|
164
|
+
}
|
|
165
|
+
|
|
134
166
|
// Rewrite a survivor's frontmatter `cites:` DSL so a re-grounded citation points
|
|
135
167
|
// at where the token NOW lives on main — otherwise banking it as authored would
|
|
136
168
|
// fail the checker (the stale line no longer resolves). Mechanical string
|
package/lib/distiller.mjs
CHANGED
|
@@ -394,7 +394,28 @@ export default async function distiller(_args = []) {
|
|
|
394
394
|
const doPush = () => push(["--verb=distill"]); // push.mjs re-runs the same gates + pushes; NEVER --force
|
|
395
395
|
|
|
396
396
|
// Mirror the org brain first (the authoring target the survivors fold into).
|
|
397
|
-
|
|
397
|
+
// Prep failures MUST self-report (live-caught 2026-07-18: a safe.directory
|
|
398
|
+
// throw here died before the state plane ever started — running row, no
|
|
399
|
+
// lastError, no logs, invisible until someone ssh'd the sandbox). Best-effort
|
|
400
|
+
// report, then rethrow: the row shows the reason and the sweep retries.
|
|
401
|
+
try {
|
|
402
|
+
await pull(["--full"]);
|
|
403
|
+
} catch (e) {
|
|
404
|
+
const message = `brain mirror (pull --full) failed: ${e instanceof Error ? e.message.split("\n")[0] : e}`;
|
|
405
|
+
try {
|
|
406
|
+
await reportFailure({
|
|
407
|
+
call,
|
|
408
|
+
reconciliationId: env.RAFA_LEAD_RECONCILIATION_ID,
|
|
409
|
+
attempt: Number(env.RAFA_ATTEMPT),
|
|
410
|
+
actor: { model: env.RAFA_MODEL || "claude-opus-4-8", agent: "distiller", runner: "sandbox" },
|
|
411
|
+
errorClass: "transient",
|
|
412
|
+
message,
|
|
413
|
+
});
|
|
414
|
+
} catch {
|
|
415
|
+
/* the report is best-effort — the lease/sweep loop still recovers */
|
|
416
|
+
}
|
|
417
|
+
throw e;
|
|
418
|
+
}
|
|
398
419
|
if (!existsSync(brainRoot)) mkdirSync(brainRoot, { recursive: true });
|
|
399
420
|
|
|
400
421
|
const result = await runDistiller({
|
package/lib/gather.mjs
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// git-native gather (capture-engine spec r2 §2.4) — the ONE mechanism that reads candidate
|
|
2
|
+
// brain deltas from a brain branch's history, unioned latest-wins per path. Two consumers:
|
|
3
|
+
//
|
|
4
|
+
// • session hydration — pull the branch's accumulated NON-main knowledge (an epic's task
|
|
5
|
+
// branches, inherited through git), so a dev has it while building the feature.
|
|
6
|
+
// • the distiller — gather a merged branch's deltas since it diverged, to reconcile into
|
|
7
|
+
// the org brain at main.
|
|
8
|
+
//
|
|
9
|
+
// Union-latest-wins is FREE: a branch tip already holds the latest version of every note in
|
|
10
|
+
// its ancestry (git inheritance from parent branches), so the net `base..tip` diff IS the
|
|
11
|
+
// union. The base picks the lens:
|
|
12
|
+
// • distill → base = branchPoint(branch, main): the branch's OWN deltas since it forked.
|
|
13
|
+
// • pull → base = main's brain tip: everything NOT yet distilled into the org brain.
|
|
14
|
+
|
|
15
|
+
import { execSync } from "node:child_process";
|
|
16
|
+
|
|
17
|
+
// The candidate authoring surfaces (mirrors working-set WORKING_DIRS; intent/ is provenance,
|
|
18
|
+
// never a candidate, so it is excluded here).
|
|
19
|
+
const CANDIDATE_DIRS = ["brain/rules", "brain/playbooks", "improve/improvements"];
|
|
20
|
+
|
|
21
|
+
const git = (dir, cmd) =>
|
|
22
|
+
execSync(cmd, { cwd: dir, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
23
|
+
|
|
24
|
+
// The branch point: where `branch` diverged from `from` (both brain refs). The distiller's
|
|
25
|
+
// base — the branch's own accumulated deltas are everything since here. "" if no common
|
|
26
|
+
// ancestor (an orphan/first branch → gather the whole branch).
|
|
27
|
+
export function branchPoint(brainDir, branch, from = "main") {
|
|
28
|
+
try {
|
|
29
|
+
return git(brainDir, `git merge-base ${JSON.stringify(from)} ${JSON.stringify(branch)}`);
|
|
30
|
+
} catch {
|
|
31
|
+
return "";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Gather candidate deltas from `branch` since `base` (both brain refs) in the brain repo at
|
|
36
|
+
// `brainDir`. base "" ⇒ the whole branch. Returns:
|
|
37
|
+
// { tip, candidates: [{ path, content }], deleted: [path] }
|
|
38
|
+
// candidates = notes present at the tip that changed since base (latest-wins content).
|
|
39
|
+
// deleted = notes that existed at base and are GONE at the tip → prune signals.
|
|
40
|
+
export function gather({ brainDir, branch, base = "" }) {
|
|
41
|
+
const tip = git(brainDir, `git rev-parse ${JSON.stringify(branch)}`);
|
|
42
|
+
const dirspec = CANDIDATE_DIRS.map((d) => JSON.stringify(d)).join(" ");
|
|
43
|
+
const names = base
|
|
44
|
+
? git(brainDir, `git diff --name-only ${base} ${tip} -- ${dirspec}`)
|
|
45
|
+
: git(brainDir, `git ls-tree -r --name-only ${tip} -- ${dirspec}`);
|
|
46
|
+
|
|
47
|
+
const candidates = [];
|
|
48
|
+
const deleted = [];
|
|
49
|
+
for (const path of names.split("\n").filter(Boolean)) {
|
|
50
|
+
const leaf = path.split("/").pop() ?? "";
|
|
51
|
+
if (path.endsWith(".theirs.md") || leaf.startsWith("_")) continue; // conflict/scratch
|
|
52
|
+
try {
|
|
53
|
+
const content = execSync(`git show ${tip}:${JSON.stringify(path)}`, {
|
|
54
|
+
cwd: brainDir,
|
|
55
|
+
encoding: "utf8",
|
|
56
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
57
|
+
});
|
|
58
|
+
candidates.push({ path, content });
|
|
59
|
+
} catch {
|
|
60
|
+
deleted.push(path); // in the diff but not at the tip ⇒ deleted since base
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return { tip, candidates, deleted };
|
|
64
|
+
}
|
package/lib/push.mjs
CHANGED
|
@@ -19,6 +19,23 @@ import { ensureBrainRepo, remoteDefaultBranch, inDir, die } from "./brain-repo.m
|
|
|
19
19
|
import { CLI_VERSION } from "./releases.mjs";
|
|
20
20
|
|
|
21
21
|
export default async function push(args = []) {
|
|
22
|
+
// Doctrine A (2026-07-20): `rafa push` wrote the brain TRUNK directly, bypassing the
|
|
23
|
+
// reconciler — which is now the org brain's ONLY writer. A scan no longer publishes the
|
|
24
|
+
// trunk; its validated `.rafa/` brain lands through the commit hooks (post-commit mirrors
|
|
25
|
+
// it 1-1 onto the matching brain branch → pre-push checkpoints → merge → the reconciler
|
|
26
|
+
// authors the trunk at the merge sha). Reconciler runs (`--verb=distill`) still pass, and
|
|
27
|
+
// `--force-trunk` stays as an explicit emergency override.
|
|
28
|
+
if (!args.includes("--verb=distill") && !args.includes("--force-trunk")) {
|
|
29
|
+
console.log(
|
|
30
|
+
"✗ `rafa push` (direct brain-trunk write) is retired — the reconciler is the org brain's\n" +
|
|
31
|
+
" only writer. Land a scan the normal way, ON A FEATURE BRANCH:\n" +
|
|
32
|
+
" • git add -A && git commit → post-commit mirrors the validated .rafa/ brain 1-1\n" +
|
|
33
|
+
" • git push → pre-push checkpoints the working set to the platform\n" +
|
|
34
|
+
" • merge to main (PR or plain git merge) → the reconciler authors the org brain\n" +
|
|
35
|
+
" (Emergency direct trunk write, discouraged: rerun with --force-trunk.)",
|
|
36
|
+
);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
22
39
|
const ROOT = process.cwd();
|
|
23
40
|
const { rafaDir } = ensureBrainRepo(ROOT);
|
|
24
41
|
const inRafa = inDir(rafaDir);
|
package/lib/releases.mjs
CHANGED
|
@@ -229,6 +229,20 @@ export const RELEASES = [
|
|
|
229
229
|
"value is needed it is rafa.json → repoId, never a folder-name guess. `rafa " +
|
|
230
230
|
"update` re-vendors the cards.",
|
|
231
231
|
},
|
|
232
|
+
{
|
|
233
|
+
version: "0.8.5",
|
|
234
|
+
contract: 1,
|
|
235
|
+
plans: 2,
|
|
236
|
+
requires: "update",
|
|
237
|
+
summary:
|
|
238
|
+
"Doctrine: the reconciler is the org brain's ONLY trunk writer. `rafa push` " +
|
|
239
|
+
"(direct brain-trunk write) is RETIRED — a scan now LANDS through the commit hooks: " +
|
|
240
|
+
"commit your code on a feature branch (post-commit mirrors the validated .rafa/ " +
|
|
241
|
+
"brain 1-1 onto the matching brain branch) → git push (pre-push checkpoints the " +
|
|
242
|
+
"working set) → merge to main → the reconciler authors the org brain at the merge " +
|
|
243
|
+
"sha and mints the node (brain ↔ code). Merges need NOT be PR-driven: a plain " +
|
|
244
|
+
"`git merge` + push to main reconciles too. `rafa update` re-vendors the scan SOP.",
|
|
245
|
+
},
|
|
232
246
|
];
|
|
233
247
|
|
|
234
248
|
// The release this CLI build ships (last entry).
|
package/lib/working-set.mjs
CHANGED
|
@@ -48,6 +48,16 @@ export function kindOfPath(path) {
|
|
|
48
48
|
|
|
49
49
|
export const hashOf = (text) => createHash("sha256").update(text).digest("hex");
|
|
50
50
|
|
|
51
|
+
// The ONE disposal rule of the working-set architecture: a file is a DISPOSABLE
|
|
52
|
+
// unedited hydration — never a candidate on EITHER plane — iff it is not a synced
|
|
53
|
+
// working-set file and its content still hashes to its hydration base. Extracted so the
|
|
54
|
+
// checkpoint (platform plane) and the brain-commit hook (git plane) apply the IDENTICAL
|
|
55
|
+
// test. The hook is standalone by design (node built-ins only), so it mirrors this
|
|
56
|
+
// one-liner inline — keep the two in sync (there is only one rule).
|
|
57
|
+
export function isDisposableHydration({ synced, hydrated, hash }) {
|
|
58
|
+
return !synced && hydrated?.hash === hash;
|
|
59
|
+
}
|
|
60
|
+
|
|
51
61
|
export function sidecarPath(cwd) {
|
|
52
62
|
return join(cwd, ".rafa", "hydration.json");
|
|
53
63
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rafinery/cli",
|
|
3
|
-
"version": "0.8.
|
|
4
|
-
"description": "rafa
|
|
3
|
+
"version": "0.8.5",
|
|
4
|
+
"description": "rafa \u2014 the AI engineer CLI. Vendors the rafa blueprint into your repo (shadcn-style) and runs the deterministic contract gates.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"rafa": "bin/rafa.mjs",
|
|
@@ -18,11 +18,16 @@
|
|
|
18
18
|
"node": ">=18"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@rafinery/okf": "
|
|
21
|
+
"@rafinery/okf": "workspace:*"
|
|
22
22
|
},
|
|
23
23
|
"publishConfig": {
|
|
24
24
|
"access": "public"
|
|
25
25
|
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"bundle": "node scripts/bundle-blueprint.mjs",
|
|
28
|
+
"prepare": "node scripts/bundle-blueprint.mjs",
|
|
29
|
+
"test": "node --test"
|
|
30
|
+
},
|
|
26
31
|
"keywords": [
|
|
27
32
|
"rafa",
|
|
28
33
|
"rafinery",
|
|
@@ -38,9 +43,5 @@
|
|
|
38
43
|
"url": "git+https://github.com/rafinery-ai/rafinery.git",
|
|
39
44
|
"directory": "packages/cli"
|
|
40
45
|
},
|
|
41
|
-
"homepage": "https://github.com/rafinery-ai/rafinery/tree/main/packages/cli"
|
|
42
|
-
|
|
43
|
-
"bundle": "node scripts/bundle-blueprint.mjs",
|
|
44
|
-
"test": "node --test"
|
|
45
|
-
}
|
|
46
|
-
}
|
|
46
|
+
"homepage": "https://github.com/rafinery-ai/rafinery/tree/main/packages/cli"
|
|
47
|
+
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
The MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) Atai Barkai
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in
|
|
13
|
-
all copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
-
THE SOFTWARE.
|