@izkac/forgekit 0.1.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/bin/forge.mjs +100 -0
- package/bin/forgekit.mjs +84 -0
- package/bin/review.mjs +82 -0
- package/package.json +46 -0
- package/scripts/prepack.mjs +78 -0
- package/scripts/run-tests.mjs +43 -0
- package/src/adr.mjs +236 -0
- package/src/adr.test.mjs +170 -0
- package/src/change.mjs +234 -0
- package/src/change.test.mjs +83 -0
- package/src/cleanup-sessions.mjs +70 -0
- package/src/config.mjs +103 -0
- package/src/defer.mjs +75 -0
- package/src/doctor.mjs +341 -0
- package/src/doctor.test.mjs +114 -0
- package/src/init.mjs +575 -0
- package/src/install.mjs +777 -0
- package/src/install.test.mjs +104 -0
- package/src/integrity-check.mjs +58 -0
- package/src/integrity.mjs +317 -0
- package/src/integrity.test.mjs +296 -0
- package/src/lib/workspaces.mjs +55 -0
- package/src/lib.mjs +138 -0
- package/src/models.defaults.json +41 -0
- package/src/new-session.mjs +82 -0
- package/src/openspec-overlays/README.md +19 -0
- package/src/openspec-overlays/openspec-apply-change-footer.md +14 -0
- package/src/openspec-overlays/opsx-apply-completion-step.md +1 -0
- package/src/openspec-overlays/opsx-apply-implement-step.md +11 -0
- package/src/paths.mjs +92 -0
- package/src/plan-engine.mjs +260 -0
- package/src/plan-engine.test.mjs +245 -0
- package/src/preferences.defaults.json +78 -0
- package/src/preferences.mjs +438 -0
- package/src/preferences.test.mjs +174 -0
- package/src/record-evidence.mjs +204 -0
- package/src/record-evidence.test.mjs +260 -0
- package/src/resolve-model.mjs +312 -0
- package/src/resolve-model.test.mjs +194 -0
- package/src/review/carryforward.mjs +413 -0
- package/src/review/carryforward.test.mjs +587 -0
- package/src/review/cli.test.mjs +117 -0
- package/src/review/export.mjs +172 -0
- package/src/review/export.test.mjs +197 -0
- package/src/review/fixtures/valid-review.json +42 -0
- package/src/review/lib.mjs +894 -0
- package/src/review/lib.test.mjs +266 -0
- package/src/review/merge-tentative.mjs +292 -0
- package/src/review/merge-tentative.test.mjs +363 -0
- package/src/review/new-review.mjs +200 -0
- package/src/review/render.mjs +108 -0
- package/src/review/schema-consistency.test.mjs +83 -0
- package/src/review/schema.json +196 -0
- package/src/review/signals.mjs +144 -0
- package/src/review/signals.test.mjs +62 -0
- package/src/score-cli.mjs +68 -0
- package/src/score.mjs +489 -0
- package/src/score.test.mjs +253 -0
- package/src/session-reminder.mjs +168 -0
- package/src/session-status.mjs +70 -0
- package/src/set-models.mjs +186 -0
- package/src/set-phase.mjs +177 -0
- package/src/set-phase.test.mjs +317 -0
- package/src/set-prefs.mjs +294 -0
- package/src/spine.mjs +91 -0
- package/src/triage-prompt.mjs +175 -0
- package/src/triage-prompt.test.mjs +50 -0
- package/src/vendor-openspec-overlays.mjs +176 -0
- package/src/vendor-openspec-overlays.test.mjs +62 -0
- package/vendor/skills/archive-to-adr/SKILL.md +149 -0
- package/vendor/skills/forge/SKILL.md +136 -0
- package/vendor/skills/forge/phases/brainstorm.md +23 -0
- package/vendor/skills/forge/phases/finish.md +87 -0
- package/vendor/skills/forge/phases/implement.md +76 -0
- package/vendor/skills/forge/phases/plan-openspec.md +40 -0
- package/vendor/skills/forge/phases/plan-specs.md +97 -0
- package/vendor/skills/forge/phases/review.md +25 -0
- package/vendor/skills/forge/phases/verify.md +120 -0
- package/vendor/skills/forge/references/forge-layout.md +85 -0
- package/vendor/skills/forge/references/pace.md +115 -0
- package/vendor/skills/forge/references/plan-routing.md +51 -0
- package/vendor/skills/forge/references/runtime-integrity.md +157 -0
- package/vendor/skills/forge/references/substantial-work.md +37 -0
- package/vendor/skills/forge/references/tdd-core.md +29 -0
- package/vendor/skills/forge/references/test-evidence.md +30 -0
- package/vendor/skills/forge/references/test-strategy.md +68 -0
- package/vendor/skills/forge/skills/NOTICE.md +17 -0
- package/vendor/skills/forge/skills/brainstorming/SKILL.md +120 -0
- package/vendor/skills/forge/skills/requesting-code-review/SKILL.md +67 -0
- package/vendor/skills/forge/skills/requesting-code-review/code-reviewer.md +146 -0
- package/vendor/skills/forge/skills/subagent-driven-development/SKILL.md +87 -0
- package/vendor/skills/forge/skills/systematic-debugging/SKILL.md +234 -0
- package/vendor/skills/forge/skills/systematic-debugging/condition-based-waiting.md +115 -0
- package/vendor/skills/forge/skills/systematic-debugging/defense-in-depth.md +122 -0
- package/vendor/skills/forge/skills/systematic-debugging/find-polluter.sh +63 -0
- package/vendor/skills/forge/skills/systematic-debugging/root-cause-tracing.md +169 -0
- package/vendor/skills/forge/skills/test-driven-development/SKILL.md +290 -0
- package/vendor/skills/forge/skills/test-driven-development/testing-anti-patterns.md +299 -0
- package/vendor/skills/forge/skills/verification-before-completion/SKILL.md +59 -0
- package/vendor/skills/forge/subagents/final-reviewer-prompt.md +53 -0
- package/vendor/skills/forge/subagents/implementer-prompt.md +38 -0
- package/vendor/skills/forge/subagents/task-reviewer-prompt.md +61 -0
- package/vendor/skills/git-resolve-adr-conflict/SKILL.md +132 -0
- package/vendor/skills/thorough-code-review/SKILL.md +290 -0
- package/vendor/skills/thorough-code-review/examples/accepted-risks-janus.md +32 -0
- package/vendor/skills/thorough-code-review/examples.md +133 -0
- package/vendor/skills/thorough-code-review/reference/accepted-risks.md +26 -0
- package/vendor/skills/thorough-code-review/reference/lenses.md +96 -0
- package/vendor/skills/thorough-code-review/reference/phase1-scout.md +62 -0
- package/vendor/skills/thorough-code-review/reference/phase1c-coverage.md +44 -0
- package/vendor/skills/thorough-code-review/reference/phase2-skeptic.md +105 -0
- package/vendor/skills/thorough-code-review/reference/report-schema.json +222 -0
- package/vendor/skills/thorough-code-review/reference/report-template.md +115 -0
- package/vendor/skills/thorough-code-review/reference/severity-rubric.md +49 -0
- package/vendor/skills/thorough-code-review/reference/signals-preflight.md +55 -0
- package/vendor/templates/adr/README.md +7 -0
- package/vendor/templates/adr/decisions.md +141 -0
- package/vendor/templates/adr/hooks/check-pending-adrs.mjs +74 -0
- package/vendor/templates/adr/hooks/check-pending-adrs.sh +3 -0
- package/vendor/templates/adr/hooks/openspec-archive-agent-message.mjs +52 -0
- package/vendor/templates/adr/hooks/openspec-archive-agent-message.sh +3 -0
- package/vendor/templates/project/claude/commands/forge-apply.md +75 -0
- package/vendor/templates/project/claude/commands/forge-brainstorm.md +7 -0
- package/vendor/templates/project/claude/commands/forge-build.md +17 -0
- package/vendor/templates/project/claude/commands/forge-plan.md +12 -0
- package/vendor/templates/project/claude/commands/forge-skip.md +14 -0
- package/vendor/templates/project/claude/commands/forge-status.md +16 -0
- package/vendor/templates/project/claude/commands/forge.md +16 -0
- package/vendor/templates/project/claude/hooks/forge-prompt-hook.mjs +73 -0
- package/vendor/templates/project/claude/hooks/forge-session-start.mjs +19 -0
- package/vendor/templates/project/claude/hooks/forge-triage-hook.mjs +77 -0
- package/vendor/templates/project/claude/rules/forge.md +16 -0
- package/vendor/templates/project/codex/rules/forge.md +10 -0
- package/vendor/templates/project/cursor/commands/forge-apply.md +75 -0
- package/vendor/templates/project/cursor/commands/forge-brainstorm.md +10 -0
- package/vendor/templates/project/cursor/commands/forge-build.md +17 -0
- package/vendor/templates/project/cursor/commands/forge-plan.md +15 -0
- package/vendor/templates/project/cursor/commands/forge-skip.md +14 -0
- package/vendor/templates/project/cursor/commands/forge-status.md +16 -0
- package/vendor/templates/project/cursor/commands/forge.md +16 -0
- package/vendor/templates/project/cursor/hooks/forge-session-start.mjs +30 -0
- package/vendor/templates/project/cursor/hooks/forge-session-start.sh +3 -0
- package/vendor/templates/project/cursor/rules/forge.mdc +21 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Re-apply Forge overlays to vendor OpenSpec skills and opsx:apply commands.
|
|
4
|
+
*
|
|
5
|
+
* forge overlay
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'node:fs';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
11
|
+
|
|
12
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
|
|
14
|
+
export function resolveOverlayDir() {
|
|
15
|
+
const fromEnv = process.env.FORGEKIT_ROOT
|
|
16
|
+
? path.join(process.env.FORGEKIT_ROOT, 'packages', 'cli', 'src', 'openspec-overlays')
|
|
17
|
+
: null;
|
|
18
|
+
const nextToSrc = path.join(__dirname, 'openspec-overlays');
|
|
19
|
+
for (const c of [fromEnv, nextToSrc].filter(Boolean)) {
|
|
20
|
+
if (c && fs.existsSync(c)) return c;
|
|
21
|
+
}
|
|
22
|
+
throw new Error('openspec-overlays directory not found');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const OVERLAY_START = '<!-- forgekit:openspec-overlay:start -->';
|
|
26
|
+
const OVERLAY_END = '<!-- forgekit:openspec-overlay:end -->';
|
|
27
|
+
const LEGACY_START = '<!-- janus-forge:openspec-overlay:start -->';
|
|
28
|
+
const LEGACY_END = '<!-- janus-forge:openspec-overlay:end -->';
|
|
29
|
+
const STEP_MARKER = 'REQUIRED (Forge):';
|
|
30
|
+
const LEGACY_STEP_MARKER = 'REQUIRED (Janus Forge):';
|
|
31
|
+
|
|
32
|
+
const AGENT_PATHS = {
|
|
33
|
+
cursor: {
|
|
34
|
+
skillRoot: '.cursor/skills/forge',
|
|
35
|
+
opsxApply: '.cursor/commands/opsx-apply.md',
|
|
36
|
+
applySkill: '.cursor/skills/openspec-apply-change/SKILL.md',
|
|
37
|
+
},
|
|
38
|
+
claude: {
|
|
39
|
+
skillRoot: '.claude/skills/forge',
|
|
40
|
+
opsxApply: '.claude/commands/opsx/apply.md',
|
|
41
|
+
applySkill: '.claude/skills/openspec-apply-change/SKILL.md',
|
|
42
|
+
},
|
|
43
|
+
codex: {
|
|
44
|
+
skillRoot: '.codex/skills/forge',
|
|
45
|
+
opsxApply: null,
|
|
46
|
+
applySkill: '.codex/skills/openspec-apply-change/SKILL.md',
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
function readOverlay(overlayDir, name) {
|
|
51
|
+
return fs.readFileSync(path.join(overlayDir, name), 'utf8');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function renderOverlay(template, agentKey) {
|
|
55
|
+
const root = AGENT_PATHS[agentKey].skillRoot;
|
|
56
|
+
return template
|
|
57
|
+
.replaceAll('{{PHASES_IMPLEMENT}}', `${root}/phases/implement.md`)
|
|
58
|
+
.replaceAll('{{PHASES_VERIFY}}', `${root}/phases/verify.md`)
|
|
59
|
+
.replaceAll('{{PHASES_REVIEW}}', `${root}/phases/review.md`)
|
|
60
|
+
.replaceAll('{{SKILL_ROOT}}', root);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function stripOverlayBlock(content) {
|
|
64
|
+
let out = content;
|
|
65
|
+
for (const [start, end] of [
|
|
66
|
+
[OVERLAY_START, OVERLAY_END],
|
|
67
|
+
[LEGACY_START, LEGACY_END],
|
|
68
|
+
]) {
|
|
69
|
+
const blockRe = new RegExp(
|
|
70
|
+
`(\\r?\\n---\\r?\\n\\r?\\n)?${escapeRegExp(start)}[\\s\\S]*?${escapeRegExp(end)}\\r?\\n?`,
|
|
71
|
+
'g',
|
|
72
|
+
);
|
|
73
|
+
out = out.replace(blockRe, '\n');
|
|
74
|
+
}
|
|
75
|
+
return `${out.replace(/\n{3,}/g, '\n\n').trimEnd()}\n`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function escapeRegExp(s) {
|
|
79
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function applySkillFooter(content, agentKey, overlayDir = resolveOverlayDir()) {
|
|
83
|
+
const footer = renderOverlay(
|
|
84
|
+
readOverlay(overlayDir, 'openspec-apply-change-footer.md'),
|
|
85
|
+
agentKey,
|
|
86
|
+
);
|
|
87
|
+
let out = stripOverlayBlock(content);
|
|
88
|
+
if (!out.endsWith('\n')) out += '\n';
|
|
89
|
+
return out + footer;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const STEP6_RE =
|
|
93
|
+
/(6\. \*\*Implement tasks \(loop until done or blocked\)\*\*\r?\n\r?\n)([\s\S]*?)(\r?\n\r?\n \*\*Pause if:\*\*)/;
|
|
94
|
+
|
|
95
|
+
export function patchOpsxApplyContent(content, agentKey, overlayDir = resolveOverlayDir()) {
|
|
96
|
+
if (!STEP6_RE.test(content)) {
|
|
97
|
+
return { content, status: 'no-match' };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const implementStep = renderOverlay(
|
|
101
|
+
readOverlay(overlayDir, 'opsx-apply-implement-step.md'),
|
|
102
|
+
agentKey,
|
|
103
|
+
);
|
|
104
|
+
let out = content.replace(STEP6_RE, `$1${implementStep}$3`);
|
|
105
|
+
|
|
106
|
+
const completionLine = renderOverlay(
|
|
107
|
+
readOverlay(overlayDir, 'opsx-apply-completion-step.md'),
|
|
108
|
+
agentKey,
|
|
109
|
+
).trimEnd();
|
|
110
|
+
|
|
111
|
+
if (/ - If all done:.*\r?\n/.test(out)) {
|
|
112
|
+
out = out.replace(/ - If all done:.*\r?\n/, `${completionLine}\r\n`);
|
|
113
|
+
} else {
|
|
114
|
+
out = out.replace(
|
|
115
|
+
/(7\. \*\*On completion or pause, show status\*\*\r?\n\r?\n Display:\r?\n)/,
|
|
116
|
+
`$1${completionLine}\r\n`,
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const already =
|
|
121
|
+
content.includes(STEP_MARKER) || content.includes(LEGACY_STEP_MARKER);
|
|
122
|
+
const status = already ? 're-patched' : 'patched';
|
|
123
|
+
return { content: out, status };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function patchFile(relPath, transform, label) {
|
|
127
|
+
const abs = path.join(process.cwd(), relPath);
|
|
128
|
+
if (!fs.existsSync(abs)) {
|
|
129
|
+
process.stdout.write(`skip (missing): ${relPath}\n`);
|
|
130
|
+
return 'missing';
|
|
131
|
+
}
|
|
132
|
+
const before = fs.readFileSync(abs, 'utf8');
|
|
133
|
+
const after = transform(before);
|
|
134
|
+
if (after === before) {
|
|
135
|
+
process.stdout.write(`unchanged: ${relPath}\n`);
|
|
136
|
+
return 'unchanged';
|
|
137
|
+
}
|
|
138
|
+
fs.writeFileSync(abs, after, 'utf8');
|
|
139
|
+
process.stdout.write(`${label}: ${relPath}\n`);
|
|
140
|
+
return label;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function main() {
|
|
144
|
+
const overlayDir = resolveOverlayDir();
|
|
145
|
+
|
|
146
|
+
for (const [agentKey, paths] of Object.entries(AGENT_PATHS)) {
|
|
147
|
+
if (paths.opsxApply) {
|
|
148
|
+
patchFile(
|
|
149
|
+
paths.opsxApply,
|
|
150
|
+
(content) => patchOpsxApplyContent(content, agentKey, overlayDir).content,
|
|
151
|
+
'patched-opsx-apply',
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
patchFile(
|
|
156
|
+
paths.applySkill,
|
|
157
|
+
(content) => applySkillFooter(content, agentKey, overlayDir),
|
|
158
|
+
'patched-skill-footer',
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
process.stdout.write('\nDone. Forge-owned /forge:apply commands are not modified.\n');
|
|
163
|
+
process.stdout.write('After OpenSpec upgrade: forge overlay\n');
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const isDirect =
|
|
167
|
+
process.argv[1] && pathToFileURL(path.resolve(process.argv[1])).href === import.meta.url;
|
|
168
|
+
|
|
169
|
+
if (isDirect) {
|
|
170
|
+
try {
|
|
171
|
+
main();
|
|
172
|
+
} catch (err) {
|
|
173
|
+
process.stderr.write(`${err.message || err}\n`);
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {
|
|
4
|
+
applySkillFooter,
|
|
5
|
+
patchOpsxApplyContent,
|
|
6
|
+
stripOverlayBlock,
|
|
7
|
+
} from './vendor-openspec-overlays.mjs';
|
|
8
|
+
|
|
9
|
+
const VANILLA = `---
|
|
10
|
+
name: test
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
6. **Implement tasks (loop until done or blocked)**
|
|
14
|
+
|
|
15
|
+
For each pending task:
|
|
16
|
+
- Show which task is being worked on
|
|
17
|
+
- Make the code changes required
|
|
18
|
+
- Keep changes minimal and focused
|
|
19
|
+
- Mark task complete in the tasks file: \`- [ ]\` → \`- [x]\`
|
|
20
|
+
- Continue to next task
|
|
21
|
+
|
|
22
|
+
**Pause if:**
|
|
23
|
+
- Task is unclear
|
|
24
|
+
|
|
25
|
+
7. **On completion or pause, show status**
|
|
26
|
+
|
|
27
|
+
Display:
|
|
28
|
+
- Tasks completed this session
|
|
29
|
+
- Overall progress: "N/M tasks complete"
|
|
30
|
+
- If all done: suggest archive
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
test('patchOpsxApplyContent injects Forge implement step', () => {
|
|
34
|
+
const { content, status } = patchOpsxApplyContent(VANILLA, 'cursor');
|
|
35
|
+
assert.equal(status, 'patched');
|
|
36
|
+
assert.match(content, /REQUIRED \(Forge\):/);
|
|
37
|
+
assert.match(content, /\.cursor\/skills\/forge\/phases\/implement\.md/);
|
|
38
|
+
assert.match(content, /Dispatch \*\*implementer\*\* subagent/);
|
|
39
|
+
assert.doesNotMatch(content, /Make the code changes required/);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('patchOpsxApplyContent is idempotent on re-run', () => {
|
|
43
|
+
const first = patchOpsxApplyContent(VANILLA, 'claude');
|
|
44
|
+
const second = patchOpsxApplyContent(first.content, 'claude');
|
|
45
|
+
assert.equal(second.status, 're-patched');
|
|
46
|
+
assert.match(second.content, /\.claude\/skills\/forge\/phases\/verify\.md/);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('applySkillFooter adds overlay block once', () => {
|
|
50
|
+
const skill = '---\nname: openspec-apply-change\n---\n\nBody.\n';
|
|
51
|
+
const once = applySkillFooter(skill, 'codex');
|
|
52
|
+
assert.match(once, /forgekit:openspec-overlay:start/);
|
|
53
|
+
assert.match(once, /\/forge:apply/);
|
|
54
|
+
const twice = applySkillFooter(once, 'codex');
|
|
55
|
+
assert.equal((twice.match(/## Forge overlay/g) || []).length, 1);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('stripOverlayBlock removes footer', () => {
|
|
59
|
+
const withFooter = applySkillFooter('body\n', 'cursor');
|
|
60
|
+
const stripped = stripOverlayBlock(withFooter);
|
|
61
|
+
assert.doesNotMatch(stripped, /forgekit:openspec-overlay/);
|
|
62
|
+
});
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: archive-to-adr
|
|
3
|
+
description: >-
|
|
4
|
+
Create or update Architecture Decision Records (ADRs) from an archived OpenSpec
|
|
5
|
+
change. Fire after archive-shaped Bash completes (hooks wrap `openspec archive`
|
|
6
|
+
plus `mv` into dated `openspec/changes/archive/…`). Also use when sessionStart
|
|
7
|
+
reports pending archives, the user says `/archive-to-adr`, or they ask to
|
|
8
|
+
generate ADRs from an archive. Skip entirely when the project has ADRs disabled
|
|
9
|
+
(`.forge/config.json` → `adr.enabled: false`).
|
|
10
|
+
disable-model-invocation: false
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Archive to ADR
|
|
14
|
+
|
|
15
|
+
Turn a **completed and archived** OpenSpec change into one or more ADRs, following
|
|
16
|
+
the project's decisions doc (scaffolded as `docs/decisions.md` by default via
|
|
17
|
+
`forge init --adr`).
|
|
18
|
+
|
|
19
|
+
This skill is **decoupled** from OpenSpec itself: archive hooks inject a reminder
|
|
20
|
+
after successful archive-shaped shells; you judge whether an ADR is warranted.
|
|
21
|
+
|
|
22
|
+
## Project config
|
|
23
|
+
|
|
24
|
+
Read **`.forge/config.json`** at the repo root (written by `forge init` / optional
|
|
25
|
+
`forgekit install` project scaffold):
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"adr": {
|
|
30
|
+
"enabled": true,
|
|
31
|
+
"dir": "docs/adr",
|
|
32
|
+
"decisionsDoc": "docs/decisions.md"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
| Field | Default | Meaning |
|
|
38
|
+
|-------|---------|---------|
|
|
39
|
+
| `adr.enabled` | `true` if file missing and `docs/adr/` exists; else follow install | When `false`, do **not** run this skill — no ADR, no "No ADR" stamp |
|
|
40
|
+
| `adr.dir` | `docs/adr` | Directory for `NNNN-short-topic.md` + status `README.md` |
|
|
41
|
+
| `adr.decisionsDoc` | sibling `decisions.md` of `adr.dir` | Process / template (when to write, format, hooks) |
|
|
42
|
+
| `plan.engine` | `openspec` | `specs` = built-in engine; archives live under `<plan.dir>/changes/archive/` instead of `openspec/changes/archive/` |
|
|
43
|
+
|
|
44
|
+
Below, **`{adrDir}`** and **`{decisionsDoc}`** mean those configured paths, and
|
|
45
|
+
**`{archiveRoot}`** means `openspec/changes/archive` (OpenSpec engine) or
|
|
46
|
+
`<plan.dir>/changes/archive` (specs engine, default `specs/changes/archive`).
|
|
47
|
+
|
|
48
|
+
If `adr.enabled` is `false`, stop and tell the user ADRs are disabled for this project.
|
|
49
|
+
|
|
50
|
+
## When to skip entirely (ADRs enabled)
|
|
51
|
+
|
|
52
|
+
If the change is a bug fix, copy tweak, docs-only edit, or refactor with no
|
|
53
|
+
architectural choice, **do not** create an ADR. Instead add this **single line**
|
|
54
|
+
to the archived `proposal.md`:
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
No ADR — non-architectural change
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
That silences the session-start pending-ADR backstop.
|
|
61
|
+
|
|
62
|
+
## Input
|
|
63
|
+
|
|
64
|
+
**Required:** the archive directory path, e.g.
|
|
65
|
+
`{archiveRoot}/2026-05-15-payment-add-service/`.
|
|
66
|
+
|
|
67
|
+
If the user only gives the change name, resolve it: list `{archiveRoot}/`
|
|
68
|
+
and pick the matching dated folder.
|
|
69
|
+
|
|
70
|
+
## Steps
|
|
71
|
+
|
|
72
|
+
1. **Confirm ADRs are enabled** — read `.forge/config.json` as above.
|
|
73
|
+
|
|
74
|
+
2. **Read the archived artifacts**
|
|
75
|
+
- `proposal.md` — Why, What Changes, Capabilities, Impact
|
|
76
|
+
- `design.md` — Context, Decisions, Risks, Migration
|
|
77
|
+
- `specs/**/*.md` in the archive (capability deltas)
|
|
78
|
+
|
|
79
|
+
3. **Apply the ADR gate** (`{decisionsDoc}` § "When to write an ADR")
|
|
80
|
+
- Write an ADR when the change establishes/revises a **boundary**, picks one
|
|
81
|
+
approach over a **real** alternative, introduces a **constraint** future code
|
|
82
|
+
must respect, picks a **vendor/protocol/library** that is expensive to swap,
|
|
83
|
+
or codifies a **repo-wide** convention.
|
|
84
|
+
- Skip when it's purely implementation detail inside an existing decision.
|
|
85
|
+
|
|
86
|
+
4. **Pick the next ADR number**
|
|
87
|
+
- List `{adrDir}/*.md` (four-digit prefix pattern `NNNN-*.md`).
|
|
88
|
+
- Use **the next sequential integer**; never reuse a number.
|
|
89
|
+
- If multiple distinct architectural decisions warrant separate ADRs in one
|
|
90
|
+
archive, create multiple files (`NNNN-…`, `NNNN+1-…`), each with one sharp decision.
|
|
91
|
+
|
|
92
|
+
5. **Author the ADR file** at `{adrDir}/NNNN-short-topic.md` using the **exact
|
|
93
|
+
section structure** from `{decisionsDoc}`:
|
|
94
|
+
- Title: `# NNNN. <Short, decision-shaped title>`
|
|
95
|
+
- Frontmatter lines: **Status**, **Date**, **Area**, **Related** (link the
|
|
96
|
+
archive path, sibling ADRs)
|
|
97
|
+
- Body: Context, Decision, Alternatives considered, Consequences
|
|
98
|
+
(Positive / Negative / Neutral), References
|
|
99
|
+
|
|
100
|
+
Keep it terse (target ~80–200 lines total across all new ADRs for the change).
|
|
101
|
+
Link to the archive for forensic detail; don't paste the whole design.
|
|
102
|
+
|
|
103
|
+
6. **Update the status index** `{adrDir}/README.md` — add or amend the table
|
|
104
|
+
row(s). If `README.md` is missing, create it with the table scaffold from
|
|
105
|
+
`{decisionsDoc}`.
|
|
106
|
+
|
|
107
|
+
7. **Cross-reference from the archive**
|
|
108
|
+
- In `{archiveRoot}/<dated-change>/proposal.md`, add (or extend):
|
|
109
|
+
|
|
110
|
+
```markdown
|
|
111
|
+
## Decision record
|
|
112
|
+
|
|
113
|
+
This change is recorded as ADR-NNNN ({adrDir}/NNNN-short-topic.md).
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Use `ADR-NNNN` text so pending-ADR hooks (`grep 'ADR-[0-9]'`) recognize the link.
|
|
117
|
+
|
|
118
|
+
8. **If capability specs were promoted** during archive: consider a one-line
|
|
119
|
+
pointer in the ADR **References** to `openspec/specs/<capability>/spec.md`
|
|
120
|
+
(optional but valuable).
|
|
121
|
+
|
|
122
|
+
## Output
|
|
123
|
+
|
|
124
|
+
- Paths of created/updated `{adrDir}/*.md`
|
|
125
|
+
- Confirmation `{adrDir}/README.md` was updated
|
|
126
|
+
- Confirmation the archived `proposal.md` now references `ADR-NNNN`
|
|
127
|
+
- One-sentence summary per ADR written
|
|
128
|
+
|
|
129
|
+
## Guardrails
|
|
130
|
+
|
|
131
|
+
- Naming: **`NNNN-short-topic.md`**, not date-only filenames.
|
|
132
|
+
- **Never** add `{adrDir}/` to `.cursorindexignore` (or equivalent ignore that
|
|
133
|
+
blocks agent retrieval).
|
|
134
|
+
- Don't duplicate content that belongs only in OpenSpec — the archive remains
|
|
135
|
+
the verbose record; the ADR is the distilled "why."
|
|
136
|
+
- If unsure whether to write an ADR, re-read the decisions gate; when still
|
|
137
|
+
ambiguous, ask the user once rather than inventing ceremony.
|
|
138
|
+
|
|
139
|
+
## Relationship to hooks
|
|
140
|
+
|
|
141
|
+
Project hooks (from `forge init --adr`) may:
|
|
142
|
+
|
|
143
|
+
- Remind agents to run **archive-to-adr** after `openspec archive` / an archive
|
|
144
|
+
move (either engine)
|
|
145
|
+
- At session start, list archives (both `openspec/changes/archive/` and
|
|
146
|
+
`specs/changes/archive/`) whose `proposal.md` lacks `ADR-*` **and** lacks
|
|
147
|
+
the `No ADR — non-architectural change` stamp
|
|
148
|
+
|
|
149
|
+
Neither hook writes ADR files; **this skill** does.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: forge
|
|
3
|
+
description: >-
|
|
4
|
+
Forge — self-contained disciplined development workflow. Triage substantial work,
|
|
5
|
+
brainstorm, tracked plan (OpenSpec or built-in specs engine), subagent-driven TDD
|
|
6
|
+
implementation, verify, review, and finish.
|
|
7
|
+
Use when building features, fixing non-trivial bugs, or when the user invokes /forge.
|
|
8
|
+
Skip only when user says /forge:skip or work is trivial.
|
|
9
|
+
disable-model-invocation: false
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Forge
|
|
13
|
+
|
|
14
|
+
Spec-tracked development pipeline. Planning engine is per-project
|
|
15
|
+
(`.forge/config.json` → `plan.engine`): **OpenSpec** (vendor CLI) or the
|
|
16
|
+
**built-in specs engine** (`specs/changes/`, same layout). **Self-contained** —
|
|
17
|
+
all workflow skills live under `./skills/` (vendored from Superpowers MIT; see
|
|
18
|
+
[skills/NOTICE.md](./skills/NOTICE.md)).
|
|
19
|
+
|
|
20
|
+
Full reference: forgekit `docs/forge.md` (shipped with this skill’s source repo).
|
|
21
|
+
|
|
22
|
+
**Announce at start:** "Using Forge for this work." Include effective pace from
|
|
23
|
+
`forge status` (e.g. `Pace: auto → brisk (…)`) — see [references/pace.md](./references/pace.md).
|
|
24
|
+
|
|
25
|
+
## Instruction priority
|
|
26
|
+
|
|
27
|
+
1. User explicit instructions (including `/forge:skip` and pace overrides)
|
|
28
|
+
2. This skill + `./phases/`, `./references/`, and `./skills/`
|
|
29
|
+
3. Project OpenSpec skills (`openspec-propose`, `openspec-apply-change`) — do not edit vendor copies (OpenSpec-engine projects only)
|
|
30
|
+
|
|
31
|
+
## Pace (thoroughness)
|
|
32
|
+
|
|
33
|
+
Checkout-local prefs control review/verify ceremony. Default pace is **`auto`**
|
|
34
|
+
(resolves once per session from risk signals).
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
forge prefs # print effective pace (does NOT write a file)
|
|
38
|
+
forge prefs brisk # WRITE .forge/preferences.local.json
|
|
39
|
+
forge prefs --session-set lite
|
|
40
|
+
forge models # print billing (does NOT write); set: included|metered
|
|
41
|
+
forge doctor # plan-engine readiness (OpenSpec CLI or specs/ layout)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Honor [references/pace.md](./references/pace.md) in implement / verify / review.
|
|
45
|
+
Hard floor: money/auth/contracts/migrations always get per-task review (even under `standard` mid-group / `brisk` / `lite`).
|
|
46
|
+
Local overlays: forgekit `docs/forge.md` § Checkout-local overrides.
|
|
47
|
+
|
|
48
|
+
## Bundled skills
|
|
49
|
+
|
|
50
|
+
| Skill | Path | When |
|
|
51
|
+
| ----- | ---- | ---- |
|
|
52
|
+
| Brainstorming | [skills/brainstorming/SKILL.md](./skills/brainstorming/SKILL.md) | brainstorm phase |
|
|
53
|
+
| TDD | [skills/test-driven-development/SKILL.md](./skills/test-driven-development/SKILL.md) | every implement task |
|
|
54
|
+
| Subagent-driven dev | [skills/subagent-driven-development/SKILL.md](./skills/subagent-driven-development/SKILL.md) | implement phase |
|
|
55
|
+
| Systematic debugging | [skills/systematic-debugging/SKILL.md](./skills/systematic-debugging/SKILL.md) | blockers / test failures |
|
|
56
|
+
| Verification | [skills/verification-before-completion/SKILL.md](./skills/verification-before-completion/SKILL.md) | verify phase |
|
|
57
|
+
| Code review | [skills/requesting-code-review/SKILL.md](./skills/requesting-code-review/SKILL.md) | review phase |
|
|
58
|
+
|
|
59
|
+
## Step 0 — Triage (default)
|
|
60
|
+
|
|
61
|
+
Before coding on any non-trivial request, run triage per
|
|
62
|
+
[references/substantial-work.md](./references/substantial-work.md).
|
|
63
|
+
|
|
64
|
+
- **Substantial (tracked-change-worthy)** → continue Forge (bootstrap session if needed)
|
|
65
|
+
- **Too small for a tracked change** → execute directly, no session
|
|
66
|
+
- **`/forge:skip`** → mark session `phase: skipped` if one exists; execute directly
|
|
67
|
+
|
|
68
|
+
Bootstrap session when entering Forge:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
forge new <kebab-slug>
|
|
72
|
+
# optional: forge new <slug> --signal "add stripe refund"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`forge new` resolves pace (default `auto`) onto the session and runs the
|
|
76
|
+
plan-engine doctor in warn-only mode (missing OpenSpec CLI does not block
|
|
77
|
+
session creation; specs-engine projects skip the CLI check).
|
|
78
|
+
|
|
79
|
+
Resume: read `.forge/active.json` → `forge status`.
|
|
80
|
+
|
|
81
|
+
Update phase as you progress:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
forge phase <phase> [--plan-type openspec|specs] [--openspec <change>]
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Valid phases: `triage`, `brainstorm`, `plan`, `implement`, `verify`, `review`, `finish`, `done`, `skipped`.
|
|
88
|
+
|
|
89
|
+
## Phase flow
|
|
90
|
+
|
|
91
|
+
| Phase | Action |
|
|
92
|
+
| ----- | ------ |
|
|
93
|
+
| brainstorm | [phases/brainstorm.md](./phases/brainstorm.md) → **skills/brainstorming** |
|
|
94
|
+
| plan | [references/plan-routing.md](./references/plan-routing.md) → engine from `.forge/config.json`: **OpenSpec** ([plan-openspec.md](./phases/plan-openspec.md)) or **specs** ([plan-specs.md](./phases/plan-specs.md)) |
|
|
95
|
+
| implement | [phases/implement.md](./phases/implement.md) → **subagent-driven-development** + **TDD** |
|
|
96
|
+
| verify | [phases/verify.md](./phases/verify.md) → **verification-before-completion** |
|
|
97
|
+
| review | [phases/review.md](./phases/review.md) → **requesting-code-review** |
|
|
98
|
+
| finish | [phases/finish.md](./phases/finish.md) |
|
|
99
|
+
|
|
100
|
+
<HARD-GATE>
|
|
101
|
+
Do NOT write implementation code during brainstorm or plan phases until the user approves the tracked change (OpenSpec or specs).
|
|
102
|
+
</HARD-GATE>
|
|
103
|
+
|
|
104
|
+
<HARD-GATE>
|
|
105
|
+
Subagent dispatch: NEVER pass a model slug you picked yourself (including any from the host's model list). Run `forge resolve-model --tier <fast|standard|capable>` and honor its JSON — omit the `model` parameter when `omitModel` is true, else pass `model` exactly. Metered/API models only on explicit user request. This applies to retries and fallbacks too: if a dispatch fails, re-resolve — do not hand-pick a replacement slug.
|
|
106
|
+
</HARD-GATE>
|
|
107
|
+
|
|
108
|
+
## Session artefacts
|
|
109
|
+
|
|
110
|
+
Layout: [references/forge-layout.md](./references/forge-layout.md)
|
|
111
|
+
|
|
112
|
+
Testing: [references/test-strategy.md](./references/test-strategy.md) — tier 1 scoped TDD per task, tier 2 narrow evidence per task, tier 3 full workspace once at verify.
|
|
113
|
+
|
|
114
|
+
## Guardrails (every phase)
|
|
115
|
+
|
|
116
|
+
- No autonomous `git commit` / push unless the user explicitly asks
|
|
117
|
+
- Tests required for behavior changes
|
|
118
|
+
- Trace ecosystem consumers when contracts change
|
|
119
|
+
- Honor `openspec/config.yaml` prefixes when the project uses them (OpenSpec engine)
|
|
120
|
+
- **Runtime integrity** — [references/runtime-integrity.md](./references/runtime-integrity.md): **spine.json mandatory every change** (rows or `notApplicable` — not keyword-gated); no stubs / false success; capability specs beat narrow task wording; every claimed capability needs a named production caller; product-loop when spine has rows (or BLOCKED); deferred wiring only via `forge defer` — `forge phase done` mechanically refuses on `forge integrity-check` failures
|
|
121
|
+
|
|
122
|
+
## Agent surfaces
|
|
123
|
+
|
|
124
|
+
| Agent | Skill (after `forgekit install`) | Project wiring (`forge init`) |
|
|
125
|
+
| ----- | ----------------------------- | ----------------------------- |
|
|
126
|
+
| **Cursor** | `~/.cursor/skills/forge/` | commands, `forge.mdc`, SessionStart hook |
|
|
127
|
+
| **Claude Code** | `~/.claude/skills/forge/` | commands, `forge.md`, SessionStart + prompt hooks |
|
|
128
|
+
| **Codex CLI** | `~/.codex/skills/forge/` | thin rule |
|
|
129
|
+
|
|
130
|
+
**Planning (all agents):** after brainstorm, proceed directly to the configured engine — no plan-mode prompt. See [references/plan-routing.md](./references/plan-routing.md). Hooks remind agents to run the propose flow when `planType` is unset.
|
|
131
|
+
|
|
132
|
+
**Distribute:** edit `skills/forge/` in forgekit, then `forgekit install --skills forge --force` on each machine. The bundled skills are a maintained fork (see [skills/NOTICE.md](./skills/NOTICE.md)) — do not re-vendor from Superpowers.
|
|
133
|
+
|
|
134
|
+
## Do not edit vendor OpenSpec skills
|
|
135
|
+
|
|
136
|
+
OpenSpec vendor skills upgrade in place. Forge behaviour lives in this tree and forgekit `docs/forge.md`. Re-apply vendor patches with `forge overlay` after OpenSpec upgrades.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Brainstorm phase
|
|
2
|
+
|
|
3
|
+
Read and follow [../skills/brainstorming/SKILL.md](../skills/brainstorming/SKILL.md) in full.
|
|
4
|
+
|
|
5
|
+
**Pace:** Honor `brainstorm.depth` from [../references/pace.md](../references/pace.md) / `forge status` — `full` (default skill), `short` (≤2–3 approaches), or `minimal` (confirm intent + one approach when design is obvious).
|
|
6
|
+
|
|
7
|
+
## Terminal state
|
|
8
|
+
|
|
9
|
+
After user approves the design:
|
|
10
|
+
|
|
11
|
+
1. Save to `.forge/sessions/<id>/brainstorm/notes.md` and `decisions.md`
|
|
12
|
+
2. Read [../references/plan-routing.md](../references/plan-routing.md) and **proceed to OpenSpec propose** — do not ask for a plan mode
|
|
13
|
+
3. Follow [plan-openspec.md](./plan-openspec.md) — **not** implementation until OpenSpec artefacts are approved
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
forge phase brainstorm
|
|
17
|
+
# after OpenSpec propose:
|
|
18
|
+
forge phase plan --plan-type openspec --openspec <name>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
<HARD-GATE>
|
|
22
|
+
Do NOT invoke implement phase or write production code until OpenSpec plan phase completes and user approves.
|
|
23
|
+
</HARD-GATE>
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Finish phase
|
|
2
|
+
|
|
3
|
+
Before marking done, integrity must pass (or the user must approve an incomplete finish):
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
forge integrity-check # spine + deferrals + product-loop / BLOCKED
|
|
7
|
+
forge score # preview L2 scorecard (optional)
|
|
8
|
+
forge phase done # runs integrity checks + writes scorecard.md/json
|
|
9
|
+
# escape hatch only with an honest reason:
|
|
10
|
+
# forge phase done --allow-incomplete "E2E blocked: no Compose in this environment"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`forge phase done|finish` always writes `.forge/sessions/<id>/scorecard.md` (and
|
|
14
|
+
`.json`) — an L2 grade of session artifacts. Answer the **human ship-check**
|
|
15
|
+
questions in that file for platform/async work (L3). See [usage.md](../../../docs/usage.md)
|
|
16
|
+
§ Session success.
|
|
17
|
+
|
|
18
|
+
## OpenSpec path (`planType: openspec`)
|
|
19
|
+
|
|
20
|
+
1. Confirm all tasks complete in `tasks.md`.
|
|
21
|
+
2. User runs or approves `/opsx:archive` / `openspec archive`.
|
|
22
|
+
3. **ADR follow-up (optional):** if `.forge/config.json` has `adr.enabled: true`
|
|
23
|
+
(or the project uses ADRs), follow the **`archive-to-adr`** skill using
|
|
24
|
+
`adr.dir` / `adr.decisionsDoc`. If ADRs are disabled, skip.
|
|
25
|
+
4. End with **Suggested commit** block (display only — do not commit).
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
forge phase done
|
|
29
|
+
forge cleanup
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Specs path (`planType: specs`)
|
|
33
|
+
|
|
34
|
+
1. Confirm all tasks complete in `<specsDir>/changes/<name>/tasks.md`.
|
|
35
|
+
2. **Archive** (with user approval) — prefer the CLI:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
forge change archive <name>
|
|
39
|
+
# → <specsDir>/changes/archive/YYYY-MM-DD-<name>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Or move manually:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
git mv <specsDir>/changes/<name> <specsDir>/changes/archive/<YYYY-MM-DD>-<name>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
(plain `mv` if the dir is untracked).
|
|
49
|
+
3. **ADR follow-up (optional):** same rule as OpenSpec — if `adr.enabled`,
|
|
50
|
+
follow **`archive-to-adr`** on the archived change; otherwise skip.
|
|
51
|
+
4. End with **Suggested commit** block (display only — do not commit).
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
forge phase done
|
|
55
|
+
forge cleanup
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Throwaway path (`planType: throwaway`)
|
|
59
|
+
|
|
60
|
+
1. Confirm all tasks in `plan.md` checked off.
|
|
61
|
+
2. Summarize what shipped.
|
|
62
|
+
3. Mark session done and cleanup:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
forge phase done
|
|
66
|
+
forge cleanup
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Direct path (`planType: direct`)
|
|
70
|
+
|
|
71
|
+
1. Confirm implementation matches brainstorm `notes.md` / `decisions.md`.
|
|
72
|
+
2. Summarize what shipped.
|
|
73
|
+
3. Mark session done and cleanup:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
forge phase done
|
|
77
|
+
forge cleanup
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Skipped path (`/forge:skip`)
|
|
81
|
+
|
|
82
|
+
Session already at `phase: skipped`. No finish steps.
|
|
83
|
+
|
|
84
|
+
## Hand-off
|
|
85
|
+
|
|
86
|
+
Leave implementation files staged/unstaged per user preference. Never commit unless
|
|
87
|
+
explicitly asked (project git policy).
|