@izkac/forgekit 0.3.12 → 0.3.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/bin/forge.mjs +107 -107
  2. package/bin/forgekit.mjs +83 -83
  3. package/bin/review.mjs +81 -81
  4. package/package.json +1 -1
  5. package/scripts/prepack.mjs +78 -78
  6. package/scripts/run-tests.mjs +50 -50
  7. package/src/adr.mjs +236 -236
  8. package/src/adr.test.mjs +170 -170
  9. package/src/change.mjs +327 -234
  10. package/src/change.test.mjs +145 -83
  11. package/src/cleanup-sessions.mjs +84 -84
  12. package/src/config.mjs +103 -103
  13. package/src/defer.mjs +75 -75
  14. package/src/doctor.mjs +350 -341
  15. package/src/doctor.test.mjs +114 -114
  16. package/src/init.mjs +680 -621
  17. package/src/install.mjs +815 -815
  18. package/src/install.test.mjs +180 -180
  19. package/src/integrity-check.mjs +60 -60
  20. package/src/integrity.mjs +682 -682
  21. package/src/integrity.test.mjs +566 -566
  22. package/src/lib.mjs +143 -143
  23. package/src/models.defaults.json +41 -41
  24. package/src/new-session.mjs +99 -99
  25. package/src/openspec-overlays/README.md +19 -19
  26. package/src/openspec-overlays/openspec-apply-change-footer.md +14 -14
  27. package/src/openspec-overlays/opsx-apply-completion-step.md +1 -1
  28. package/src/openspec-overlays/opsx-apply-implement-step.md +11 -11
  29. package/src/paths.mjs +92 -92
  30. package/src/plan-engine.mjs +321 -278
  31. package/src/plan-engine.test.mjs +447 -283
  32. package/src/preferences.defaults.json +78 -78
  33. package/src/preferences.mjs +438 -438
  34. package/src/preferences.test.mjs +174 -174
  35. package/src/record-evidence.mjs +204 -204
  36. package/src/resolve-model.mjs +312 -312
  37. package/src/resolve-model.test.mjs +194 -194
  38. package/src/review/cli.test.mjs +117 -117
  39. package/src/review/export.mjs +172 -172
  40. package/src/review/export.test.mjs +197 -197
  41. package/src/review/fixtures/valid-review.json +42 -42
  42. package/src/review/lib.mjs +894 -894
  43. package/src/review/lib.test.mjs +266 -266
  44. package/src/review/schema.json +196 -196
  45. package/src/review/signals.test.mjs +62 -62
  46. package/src/score-cli.mjs +68 -68
  47. package/src/score.mjs +568 -568
  48. package/src/score.test.mjs +366 -366
  49. package/src/session-reminder.mjs +207 -207
  50. package/src/session-status.mjs +70 -70
  51. package/src/set-models.mjs +186 -186
  52. package/src/set-phase.mjs +205 -205
  53. package/src/set-prefs.mjs +294 -294
  54. package/src/specs-sync.mjs +234 -0
  55. package/src/specs-sync.test.mjs +114 -0
  56. package/src/spine.mjs +93 -93
  57. package/src/triage-prompt.mjs +175 -175
  58. package/src/triage-prompt.test.mjs +50 -50
  59. package/src/vendor-openspec-overlays.mjs +176 -176
  60. package/src/vendor-openspec-overlays.test.mjs +62 -62
  61. package/vendor/skills/archive-to-adr/SKILL.md +149 -149
  62. package/vendor/skills/forge/SKILL.md +136 -136
  63. package/vendor/skills/forge/docs/forge.md +650 -647
  64. package/vendor/skills/forge/phases/brainstorm.md +23 -23
  65. package/vendor/skills/forge/phases/finish.md +90 -87
  66. package/vendor/skills/forge/phases/implement.md +77 -77
  67. package/vendor/skills/forge/phases/plan-openspec.md +60 -60
  68. package/vendor/skills/forge/phases/plan-specs.md +163 -117
  69. package/vendor/skills/forge/phases/review.md +25 -25
  70. package/vendor/skills/forge/phases/verify.md +124 -124
  71. package/vendor/skills/forge/references/forge-layout.md +85 -85
  72. package/vendor/skills/forge/references/pace.md +115 -115
  73. package/vendor/skills/forge/references/plan-routing.md +52 -51
  74. package/vendor/skills/forge/references/runtime-integrity.md +232 -232
  75. package/vendor/skills/forge/references/substantial-work.md +37 -37
  76. package/vendor/skills/forge/references/test-evidence.md +30 -30
  77. package/vendor/skills/forge/references/test-strategy.md +68 -68
  78. package/vendor/skills/forge/skills/subagent-driven-development/SKILL.md +87 -87
  79. package/vendor/skills/forge/subagents/final-reviewer-prompt.md +56 -56
  80. package/vendor/skills/forge/subagents/implementer-prompt.md +38 -38
  81. package/vendor/skills/git-resolve-adr-conflict/SKILL.md +132 -132
  82. package/vendor/skills/thorough-code-review/SKILL.md +290 -290
  83. package/vendor/skills/thorough-code-review/examples.md +133 -133
  84. package/vendor/skills/thorough-code-review/reference/accepted-risks.md +26 -26
  85. package/vendor/skills/thorough-code-review/reference/lenses.md +96 -96
  86. package/vendor/skills/thorough-code-review/reference/phase1-scout.md +62 -62
  87. package/vendor/skills/thorough-code-review/reference/phase2-skeptic.md +105 -105
  88. package/vendor/skills/thorough-code-review/reference/report-schema.json +222 -222
  89. package/vendor/skills/thorough-code-review/reference/report-template.md +115 -115
  90. package/vendor/skills/thorough-code-review/reference/severity-rubric.md +49 -49
  91. package/vendor/skills/thorough-code-review/reference/signals-preflight.md +55 -55
  92. package/vendor/templates/adr/README.md +7 -7
  93. package/vendor/templates/adr/decisions.md +141 -141
  94. package/vendor/templates/adr/hooks/check-pending-adrs.mjs +74 -74
  95. package/vendor/templates/adr/hooks/check-pending-adrs.sh +3 -3
  96. package/vendor/templates/adr/hooks/openspec-archive-agent-message.mjs +52 -52
  97. package/vendor/templates/adr/hooks/openspec-archive-agent-message.sh +3 -3
  98. package/vendor/templates/project/claude/commands/forge-apply.md +75 -75
  99. package/vendor/templates/project/claude/commands/forge-brainstorm.md +7 -7
  100. package/vendor/templates/project/claude/commands/forge-build.md +17 -17
  101. package/vendor/templates/project/claude/commands/forge-plan.md +12 -12
  102. package/vendor/templates/project/claude/commands/forge-skip.md +14 -14
  103. package/vendor/templates/project/claude/commands/forge-status.md +16 -16
  104. package/vendor/templates/project/claude/commands/forge.md +16 -16
  105. package/vendor/templates/project/claude/hooks/forge-prompt-hook.mjs +73 -73
  106. package/vendor/templates/project/claude/hooks/forge-session-start.mjs +19 -19
  107. package/vendor/templates/project/claude/hooks/forge-triage-hook.mjs +77 -77
  108. package/vendor/templates/project/cursor/commands/forge-apply.md +75 -75
  109. package/vendor/templates/project/cursor/commands/forge-brainstorm.md +10 -10
  110. package/vendor/templates/project/cursor/commands/forge-build.md +17 -17
  111. package/vendor/templates/project/cursor/commands/forge-plan.md +15 -15
  112. package/vendor/templates/project/cursor/commands/forge-skip.md +14 -14
  113. package/vendor/templates/project/cursor/commands/forge-status.md +16 -16
  114. package/vendor/templates/project/cursor/commands/forge.md +16 -16
  115. package/vendor/templates/project/cursor/hooks/forge-session-start.mjs +30 -30
  116. package/vendor/templates/project/cursor/hooks/forge-session-start.sh +3 -3
package/src/adr.test.mjs CHANGED
@@ -1,170 +1,170 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import fs from 'node:fs';
4
- import os from 'node:os';
5
- import path from 'node:path';
6
- import {
7
- ADR_SKILLS,
8
- DEFAULT_ADR_DIR,
9
- decisionsDocFor,
10
- decisionsRelFromAdrReadme,
11
- disableProjectAdr,
12
- loadProjectConfig,
13
- loadUserConfig,
14
- normalizeAdrDir,
15
- resolveProjectAdr,
16
- saveUserConfig,
17
- scaffoldAdr,
18
- } from './adr.mjs';
19
- import {
20
- applyAdrSkills,
21
- inferAdrFromSkills,
22
- parseArgs,
23
- installSkillsToAgents,
24
- SKILL_IDS,
25
- } from './install.mjs';
26
- import { initProject, parseArgs as parseInitArgs } from './init.mjs';
27
-
28
- test('normalizeAdrDir defaults and rejects escapes', () => {
29
- assert.equal(normalizeAdrDir(''), DEFAULT_ADR_DIR);
30
- assert.equal(normalizeAdrDir('docs/adr'), 'docs/adr');
31
- assert.equal(normalizeAdrDir('docs\\architecture\\adr\\'), 'docs/architecture/adr');
32
- assert.throws(() => normalizeAdrDir('../outside'), /relative path/);
33
- assert.throws(() => normalizeAdrDir('/abs'), /relative path/);
34
- });
35
-
36
- test('decisionsDocFor derives sibling decisions.md', () => {
37
- assert.equal(decisionsDocFor('docs/adr'), 'docs/decisions.md');
38
- assert.equal(decisionsDocFor('architecture/decisions'), 'architecture/decisions.md');
39
- });
40
-
41
- test('decisionsRelFromAdrReadme', () => {
42
- assert.equal(decisionsRelFromAdrReadme('docs/adr', 'docs/decisions.md'), '../decisions.md');
43
- });
44
-
45
- test('user config round-trip', () => {
46
- const home = fs.mkdtempSync(path.join(os.tmpdir(), 'forgekit-adr-user-'));
47
- try {
48
- saveUserConfig({ adr: { enabled: true, dir: 'arch/adr' } }, home);
49
- const loaded = loadUserConfig(home);
50
- assert.equal(loaded.adr.enabled, true);
51
- assert.equal(loaded.adr.dir, 'arch/adr');
52
- } finally {
53
- fs.rmSync(home, { recursive: true, force: true });
54
- }
55
- });
56
-
57
- test('scaffoldAdr writes decisions, index, config, hooks', () => {
58
- const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'forgekit-adr-proj-'));
59
- try {
60
- const result = scaffoldAdr(cwd, { dir: 'docs/adr', force: true });
61
- assert.equal(result.dir, 'docs/adr');
62
- assert.equal(result.decisionsDoc, 'docs/decisions.md');
63
- assert.ok(fs.existsSync(path.join(cwd, 'docs', 'decisions.md')));
64
- assert.ok(fs.existsSync(path.join(cwd, 'docs', 'adr', 'README.md')));
65
- assert.ok(fs.existsSync(path.join(cwd, '.forge', 'config.json')));
66
- assert.ok(
67
- fs.existsSync(path.join(cwd, 'scripts', 'hooks', 'check-pending-adrs.sh')),
68
- );
69
- const cfg = loadProjectConfig(cwd);
70
- assert.equal(cfg.adr.enabled, true);
71
- assert.equal(cfg.adr.dir, 'docs/adr');
72
- const decisions = fs.readFileSync(path.join(cwd, 'docs', 'decisions.md'), 'utf8');
73
- assert.match(decisions, /docs\/adr/);
74
- } finally {
75
- fs.rmSync(cwd, { recursive: true, force: true });
76
- }
77
- });
78
-
79
- test('scaffoldAdr custom dir', () => {
80
- const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'forgekit-adr-custom-'));
81
- try {
82
- scaffoldAdr(cwd, { dir: 'architecture/adr', force: true });
83
- assert.ok(fs.existsSync(path.join(cwd, 'architecture', 'decisions.md')));
84
- assert.ok(fs.existsSync(path.join(cwd, 'architecture', 'adr', 'README.md')));
85
- } finally {
86
- fs.rmSync(cwd, { recursive: true, force: true });
87
- }
88
- });
89
-
90
- test('disableProjectAdr', () => {
91
- const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'forgekit-adr-off-'));
92
- try {
93
- scaffoldAdr(cwd, { force: true });
94
- disableProjectAdr(cwd);
95
- assert.equal(loadProjectConfig(cwd).adr.enabled, false);
96
- assert.equal(resolveProjectAdr(cwd).enabled, false);
97
- } finally {
98
- fs.rmSync(cwd, { recursive: true, force: true });
99
- }
100
- });
101
-
102
- test('applyAdrSkills / inferAdrFromSkills', () => {
103
- assert.deepEqual(
104
- applyAdrSkills(['forge'], true).sort(),
105
- ['archive-to-adr', 'forge', 'git-resolve-adr-conflict'].sort(),
106
- );
107
- assert.deepEqual(applyAdrSkills(['forge', 'archive-to-adr'], false), ['forge']);
108
- assert.equal(inferAdrFromSkills(['forge'], null), null);
109
- assert.equal(inferAdrFromSkills(['archive-to-adr'], null), true);
110
- assert.equal(inferAdrFromSkills(['forge'], false), false);
111
- assert.equal(inferAdrFromSkills(['forge'], true), true);
112
- assert.ok(ADR_SKILLS.every((id) => SKILL_IDS.includes(id)));
113
- });
114
-
115
- test('parseArgs adr flags', () => {
116
- const opts = parseArgs([
117
- '--skills',
118
- 'forge',
119
- '--adr',
120
- '--adr-dir',
121
- 'arch/adr',
122
- '--adr-project',
123
- ]);
124
- assert.equal(opts.adr, true);
125
- assert.equal(opts.adrDir, 'arch/adr');
126
- assert.equal(opts.adrProject, true);
127
- assert.equal(parseArgs(['--no-adr']).adr, false);
128
- });
129
-
130
- test('install with --adr installs ADR skills', () => {
131
- const home = fs.mkdtempSync(path.join(os.tmpdir(), 'forgekit-adr-inst-'));
132
- try {
133
- const results = installSkillsToAgents(
134
- applyAdrSkills(['forge'], true),
135
- ['cursor'],
136
- { home, force: true },
137
- );
138
- assert.ok(results.some((r) => r.skill === 'archive-to-adr'));
139
- assert.ok(
140
- fs.existsSync(path.join(home, '.cursor', 'skills', 'archive-to-adr', 'SKILL.md')),
141
- );
142
- assert.ok(
143
- fs.existsSync(
144
- path.join(home, '.cursor', 'skills', 'git-resolve-adr-conflict', 'SKILL.md'),
145
- ),
146
- );
147
- } finally {
148
- fs.rmSync(home, { recursive: true, force: true });
149
- }
150
- });
151
-
152
- test('initProject --adr scaffolds and allows config.json in gitignore', () => {
153
- const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'forgekit-init-adr-'));
154
- try {
155
- const report = initProject(['codex'], {
156
- cwd,
157
- force: true,
158
- adr: true,
159
- adrDir: 'docs/adr',
160
- });
161
- assert.ok(report.adr);
162
- assert.ok(fs.existsSync(path.join(cwd, '.forge', 'config.json')));
163
- const gi = fs.readFileSync(path.join(cwd, '.forge', '.gitignore'), 'utf8');
164
- assert.match(gi, /!config\.json/);
165
- assert.equal(parseInitArgs(['--adr', '--adr-dir', 'x/y']).adr, true);
166
- assert.equal(parseInitArgs(['--no-adr']).adr, false);
167
- } finally {
168
- fs.rmSync(cwd, { recursive: true, force: true });
169
- }
170
- });
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import fs from 'node:fs';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import {
7
+ ADR_SKILLS,
8
+ DEFAULT_ADR_DIR,
9
+ decisionsDocFor,
10
+ decisionsRelFromAdrReadme,
11
+ disableProjectAdr,
12
+ loadProjectConfig,
13
+ loadUserConfig,
14
+ normalizeAdrDir,
15
+ resolveProjectAdr,
16
+ saveUserConfig,
17
+ scaffoldAdr,
18
+ } from './adr.mjs';
19
+ import {
20
+ applyAdrSkills,
21
+ inferAdrFromSkills,
22
+ parseArgs,
23
+ installSkillsToAgents,
24
+ SKILL_IDS,
25
+ } from './install.mjs';
26
+ import { initProject, parseArgs as parseInitArgs } from './init.mjs';
27
+
28
+ test('normalizeAdrDir defaults and rejects escapes', () => {
29
+ assert.equal(normalizeAdrDir(''), DEFAULT_ADR_DIR);
30
+ assert.equal(normalizeAdrDir('docs/adr'), 'docs/adr');
31
+ assert.equal(normalizeAdrDir('docs\\architecture\\adr\\'), 'docs/architecture/adr');
32
+ assert.throws(() => normalizeAdrDir('../outside'), /relative path/);
33
+ assert.throws(() => normalizeAdrDir('/abs'), /relative path/);
34
+ });
35
+
36
+ test('decisionsDocFor derives sibling decisions.md', () => {
37
+ assert.equal(decisionsDocFor('docs/adr'), 'docs/decisions.md');
38
+ assert.equal(decisionsDocFor('architecture/decisions'), 'architecture/decisions.md');
39
+ });
40
+
41
+ test('decisionsRelFromAdrReadme', () => {
42
+ assert.equal(decisionsRelFromAdrReadme('docs/adr', 'docs/decisions.md'), '../decisions.md');
43
+ });
44
+
45
+ test('user config round-trip', () => {
46
+ const home = fs.mkdtempSync(path.join(os.tmpdir(), 'forgekit-adr-user-'));
47
+ try {
48
+ saveUserConfig({ adr: { enabled: true, dir: 'arch/adr' } }, home);
49
+ const loaded = loadUserConfig(home);
50
+ assert.equal(loaded.adr.enabled, true);
51
+ assert.equal(loaded.adr.dir, 'arch/adr');
52
+ } finally {
53
+ fs.rmSync(home, { recursive: true, force: true });
54
+ }
55
+ });
56
+
57
+ test('scaffoldAdr writes decisions, index, config, hooks', () => {
58
+ const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'forgekit-adr-proj-'));
59
+ try {
60
+ const result = scaffoldAdr(cwd, { dir: 'docs/adr', force: true });
61
+ assert.equal(result.dir, 'docs/adr');
62
+ assert.equal(result.decisionsDoc, 'docs/decisions.md');
63
+ assert.ok(fs.existsSync(path.join(cwd, 'docs', 'decisions.md')));
64
+ assert.ok(fs.existsSync(path.join(cwd, 'docs', 'adr', 'README.md')));
65
+ assert.ok(fs.existsSync(path.join(cwd, '.forge', 'config.json')));
66
+ assert.ok(
67
+ fs.existsSync(path.join(cwd, 'scripts', 'hooks', 'check-pending-adrs.sh')),
68
+ );
69
+ const cfg = loadProjectConfig(cwd);
70
+ assert.equal(cfg.adr.enabled, true);
71
+ assert.equal(cfg.adr.dir, 'docs/adr');
72
+ const decisions = fs.readFileSync(path.join(cwd, 'docs', 'decisions.md'), 'utf8');
73
+ assert.match(decisions, /docs\/adr/);
74
+ } finally {
75
+ fs.rmSync(cwd, { recursive: true, force: true });
76
+ }
77
+ });
78
+
79
+ test('scaffoldAdr custom dir', () => {
80
+ const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'forgekit-adr-custom-'));
81
+ try {
82
+ scaffoldAdr(cwd, { dir: 'architecture/adr', force: true });
83
+ assert.ok(fs.existsSync(path.join(cwd, 'architecture', 'decisions.md')));
84
+ assert.ok(fs.existsSync(path.join(cwd, 'architecture', 'adr', 'README.md')));
85
+ } finally {
86
+ fs.rmSync(cwd, { recursive: true, force: true });
87
+ }
88
+ });
89
+
90
+ test('disableProjectAdr', () => {
91
+ const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'forgekit-adr-off-'));
92
+ try {
93
+ scaffoldAdr(cwd, { force: true });
94
+ disableProjectAdr(cwd);
95
+ assert.equal(loadProjectConfig(cwd).adr.enabled, false);
96
+ assert.equal(resolveProjectAdr(cwd).enabled, false);
97
+ } finally {
98
+ fs.rmSync(cwd, { recursive: true, force: true });
99
+ }
100
+ });
101
+
102
+ test('applyAdrSkills / inferAdrFromSkills', () => {
103
+ assert.deepEqual(
104
+ applyAdrSkills(['forge'], true).sort(),
105
+ ['archive-to-adr', 'forge', 'git-resolve-adr-conflict'].sort(),
106
+ );
107
+ assert.deepEqual(applyAdrSkills(['forge', 'archive-to-adr'], false), ['forge']);
108
+ assert.equal(inferAdrFromSkills(['forge'], null), null);
109
+ assert.equal(inferAdrFromSkills(['archive-to-adr'], null), true);
110
+ assert.equal(inferAdrFromSkills(['forge'], false), false);
111
+ assert.equal(inferAdrFromSkills(['forge'], true), true);
112
+ assert.ok(ADR_SKILLS.every((id) => SKILL_IDS.includes(id)));
113
+ });
114
+
115
+ test('parseArgs adr flags', () => {
116
+ const opts = parseArgs([
117
+ '--skills',
118
+ 'forge',
119
+ '--adr',
120
+ '--adr-dir',
121
+ 'arch/adr',
122
+ '--adr-project',
123
+ ]);
124
+ assert.equal(opts.adr, true);
125
+ assert.equal(opts.adrDir, 'arch/adr');
126
+ assert.equal(opts.adrProject, true);
127
+ assert.equal(parseArgs(['--no-adr']).adr, false);
128
+ });
129
+
130
+ test('install with --adr installs ADR skills', () => {
131
+ const home = fs.mkdtempSync(path.join(os.tmpdir(), 'forgekit-adr-inst-'));
132
+ try {
133
+ const results = installSkillsToAgents(
134
+ applyAdrSkills(['forge'], true),
135
+ ['cursor'],
136
+ { home, force: true },
137
+ );
138
+ assert.ok(results.some((r) => r.skill === 'archive-to-adr'));
139
+ assert.ok(
140
+ fs.existsSync(path.join(home, '.cursor', 'skills', 'archive-to-adr', 'SKILL.md')),
141
+ );
142
+ assert.ok(
143
+ fs.existsSync(
144
+ path.join(home, '.cursor', 'skills', 'git-resolve-adr-conflict', 'SKILL.md'),
145
+ ),
146
+ );
147
+ } finally {
148
+ fs.rmSync(home, { recursive: true, force: true });
149
+ }
150
+ });
151
+
152
+ test('initProject --adr scaffolds and allows config.json in gitignore', () => {
153
+ const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'forgekit-init-adr-'));
154
+ try {
155
+ const report = initProject(['codex'], {
156
+ cwd,
157
+ force: true,
158
+ adr: true,
159
+ adrDir: 'docs/adr',
160
+ });
161
+ assert.ok(report.adr);
162
+ assert.ok(fs.existsSync(path.join(cwd, '.forge', 'config.json')));
163
+ const gi = fs.readFileSync(path.join(cwd, '.forge', '.gitignore'), 'utf8');
164
+ assert.match(gi, /!config\.json/);
165
+ assert.equal(parseInitArgs(['--adr', '--adr-dir', 'x/y']).adr, true);
166
+ assert.equal(parseInitArgs(['--no-adr']).adr, false);
167
+ } finally {
168
+ fs.rmSync(cwd, { recursive: true, force: true });
169
+ }
170
+ });