@izkac/forgekit 0.1.1 → 0.1.3
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/README.md +34 -0
- package/package.json +5 -2
- package/src/init.mjs +26 -58
- package/src/install.mjs +31 -75
- package/src/session-reminder.mjs +1 -1
- package/src/triage-prompt.mjs +1 -1
- package/vendor/skills/forge/SKILL.md +3 -3
- package/vendor/skills/forge/docs/forge.md +621 -0
- package/vendor/skills/forge/references/forge-layout.md +1 -1
- package/vendor/skills/forge/references/pace.md +1 -1
- package/vendor/skills/thorough-code-review/SKILL.md +1 -1
- package/vendor/templates/project/claude/commands/forge-apply.md +1 -1
- package/vendor/templates/project/claude/commands/forge-status.md +1 -1
- package/vendor/templates/project/claude/commands/forge.md +1 -1
- package/vendor/templates/project/claude/rules/forge.md +1 -1
- package/vendor/templates/project/codex/rules/forge.md +1 -1
- package/vendor/templates/project/cursor/commands/forge-apply.md +1 -1
- package/vendor/templates/project/cursor/commands/forge-status.md +1 -1
- package/vendor/templates/project/cursor/commands/forge.md +1 -1
- package/vendor/templates/project/cursor/rules/forge.mdc +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# @izkac/forgekit
|
|
2
|
+
|
|
3
|
+
Portable agent skills + CLIs. One package, three bins:
|
|
4
|
+
|
|
5
|
+
| Bin | Role |
|
|
6
|
+
|-----|------|
|
|
7
|
+
| **`forgekit`** | Install / list / update / uninstall skills × agents |
|
|
8
|
+
| **`forge`** | Forge workflow sessions |
|
|
9
|
+
| **`review`** | Thorough code review pipeline |
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm i -g @izkac/forgekit
|
|
15
|
+
forgekit install
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Interactive install lets you pick **one or more** skills (comma-separated, e.g. `1,3`) or all. Same for agent environments.
|
|
19
|
+
|
|
20
|
+
Non-interactive:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
forgekit install --skills forge,thorough-code-review --agents cursor,claude --force
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Docs
|
|
27
|
+
|
|
28
|
+
- After install, full Forge reference: `~/.cursor/skills/forge/docs/forge.md` (or `~/.claude/…` / `~/.codex/…`)
|
|
29
|
+
- [How to use Forgekit](https://github.com/izkac/forgekit/blob/main/docs/usage.md)
|
|
30
|
+
- [Repository README](https://github.com/izkac/forgekit#readme)
|
|
31
|
+
|
|
32
|
+
## License
|
|
33
|
+
|
|
34
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@izkac/forgekit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Forgekit CLIs — forgekit (install), forge (workflow), review (code review)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -42,5 +42,8 @@
|
|
|
42
42
|
"claude",
|
|
43
43
|
"codex"
|
|
44
44
|
],
|
|
45
|
-
"license": "MIT"
|
|
45
|
+
"license": "MIT",
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@inquirer/prompts": "^8.5.2"
|
|
48
|
+
}
|
|
46
49
|
}
|
package/src/init.mjs
CHANGED
|
@@ -11,10 +11,9 @@
|
|
|
11
11
|
|
|
12
12
|
import fs from 'node:fs';
|
|
13
13
|
import path from 'node:path';
|
|
14
|
-
import readline from 'node:readline/promises';
|
|
15
14
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
16
15
|
import { spawnSync } from 'node:child_process';
|
|
17
|
-
import {
|
|
16
|
+
import { checkbox, confirm, input } from '@inquirer/prompts';
|
|
18
17
|
import {
|
|
19
18
|
DEFAULT_ADR_DIR,
|
|
20
19
|
disableProjectAdr,
|
|
@@ -391,30 +390,15 @@ export function initProject(selected, opts) {
|
|
|
391
390
|
}
|
|
392
391
|
|
|
393
392
|
async function promptAgents() {
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
if (!answer || answer === '4') return ['cursor', 'claude', 'codex'];
|
|
404
|
-
const map = { 1: 'cursor', 2: 'claude', 3: 'codex' };
|
|
405
|
-
const picked = [
|
|
406
|
-
...new Set(
|
|
407
|
-
answer
|
|
408
|
-
.split(/[,\s]+/)
|
|
409
|
-
.map((s) => map[s.trim()])
|
|
410
|
-
.filter(Boolean),
|
|
411
|
-
),
|
|
412
|
-
];
|
|
413
|
-
if (picked.length === 0) throw new Error('No agents selected');
|
|
414
|
-
return picked;
|
|
415
|
-
} finally {
|
|
416
|
-
rl.close();
|
|
417
|
-
}
|
|
393
|
+
return checkbox({
|
|
394
|
+
message: 'Init Forge project wiring for which environments?',
|
|
395
|
+
choices: [
|
|
396
|
+
{ value: 'cursor', name: 'Cursor' },
|
|
397
|
+
{ value: 'claude', name: 'Claude Code' },
|
|
398
|
+
{ value: 'codex', name: 'Codex CLI' },
|
|
399
|
+
],
|
|
400
|
+
required: true,
|
|
401
|
+
});
|
|
418
402
|
}
|
|
419
403
|
|
|
420
404
|
/**
|
|
@@ -422,19 +406,11 @@ async function promptAgents() {
|
|
|
422
406
|
* @returns {Promise<boolean>} true = user accepted OpenSpec setup
|
|
423
407
|
*/
|
|
424
408
|
async function promptOpenSpecSetup() {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
)
|
|
431
|
-
)
|
|
432
|
-
.trim()
|
|
433
|
-
.toLowerCase();
|
|
434
|
-
return !(yn === 'n' || yn === 'no');
|
|
435
|
-
} finally {
|
|
436
|
-
rl.close();
|
|
437
|
-
}
|
|
409
|
+
return confirm({
|
|
410
|
+
message:
|
|
411
|
+
'OpenSpec is not set up in this project. Install and set it up now? (No = built-in specs engine)',
|
|
412
|
+
default: true,
|
|
413
|
+
});
|
|
438
414
|
}
|
|
439
415
|
|
|
440
416
|
/**
|
|
@@ -493,25 +469,16 @@ async function resolveInitPlanEngine(opts) {
|
|
|
493
469
|
* @returns {Promise<{ enabled: boolean, dir: string }>}
|
|
494
470
|
*/
|
|
495
471
|
async function promptAdrForInit(defaultDir = DEFAULT_ADR_DIR) {
|
|
496
|
-
const
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
enabled = yn === 'y' || yn === 'yes';
|
|
507
|
-
if (!enabled) return { enabled: false, dir: defaultDir };
|
|
508
|
-
const dirAnswer = (
|
|
509
|
-
await rl.question(`ADR directory inside the repo [${defaultDir}]: `)
|
|
510
|
-
).trim();
|
|
511
|
-
return { enabled: true, dir: normalizeAdrDir(dirAnswer || defaultDir) };
|
|
512
|
-
} finally {
|
|
513
|
-
rl.close();
|
|
514
|
-
}
|
|
472
|
+
const enabled = await confirm({
|
|
473
|
+
message: 'Use Architecture Decision Records (ADRs) in this project?',
|
|
474
|
+
default: false,
|
|
475
|
+
});
|
|
476
|
+
if (!enabled) return { enabled: false, dir: defaultDir };
|
|
477
|
+
const dir = await input({
|
|
478
|
+
message: 'ADR directory inside the repo',
|
|
479
|
+
default: defaultDir,
|
|
480
|
+
});
|
|
481
|
+
return { enabled: true, dir: normalizeAdrDir(dir.trim() || defaultDir) };
|
|
515
482
|
}
|
|
516
483
|
|
|
517
484
|
async function main(argv = process.argv.slice(2)) {
|
|
@@ -569,6 +536,7 @@ if (isDirect) {
|
|
|
569
536
|
main()
|
|
570
537
|
.then((code) => process.exit(code))
|
|
571
538
|
.catch((err) => {
|
|
539
|
+
if (err?.name === 'ExitPromptError') process.exit(130);
|
|
572
540
|
process.stderr.write(`${err.message || err}\n`);
|
|
573
541
|
process.exit(1);
|
|
574
542
|
});
|
package/src/install.mjs
CHANGED
|
@@ -16,9 +16,8 @@
|
|
|
16
16
|
import fs from 'node:fs';
|
|
17
17
|
import os from 'node:os';
|
|
18
18
|
import path from 'node:path';
|
|
19
|
-
import readline from 'node:readline/promises';
|
|
20
19
|
import { pathToFileURL } from 'node:url';
|
|
21
|
-
import {
|
|
20
|
+
import { checkbox, confirm, input, select } from '@inquirer/prompts';
|
|
22
21
|
import {
|
|
23
22
|
ADR_SKILLS,
|
|
24
23
|
DEFAULT_ADR_DIR,
|
|
@@ -414,51 +413,27 @@ export function listInstallStatus(opts = {}) {
|
|
|
414
413
|
}
|
|
415
414
|
|
|
416
415
|
/**
|
|
417
|
-
* @param {string}
|
|
418
|
-
* @param {
|
|
419
|
-
* @param {string[]} allIds
|
|
416
|
+
* @param {string} message
|
|
417
|
+
* @param {string[]} ids
|
|
420
418
|
* @returns {Promise<string[]>}
|
|
421
419
|
*/
|
|
422
|
-
async function promptMulti(
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
}
|
|
432
|
-
const allNum = String(entries.length + 1);
|
|
433
|
-
process.stdout.write(` ${allNum}) All\n`);
|
|
434
|
-
process.stdout.write(
|
|
435
|
-
`Enter numbers separated by commas (e.g. 1,2) or ${allNum} for all: `,
|
|
436
|
-
);
|
|
437
|
-
const answer = (await rl.question('')).trim();
|
|
438
|
-
if (!answer || answer === allNum) return [...allIds];
|
|
439
|
-
const picked = [
|
|
440
|
-
...new Set(
|
|
441
|
-
answer
|
|
442
|
-
.split(/[,\s]+/)
|
|
443
|
-
.map((s) => map[s.trim()])
|
|
444
|
-
.filter(Boolean),
|
|
445
|
-
),
|
|
446
|
-
];
|
|
447
|
-
if (picked.length === 0) throw new Error('Nothing selected');
|
|
448
|
-
return picked;
|
|
449
|
-
} finally {
|
|
450
|
-
rl.close();
|
|
451
|
-
}
|
|
420
|
+
async function promptMulti(message, ids) {
|
|
421
|
+
return checkbox({
|
|
422
|
+
message,
|
|
423
|
+
choices: ids.map((id) => ({
|
|
424
|
+
value: id,
|
|
425
|
+
name: SKILLS[id]?.label ?? AGENTS[id]?.label ?? id,
|
|
426
|
+
})),
|
|
427
|
+
required: true,
|
|
428
|
+
});
|
|
452
429
|
}
|
|
453
430
|
|
|
454
431
|
async function promptSkills() {
|
|
455
|
-
|
|
456
|
-
return promptMulti('Install which skills?', map, SKILL_IDS);
|
|
432
|
+
return promptMulti('Install which skills?', SKILL_IDS);
|
|
457
433
|
}
|
|
458
434
|
|
|
459
435
|
async function promptAgents() {
|
|
460
|
-
|
|
461
|
-
return promptMulti('Install for which environments?', map, AGENT_IDS);
|
|
436
|
+
return promptMulti('Install for which environments?', AGENT_IDS);
|
|
462
437
|
}
|
|
463
438
|
|
|
464
439
|
/**
|
|
@@ -466,35 +441,21 @@ async function promptAgents() {
|
|
|
466
441
|
* @returns {Promise<string>}
|
|
467
442
|
*/
|
|
468
443
|
export async function promptAdrDir(defaultDir = DEFAULT_ADR_DIR) {
|
|
469
|
-
const
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
return normalizeAdrDir(dirAnswer || defaultDir);
|
|
475
|
-
} finally {
|
|
476
|
-
rl.close();
|
|
477
|
-
}
|
|
444
|
+
const dir = await input({
|
|
445
|
+
message: 'ADR directory inside each repo',
|
|
446
|
+
default: defaultDir,
|
|
447
|
+
});
|
|
448
|
+
return normalizeAdrDir(dir.trim() || defaultDir);
|
|
478
449
|
}
|
|
479
450
|
|
|
480
451
|
/**
|
|
481
452
|
* @returns {Promise<{ enabled: boolean, dir: string }>}
|
|
482
453
|
*/
|
|
483
454
|
export async function promptAdrOptions() {
|
|
484
|
-
const
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
await rl.question(
|
|
489
|
-
'Use Architecture Decision Records (ADRs) after OpenSpec archive? [y/N] ',
|
|
490
|
-
)
|
|
491
|
-
)
|
|
492
|
-
.trim()
|
|
493
|
-
.toLowerCase();
|
|
494
|
-
enabled = yn === 'y' || yn === 'yes';
|
|
495
|
-
} finally {
|
|
496
|
-
rl.close();
|
|
497
|
-
}
|
|
455
|
+
const enabled = await confirm({
|
|
456
|
+
message: 'Use Architecture Decision Records (ADRs) after OpenSpec archive?',
|
|
457
|
+
default: false,
|
|
458
|
+
});
|
|
498
459
|
if (!enabled) return { enabled: false, dir: DEFAULT_ADR_DIR };
|
|
499
460
|
const dir = await promptAdrDir(DEFAULT_ADR_DIR);
|
|
500
461
|
return { enabled: true, dir };
|
|
@@ -504,19 +465,13 @@ export async function promptAdrOptions() {
|
|
|
504
465
|
* @returns {Promise<boolean>} true = OpenSpec, false = built-in specs engine
|
|
505
466
|
*/
|
|
506
467
|
export async function promptOpenSpec() {
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
.trim()
|
|
515
|
-
.toLowerCase();
|
|
516
|
-
return !(yn === 'n' || yn === 'no');
|
|
517
|
-
} finally {
|
|
518
|
-
rl.close();
|
|
519
|
-
}
|
|
468
|
+
return select({
|
|
469
|
+
message: 'Planning engine?',
|
|
470
|
+
choices: [
|
|
471
|
+
{ value: true, name: 'OpenSpec (vendor CLI)' },
|
|
472
|
+
{ value: false, name: 'Built-in specs engine' },
|
|
473
|
+
],
|
|
474
|
+
});
|
|
520
475
|
}
|
|
521
476
|
|
|
522
477
|
/**
|
|
@@ -771,6 +726,7 @@ if (isDirect) {
|
|
|
771
726
|
runInstall()
|
|
772
727
|
.then((code) => process.exit(code))
|
|
773
728
|
.catch((err) => {
|
|
729
|
+
if (err?.name === 'ExitPromptError') process.exit(130);
|
|
774
730
|
process.stderr.write(`${err.message || err}\n`);
|
|
775
731
|
process.exit(1);
|
|
776
732
|
});
|
package/src/session-reminder.mjs
CHANGED
|
@@ -88,7 +88,7 @@ export function buildForgeMessage(info) {
|
|
|
88
88
|
lines.push('Resume: invoke the forge skill for the current phase.');
|
|
89
89
|
lines.push('Honor pace: see forge references/pace.md (`forge prefs`).');
|
|
90
90
|
lines.push('Skip Forge for this task only: /forge:skip');
|
|
91
|
-
lines.push('Guide: Forge skill +
|
|
91
|
+
lines.push('Guide: Forge skill + docs/forge.md (under the installed forge skill)');
|
|
92
92
|
return lines.join('\n');
|
|
93
93
|
}
|
|
94
94
|
|
package/src/triage-prompt.mjs
CHANGED
|
@@ -91,7 +91,7 @@ export function buildForgeTriageMessage(options = {}) {
|
|
|
91
91
|
for (const line of sessionLines) lines.push(` ${line}`);
|
|
92
92
|
}
|
|
93
93
|
lines.push('3. Skip Forge for this task only: `/forge:skip`');
|
|
94
|
-
lines.push('Guide: Forge skill +
|
|
94
|
+
lines.push('Guide: Forge skill + docs/forge.md (under the installed forge skill)');
|
|
95
95
|
return lines.join('\n');
|
|
96
96
|
}
|
|
97
97
|
|
|
@@ -17,7 +17,7 @@ Spec-tracked development pipeline. Planning engine is per-project
|
|
|
17
17
|
all workflow skills live under `./skills/` (vendored from Superpowers MIT; see
|
|
18
18
|
[skills/NOTICE.md](./skills/NOTICE.md)).
|
|
19
19
|
|
|
20
|
-
Full reference:
|
|
20
|
+
Full reference: [docs/forge.md](./docs/forge.md) (ships with this skill).
|
|
21
21
|
|
|
22
22
|
**Announce at start:** "Using Forge for this work." Include effective pace from
|
|
23
23
|
`forge status` (e.g. `Pace: auto → brisk (…)`) — see [references/pace.md](./references/pace.md).
|
|
@@ -43,7 +43,7 @@ forge doctor # plan-engine readiness (OpenSpec CLI or spe
|
|
|
43
43
|
|
|
44
44
|
Honor [references/pace.md](./references/pace.md) in implement / verify / review.
|
|
45
45
|
Hard floor: money/auth/contracts/migrations always get per-task review (even under `standard` mid-group / `brisk` / `lite`).
|
|
46
|
-
Local overlays:
|
|
46
|
+
Local overlays: [docs/forge.md](./docs/forge.md) § Checkout-local overrides.
|
|
47
47
|
|
|
48
48
|
## Bundled skills
|
|
49
49
|
|
|
@@ -133,4 +133,4 @@ Testing: [references/test-strategy.md](./references/test-strategy.md) — tier 1
|
|
|
133
133
|
|
|
134
134
|
## Do not edit vendor OpenSpec skills
|
|
135
135
|
|
|
136
|
-
OpenSpec vendor skills upgrade in place. Forge behaviour lives in this tree and
|
|
136
|
+
OpenSpec vendor skills upgrade in place. Forge behaviour lives in this tree and [docs/forge.md](./docs/forge.md). Re-apply vendor patches with `forge overlay` after OpenSpec upgrades.
|
|
@@ -0,0 +1,621 @@
|
|
|
1
|
+
# Forge — disciplined development workflow
|
|
2
|
+
|
|
3
|
+
Forge is an **OpenSpec-native**, **self-contained** development pipeline.
|
|
4
|
+
All workflow skills (brainstorm, TDD, subagents, verify, review) live under the
|
|
5
|
+
Forge skill’s `skills/` folder — **no Superpowers plugin required**.
|
|
6
|
+
|
|
7
|
+
It does not stop at green unit tests or checked-off tasks: **runtime integrity**
|
|
8
|
+
requires a named production path for every claimed capability, and (for
|
|
9
|
+
jobs/workers) a closed product loop before `forge phase done`. See
|
|
10
|
+
[Runtime integrity](#runtime-integrity).
|
|
11
|
+
|
|
12
|
+
**Using Forgekit for the first time?** Start with the tutorial:
|
|
13
|
+
[**How to use Forgekit**](https://github.com/izkac/forgekit/blob/main/docs/usage.md) (install → init → `/forge:apply` → examples).
|
|
14
|
+
|
|
15
|
+
**Skill:** `forge` (Cursor, Claude Code, Codex CLI)
|
|
16
|
+
**Commands:** `/forge`, `/forge:*` (after `forge init`; Cursor and Claude Code)
|
|
17
|
+
**Scratch space:** `.forge/` (gitignored except README)
|
|
18
|
+
**CLI:** `@izkac/forgekit` → `forgekit` (install) · `forge` (workflow) · `review` (standalone deep review)
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# Preferred — once per machine
|
|
26
|
+
npm i -g @izkac/forgekit
|
|
27
|
+
forgekit install --skills forge --agents cursor,claude
|
|
28
|
+
# or: forge install # alias → --skills forge
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# Once per project
|
|
33
|
+
forge init --cursor --claude # commands, rules, hooks, .forge/
|
|
34
|
+
forge init --overlay # optional OpenSpec vendor patches
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Hooks call `forge` on PATH. Merge the generated `forge-hooks.snippet.json`
|
|
38
|
+
into your agent settings if hooks are not picked up automatically.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## When Forge runs
|
|
43
|
+
|
|
44
|
+
**Default:** agents **triage** every task. Substantial work enters Forge
|
|
45
|
+
automatically unless the user explicitly skips with **`/forge:skip`**.
|
|
46
|
+
|
|
47
|
+
### Substantial work (enter Forge)
|
|
48
|
+
|
|
49
|
+
**Forge = OpenSpec.** Enter the full flow only when the work warrants a tracked OpenSpec change — when **any** of:
|
|
50
|
+
|
|
51
|
+
- New feature or behavior change
|
|
52
|
+
- Multi-file or multi-package change
|
|
53
|
+
- Public API, contract, or config schema change
|
|
54
|
+
- Cross-product / ecosystem impact
|
|
55
|
+
- User invokes `/forge`, `/forge:brainstorm`, `/forge:plan`, or `/forge:build`
|
|
56
|
+
- Work would likely need an ADR or new OpenSpec capability when done
|
|
57
|
+
|
|
58
|
+
### Trivial work (skip Forge)
|
|
59
|
+
|
|
60
|
+
Execute directly when **all** of:
|
|
61
|
+
|
|
62
|
+
- Question, explanation, or read-only review
|
|
63
|
+
- Typo, comment, or purely cosmetic edit
|
|
64
|
+
- Single localized change with no contract impact
|
|
65
|
+
- User explicitly says **`/forge:skip`** for this task
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## End-to-end decision tree
|
|
70
|
+
|
|
71
|
+
This doc uses an **ASCII tree only** — no Mermaid. Lightweight viewers (e.g.
|
|
72
|
+
MarkView) render Mermaid flowcharts with broken fills and connectors; ASCII
|
|
73
|
+
works everywhere.
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
User request
|
|
77
|
+
│
|
|
78
|
+
├─ /forge:skip? ── yes ──► Direct execution (no Forge session)
|
|
79
|
+
│
|
|
80
|
+
└─ no
|
|
81
|
+
│
|
|
82
|
+
└─ OpenSpec-worthy / substantial? ── no ──► Direct execution
|
|
83
|
+
│
|
|
84
|
+
yes
|
|
85
|
+
│
|
|
86
|
+
▼
|
|
87
|
+
Start / resume Forge session
|
|
88
|
+
│
|
|
89
|
+
▼
|
|
90
|
+
Phase: Brainstorm
|
|
91
|
+
│
|
|
92
|
+
├─ design not approved ──► (loop back to Brainstorm)
|
|
93
|
+
│
|
|
94
|
+
└─ design approved
|
|
95
|
+
│
|
|
96
|
+
▼
|
|
97
|
+
Phase: Plan (OpenSpec)
|
|
98
|
+
│
|
|
99
|
+
▼
|
|
100
|
+
/opsx:propose
|
|
101
|
+
openspec/changes/<name>/
|
|
102
|
+
│
|
|
103
|
+
▼
|
|
104
|
+
Phase: Implement
|
|
105
|
+
│
|
|
106
|
+
┌─────────────┴─────────────┐
|
|
107
|
+
│ PER TASK (loop until done) │
|
|
108
|
+
│ 1. Subagent implementer │
|
|
109
|
+
│ 2. TDD (scoped tests) │
|
|
110
|
+
│ 3. Reviewer (pace): │
|
|
111
|
+
│ thorough=per task │
|
|
112
|
+
│ standard=per group │
|
|
113
|
+
│ └─ fail ──► retry │
|
|
114
|
+
│ (tier 2 narrow evidence) │
|
|
115
|
+
└─────────────┬─────────────┘
|
|
116
|
+
▼
|
|
117
|
+
Verify: audit tier 2 + tier 3 (scope from pace)
|
|
118
|
+
+ ## Product loop (or BLOCKED)
|
|
119
|
+
+ forge integrity-check
|
|
120
|
+
│
|
|
121
|
+
▼
|
|
122
|
+
Final review (pace) — spine + product loop
|
|
123
|
+
│
|
|
124
|
+
▼
|
|
125
|
+
/opsx:archive (+ project ADR follow-up if any)
|
|
126
|
+
│
|
|
127
|
+
▼
|
|
128
|
+
forge phase done ← integrity gate (refuses if incomplete)
|
|
129
|
+
Done + cleanup .forge session
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Jobs / workers / queues:** spine is mandatory for *every* change (`forge spine
|
|
133
|
+
init` — rows or `notApplicable`). Async work also needs wiring + product-loop
|
|
134
|
+
tasks. See [Runtime integrity](#runtime-integrity).
|
|
135
|
+
|
|
136
|
+
### Triage (top of tree)
|
|
137
|
+
|
|
138
|
+
| Check | Outcome |
|
|
139
|
+
| ----- | ------- |
|
|
140
|
+
| User sent `/forge:skip` | Direct execution |
|
|
141
|
+
| Not substantial / not tracked-change-worthy | Direct execution |
|
|
142
|
+
| Otherwise | Enter Forge session (tracked change) |
|
|
143
|
+
|
|
144
|
+
### Planning engine (per project)
|
|
145
|
+
|
|
146
|
+
Forge always produces a tracked change; the **engine** is project config
|
|
147
|
+
(`.forge/config.json` → `plan.engine`, set by `forge init`):
|
|
148
|
+
|
|
149
|
+
| Engine | Change location | Tooling |
|
|
150
|
+
| ------ | --------------- | ------- |
|
|
151
|
+
| `openspec` | `openspec/changes/<name>/` | OpenSpec CLI + `/opsx:*` vendor skills |
|
|
152
|
+
| `specs` | `specs/changes/<name>/` (dir from `plan.dir`) | Built-in — plain markdown, same layout |
|
|
153
|
+
|
|
154
|
+
Selection flow: `forgekit install` asks once for a user default
|
|
155
|
+
(`~/.forgekit/config.json` → `plan.engine`); `forge init` auto-detects
|
|
156
|
+
(`openspec/config.yaml` present → openspec, silent), otherwise offers to
|
|
157
|
+
install + `openspec init`, and falls back to the specs engine on decline
|
|
158
|
+
(`--openspec` / `--no-openspec` skip prompts). Migration later: `openspec
|
|
159
|
+
init`, then move `specs/changes/*` into `openspec/changes/`.
|
|
160
|
+
|
|
161
|
+
### Planning (after brainstorm)
|
|
162
|
+
|
|
163
|
+
Proceed directly to the configured engine's propose flow — do not ask for a plan mode.
|
|
164
|
+
|
|
165
|
+
| Step | What happens |
|
|
166
|
+
| ---- | ------------ |
|
|
167
|
+
| **Propose** | `/opsx:propose` → `openspec/changes/<name>/` (openspec) or author `specs/changes/<name>/{proposal,tasks}.md` per [plan-specs.md](../phases/plan-specs.md) (specs) |
|
|
168
|
+
| **User approval** | Confirm proposal, design, tasks before implement |
|
|
169
|
+
| **Implement** | `/forge:apply` or `/forge:build` against `tasks.md` |
|
|
170
|
+
|
|
171
|
+
See the Forge skill’s [references/plan-routing.md](../references/plan-routing.md).
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Phases
|
|
176
|
+
|
|
177
|
+
| Phase | What happens | Skills / commands |
|
|
178
|
+
| ----- | ------------ | ----------------- |
|
|
179
|
+
| **triage** | Substantial? Skip allowed? Bootstrap session | `forge` skill |
|
|
180
|
+
| **brainstorm** | Explore intent, approaches, approval | `skills/brainstorming` |
|
|
181
|
+
| **plan** | Tracked-change propose; **`forge spine init` every change** (rows or `notApplicable`); wiring + product-loop tasks when async | [plan-routing.md](../references/plan-routing.md) |
|
|
182
|
+
| **implement** | Subagent per task, TDD, tier 2 evidence; update spine rows; `forge defer` for deferred wiring | **`/forge:apply`** (OpenSpec) or `/forge:build` + `skills/subagent-driven-development` + `skills/test-driven-development` + [test-strategy](../references/test-strategy.md) |
|
|
183
|
+
| **verify** | Audit tier 2; tier 3; product-loop evidence; `forge integrity-check` | `skills/verification-before-completion` + `verify-evidence.md` |
|
|
184
|
+
| **review** | Combined task reviewer (spec + quality) per task; final review (spine + product loop) | `skills/requesting-code-review` |
|
|
185
|
+
| **finish** | Archive (+ ADR if the project uses that); `forge phase done` (integrity gate); cleanup | `/opsx:archive`, `forge cleanup` |
|
|
186
|
+
|
|
187
|
+
**Standalone deep review (outside Forge):** for pre-merge audits with adversarial false-positive filtering, use the **thorough code review** skill — see [thorough-code-review.md](https://github.com/izkac/forgekit/blob/main/docs/thorough-code-review.md). Forge's `requesting-code-review` stays the per-task checkpoint during `/forge:build`.
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## `.forge/` session layout
|
|
192
|
+
|
|
193
|
+
One session per substantial task. **Per-checkout** active pointer — works across
|
|
194
|
+
Cursor, Claude Code, and Codex without requiring a chat ID.
|
|
195
|
+
|
|
196
|
+
```
|
|
197
|
+
.forge/
|
|
198
|
+
active.json ← current session (gitignored)
|
|
199
|
+
models.local.json ← optional billing overlay
|
|
200
|
+
preferences.local.json ← optional pace overlay
|
|
201
|
+
sessions/
|
|
202
|
+
2026-06-05T143022Z-my-feature-a3f9b2/
|
|
203
|
+
session.json ← phase, planType, openspecChange, pace
|
|
204
|
+
status.json ← machine-readable progress
|
|
205
|
+
brainstorm/
|
|
206
|
+
notes.md
|
|
207
|
+
decisions.md
|
|
208
|
+
plan.md ← legacy throwaway plans only (deprecated)
|
|
209
|
+
verify-evidence.md ← tier 3 + ## Product loop (or BLOCKED)
|
|
210
|
+
deferrals.json ← forge defer registry (when used)
|
|
211
|
+
spine.json ← fallback if no tracked change dir
|
|
212
|
+
scorecard.md / scorecard.json ← L2 session score (written at done/finish)
|
|
213
|
+
tasks/
|
|
214
|
+
01-first-task/
|
|
215
|
+
brief.md
|
|
216
|
+
test-evidence.md
|
|
217
|
+
task-review.md ← combined spec + quality verdict
|
|
218
|
+
reviews/
|
|
219
|
+
final-review.md
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
For OpenSpec / specs-engine changes, the canonical **spine matrix** lives next to
|
|
223
|
+
the plan: `openspec/changes/<name>/spine.json` (or `<specsDir>/changes/<name>/spine.json`).
|
|
224
|
+
|
|
225
|
+
**Session ID:** `<UTC-compact>-<kebab-slug>-<6-hex>`
|
|
226
|
+
|
|
227
|
+
**Retention:** 14 days. Finished sessions (`phase: done|skipped`) are removed
|
|
228
|
+
on cleanup. Active session is never removed unless `--include-active`.
|
|
229
|
+
|
|
230
|
+
Optional `cursorChatId` on `session.json` when a hook can supply it — not
|
|
231
|
+
required for correctness.
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Commands (project slash)
|
|
236
|
+
|
|
237
|
+
| Command | Purpose |
|
|
238
|
+
| ------- | ------- |
|
|
239
|
+
| `/forge` | Start or resume from active session / current phase |
|
|
240
|
+
| `/forge:brainstorm` | Brainstorm phase only |
|
|
241
|
+
| `/forge:plan` | Plan phase — tracked-change propose (engine from `.forge/config.json`) |
|
|
242
|
+
| `/forge:apply` | **Tracked-change implement** — subagent TDD + verify + review (preferred over `/opsx:apply`) |
|
|
243
|
+
| `/forge:build` | Implement phase (`tasks.md` from either engine) |
|
|
244
|
+
| `/forge:status` | Show active session progress |
|
|
245
|
+
| `/forge:skip` | **Explicit** opt-out of Forge for this task |
|
|
246
|
+
|
|
247
|
+
OpenSpec commands remain available standalone (OpenSpec-engine projects):
|
|
248
|
+
|
|
249
|
+
| Command | Purpose |
|
|
250
|
+
| ------- | ------- |
|
|
251
|
+
| `/opsx:propose` | Create OpenSpec change + artifacts |
|
|
252
|
+
| `/opsx:apply` | Vendor OpenSpec task loop — **re-overlay** with Forge via `forge overlay`; prefer **`/forge:apply`** |
|
|
253
|
+
| `/opsx:archive` | Archive completed change |
|
|
254
|
+
| `/opsx:explore` | Explore without committing to a change |
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## CLI (`forge`)
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
forge new <slug> [--signal "…"] # new session + set active (resolves pace; warn-only doctor)
|
|
262
|
+
forge status # active session JSON (+ effective pace)
|
|
263
|
+
forge phase <phase> […] # update phase / openspec / task counters
|
|
264
|
+
forge cleanup [--dry-run] # prune sessions >14 days or finished
|
|
265
|
+
forge evidence --task <nn>-<slug> --command "<cmd>" --exit 0 --summary "<text>"
|
|
266
|
+
# stamp tier-2 test-evidence.md
|
|
267
|
+
forge resolve-model --tier <fast|standard|capable>
|
|
268
|
+
# JSON model resolution (included billing by default)
|
|
269
|
+
forge models # print effective billing (does NOT write a file)
|
|
270
|
+
forge models included|metered # WRITE .forge/models.local.json
|
|
271
|
+
forge prefs # print effective pace (does NOT write a file)
|
|
272
|
+
forge prefs auto|thorough|standard|brisk|lite
|
|
273
|
+
# WRITE .forge/preferences.local.json
|
|
274
|
+
forge prefs --session-set lite # pin active session only
|
|
275
|
+
forge doctor # plan-engine readiness (OpenSpec or specs layout)
|
|
276
|
+
forge doctor --install # attempt npm install -g @fission-ai/openspec
|
|
277
|
+
forge spine init|check # capability→runtime spine matrix (spine.json in change dir)
|
|
278
|
+
forge defer add|resolve|list # deferral registry — deferred wiring is tracked debt
|
|
279
|
+
forge integrity-check # mechanical gate: spine + deferrals + product-loop evidence
|
|
280
|
+
forge score [--write] [--md] # L2 session scorecard (also auto-written at phase done)
|
|
281
|
+
forge overlay # re-apply OpenSpec vendor overlays in this project
|
|
282
|
+
forge init […] # wire project commands / hooks / rules
|
|
283
|
+
forge install […] # alias → forgekit install --skills forge
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Meta install (skills × agents):
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
forgekit install
|
|
290
|
+
forgekit install --skills forge,thorough-code-review --agents cursor,claude
|
|
291
|
+
forgekit list
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## Checkout-local overrides (per developer)
|
|
297
|
+
|
|
298
|
+
Forge has two **optional**, **gitignored** overlays under `.forge/`.
|
|
299
|
+
They appear on disk **only after you set them**. Bare get commands only print the
|
|
300
|
+
merged effective value from package defaults.
|
|
301
|
+
|
|
302
|
+
| Concern | Defaults (in `@izkac/forgekit`) | Local file (gitignored) | Get (print only) | Set (creates/updates file) |
|
|
303
|
+
| ------- | ----------------------------- | ----------------------- | ---------------- | -------------------------- |
|
|
304
|
+
| Subagent **billing** (`included` / `metered`) | `packages/cli/src/models.defaults.json` | `.forge/models.local.json` | `forge models` | `forge models included\|metered` |
|
|
305
|
+
| Forge **pace** (review / verify ceremony) | `packages/cli/src/preferences.defaults.json` | `.forge/preferences.local.json` | `forge prefs` | `forge prefs auto\|thorough\|…` |
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
# Example: you ran forge models and only saw "included" —
|
|
309
|
+
# that means the default lane is in effect. No models.local.json exists yet.
|
|
310
|
+
forge models --json # localExists: false until you set
|
|
311
|
+
forge models included # now creates .forge/models.local.json
|
|
312
|
+
|
|
313
|
+
forge prefs --session-set lite # pin active session only; no local file
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
These are **per-checkout** (each developer’s clone), not committed to git — same
|
|
317
|
+
idea as a personal `.env`.
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
## Pace (thoroughness)
|
|
322
|
+
|
|
323
|
+
Forge ceremony (per-task review, final review, tier-3 verify, model bias, brainstorm
|
|
324
|
+
depth) is controlled by a **pace** preset. Default is **`auto`**: resolve once at
|
|
325
|
+
session start from risk signals, sticky for the session.
|
|
326
|
+
|
|
327
|
+
| Pace | Intent |
|
|
328
|
+
|------|--------|
|
|
329
|
+
| `auto` | Pick thorough / standard / brisk / lite from signals (default) |
|
|
330
|
+
| `thorough` | Always review **each task**; full-workspace tier 3 |
|
|
331
|
+
| `standard` | Review once per **OpenSpec group**; full-workspace tier 3 |
|
|
332
|
+
| `brisk` | Review high-risk tasks only; affected-workspace tier 3 |
|
|
333
|
+
| `lite` | Skip reviews for low-risk; audit tier-2 only at verify |
|
|
334
|
+
|
|
335
|
+
### Effort matrix (exact knobs)
|
|
336
|
+
|
|
337
|
+
Defaults from `packages/cli/src/preferences.defaults.json`:
|
|
338
|
+
|
|
339
|
+
| Knob | `thorough` | `standard` | `brisk` | `lite` |
|
|
340
|
+
|------|------------|------------|---------|--------|
|
|
341
|
+
| **review.perTask** | always | per-group | high-risk-only | never\* |
|
|
342
|
+
| **review.final** | always | always | high-risk-only | never\* |
|
|
343
|
+
| **review.depth** | full | full | spec-only | spec-only |
|
|
344
|
+
| **review.maxRounds** | 3 | 2 | 1 | 0 |
|
|
345
|
+
| **verify.tier3** | full-workspace | full-workspace | affected-only | audit-tier2-only |
|
|
346
|
+
| **models.bias** | default | default | prefer-fast | prefer-fast |
|
|
347
|
+
| **brainstorm.depth** | full | full | short (≤2–3 options) | minimal |
|
|
348
|
+
|
|
349
|
+
\*Hard floor: money / auth / contracts / migrations / secrets still get per-task review (and final if the session touched high-risk), even under `brisk` / `lite` / mid-group `standard`.
|
|
350
|
+
|
|
351
|
+
**`thorough` vs `standard`:** thorough reviews every task; standard reviews once per OpenSpec `tasks.md` group (`##` section), except high-risk tasks which still get an immediate per-task review.
|
|
352
|
+
|
|
353
|
+
**`auto`** is not a preset — it picks one of the four once at session start (sticky):
|
|
354
|
+
|
|
355
|
+
1. money / payment / auth / secret / migration / contract / gdpr → **thorough**
|
|
356
|
+
2. ecosystem / API / multi-file / shared package / worker / job queue / pipeline / etl / platform / orchestration / openspec → **standard**
|
|
357
|
+
3. docs / typo / rename / scaffold / changelog → **lite**
|
|
358
|
+
4. fix / tweak / toolbar / style / padding (explicitly small) → **brisk**
|
|
359
|
+
5. else (including empty / unrecognized) → **standard** (fail closed)
|
|
360
|
+
|
|
361
|
+
When `--tasks-total N` is set with **N ≥ 15** and resolved pace is still `brisk`/`lite` (not user-pinned), Forge escalates the session to **standard**.
|
|
362
|
+
|
|
363
|
+
**Unchanged on all paces:** tier-1 TDD + tier-2 evidence, no autonomous commit, OpenSpec when in Forge. Runtime integrity (below) applies at every pace.
|
|
364
|
+
|
|
365
|
+
Agent rules for each knob: [pace.md](../references/pace.md).
|
|
366
|
+
|
|
367
|
+
Prefs are gitignored (`.forge/preferences.local.json`), same pattern as `models.local.json`.
|
|
368
|
+
|
|
369
|
+
### OpenSpec doctor
|
|
370
|
+
|
|
371
|
+
`forge doctor` checks `openspec/config.yaml` and that `openspec` is on PATH.
|
|
372
|
+
If the CLI is missing, it warns and offers `npm install -g @fission-ai/openspec`
|
|
373
|
+
(`--install` to attempt). `forge new` runs doctor warn-only so a missing CLI does
|
|
374
|
+
not block session creation.
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
## Runtime integrity
|
|
379
|
+
|
|
380
|
+
Forge’s job is to ship **working product paths**, not green checkboxes over
|
|
381
|
+
orphan libraries. Integrity rules live in
|
|
382
|
+
[runtime-integrity.md](../references/runtime-integrity.md) and are
|
|
383
|
+
enforced by both skill prompts and the CLI.
|
|
384
|
+
|
|
385
|
+
### The problem it prevents
|
|
386
|
+
|
|
387
|
+
Without integrity, a large change can look “done” while the product is hollow:
|
|
388
|
+
|
|
389
|
+
- Libraries (matcher, BI exporter, …) are unit-tested and marked complete
|
|
390
|
+
- A worker job logs and marks `succeeded` (or a thin concat job writes a `.sav`)
|
|
391
|
+
- The UI can enqueue kinds nobody handles, or read collections nobody writes
|
|
392
|
+
- OpenSpec shows 57/57 — but upload → analyze → ratify → run never works
|
|
393
|
+
|
|
394
|
+
Integrity upgrades Forge from “no false job success” to **product-loop acceptance**.
|
|
395
|
+
|
|
396
|
+
### Rules (plain language)
|
|
397
|
+
|
|
398
|
+
1. **No stubs / false success** — a handler that only logs and succeeds is forbidden.
|
|
399
|
+
2. **Runtime owner required** — a library alone does not satisfy a capability; name the production caller (job, endpoint, CLI).
|
|
400
|
+
3. **Tests must fail on a no-op** — asserting “job status became succeeded” is not enough.
|
|
401
|
+
4. **Specs beat narrow tasks** — capability specs win when they conflict with a thin task reading.
|
|
402
|
+
5. **E2E = product loop** — produce → consume → decision changes output. A single job slice (ingest → Parquet) is **not** platform E2E.
|
|
403
|
+
6. **Job-kind closure** — every product-surface job kind is wired end-to-end **or deleted** before complete. “Fail closed” is only a temporary `BLOCKED` state.
|
|
404
|
+
7. **Consumer–producer** — if UI/API reads it, production must write it (proven in evidence).
|
|
405
|
+
8. **Deferrals are tracked** — “wiring later” only via `forge defer`; unresolved deferrals block `done`.
|
|
406
|
+
|
|
407
|
+
### Mechanics
|
|
408
|
+
|
|
409
|
+
| Tool | Purpose |
|
|
410
|
+
|------|---------|
|
|
411
|
+
| `forge spine init\|check` | **Mandatory every change.** `spine.json`: rows **or** `notApplicable`. Not keyword-gated. |
|
|
412
|
+
| `forge defer add\|resolve\|list` | Deferred wiring as tracked debt in the session |
|
|
413
|
+
| `forge integrity-check` | Combined gate — also run automatically by `forge phase done\|finish` |
|
|
414
|
+
|
|
415
|
+
Defaults (`integrity.forbidStubs`, `specsBeatNarrowTasks`, `requireE2E`) live in
|
|
416
|
+
`preferences.defaults.json` and appear in `forge status`.
|
|
417
|
+
|
|
418
|
+
Escape hatch: `forge phase done --allow-incomplete "<reason>"` records an honest
|
|
419
|
+
exception in the session — it does not silently checkbox past gaps.
|
|
420
|
+
|
|
421
|
+
### What runs automatically every session
|
|
422
|
+
|
|
423
|
+
You do **not** paste a long definition-of-done prompt. After
|
|
424
|
+
`forgekit install --skills forge`, every Forge session gets:
|
|
425
|
+
|
|
426
|
+
| Automatic (CLI / hooks) | Agent-driven (skill phases — required) |
|
|
427
|
+
| ----------------------- | -------------------------------------- |
|
|
428
|
+
| Integrity reminder on every session/prompt hook | Plan: **`forge spine init` every change** — fill rows or `notApplicable` |
|
|
429
|
+
| Pace `auto` fail-closed to **standard**; task-count escalation at ≥15 | Implement: update spine rows; `forge defer add` if wiring is deferred |
|
|
430
|
+
| `forge phase done\|finish` requires valid spine + writes L2 scorecard | Verify: `## Product loop` when spine has rows (sync-only → prefer `notApplicable`) |
|
|
431
|
+
| `forge status` surfaces `integrity.*` defaults | After done: answer L3 ship-check in `scorecard.md` |
|
|
432
|
+
|
|
433
|
+
**Gates are automatic. Filling evidence is part of the normal phase flow.**
|
|
434
|
+
Skipping those steps fails at `forge phase done`, not silently.
|
|
435
|
+
|
|
436
|
+
### Worked example (jobs / workers change)
|
|
437
|
+
|
|
438
|
+
**Plan**
|
|
439
|
+
|
|
440
|
+
```bash
|
|
441
|
+
forge spine init
|
|
442
|
+
# edit openspec/changes/<name>/spine.json — one row per capability
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
```json
|
|
446
|
+
{
|
|
447
|
+
"change": "etl-surveydb-pipeline-closure",
|
|
448
|
+
"notApplicable": null,
|
|
449
|
+
"rows": [
|
|
450
|
+
{
|
|
451
|
+
"capability": "REQ-GOV-01 matching",
|
|
452
|
+
"library": "services/etl-core/src/etl_core/matcher.py",
|
|
453
|
+
"runtimeOwner": "worker job analyze_study",
|
|
454
|
+
"writes": "study_proposals",
|
|
455
|
+
"reads": "N/A",
|
|
456
|
+
"uiConsumer": "Proposals page",
|
|
457
|
+
"evidence": "tasks/12-analyze/test-evidence.md"
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
"capability": "REQ-OUT-BI star schema",
|
|
461
|
+
"library": "services/etl-core/src/etl_core/bi_star.py",
|
|
462
|
+
"runtimeOwner": "worker job harmonization_run",
|
|
463
|
+
"writes": "runs/<id>/bi/*.parquet",
|
|
464
|
+
"reads": "decisions tip + weight_map tips",
|
|
465
|
+
"uiConsumer": "Runs artifact download",
|
|
466
|
+
"evidence": "verify-evidence.md#product-loop"
|
|
467
|
+
}
|
|
468
|
+
]
|
|
469
|
+
}
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
Docs-only / no-runtime changes may set `"notApplicable": "docs-only change"` instead of rows.
|
|
473
|
+
|
|
474
|
+
**If wiring must wait for a later task**
|
|
475
|
+
|
|
476
|
+
```bash
|
|
477
|
+
forge defer add --task 9.7 --reason "analyze_study handler lands in 9.7"
|
|
478
|
+
# … when 9.7 is done:
|
|
479
|
+
forge defer resolve --task 9.7
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
**Verify evidence** (required when spine has rows):
|
|
483
|
+
|
|
484
|
+
```markdown
|
|
485
|
+
# Verify evidence — tier 3
|
|
486
|
+
|
|
487
|
+
- **Command:** `pytest …` / `npm test …`
|
|
488
|
+
- **Exit code:** 0
|
|
489
|
+
|
|
490
|
+
## Product loop
|
|
491
|
+
|
|
492
|
+
Fixture: OP1086 three sources
|
|
493
|
+
|
|
494
|
+
1. ingest_source ×3 → study_sources + Parquet
|
|
495
|
+
2. analyze_study → study_proposals (match / loop)
|
|
496
|
+
3. ratify subset via API → decisions tip at revision R
|
|
497
|
+
4. harmonization_run @R → .sav + Master QML + BI
|
|
498
|
+
5. Assert: output at R differs from unratified baseline
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
Or an explicit `BLOCKED: …` line — then `forge phase done` refuses until unblocked
|
|
502
|
+
or the user passes `--allow-incomplete`.
|
|
503
|
+
|
|
504
|
+
**Finish**
|
|
505
|
+
|
|
506
|
+
```bash
|
|
507
|
+
forge integrity-check # optional preview
|
|
508
|
+
forge phase done # same checks; exit 1 if incomplete
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
---
|
|
512
|
+
|
|
513
|
+
## Subagent model
|
|
514
|
+
|
|
515
|
+
Each implementation task:
|
|
516
|
+
|
|
517
|
+
1. Coordinator writes `tasks/<nn>-<slug>/brief.md` (task text + file paths + constraints — **no chat history**).
|
|
518
|
+
2. **Implementer** subagent — must follow `skills/test-driven-development` first.
|
|
519
|
+
3. **Task reviewer** subagent (spec then quality) — unless pace skips low-risk tasks.
|
|
520
|
+
4. Mark task complete (`tasks.md` checkbox or session progress).
|
|
521
|
+
5. After all tasks: **verify** (tier 3 scope from pace) → **final reviewer** (unless pace skips) → finish.
|
|
522
|
+
|
|
523
|
+
Test tiers: [test-strategy.md](../references/test-strategy.md) — scoped TDD per task, narrow evidence per task, full workspace **once** at verify when pace requires it (not every task).
|
|
524
|
+
|
|
525
|
+
### Model selection (capability × billing)
|
|
526
|
+
|
|
527
|
+
Subagents resolve models through **two axes** so Cursor / Claude Code / Codex stay on **subscription/included** pools by default:
|
|
528
|
+
|
|
529
|
+
| Axis | Values | Default |
|
|
530
|
+
| ---- | ------ | ------- |
|
|
531
|
+
| Capability | `fast` · `standard` · `capable` | role-based |
|
|
532
|
+
| Billing | `included` · `metered` | **`included`** |
|
|
533
|
+
|
|
534
|
+
```bash
|
|
535
|
+
forge resolve-model --tier standard # JSON: { model, omitModel, billing, … }
|
|
536
|
+
forge models # print effective billing (no file write)
|
|
537
|
+
forge models metered # WRITE .forge/models.local.json
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
- Defaults: `packages/cli/src/models.defaults.json` (Cursor `included` = `inherit` → omit Task `model`).
|
|
541
|
+
- Local overlay is optional — see **Checkout-local overrides** above.
|
|
542
|
+
- **Never invent** host model slugs; honor `omitModel` / `model` from the resolver.
|
|
543
|
+
- Escalate **capability** within `included` on `BLOCKED`; switch to `metered` only on explicit user request.
|
|
544
|
+
- Keep the **parent** session on Auto/Composer (Cursor) or Max (Claude Code) — `inherit` follows the parent.
|
|
545
|
+
|
|
546
|
+
Guardrails in every subagent brief (honor the **project’s** agent docs too):
|
|
547
|
+
|
|
548
|
+
- No autonomous `git commit` / push unless the user asks
|
|
549
|
+
- Implementer runs tier 1 (scoped) + tier 2 (narrow) tests; coordinator saves `tasks/<nn>-<slug>/test-evidence.md` before marking task done
|
|
550
|
+
- Trace downstream consumers when contracts change
|
|
551
|
+
|
|
552
|
+
Prompt templates: [subagents/](../subagents/)
|
|
553
|
+
|
|
554
|
+
---
|
|
555
|
+
|
|
556
|
+
## Bundled skills (self-contained)
|
|
557
|
+
|
|
558
|
+
Forge vendors adapted Superpowers skills (MIT) under `skills/forge/skills/`.
|
|
559
|
+
See [skills/NOTICE.md](../skills/NOTICE.md).
|
|
560
|
+
|
|
561
|
+
| Skill | Purpose |
|
|
562
|
+
| ----- | ------- |
|
|
563
|
+
| brainstorming | Brainstorm phase |
|
|
564
|
+
| test-driven-development | Implement — per task |
|
|
565
|
+
| subagent-driven-development | Implement — orchestration |
|
|
566
|
+
| systematic-debugging | Blockers during implement |
|
|
567
|
+
| verification-before-completion | Verify phase |
|
|
568
|
+
| requesting-code-review | Review phase |
|
|
569
|
+
|
|
570
|
+
The bundled skills are a **maintained fork** of Superpowers (MIT — see `skills/NOTICE.md`), restructured for Forge (single task reviewer, tiered testing, trimmed prose). Do not re-vendor from upstream; edit `skills/forge/` in this repo and run `forgekit install --skills forge --force`.
|
|
571
|
+
|
|
572
|
+
## Relationship to OpenSpec
|
|
573
|
+
|
|
574
|
+
| Piece | Source | Policy |
|
|
575
|
+
| ----- | ------ | ------ |
|
|
576
|
+
| Brainstorm, TDD, subagents, verify, review | **skills/forge/skills/** (bundled) | Self-contained; Superpowers plugin not required |
|
|
577
|
+
| Planning sink | OpenSpec or built-in specs engine | Engine per project (`.forge/config.json`); no throwaway or direct modes for new work |
|
|
578
|
+
| OpenSpec skills | Vendor (`openspec-*`, `opsx:*`) | **Do not hand-edit** — run `forge overlay` after upgrade |
|
|
579
|
+
| OpenSpec implement | Forge **`/forge:apply`** | Full subagent TDD + verify + review; survives OpenSpec upgrades |
|
|
580
|
+
| Archive follow-up | Optional ADRs (`forge init --adr`) | When `.forge/config.json` has `adr.enabled`, run **archive-to-adr** (path from `adr.dir`, default `docs/adr`) |
|
|
581
|
+
|
|
582
|
+
---
|
|
583
|
+
|
|
584
|
+
## Agent surfaces
|
|
585
|
+
|
|
586
|
+
Same workflow across Cursor, Claude Code, and Codex CLI. Install the skill once
|
|
587
|
+
per machine with `forgekit install`; wire project commands/hooks with `forge init`.
|
|
588
|
+
|
|
589
|
+
| Agent | Skill (after install) | Project wiring (`forge init`) | Session hooks |
|
|
590
|
+
| ----- | --------------------- | ----------------------------- | ------------- |
|
|
591
|
+
| **Cursor** | `~/.cursor/skills/forge/` | commands, `forge.mdc`, SessionStart hook | SessionStart → active session reminder |
|
|
592
|
+
| **Claude Code** | `~/.claude/skills/forge/` | commands, `forge.md`, SessionStart + prompt hooks | SessionStart + substantial-work UserPromptSubmit + `/forge` UserPromptSubmit |
|
|
593
|
+
| **Codex CLI** | `~/.codex/skills/forge/` | thin rule | *(none — read skill on substantial work)* |
|
|
594
|
+
|
|
595
|
+
### Slash commands (Cursor + Claude Code)
|
|
596
|
+
|
|
597
|
+
| Command | Purpose |
|
|
598
|
+
| ------- | ------- |
|
|
599
|
+
| `/forge` | Start or resume current phase |
|
|
600
|
+
| `/forge:brainstorm` | Brainstorm only |
|
|
601
|
+
| `/forge:plan` | Tracked-change propose (engine from `.forge/config.json`) |
|
|
602
|
+
| `/forge:apply` | Tracked-change implement + verify + review (preferred) |
|
|
603
|
+
| `/forge:build` | Implement phase (`tasks.md` from either engine) |
|
|
604
|
+
| `/forge:status` | Session progress |
|
|
605
|
+
| `/forge:skip` | Explicit skip for this task |
|
|
606
|
+
|
|
607
|
+
### Codex CLI
|
|
608
|
+
|
|
609
|
+
No slash commands. On substantial work: read the **`forge`** skill, check
|
|
610
|
+
`forge status`, bootstrap with `forge new <slug>` when needed.
|
|
611
|
+
After brainstorm, proceed directly to the configured engine's propose flow — see
|
|
612
|
+
[plan-routing.md](../references/plan-routing.md).
|
|
613
|
+
User can say “skip forge” or `/forge:skip` to opt out.
|
|
614
|
+
|
|
615
|
+
---
|
|
616
|
+
|
|
617
|
+
## What we deliberately dropped from Superpowers
|
|
618
|
+
|
|
619
|
+
- `docs/superpowers/plans/` and `docs/superpowers/specs/` — use OpenSpec / `specs/changes/` + `.forge` (the built-in specs engine covers the no-OpenSpec case with an OpenSpec-compatible layout)
|
|
620
|
+
- Mandatory git worktree per brainstorm — optional
|
|
621
|
+
- Autonomous commits in subagent prompts — forbidden unless the user asks
|
|
@@ -43,7 +43,7 @@ Optional `cursorChatId` on `session.json` when available — not required.
|
|
|
43
43
|
|
|
44
44
|
Bare `forge models` / `forge:prefs` **print** effective values from committed
|
|
45
45
|
defaults and do **not** create the `*.local.json` files. See [pace.md](./pace.md) and
|
|
46
|
-
[docs/forge.md](
|
|
46
|
+
[docs/forge.md](../docs/forge.md) § Checkout-local overrides.
|
|
47
47
|
|
|
48
48
|
## session.json fields
|
|
49
49
|
|
|
@@ -15,7 +15,7 @@ forge doctor # OpenSpec project + CLI
|
|
|
15
15
|
|
|
16
16
|
Billing lane (orthogonal): `forge models` prints only;
|
|
17
17
|
`forge models included|metered` writes `.forge/models.local.json`.
|
|
18
|
-
See [docs/forge.md](
|
|
18
|
+
See [docs/forge.md](../docs/forge.md) § Checkout-local overrides.
|
|
19
19
|
|
|
20
20
|
## Announce
|
|
21
21
|
|
|
@@ -231,7 +231,7 @@ When reviewing a project that documents accepted risks:
|
|
|
231
231
|
|
|
232
232
|
## Model selection for subagents
|
|
233
233
|
|
|
234
|
-
**Choose the model tier per dispatch — never default to the most capable (priciest) model.** Match the tier to the judgment the role actually needs. Map roles to Forge capability tiers (`fast` / `standard` / `capable`) and resolve via `forge resolve-model --tier <…>` so billing stays on the **`included`** (subscription) lane unless the user explicitly asks for metered/API models — see
|
|
234
|
+
**Choose the model tier per dispatch — never default to the most capable (priciest) model.** Match the tier to the judgment the role actually needs. Map roles to Forge capability tiers (`fast` / `standard` / `capable`) and resolve via `forge resolve-model --tier <…>` so billing stays on the **`included`** (subscription) lane unless the user explicitly asks for metered/API models — see forge skill [docs/forge.md](../forge/docs/forge.md) § Subagent model.
|
|
235
235
|
|
|
236
236
|
| Role | Tier | Why |
|
|
237
237
|
| ---- | ---- | --- |
|
|
@@ -7,7 +7,7 @@ tags: [workflow, forge, openspec]
|
|
|
7
7
|
|
|
8
8
|
**Forge-owned command.** Use this instead of bare `/opsx:apply` for disciplined implementation of a tracked change (OpenSpec or built-in specs engine — `.forge/config.json` → `plan.engine`).
|
|
9
9
|
|
|
10
|
-
Read the Forge skill (`~/.claude/skills/forge/SKILL.md`) and
|
|
10
|
+
Read the Forge skill (`~/.claude/skills/forge/SKILL.md`) and `~/.claude/skills/forge/docs/forge.md`.
|
|
11
11
|
|
|
12
12
|
**Input**: Optionally specify a change name (e.g., `/forge:apply add-auth`). If omitted, infer from context or active Forge session.
|
|
13
13
|
|
|
@@ -7,7 +7,7 @@ tags: [workflow, forge, openspec]
|
|
|
7
7
|
|
|
8
8
|
Run the **Forge** workflow for substantial work.
|
|
9
9
|
|
|
10
|
-
Read and follow the Forge skill (`~/.claude/skills/forge/SKILL.md`) and
|
|
10
|
+
Read and follow the Forge skill (`~/.claude/skills/forge/SKILL.md`) and `~/.claude/skills/forge/docs/forge.md`.
|
|
11
11
|
|
|
12
12
|
1. Triage — substantial work? (skip only if user said `/forge:skip`)
|
|
13
13
|
2. Resume from `.forge/active.json` or `forge new <slug>`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Forge (thin rule)
|
|
2
2
|
|
|
3
|
-
Full workflow:
|
|
3
|
+
Full workflow: `~/.claude/skills/forge/docs/forge.md` · Skill: user-installed **Forge** (`~/.claude/skills/forge/SKILL.md` after `forge install`).
|
|
4
4
|
|
|
5
5
|
**Default:** triage before implementation. Substantial work → **Forge**
|
|
6
6
|
(brainstorm → OpenSpec → subagent TDD implement → review).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Forge (thin rule)
|
|
2
2
|
|
|
3
|
-
Full workflow:
|
|
3
|
+
Full workflow: `~/.codex/skills/forge/docs/forge.md` · Skill: user-installed **Forge** (`~/.codex/skills/forge/SKILL.md` after `forge install`).
|
|
4
4
|
|
|
5
5
|
**Default:** triage before implementation. Substantial work → **Forge**
|
|
6
6
|
(brainstorm → OpenSpec → subagent TDD implement → review).
|
|
@@ -7,7 +7,7 @@ description: Forge — apply a tracked change with subagent TDD, verify, and rev
|
|
|
7
7
|
|
|
8
8
|
**Forge-owned command.** Use this instead of bare `/opsx:apply` for disciplined implementation of a tracked change (OpenSpec or built-in specs engine — `.forge/config.json` → `plan.engine`).
|
|
9
9
|
|
|
10
|
-
Read the Forge skill (`~/.cursor/skills/forge/SKILL.md`) and
|
|
10
|
+
Read the Forge skill (`~/.cursor/skills/forge/SKILL.md`) and `~/.cursor/skills/forge/docs/forge.md`.
|
|
11
11
|
|
|
12
12
|
**Input**: Optionally specify a change name (e.g., `/forge:apply add-auth`). If omitted, infer from context or active Forge session.
|
|
13
13
|
|
|
@@ -7,7 +7,7 @@ description: Forge — start or resume disciplined development (brainstorm → p
|
|
|
7
7
|
|
|
8
8
|
Run the **Forge** workflow for substantial work.
|
|
9
9
|
|
|
10
|
-
Read and follow the Forge skill (`~/.cursor/skills/forge/SKILL.md`) and
|
|
10
|
+
Read and follow the Forge skill (`~/.cursor/skills/forge/SKILL.md`) and `~/.cursor/skills/forge/docs/forge.md`.
|
|
11
11
|
|
|
12
12
|
1. Triage — substantial work? (skip only if user said `/forge:skip`)
|
|
13
13
|
2. Resume from `.forge/active.json` or `forge new <slug>`
|
|
@@ -5,7 +5,7 @@ alwaysApply: true
|
|
|
5
5
|
|
|
6
6
|
# Forge (thin rule)
|
|
7
7
|
|
|
8
|
-
Full workflow:
|
|
8
|
+
Full workflow: `~/.cursor/skills/forge/docs/forge.md` · Skill: user-installed **Forge** (`~/.cursor/skills/forge/SKILL.md` after `forge install`).
|
|
9
9
|
|
|
10
10
|
**Default:** triage before implementation. Substantial work → **Forge**
|
|
11
11
|
(brainstorm → OpenSpec → subagent TDD implement → review).
|