@rafinery/cli 0.13.0 → 0.15.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/lib/ci-setup.mjs DELETED
@@ -1,172 +0,0 @@
1
- // rafa ci-setup — install the reconciliation workflow (working-set
2
- // architecture, ratified 2026-07-10, rules 6–7): the org's own CI folds
3
- // branch→branch merges mechanically and DISTILLS merge-to-main with the org's
4
- // own LLM key. Writes .github/workflows/rafa-reconcile.yml and names the
5
- // secrets to configure — it configures nothing itself (secrets are the org's;
6
- // nothing sensitive ever transits rafinery).
7
-
8
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
9
- import { dirname, join } from "node:path";
10
-
11
- const WORKFLOW_PATH = ".github/workflows/rafa-reconcile.yml";
12
-
13
- const WORKFLOW = `# rafa reconcile — knowledge propagates exactly like the code it describes.
14
- #
15
- # branch → parent branch merge = MECHANICAL FOLD (no LLM, no prism)
16
- # branch → default branch merge = DISTILLATION (prism-validated vs merged main,
17
- # compile-gated, pushed to the brain repo)
18
- #
19
- # Secrets (Settings → Secrets and variables → Actions):
20
- # RAFA_MCP_KEY an agent key for this repo (platform → repo → Agent access)
21
- # ANTHROPIC_API_KEY the ORG'S OWN LLM key — used only inside this CI for
22
- # distillation; never stored on the rafinery platform
23
- # RAFA_BRAIN_TOKEN a token with push access to the BRAIN repo (distill only)
24
- #
25
- # Fires on the merge EVENT itself (PR closed+merged), so squash/rebase merges are
26
- # detected correctly. The distill job ALSO runs an ancestry sweep: any still-active
27
- # working-set row (any branch) whose capturedAtSha is reachable from merged main is
28
- # provably-merged backlog — an earlier merge whose distill never ran, or a direct
29
- # push — and folds into the same run. The sweep needs full history (fetch-depth: 0).
30
- # If distillation cannot run (missing secret, failed gate) it fails LOUD and the
31
- # branch's working set stays intact — the next dev session gets the standard
32
- # distill offer (fallback chain).
33
-
34
- name: rafa reconcile
35
-
36
- on:
37
- pull_request:
38
- types: [closed]
39
-
40
- jobs:
41
- fold:
42
- name: fold working set (branch → parent)
43
- if: github.event.pull_request.merged == true && github.event.pull_request.base.ref != github.event.repository.default_branch
44
- runs-on: ubuntu-latest
45
- steps:
46
- - uses: actions/checkout@v4
47
- - uses: actions/setup-node@v4
48
- with:
49
- node-version: 20
50
- - name: Mechanical fold (no LLM — rigor lives at main)
51
- env:
52
- RAFA_MCP_KEY: \${{ secrets.RAFA_MCP_KEY }}
53
- RAFA_MCP_URL: \${{ secrets.RAFA_MCP_URL }} # deployed platform /api/mcp — localhost never works from CI
54
- RAFA_HOOKS_DISABLED: "1" # headless — the M5 sensors are session instruments
55
- run: npx -y @rafinery/cli fold --from "\${{ github.event.pull_request.head.ref }}" --to "\${{ github.event.pull_request.base.ref }}"
56
-
57
- distill:
58
- name: distill working set (merge to main)
59
- if: github.event.pull_request.merged == true && github.event.pull_request.base.ref == github.event.repository.default_branch
60
- runs-on: ubuntu-latest
61
- steps:
62
- - uses: actions/checkout@v4
63
- with:
64
- ref: \${{ github.event.repository.default_branch }} # merged main = the validation target
65
- fetch-depth: 0 # full history — the ancestry sweep proves prior merges
66
- - uses: actions/setup-node@v4
67
- with:
68
- node-version: 20
69
- # ISOLATED install — never \`npm i\` inside the client checkout: a pnpm
70
- # workspace repo has "workspace:*" deps npm cannot parse (EUNSUPPORTEDPROTOCOL).
71
- - name: Install the Agent SDK (isolated dir; runs on the org's own key)
72
- run: |
73
- mkdir -p "\$RUNNER_TEMP/rafa-sdk" && cd "\$RUNNER_TEMP/rafa-sdk"
74
- npm init -y > /dev/null
75
- npm i @anthropic-ai/claude-agent-sdk
76
- - name: Distill into the org brain (prism-validated, compile-gated)
77
- env:
78
- ANTHROPIC_API_KEY: \${{ secrets.ANTHROPIC_API_KEY }}
79
- RAFA_MCP_KEY: \${{ secrets.RAFA_MCP_KEY }}
80
- RAFA_MCP_URL: \${{ secrets.RAFA_MCP_URL }} # deployed platform /api/mcp — localhost never works from CI
81
- RAFA_BRAIN_TOKEN: \${{ secrets.RAFA_BRAIN_TOKEN }}
82
- RAFA_AGENT_SDK_DIR: \${{ runner.temp }}/rafa-sdk
83
- RAFA_HOOKS_DISABLED: "1" # headless — the M5 sensors are session instruments
84
- RAFA_MERGE_SHA: \${{ github.event.pull_request.merge_commit_sha }} # keys the run record
85
- RAFA_TARGET_BRANCH: \${{ github.event.pull_request.base.ref }}
86
- run: npx -y @rafinery/cli distill --headless "\${{ github.event.pull_request.head.ref }}"
87
- `;
88
-
89
- // Pristine-detection for upgrades: a byte-diff alone cannot tell "user tuned
90
- // this" from "our template moved on" — and mislabeling a stale pristine
91
- // install as tuned strands it on old behavior forever (the ancestry sweep is
92
- // inert without fetch-depth: 0). Comparison ignores comments/blank lines;
93
- // PREVIOUS_NORMALIZED holds every historical pristine body, so a file that
94
- // matches one is upgraded in place and only a genuinely tuned file is left
95
- // alone. (Comment-only tuning is knowingly not preserved.)
96
- const normalize = (s) =>
97
- s
98
- .split("\n")
99
- .map((l) => l.trimEnd())
100
- .filter((l) => {
101
- const t = l.trim();
102
- return t !== "" && !t.startsWith("#");
103
- })
104
- .join("\n");
105
- const PREVIOUS_NORMALIZED = [
106
- // v1 (pre ancestry-sweep, pre run-record): identical body minus the lines
107
- // those two features added. Derived, not duplicated — v1 never shipped a
108
- // byte that differs otherwise.
109
- normalize(WORKFLOW)
110
- .split("\n")
111
- .filter(
112
- (l) =>
113
- !l.includes("fetch-depth: 0") &&
114
- !l.includes("RAFA_MERGE_SHA") &&
115
- !l.includes("RAFA_TARGET_BRANCH"),
116
- )
117
- .join("\n"),
118
- ];
119
-
120
- export default async function ciSetup(args = []) {
121
- const ROOT = process.cwd();
122
- const target = join(ROOT, WORKFLOW_PATH);
123
-
124
- console.log(
125
- "OPTIONAL — the platform reconciles merges automatically (no CI, no repo secrets).\n" +
126
- "This wires the org-CI ADAPTER instead: reconciliation compute runs in YOUR\n" +
127
- "GitHub Actions on YOUR secrets. Most teams should skip this.\n",
128
- );
129
-
130
- if (existsSync(target)) {
131
- const current = readFileSync(target, "utf8");
132
- if (current === WORKFLOW) {
133
- console.log(`✓ ${WORKFLOW_PATH} is already current`);
134
- } else if (args.includes("--overwrite")) {
135
- writeFileSync(target, WORKFLOW);
136
- console.log(`✓ ${WORKFLOW_PATH} updated (--overwrite)`);
137
- } else if (PREVIOUS_NORMALIZED.includes(normalize(current))) {
138
- writeFileSync(target, WORKFLOW);
139
- console.log(
140
- `✓ ${WORKFLOW_PATH} upgraded from an older pristine template (no local tuning detected)`,
141
- );
142
- } else {
143
- console.log(
144
- `! ${WORKFLOW_PATH} exists and differs — left untouched (yours may be tuned).\n` +
145
- " Re-run with --overwrite to replace it.",
146
- );
147
- }
148
- } else {
149
- mkdirSync(dirname(target), { recursive: true });
150
- writeFileSync(target, WORKFLOW);
151
- console.log(`✓ wrote ${WORKFLOW_PATH}`);
152
- }
153
-
154
- console.log(`
155
- Next steps (repo Settings → Secrets and variables → Actions):
156
- 1. RAFA_MCP_KEY — mint on the platform (repo → Agent access); scope: this repo.
157
- 2. RAFA_MCP_URL — your DEPLOYED platform's MCP endpoint (https://<host>/api/mcp).
158
- A localhost dev URL can never work from a CI runner.
159
- 3. ANTHROPIC_API_KEY — the ORG'S OWN LLM key. Used only inside YOUR CI for
160
- merge-to-main distillation; never stored on the rafinery
161
- platform (hard rule).
162
- 4. RAFA_BRAIN_TOKEN — a token with push access to the BRAIN repo (the distill
163
- job pushes validated knowledge there).
164
- 5. Commit the workflow via your normal MR flow.
165
- 6. Verify the whole capture chain: npx @rafinery/cli doctor (sensors, scripts,
166
- platform round-trip — exit 1 names any fix).
167
-
168
- The rigor gradient this wires: dev↔dev = CAS + session prompt · branch↔branch =
169
- free mechanical fold · branch→main = full distillation + gates. Cost tracks
170
- consequence. Without CI, nothing is lost — the next dev session's bootstrap
171
- digest offers the same reconciliation (/rafa distill).`);
172
- }