@hecer/yoke 0.2.0 → 0.8.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.
Files changed (84) hide show
  1. package/CHANGELOG.md +130 -0
  2. package/README.md +583 -494
  3. package/bench/.runs/claude-2026-07-09T22-34-01/.yoke/config.yaml +6 -0
  4. package/bench/.runs/claude-2026-07-09T22-34-01/.yoke/context/DECISIONS.md +9 -0
  5. package/bench/.runs/claude-2026-07-09T22-34-01/.yoke/prd.yaml +38 -0
  6. package/bench/.runs/claude-2026-07-09T22-34-01/bench-verify.mjs +15 -0
  7. package/bench/.runs/claude-2026-07-09T22-34-01/package.json +9 -0
  8. package/bench/.runs/claude-2026-07-09T22-34-01/src/index.mjs +48 -0
  9. package/bench/.runs/claude-2026-07-09T22-34-01/tests/STORY-1.test.mjs +24 -0
  10. package/bench/.runs/claude-2026-07-09T22-34-01/tests/STORY-2.test.mjs +28 -0
  11. package/bench/.runs/claude-2026-07-09T22-34-01/tests/STORY-3.test.mjs +25 -0
  12. package/bench/.runs/gemini-2026-07-09T22-34-02/.yoke/config.yaml +6 -0
  13. package/bench/.runs/gemini-2026-07-09T22-34-02/.yoke/prd.yaml +32 -0
  14. package/bench/.runs/gemini-2026-07-09T22-34-02/bench-verify.mjs +15 -0
  15. package/bench/.runs/gemini-2026-07-09T22-34-02/package.json +9 -0
  16. package/bench/.runs/gemini-2026-07-09T22-34-02/src/index.mjs +3 -0
  17. package/bench/.runs/gemini-2026-07-09T22-34-02/tests/STORY-1.test.mjs +24 -0
  18. package/bench/.runs/gemini-2026-07-09T22-34-02/tests/STORY-2.test.mjs +28 -0
  19. package/bench/.runs/gemini-2026-07-09T22-34-02/tests/STORY-3.test.mjs +25 -0
  20. package/bench/README.md +42 -0
  21. package/bench/RESULTS.md +36 -0
  22. package/bench/fixtures/string-kit/.yoke/config.yaml +6 -0
  23. package/bench/fixtures/string-kit/.yoke/prd.yaml +32 -0
  24. package/bench/fixtures/string-kit/bench-verify.mjs +15 -0
  25. package/bench/fixtures/string-kit/package.json +9 -0
  26. package/bench/fixtures/string-kit/src/index.mjs +3 -0
  27. package/bench/fixtures/string-kit/tests/STORY-1.test.mjs +24 -0
  28. package/bench/fixtures/string-kit/tests/STORY-2.test.mjs +28 -0
  29. package/bench/fixtures/string-kit/tests/STORY-3.test.mjs +25 -0
  30. package/bench/results/claude-2026-07-09T22-34-01.json +40 -0
  31. package/bench/run.mjs +115 -0
  32. package/canon/loop/loop-spec.md +5 -1
  33. package/canon/manifest.yaml +3 -0
  34. package/canon/skills/yoke-retrofit/SKILL.md +1 -0
  35. package/canon/tools/claude-mem.md +15 -0
  36. package/canon/tools/ui-ux-pro-max.md +15 -0
  37. package/dist/cli.js +37 -6
  38. package/dist/loop/cleanup.js +44 -3
  39. package/dist/loop/loop.js +67 -2
  40. package/dist/loop/reporter.js +88 -4
  41. package/dist/loop/run-command.js +25 -8
  42. package/dist/loop/runner.js +133 -14
  43. package/dist/loop/watchdog.js +67 -17
  44. package/dist/retrofit/apply.js +10 -3
  45. package/dist/retrofit/config.js +9 -1
  46. package/dist/retrofit/gitignore.js +4 -0
  47. package/dist/retrofit/planners/claude.js +4 -1
  48. package/dist/retrofit/planners/gemini.js +2 -1
  49. package/dist/retrofit/preserve.js +51 -0
  50. package/dist/update/check.js +86 -0
  51. package/dist/update/refresh.js +28 -0
  52. package/dist/update/upgrade.js +30 -0
  53. package/docs/PUBLISHING.md +41 -0
  54. package/docs/superpowers/plans/2026-06-27-forge-a-canon.md +815 -0
  55. package/docs/superpowers/plans/2026-06-27-forge-b1-retrofit-claude.md +842 -0
  56. package/docs/superpowers/plans/2026-06-27-forge-b2-codex-gemini-tools.md +884 -0
  57. package/docs/superpowers/plans/2026-06-27-forge-c1-loop-engine.md +891 -0
  58. package/docs/superpowers/plans/2026-06-28-baustein-e-context-layer.md +981 -0
  59. package/docs/superpowers/plans/2026-06-28-forge-b3-settings-merge.md +364 -0
  60. package/docs/superpowers/plans/2026-06-28-forge-c2-loop-verify.md +502 -0
  61. package/docs/superpowers/plans/2026-06-28-forge-c3-multi-agent-runners.md +377 -0
  62. package/docs/superpowers/plans/2026-06-28-forge-c4-worktree-isolation.md +413 -0
  63. package/docs/superpowers/plans/2026-06-28-forge-c5-review-iteration.md +419 -0
  64. package/docs/superpowers/plans/2026-06-28-forge-cleanup-dep0190.md +233 -0
  65. package/docs/superpowers/plans/2026-06-28-forge-d-codegraph-choice-minimal-code.md +523 -0
  66. package/docs/superpowers/plans/2026-06-29-baustein-f-routing.md +258 -0
  67. package/docs/superpowers/plans/2026-06-29-baustein-g-loop-observability.md +1006 -0
  68. package/docs/superpowers/plans/2026-06-29-baustein-h-loop-robustness.md +374 -0
  69. package/docs/superpowers/plans/2026-06-30-baustein-i-visual-design-verification.md +450 -0
  70. package/docs/superpowers/plans/2026-07-02-baustein-j-cross-model-review-gstack-compose.md +645 -0
  71. package/docs/superpowers/plans/2026-07-02-baustein-k-zero-to-100-bootstrap.md +1024 -0
  72. package/docs/superpowers/plans/2026-07-02-baustein-m-flow-smoke-proofs.md +574 -0
  73. package/docs/superpowers/specs/2026-06-27-forge-cross-agent-harness-design.md +144 -0
  74. package/docs/superpowers/specs/2026-06-28-baustein-e-context-layer-design.md +146 -0
  75. package/docs/superpowers/specs/2026-06-29-baustein-f-routing-design.md +106 -0
  76. package/docs/superpowers/specs/2026-06-29-baustein-g-loop-observability-design.md +186 -0
  77. package/docs/superpowers/specs/2026-06-29-baustein-h-loop-robustness-design.md +113 -0
  78. package/docs/superpowers/specs/2026-06-30-baustein-i-visual-design-verification-design.md +98 -0
  79. package/docs/superpowers/specs/2026-07-02-baustein-j-cross-model-review-gstack-compose-design.md +137 -0
  80. package/docs/superpowers/specs/2026-07-02-baustein-k-zero-to-100-bootstrap-design.md +200 -0
  81. package/docs/superpowers/specs/2026-07-02-baustein-m-flow-smoke-proofs-design.md +155 -0
  82. package/docs/superpowers/specs/2026-07-10-companion-tools-decision.md +33 -0
  83. package/docs/superpowers/specs/2026-07-10-multi-agent-parallel-loop-design.md +99 -0
  84. package/package.json +8 -2
@@ -0,0 +1,523 @@
1
+ # Forge — Baustein D (code-graph choice + minimal-code skill) Implementation Plan
2
+
3
+ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
+
5
+ **Goal:** (1) Make the retrofit's code-graph tool a per-project **choice** — Serena (LSP-accurate) or graphify (fast/multimodal) — selectable via `--code-graph`, recorded in config, with the `forge-retrofit` skill instructing the agent to ask the user and recommend based on the project. (2) Add a Forge-native `minimal-code` canon skill (the YAGNI "lazy senior dev" idea, adapted from the MIT-licensed ponytail ruleset) that flows to all three agents and saves output tokens by writing less code.
6
+
7
+ **Architecture:** A `CodeGraph` type (`graphify | serena`) is added to config and threaded through `planRetrofit` → planners → `mcpServers(codeGraph)`, so the generated MCP config wires the chosen graph tool. The choice is resolved (flag > existing config > default `graphify`) and persisted. `minimal-code` is just a new canon skill — the existing planners already generate skills to every agent, so adding it to the manifest propagates it everywhere. Baustein D.
8
+
9
+ **Tech Stack:** Node.js (ESM), TypeScript, vitest. Touches config, tools, planners, plan dispatch, cli, and canon content.
10
+
11
+ **Builds on:** all prior bausteine on `main`. Modifies: `src/retrofit/config.ts`, `src/retrofit/tools.ts`, `src/retrofit/planners/{claude,codex,gemini}.ts`, `src/retrofit/plan.ts`, `src/cli.ts`, `canon/manifest.yaml`, `canon/skills/forge-retrofit/SKILL.md`; adds `canon/skills/minimal-code/SKILL.md`, `canon/tools/serena.md`.
12
+
13
+ ---
14
+
15
+ ## File Structure
16
+
17
+ ```
18
+ src/retrofit/config.ts # CodeGraph type + ForgeConfig.codeGraph? + schema
19
+ src/retrofit/tools.ts # mcpServers(codeGraph) + serena server entry
20
+ src/retrofit/planners/*.ts # thread codeGraph into mcpServers calls
21
+ src/retrofit/plan.ts # planRetrofit(..., codeGraph); AgentPlanner gains codeGraph
22
+ src/cli.ts # runRetrofit resolves+records codeGraph; --code-graph flag
23
+ canon/manifest.yaml # + minimal-code skill, + serena tool
24
+ canon/skills/minimal-code/SKILL.md # NEW (ponytail-derived)
25
+ canon/tools/serena.md # NEW
26
+ canon/skills/forge-retrofit/SKILL.md # MODIFY (ask code-graph + recommend)
27
+ ```
28
+
29
+ ---
30
+
31
+ ### Task 1: CodeGraph type + config field
32
+
33
+ **Files:**
34
+ - Modify: `src/retrofit/config.ts`
35
+ - Test: `tests/retrofit/config.test.ts` (extend)
36
+
37
+ - [ ] **Step 1: Add a failing test**
38
+
39
+ In `tests/retrofit/config.test.ts`, add:
40
+ ```ts
41
+ it('round-trips an optional codeGraph choice', () => {
42
+ const cfg = { canonVersion: '0.1.0', agents: ['claude'] as const, loop: { enabled: false }, codeGraph: 'serena' as const }
43
+ saveConfig(dir, cfg)
44
+ expect(loadConfig(dir)).toEqual(cfg)
45
+ })
46
+ ```
47
+
48
+ - [ ] **Step 2: Run test to verify it fails**
49
+
50
+ Run: `npm test -- retrofit/config`
51
+ Expected: FAIL — zod strips/ rejects unknown `codeGraph`, so the round-trip is not equal.
52
+
53
+ - [ ] **Step 3: Modify `src/retrofit/config.ts`**
54
+
55
+ Add the type (next to `Agent`):
56
+ ```ts
57
+ export type CodeGraph = 'graphify' | 'serena'
58
+ ```
59
+ Add the schema enum (next to `AgentSchema`):
60
+ ```ts
61
+ const CodeGraphSchema = z.enum(['graphify', 'serena'])
62
+ ```
63
+ Add `codeGraph` to the zod object (optional) and to the interface:
64
+ ```ts
65
+ const ForgeConfigSchema = z.object({
66
+ canonVersion: z.string().min(1),
67
+ agents: z.array(AgentSchema),
68
+ loop: z.object({ enabled: z.boolean() }),
69
+ verify: z.object({ command: z.string().min(1) }).optional(),
70
+ codeGraph: CodeGraphSchema.optional(),
71
+ })
72
+
73
+ export interface ForgeConfig {
74
+ canonVersion: string
75
+ agents: Agent[]
76
+ loop: { enabled: boolean }
77
+ verify?: { command: string }
78
+ codeGraph?: CodeGraph
79
+ }
80
+ ```
81
+
82
+ - [ ] **Step 4: Run test to verify it passes**
83
+
84
+ Run: `npm test -- retrofit/config`
85
+ Expected: PASS (existing + 1 new).
86
+
87
+ - [ ] **Step 5: Commit**
88
+
89
+ ```bash
90
+ git add src/retrofit/config.ts tests/retrofit/config.test.ts
91
+ git commit -m "feat: add optional codeGraph choice to config"
92
+ ```
93
+
94
+ ---
95
+
96
+ ### Task 2: Selectable code-graph MCP server
97
+
98
+ **Files:**
99
+ - Modify: `src/retrofit/tools.ts`
100
+ - Test: `tests/retrofit/tools.test.ts` (extend)
101
+
102
+ - [ ] **Step 1: Update the tests**
103
+
104
+ In `tests/retrofit/tools.test.ts`, replace the existing `mcpServers` test with parameterized ones:
105
+ ```ts
106
+ it('mcpServers defaults to graphify + playwright', () => {
107
+ const servers = mcpServers()
108
+ expect(Object.keys(servers)).toEqual(expect.arrayContaining(['graphify', 'playwright']))
109
+ expect(servers).not.toHaveProperty('serena')
110
+ expect(servers.playwright.command).toBe('npx')
111
+ expect(servers.playwright.args).toContain('@playwright/mcp@latest')
112
+ })
113
+
114
+ it('mcpServers("serena") wires serena instead of graphify', () => {
115
+ const servers = mcpServers('serena')
116
+ expect(Object.keys(servers)).toEqual(expect.arrayContaining(['serena', 'playwright']))
117
+ expect(servers).not.toHaveProperty('graphify')
118
+ expect(servers.serena.command).toBeTypeOf('string')
119
+ })
120
+ ```
121
+
122
+ - [ ] **Step 2: Run test to verify it fails**
123
+
124
+ Run: `npm test -- retrofit/tools`
125
+ Expected: FAIL — `mcpServers` takes no arg and always returns graphify.
126
+
127
+ - [ ] **Step 3: Modify `src/retrofit/tools.ts`**
128
+
129
+ Add the import and a code-graph server map, and parameterize `mcpServers`:
130
+ ```ts
131
+ import type { CodeGraph } from './config.js'
132
+
133
+ export interface McpServerConfig {
134
+ command: string
135
+ args: string[]
136
+ }
137
+
138
+ // Best-effort launch commands per code-graph tool. Users may need to adjust these
139
+ // to match their local install (graphify: `uv tool install graphifyy`; serena: `uv`,
140
+ // e.g. `uvx --from git+https://github.com/oraios/serena serena-mcp-server`).
141
+ const CODE_GRAPH_SERVERS: Record<CodeGraph, McpServerConfig> = {
142
+ graphify: { command: 'graphify', args: ['serve'] },
143
+ serena: { command: 'serena', args: ['start-mcp-server'] },
144
+ }
145
+
146
+ export function mcpServers(codeGraph: CodeGraph = 'graphify'): Record<string, McpServerConfig> {
147
+ return {
148
+ [codeGraph]: CODE_GRAPH_SERVERS[codeGraph],
149
+ playwright: { command: 'npx', args: ['@playwright/mcp@latest'] },
150
+ }
151
+ }
152
+ ```
153
+ (Leave `rtkInstruction` unchanged.)
154
+
155
+ - [ ] **Step 4: Run test to verify it passes**
156
+
157
+ Run: `npm test -- retrofit/tools`
158
+ Expected: PASS (2 tests).
159
+
160
+ - [ ] **Step 5: Commit**
161
+
162
+ ```bash
163
+ git add src/retrofit/tools.ts tests/retrofit/tools.test.ts
164
+ git commit -m "feat: select code-graph MCP server (graphify or serena)"
165
+ ```
166
+
167
+ ---
168
+
169
+ ### Task 3: Thread codeGraph through planners and dispatch
170
+
171
+ **Files:**
172
+ - Modify: `src/retrofit/planners/claude.ts`, `src/retrofit/planners/codex.ts`, `src/retrofit/planners/gemini.ts`, `src/retrofit/plan.ts`
173
+ - Test: `tests/retrofit/planners-claude.test.ts`, `tests/retrofit/plan-dispatch.test.ts` (extend)
174
+
175
+ - [ ] **Step 1: Add failing tests**
176
+
177
+ In `tests/retrofit/planners-claude.test.ts`, add:
178
+ ```ts
179
+ it('wires the chosen code-graph into .mcp.json', () => {
180
+ const mcp = planClaude(canon, '/t', false, 'serena').find(a => a.target === '.mcp.json')!
181
+ expect(mcp.content).toContain('serena')
182
+ expect(mcp.content).not.toContain('graphify')
183
+ })
184
+ ```
185
+ In `tests/retrofit/plan-dispatch.test.ts`, add:
186
+ ```ts
187
+ it('passes the code-graph choice to every planner', () => {
188
+ const actions = planRetrofit(canon, '/t', ['claude', 'codex', 'gemini'], 'serena')
189
+ const claudeMcp = actions.find(a => a.target === '.mcp.json')!
190
+ const codexToml = actions.find(a => a.target === '.codex/config.toml')!
191
+ const geminiSettings = actions.find(a => a.target === '.gemini/settings.json')!
192
+ expect(claudeMcp.content).toContain('serena')
193
+ expect(codexToml.content).toContain('mcp_servers.serena')
194
+ expect(geminiSettings.content).toContain('serena')
195
+ })
196
+ ```
197
+
198
+ - [ ] **Step 2: Run test to verify it fails**
199
+
200
+ Run: `npm test -- planners-claude plan-dispatch`
201
+ Expected: FAIL — planners don't accept/forward a code-graph choice.
202
+
203
+ - [ ] **Step 3: Modify the three planners to accept `codeGraph`**
204
+
205
+ In `src/retrofit/planners/claude.ts`: import the type and add a 4th param after `wslAvailable`, and pass it to `mcpServers`:
206
+ ```ts
207
+ import type { Action } from '../plan.js'
208
+ import type { CodeGraph } from '../config.js'
209
+ import { mcpServers, rtkInstruction } from '../tools.js'
210
+ import { hasWsl } from '../wsl.js'
211
+ // ...
212
+ export function planClaude(canonDir: string, _targetDir: string, wslAvailable: boolean = hasWsl(), codeGraph: CodeGraph = 'graphify'): Action[] {
213
+ ```
214
+ and change the `.mcp.json` action to `content: JSON.stringify({ mcpServers: mcpServers(codeGraph) }, null, 2) + '\n'` and its `reason` to `'MCP servers (code-graph + playwright)'`.
215
+
216
+ In `src/retrofit/planners/codex.ts`: thread `codeGraph` into `tomlMcp`:
217
+ ```ts
218
+ import type { CodeGraph } from '../config.js'
219
+ // ...
220
+ function tomlMcp(codeGraph: CodeGraph): string {
221
+ const servers = mcpServers(codeGraph)
222
+ // ... unchanged body ...
223
+ }
224
+
225
+ export function planCodex(canonDir: string, _targetDir: string, codeGraph: CodeGraph = 'graphify'): Action[] {
226
+ return [
227
+ // AGENTS.md action unchanged
228
+ {
229
+ kind: 'write',
230
+ target: '.codex/config.toml',
231
+ content: `# Forge: MCP servers for Codex. Merge into ~/.codex/config.toml.\n\n${tomlMcp(codeGraph)}`,
232
+ reason: 'MCP servers (code-graph + playwright)',
233
+ },
234
+ // RTK.md action unchanged
235
+ ]
236
+ }
237
+ ```
238
+
239
+ In `src/retrofit/planners/gemini.ts`: add the param and pass to `mcpServers`:
240
+ ```ts
241
+ import type { CodeGraph } from '../config.js'
242
+ // ...
243
+ export function planGemini(canonDir: string, _targetDir: string, codeGraph: CodeGraph = 'graphify'): Action[] {
244
+ ```
245
+ and change the `.gemini/settings.json` action to use `mcpServers: mcpServers(codeGraph)`.
246
+
247
+ - [ ] **Step 4: Modify `src/retrofit/plan.ts` dispatch**
248
+
249
+ Update the `AgentPlanner` type, registry (claude needs an adapter because its 3rd param is `wslAvailable`), and `planRetrofit`:
250
+ ```ts
251
+ import { planClaude } from './planners/claude.js'
252
+ import { planCodex } from './planners/codex.js'
253
+ import { planGemini } from './planners/gemini.js'
254
+ import type { Agent, CodeGraph } from './config.js'
255
+
256
+ // ... Action interface unchanged ...
257
+
258
+ export function planClaudeRetrofit(canonDir: string, targetDir: string): Action[] {
259
+ return planClaude(canonDir, targetDir)
260
+ }
261
+
262
+ export type AgentPlanner = (canonDir: string, targetDir: string, codeGraph: CodeGraph) => Action[]
263
+
264
+ export const PLANNERS: Record<Agent, AgentPlanner> = {
265
+ claude: (c, t, cg) => planClaude(c, t, undefined, cg),
266
+ codex: planCodex,
267
+ gemini: planGemini,
268
+ }
269
+
270
+ export function planRetrofit(canonDir: string, targetDir: string, agents: Agent[], codeGraph: CodeGraph = 'graphify'): Action[] {
271
+ const seen = new Set<string>()
272
+ const merged: Action[] = []
273
+ for (const agent of agents) {
274
+ for (const action of PLANNERS[agent](canonDir, targetDir, codeGraph)) {
275
+ if (seen.has(action.target)) continue
276
+ seen.add(action.target)
277
+ merged.push(action)
278
+ }
279
+ }
280
+ return merged
281
+ }
282
+ ```
283
+ Note: `planClaude(c, t, undefined, cg)` passes `undefined` for `wslAvailable`, which triggers its `= hasWsl()` default — so existing `planClaude(canon, '/t', true|false)` test calls are unaffected.
284
+
285
+ - [ ] **Step 5: Run test to verify it passes**
286
+
287
+ Run: `npm test -- planners-claude plan-dispatch retrofit/plan`
288
+ Expected: PASS — new code-graph tests and the existing B1/B2/C3 planner tests (which don't pass a code-graph and default to graphify).
289
+
290
+ - [ ] **Step 6: Commit**
291
+
292
+ ```bash
293
+ git add src/retrofit/planners/ src/retrofit/plan.ts tests/retrofit/planners-claude.test.ts tests/retrofit/plan-dispatch.test.ts
294
+ git commit -m "feat: thread code-graph choice through planners and dispatch"
295
+ ```
296
+
297
+ ---
298
+
299
+ ### Task 4: Resolve + record code-graph in runRetrofit; --code-graph flag
300
+
301
+ **Files:**
302
+ - Modify: `src/cli.ts`
303
+ - Test: `tests/retrofit/retrofit.integration.test.ts` (extend)
304
+
305
+ - [ ] **Step 1: Add failing tests**
306
+
307
+ Append to `tests/retrofit/retrofit.integration.test.ts` (inside the existing describe; `runRetrofit` and `loadConfig` are already imported):
308
+ ```ts
309
+ it('defaults codeGraph to graphify and records it', () => {
310
+ runRetrofit(target, { loop: false, agents: ['claude'] })
311
+ expect(loadConfig(target)!.codeGraph).toBe('graphify')
312
+ })
313
+
314
+ it('honors an explicit codeGraph and persists it', () => {
315
+ runRetrofit(target, { loop: false, agents: ['claude'], codeGraph: 'serena' })
316
+ expect(loadConfig(target)!.codeGraph).toBe('serena')
317
+ expect(existsSync(join(target, '.mcp.json'))).toBe(true)
318
+ })
319
+
320
+ it('keeps a previously-chosen codeGraph on a later run that does not specify one', () => {
321
+ runRetrofit(target, { loop: false, agents: ['claude'], codeGraph: 'serena' })
322
+ runRetrofit(target, { loop: false, agents: ['claude'] })
323
+ expect(loadConfig(target)!.codeGraph).toBe('serena')
324
+ })
325
+ ```
326
+
327
+ - [ ] **Step 2: Run test to verify it fails**
328
+
329
+ Run: `npm test -- retrofit.integration`
330
+ Expected: FAIL — `runRetrofit` does not accept/record `codeGraph`.
331
+
332
+ - [ ] **Step 3: Modify `src/cli.ts` `runRetrofit`**
333
+
334
+ Add the `CodeGraph` type to the config import:
335
+ ```ts
336
+ import { loadConfig, saveConfig, defaultConfig, type ForgeConfig, type CodeGraph } from './retrofit/config.js'
337
+ ```
338
+ Change the signature and body to resolve + thread + record the code-graph (flag > existing config > default graphify):
339
+ ```ts
340
+ export function runRetrofit(targetDir: string, opts: { loop: boolean; agents?: Agent[]; codeGraph?: CodeGraph }): number {
341
+ const canonDir = resolveCanonDir()
342
+ const canonVersion = loadManifest(join(canonDir, 'manifest.yaml')).version
343
+
344
+ const detection = detectProject(targetDir)
345
+ const agents: Agent[] = opts.agents && opts.agents.length > 0
346
+ ? opts.agents
347
+ : (detection.agents.length > 0 ? detection.agents : ['claude'])
348
+
349
+ const existing = loadConfig(targetDir)
350
+ const codeGraph: CodeGraph = opts.codeGraph ?? existing?.codeGraph ?? 'graphify'
351
+
352
+ const actions = planRetrofit(canonDir, targetDir, agents, codeGraph)
353
+ const backupDir = join(targetDir, '.forge', 'backup', String(Date.now()))
354
+ const applied = applyActions(actions, targetDir, { backupDir })
355
+
356
+ const priorAgents = existing?.agents ?? []
357
+ const mergedAgents = [...new Set([...priorAgents, ...agents])]
358
+ const config: ForgeConfig = {
359
+ ...(existing ?? defaultConfig(canonVersion)),
360
+ canonVersion,
361
+ agents: mergedAgents,
362
+ loop: { enabled: opts.loop },
363
+ codeGraph,
364
+ }
365
+ saveConfig(targetDir, config)
366
+
367
+ console.log(formatReport(applied, { loopEnabled: config.loop.enabled, detectedAgents: detection.agents }))
368
+ return 0
369
+ }
370
+ ```
371
+
372
+ - [ ] **Step 4: Parse `--code-graph=` in the retrofit CLI branch**
373
+
374
+ In `main`'s `retrofit` case, after parsing `agents`, add:
375
+ ```ts
376
+ const cgArg = rest.find(a => a.startsWith('--code-graph='))?.slice('--code-graph='.length)
377
+ const codeGraph = cgArg === 'serena' || cgArg === 'graphify' ? cgArg : undefined
378
+ if (cgArg && !codeGraph) {
379
+ console.error(`Invalid --code-graph value: ${cgArg} (expected graphify|serena)`)
380
+ return 1
381
+ }
382
+ return runRetrofit(targetDir, { loop, agents, codeGraph })
383
+ ```
384
+ (Replace the existing `return runRetrofit(targetDir, { loop, agents })` line.) Update the `default` usage line to include the flag:
385
+ ```ts
386
+ console.log('usage: forge <validate [canonDir] | retrofit [targetDir] [--agent=claude,codex,gemini|all] [--code-graph=graphify|serena] [--loop] | loop <on|off|status|run>>')
387
+ ```
388
+
389
+ - [ ] **Step 5: Run test to verify it passes + build**
390
+
391
+ Run: `npm test -- retrofit.integration`
392
+ Expected: PASS (existing + 3 new).
393
+
394
+ Run: `npm run build`
395
+ Expected: tsc 0 errors.
396
+
397
+ - [ ] **Step 6: Commit**
398
+
399
+ ```bash
400
+ git add src/cli.ts tests/retrofit/retrofit.integration.test.ts
401
+ git commit -m "feat: resolve and persist code-graph choice; --code-graph flag"
402
+ ```
403
+
404
+ ---
405
+
406
+ ### Task 5: Canon content — minimal-code skill, serena doc, retrofit-skill guidance
407
+
408
+ **Files:**
409
+ - Create: `canon/skills/minimal-code/SKILL.md`, `canon/tools/serena.md`
410
+ - Modify: `canon/manifest.yaml`, `canon/skills/forge-retrofit/SKILL.md`
411
+ - Test: `tests/canon/real-canon.test.ts` (already validates the whole canon)
412
+
413
+ - [ ] **Step 1: Add the new canon entries to `canon/manifest.yaml`**
414
+
415
+ Under `skills:` add:
416
+ ```yaml
417
+ - { id: minimal-code, path: skills/minimal-code, kind: methodology }
418
+ ```
419
+ Under `tools:` add:
420
+ ```yaml
421
+ - { id: serena, path: tools/serena.md }
422
+ ```
423
+
424
+ - [ ] **Step 2: Create `canon/skills/minimal-code/SKILL.md`**
425
+
426
+ ```markdown
427
+ ---
428
+ name: minimal-code
429
+ description: Use before writing any code — write the least code that fully solves the task (YAGNI, stdlib-first, no unrequested abstractions) to save tokens and reduce maintenance.
430
+ ---
431
+
432
+ # Minimal Code (lazy senior dev)
433
+
434
+ The best code is the code you never wrote. Before writing anything, walk this ladder and stop at the first rung that solves the task:
435
+
436
+ 1. **Does it already exist?** Reuse an existing function, file, or builtin before writing new code.
437
+ 2. **Can the language/stdlib do it?** Prefer the standard library and built-in platform features over a dependency or a hand-rolled version.
438
+ 3. **Is the abstraction requested?** Do not add layers, config, interfaces, or generality nobody asked for. Solve the concrete case.
439
+ 4. **Is it the shortest correct version?** Prefer deletion over addition, boring over clever, one obvious path over branching flexibility.
440
+ 5. **Did the task actually ask for this?** Build only what was requested — no speculative features (YAGNI).
441
+
442
+ Rules:
443
+ - Deletion over addition. Boring over clever. No abstractions that were not requested.
444
+ - Prefer the standard library and existing code over new code or new dependencies.
445
+ - Mark an intentional simplification with a short `minimal-code:` comment so reviewers see it was deliberate.
446
+
447
+ This saves tokens (less generated code), shrinks the review surface, and lowers maintenance — complementary to rtk, which compresses command output. Adapted from the MIT-licensed "ponytail" ruleset (github.com/DietrichGebert/ponytail).
448
+ ```
449
+
450
+ - [ ] **Step 3: Create `canon/tools/serena.md`**
451
+
452
+ ```markdown
453
+ # Tool: Serena (code-graph, LSP-accurate)
454
+
455
+ MIT, MCP-first. The alternative to graphify, selected via `forge retrofit --code-graph=serena`. Serena uses real language servers (LSP) for symbol-accurate, cross-file retrieval and refactoring (`find_symbol`, `find_referencing_symbols`, rename/move) — no static index that goes stale, so it will not miss a reference.
456
+
457
+ Wired as an MCP server for all three agents. Best for large, strongly-typed codebases (TypeScript, Python, Go) doing systematic refactoring, where missing a caller is costly.
458
+
459
+ Caveat: needs one language server per language (can be fiddly on Windows for exotic languages) and requires `uv`. The launch command is a best-effort template — adjust to your install, e.g. `uvx --from git+https://github.com/oraios/serena serena-mcp-server`.
460
+ ```
461
+
462
+ - [ ] **Step 4: Update `canon/skills/forge-retrofit/SKILL.md`**
463
+
464
+ Replace its body with guidance that adds the code-graph question and the minimal-code mention:
465
+ ```markdown
466
+ ---
467
+ name: forge-retrofit
468
+ description: Use when asked to "retrofit", "forge this project", or set up the Forge harness in a project — runs forge retrofit, picks a code-graph tool, and asks whether to enable the autonomous loop.
469
+ ---
470
+
471
+ # Forge Retrofit
472
+
473
+ Set up (or update) the Forge harness in the current project.
474
+
475
+ 1. **Choose the code-graph tool.** Ask the user which to wire, and recommend based on the project:
476
+ - **Serena** (LSP-accurate, symbol-exact refactoring, no stale index) — recommend for large, strongly-typed codebases (TypeScript, Python, Go) doing systematic refactoring, where missing a reference is costly. Needs one language server per language.
477
+ - **graphify** (fast, multimodal: code + PDFs + diagrams + images; ~70x token reduction on large mixed repos; honest INFERRED/AMBIGUOUS edges) — recommend for rapid exploration / migration / onboarding of large or unfamiliar repos, or repos with mixed non-code content.
478
+ Make a direct recommendation for THIS project, then run with `--code-graph=serena` or `--code-graph=graphify` (default graphify if the user has no preference). The choice is saved in `.forge/config.yaml`.
479
+ 2. Run `forge retrofit . --agent=all --code-graph=<choice>` (or a subset of agents). Non-destructive — existing files are backed up under `.forge/backup/` before any overwrite; `.claude/settings.json` is merged, not replaced. Generated per agent: Claude (`.claude/skills/`, `AGENTS.md`, `CLAUDE.md`, `.mcp.json`, rtk hook when WSL is available); Codex (`AGENTS.md`, `.codex/config.toml`, `RTK.md`); Gemini (`GEMINI.md`, `.gemini/commands/*.toml`, `.gemini/settings.json`).
480
+ 3. **Ask whether to enable the autonomous Loop** (default off). If yes, add `--loop`. Toggle any time with `forge loop on|off`.
481
+ 4. Show the printed report (created/overwritten/unchanged/merged + detected agents) and where backups went. Note that the generated MCP launch commands may need adjusting to the user's local tool installs.
482
+
483
+ The harness includes a `minimal-code` skill (YAGNI / lazy-senior-dev) that nudges every agent to write the least code that solves the task — saving tokens and reducing maintenance.
484
+ ```
485
+
486
+ - [ ] **Step 5: Validate the canon + full suite + build**
487
+
488
+ Run: `npm run forge -- validate canon`
489
+ Expected: `✓ canon valid (canon)`.
490
+
491
+ Run: `npm test`
492
+ Expected: all pass (real-canon now validates the canon with `minimal-code` + `serena`).
493
+
494
+ Run: `npm run build`
495
+ Expected: tsc 0 errors.
496
+
497
+ - [ ] **Step 6: Commit**
498
+
499
+ ```bash
500
+ git add canon/manifest.yaml canon/skills/minimal-code canon/tools/serena.md canon/skills/forge-retrofit/SKILL.md
501
+ git commit -m "feat: add minimal-code skill and serena tool; retrofit asks code-graph"
502
+ ```
503
+
504
+ ---
505
+
506
+ ## Self-Review
507
+
508
+ **1. Spec coverage (Baustein D):**
509
+ - Code-graph is a per-project choice (graphify | serena) → Tasks 1–4 ✓
510
+ - Selectable via `--code-graph`, recorded in `.forge/config.yaml`, default graphify, sticky across runs → Task 4 ✓
511
+ - Wired into the generated MCP config for all three agents → Tasks 2, 3 ✓
512
+ - forge-retrofit skill asks the user + recommends per project → Task 5 ✓
513
+ - minimal-code (ponytail-derived, credited) canon skill flows to all agents → Task 5 ✓
514
+ - serena documented in the canon → Task 5 ✓
515
+
516
+ **2. Placeholder scan:** No TBD/TODO. MCP launch commands are explicitly labelled best-effort templates (honest, not placeholders). Every step has complete code/content.
517
+
518
+ **3. Type consistency:** `CodeGraph` defined once in config.ts and imported by tools/planners/plan/cli. `mcpServers(codeGraph='graphify')`. Planner signatures: `planClaude(c,t,wslAvailable?,codeGraph?)`, `planCodex(c,t,codeGraph?)`, `planGemini(c,t,codeGraph?)`; `AgentPlanner=(c,t,codeGraph)=>Action[]` with a claude adapter passing `undefined` for wslAvailable (so existing C3 wslAvailable tests are unaffected). `planRetrofit(c,t,agents,codeGraph?)`. `runRetrofit` resolves flag>config>default and persists. The minimal-code skill needs no code — the existing planners propagate it. ✓
519
+
520
+ ## Notes
521
+
522
+ - ponytail build-vs-buy: BUILD chosen — Forge is itself a canon-injection harness, so the library's cross-agent install/propagation value is redundant; the MIT ruleset idea is captured natively and tailorably as a canon skill, credited.
523
+ - Serena's launch command and graphify's are best-effort; the retrofit report already warns users to adjust MCP commands to their installs.