forge-workflow 0.0.1 → 0.0.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.
Files changed (150) hide show
  1. package/.claude/commands/plan.md +93 -4
  2. package/.cline/workflows/dev.md +311 -0
  3. package/.cline/workflows/plan.md +475 -0
  4. package/.cline/workflows/premerge.md +176 -0
  5. package/.cline/workflows/research.md +39 -0
  6. package/.cline/workflows/review.md +439 -0
  7. package/.cline/workflows/rollback.md +718 -0
  8. package/.cline/workflows/ship.md +131 -0
  9. package/.cline/workflows/sonarcloud.md +146 -0
  10. package/.cline/workflows/status.md +74 -0
  11. package/.cline/workflows/validate.md +234 -0
  12. package/.cline/workflows/verify.md +218 -0
  13. package/.codex/config.toml +11 -0
  14. package/.codex/skills/dev/SKILL.md +314 -0
  15. package/.codex/skills/plan/SKILL.md +478 -0
  16. package/.codex/skills/premerge/SKILL.md +179 -0
  17. package/.codex/skills/research/SKILL.md +42 -0
  18. package/.codex/skills/review/SKILL.md +442 -0
  19. package/.codex/skills/rollback/SKILL.md +721 -0
  20. package/.codex/skills/ship/SKILL.md +134 -0
  21. package/.codex/skills/sonarcloud/SKILL.md +149 -0
  22. package/.codex/skills/status/SKILL.md +77 -0
  23. package/.codex/skills/validate/SKILL.md +237 -0
  24. package/.codex/skills/verify/SKILL.md +221 -0
  25. package/.cursor/commands/dev.md +311 -0
  26. package/.cursor/commands/plan.md +475 -0
  27. package/.cursor/commands/premerge.md +176 -0
  28. package/.cursor/commands/research.md +39 -0
  29. package/.cursor/commands/review.md +439 -0
  30. package/.cursor/commands/rollback.md +718 -0
  31. package/.cursor/commands/ship.md +131 -0
  32. package/.cursor/commands/sonarcloud.md +146 -0
  33. package/.cursor/commands/status.md +74 -0
  34. package/.cursor/commands/validate.md +234 -0
  35. package/.cursor/commands/verify.md +218 -0
  36. package/.cursor/rules/permissions-guidance.mdc +37 -0
  37. package/.github/prompts/dev.prompt.md +316 -0
  38. package/.github/prompts/plan.prompt.md +480 -0
  39. package/.github/prompts/premerge.prompt.md +181 -0
  40. package/.github/prompts/research.prompt.md +44 -0
  41. package/.github/prompts/review.prompt.md +444 -0
  42. package/.github/prompts/rollback.prompt.md +723 -0
  43. package/.github/prompts/ship.prompt.md +136 -0
  44. package/.github/prompts/sonarcloud.prompt.md +151 -0
  45. package/.github/prompts/status.prompt.md +79 -0
  46. package/.github/prompts/validate.prompt.md +239 -0
  47. package/.github/prompts/verify.prompt.md +223 -0
  48. package/.kilocode/workflows/dev.md +315 -0
  49. package/.kilocode/workflows/plan.md +479 -0
  50. package/.kilocode/workflows/premerge.md +180 -0
  51. package/.kilocode/workflows/research.md +43 -0
  52. package/.kilocode/workflows/review.md +443 -0
  53. package/.kilocode/workflows/rollback.md +722 -0
  54. package/.kilocode/workflows/ship.md +135 -0
  55. package/.kilocode/workflows/sonarcloud.md +150 -0
  56. package/.kilocode/workflows/status.md +78 -0
  57. package/.kilocode/workflows/validate.md +238 -0
  58. package/.kilocode/workflows/verify.md +222 -0
  59. package/.opencode/commands/dev.md +314 -0
  60. package/.opencode/commands/plan.md +478 -0
  61. package/.opencode/commands/premerge.md +179 -0
  62. package/.opencode/commands/research.md +42 -0
  63. package/.opencode/commands/review.md +442 -0
  64. package/.opencode/commands/rollback.md +721 -0
  65. package/.opencode/commands/ship.md +134 -0
  66. package/.opencode/commands/sonarcloud.md +149 -0
  67. package/.opencode/commands/status.md +77 -0
  68. package/.opencode/commands/validate.md +237 -0
  69. package/.opencode/commands/verify.md +221 -0
  70. package/.roo/commands/dev.md +315 -0
  71. package/.roo/commands/plan.md +479 -0
  72. package/.roo/commands/premerge.md +180 -0
  73. package/.roo/commands/research.md +43 -0
  74. package/.roo/commands/review.md +443 -0
  75. package/.roo/commands/rollback.md +722 -0
  76. package/.roo/commands/ship.md +135 -0
  77. package/.roo/commands/sonarcloud.md +150 -0
  78. package/.roo/commands/status.md +78 -0
  79. package/.roo/commands/validate.md +238 -0
  80. package/.roo/commands/verify.md +222 -0
  81. package/LICENSE +21 -21
  82. package/bin/forge.js +8 -4
  83. package/docs/ENHANCED_ONBOARDING.md +2 -2
  84. package/docs/TOOLCHAIN.md +15 -234
  85. package/install.sh +35 -39
  86. package/lib/agents/cline.plugin.json +1 -1
  87. package/lib/agents/roo.plugin.json +1 -1
  88. package/lib/commands/plan.js +11 -15
  89. package/lib/commands/recommend.js +2 -2
  90. package/lib/dep-guard/analyzer.js +294 -0
  91. package/lib/dep-guard/behavior-detector.js +98 -0
  92. package/lib/dep-guard/contract-detector.js +162 -0
  93. package/lib/dep-guard/import-detector.js +498 -0
  94. package/lib/dep-guard/path-utils.js +13 -0
  95. package/lib/dep-guard/rubric.js +120 -0
  96. package/lib/dep-guard/task-parser.js +318 -0
  97. package/lib/plugin-catalog.js +18 -28
  98. package/lib/workflow-profiles.js +5 -11
  99. package/package.json +18 -4
  100. package/skills/parallel-deep-research/SKILL.md +108 -0
  101. package/skills/parallel-deep-research/evals/README.md +27 -0
  102. package/skills/parallel-deep-research/evals/evals.json +62 -0
  103. package/skills/sonarcloud-analysis/SKILL.md +171 -0
  104. package/skills/sonarcloud-analysis/evals/README.md +27 -0
  105. package/skills/sonarcloud-analysis/evals/evals.json +50 -0
  106. package/skills/sonarcloud-analysis/references/api-reference.md +466 -0
  107. package/docs/planning/PROGRESS.md +0 -396
  108. package/docs/plans/.gitkeep +0 -0
  109. package/docs/plans/2026-02-27-forge-test-suite-v2-decisions.md +0 -21
  110. package/docs/plans/2026-02-27-forge-test-suite-v2-design.md +0 -362
  111. package/docs/plans/2026-02-27-forge-test-suite-v2-tasks.md +0 -343
  112. package/docs/plans/2026-03-02-superpowers-gaps-decisions.md +0 -26
  113. package/docs/plans/2026-03-02-superpowers-gaps-design.md +0 -239
  114. package/docs/plans/2026-03-02-superpowers-gaps-tasks.md +0 -260
  115. package/docs/plans/2026-03-04-agent-command-parity-design.md +0 -163
  116. package/docs/plans/2026-03-04-verify-worktree-cleanup-decisions.md +0 -7
  117. package/docs/plans/2026-03-04-verify-worktree-cleanup-design.md +0 -165
  118. package/docs/plans/2026-03-05-forge-uto-decisions.md +0 -6
  119. package/docs/plans/2026-03-05-forge-uto-design.md +0 -116
  120. package/docs/plans/2026-03-05-forge-uto-tasks.md +0 -244
  121. package/docs/plans/2026-03-10-command-creator-and-eval-decisions.md +0 -52
  122. package/docs/plans/2026-03-10-command-creator-and-eval-design.md +0 -350
  123. package/docs/plans/2026-03-10-command-creator-and-eval-tasks.md +0 -426
  124. package/docs/plans/2026-03-10-stale-workflow-refs-decisions.md +0 -8
  125. package/docs/plans/2026-03-10-stale-workflow-refs-design.md +0 -80
  126. package/docs/plans/2026-03-10-stale-workflow-refs-tasks.md +0 -90
  127. package/docs/plans/2026-03-14-beads-plan-context-decisions.md +0 -9
  128. package/docs/plans/2026-03-14-beads-plan-context-design.md +0 -171
  129. package/docs/plans/2026-03-14-beads-plan-context-tasks.md +0 -160
  130. package/docs/plans/2026-03-14-skill-eval-loop-decisions.md +0 -33
  131. package/docs/plans/2026-03-14-skill-eval-loop-design.md +0 -118
  132. package/docs/plans/2026-03-14-skill-eval-loop-results.md +0 -78
  133. package/docs/plans/2026-03-14-skill-eval-loop-tasks.md +0 -160
  134. package/docs/plans/2026-03-15-agent-command-parity-v2-decisions.md +0 -11
  135. package/docs/plans/2026-03-15-agent-command-parity-v2-design.md +0 -145
  136. package/docs/plans/2026-03-15-agent-command-parity-v2-tasks.md +0 -211
  137. package/docs/research/TEMPLATE.md +0 -292
  138. package/docs/research/advanced-testing.md +0 -297
  139. package/docs/research/agent-permissions.md +0 -167
  140. package/docs/research/dependency-chain.md +0 -328
  141. package/docs/research/forge-workflow-v2.md +0 -550
  142. package/docs/research/plugin-architecture.md +0 -772
  143. package/docs/research/pr4-cli-automation.md +0 -326
  144. package/docs/research/premerge-verify-restructure.md +0 -205
  145. package/docs/research/skills-restructure.md +0 -508
  146. package/docs/research/sonarcloud-perfection-plan.md +0 -166
  147. package/docs/research/sonarcloud-quality-gate.md +0 -184
  148. package/docs/research/superpowers-integration.md +0 -403
  149. package/docs/research/superpowers.md +0 -319
  150. package/docs/research/test-environment.md +0 -519
@@ -0,0 +1,318 @@
1
+ const fs = require('node:fs');
2
+ const path = require('node:path');
3
+ const { normalizeRepoPath } = require('./path-utils.js');
4
+
5
+ function ensureTaskFile(taskFile) {
6
+ if (!taskFile || typeof taskFile !== 'string') {
7
+ throw new Error('Task file path is required');
8
+ }
9
+
10
+ const absolutePath = path.resolve(taskFile);
11
+ if (!fs.existsSync(absolutePath)) {
12
+ throw new Error(`Task file does not exist: ${absolutePath}`);
13
+ }
14
+
15
+ return absolutePath;
16
+ }
17
+
18
+ function parseFilesLine(value) {
19
+ if (!value) {
20
+ return [];
21
+ }
22
+
23
+ return value
24
+ .split(',')
25
+ .map((file) => file.replaceAll('`', '').trim())
26
+ .filter(Boolean);
27
+ }
28
+
29
+ function flushTask(tasks, currentTask) {
30
+ if (!currentTask) {
31
+ return;
32
+ }
33
+
34
+ tasks.push({
35
+ number: currentTask.number,
36
+ title: currentTask.title,
37
+ files: currentTask.files,
38
+ whatToImplement: currentTask.whatToImplement.trim(),
39
+ expectedOutput: currentTask.expectedOutput.trim(),
40
+ });
41
+ }
42
+
43
+ function isIdentifierStart(character) {
44
+ return /[A-Za-z_]/.test(character);
45
+ }
46
+
47
+ function isIdentifierPart(character) {
48
+ return /\w/.test(character);
49
+ }
50
+
51
+ function extractSymbols(text) {
52
+ const symbols = new Set();
53
+ let searchIndex = 0;
54
+
55
+ while (searchIndex < text.length) {
56
+ const callIndex = text.indexOf('()', searchIndex);
57
+ if (callIndex === -1) {
58
+ break;
59
+ }
60
+
61
+ let startIndex = callIndex - 1;
62
+ while (startIndex >= 0 && isIdentifierPart(text[startIndex])) {
63
+ startIndex -= 1;
64
+ }
65
+
66
+ const symbol = text.slice(startIndex + 1, callIndex);
67
+ if (symbol && isIdentifierStart(symbol[0])) {
68
+ symbols.add(symbol);
69
+ }
70
+
71
+ searchIndex = callIndex + 2;
72
+ }
73
+
74
+ for (const match of text.match(/`([^`\r\n]+)`/g) ?? []) {
75
+ const token = match.slice(1, -1).trim();
76
+ if (!token || /[/.\\\s]/.test(token)) {
77
+ continue;
78
+ }
79
+
80
+ if (/^[A-Za-z_][A-Za-z0-9_-]*$/.test(token)) {
81
+ symbols.add(token);
82
+ }
83
+ }
84
+
85
+ return Array.from(symbols);
86
+ }
87
+
88
+ function detectContractType(symbol, annotation = '') {
89
+ if (annotation === 'data-format' || annotation === 'command-contract' || annotation === 'return-shape') {
90
+ return annotation;
91
+ }
92
+
93
+ if (/Schema$|Format$/i.test(symbol)) {
94
+ return 'data-format';
95
+ }
96
+
97
+ if (/Contract$/i.test(symbol)) {
98
+ return 'command-contract';
99
+ }
100
+
101
+ if (symbol.includes('-')) {
102
+ return 'command-contract';
103
+ }
104
+
105
+ if (/Shape$/i.test(symbol)) {
106
+ return 'return-shape';
107
+ }
108
+
109
+ return 'modified';
110
+ }
111
+
112
+ function extractContractMentions(text) {
113
+ const mentions = new Map();
114
+ for (const symbol of extractSymbols(text)) {
115
+ mentions.set(symbol, {
116
+ symbol,
117
+ annotation: text.includes(`${symbol}()`) ? 'modified' : detectContractType(symbol),
118
+ });
119
+ }
120
+
121
+ for (const match of text.match(/\b[A-Za-z_]\w*(?:Schema|Format|Contract|Shape)\b/g) ?? []) {
122
+ if (mentions.has(match)) {
123
+ continue;
124
+ }
125
+
126
+ mentions.set(match, {
127
+ symbol: match,
128
+ annotation: detectContractType(match),
129
+ });
130
+ }
131
+
132
+ return Array.from(mentions.values());
133
+ }
134
+
135
+ function escapeRegExp(value) {
136
+ return value.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
137
+ }
138
+
139
+ function resolveRepositoryFile(repositoryRoot, filePath) {
140
+ if (!repositoryRoot || !filePath || path.isAbsolute(filePath)) {
141
+ return null;
142
+ }
143
+
144
+ const absoluteRoot = path.resolve(repositoryRoot);
145
+ const absolutePath = path.resolve(absoluteRoot, filePath);
146
+ const relativePath = path.relative(absoluteRoot, absolutePath);
147
+ if (
148
+ relativePath === ''
149
+ || relativePath.startsWith(`..${path.sep}`)
150
+ || relativePath === '..'
151
+ || path.isAbsolute(relativePath)
152
+ ) {
153
+ return null;
154
+ }
155
+
156
+ return absolutePath;
157
+ }
158
+
159
+ function fileContainsSymbol(repositoryRoot, filePath, symbol) {
160
+ const absolutePath = resolveRepositoryFile(repositoryRoot, filePath);
161
+ if (!absolutePath) {
162
+ return false;
163
+ }
164
+
165
+ if (!fs.existsSync(absolutePath)) {
166
+ return false;
167
+ }
168
+
169
+ const content = fs.readFileSync(absolutePath, 'utf8');
170
+ if (/^[A-Za-z_]\w*$/.test(symbol)) {
171
+ return new RegExp(String.raw`\b${escapeRegExp(symbol)}\b`).test(content);
172
+ }
173
+
174
+ return content.includes(symbol);
175
+ }
176
+
177
+ function resolveMentionFiles(task, mention, repositoryRoot) {
178
+ const files = task.files ?? [];
179
+ if (files.length === 0) {
180
+ return [];
181
+ }
182
+
183
+ if (!repositoryRoot) {
184
+ return files.map((file) => normalizeRepoPath(file));
185
+ }
186
+
187
+ const matchingFiles = files
188
+ .filter((file) => fileContainsSymbol(repositoryRoot, file, mention.symbol))
189
+ .map((file) => normalizeRepoPath(file));
190
+ return matchingFiles;
191
+ }
192
+
193
+ function extractTaskContracts(taskContext, options = {}) {
194
+ if (!taskContext || !Array.isArray(taskContext.tasks)) {
195
+ return [];
196
+ }
197
+
198
+ const contracts = new Set();
199
+ const repositoryRoot = options.repositoryRoot;
200
+
201
+ for (const task of taskContext.tasks) {
202
+ const mentions = extractContractMentions(task.whatToImplement ?? '');
203
+ for (const mention of mentions) {
204
+ for (const file of resolveMentionFiles(task, mention, repositoryRoot)) {
205
+ contracts.add(`${file}:${mention.symbol}(${mention.annotation})`);
206
+ }
207
+ }
208
+ }
209
+
210
+ return Array.from(contracts).sort((left, right) => left.localeCompare(right));
211
+ }
212
+
213
+ function isTaskHeader(line) {
214
+ return line.startsWith('## Task ');
215
+ }
216
+
217
+ function parseTaskHeader(line) {
218
+ if (!isTaskHeader(line)) {
219
+ return null;
220
+ }
221
+
222
+ const prefixLength = '## Task '.length;
223
+ const separatorIndex = line.indexOf(':', prefixLength);
224
+ if (separatorIndex === -1) {
225
+ return null;
226
+ }
227
+
228
+ const numberText = line.slice(prefixLength, separatorIndex).trim();
229
+ if (!numberText || Array.from(numberText).some((character) => character < '0' || character > '9')) {
230
+ return null;
231
+ }
232
+
233
+ const title = line.slice(separatorIndex + 1).trim();
234
+ if (!title) {
235
+ return null;
236
+ }
237
+
238
+ return {
239
+ number: Number(numberText),
240
+ title,
241
+ };
242
+ }
243
+
244
+ function parseTaskField(currentTask, line, currentField) {
245
+ if (line.startsWith('File(s):')) {
246
+ currentTask.files = parseFilesLine(line.slice('File(s):'.length).trim());
247
+ return null;
248
+ }
249
+
250
+ if (line.startsWith('What to implement:')) {
251
+ currentTask.whatToImplement = line.slice('What to implement:'.length).trim();
252
+ return 'whatToImplement';
253
+ }
254
+
255
+ if (line.startsWith('Expected output:')) {
256
+ currentTask.expectedOutput = line.slice('Expected output:'.length).trim();
257
+ return 'expectedOutput';
258
+ }
259
+
260
+ if (line === 'TDD steps:' || line === '---') {
261
+ return null;
262
+ }
263
+
264
+ if (currentField && line.trim()) {
265
+ currentTask[currentField] = `${currentTask[currentField]} ${line.trim()}`.trim();
266
+ }
267
+
268
+ return currentField;
269
+ }
270
+
271
+ function parseTaskFile(taskFile) {
272
+ const absolutePath = ensureTaskFile(taskFile);
273
+ const content = fs.readFileSync(absolutePath, 'utf8');
274
+ const lines = content.split(/\r?\n/);
275
+ const tasks = [];
276
+ let currentTask = null;
277
+ let currentField = null;
278
+
279
+ for (const rawLine of lines) {
280
+ const line = rawLine.trimEnd();
281
+ const taskHeader = parseTaskHeader(line);
282
+
283
+ if (taskHeader) {
284
+ flushTask(tasks, currentTask);
285
+ currentTask = {
286
+ number: taskHeader.number,
287
+ title: taskHeader.title,
288
+ files: [],
289
+ whatToImplement: '',
290
+ expectedOutput: '',
291
+ };
292
+ currentField = null;
293
+ continue;
294
+ }
295
+
296
+ if (!currentTask) {
297
+ continue;
298
+ }
299
+
300
+ currentField = parseTaskField(currentTask, line, currentField);
301
+ }
302
+
303
+ flushTask(tasks, currentTask);
304
+
305
+ return {
306
+ path: absolutePath,
307
+ taskCount: tasks.length,
308
+ tasks,
309
+ };
310
+ }
311
+
312
+ module.exports = {
313
+ detectContractType,
314
+ extractContractMentions,
315
+ extractSymbols,
316
+ extractTaskContracts,
317
+ parseTaskFile,
318
+ };
@@ -24,10 +24,10 @@ const STAGES = Object.freeze({
24
24
  RESEARCH: 'research',
25
25
  PLAN: 'plan',
26
26
  DEV: 'dev',
27
- CHECK: 'check',
27
+ VALIDATE: 'validate',
28
28
  SHIP: 'ship',
29
29
  REVIEW: 'review',
30
- MERGE: 'merge',
30
+ PREMERGE: 'premerge',
31
31
  });
32
32
 
33
33
  const BUDGET_MODES = Object.freeze({
@@ -101,16 +101,6 @@ const CATALOG = Object.freeze({
101
101
  detectWhen: [],
102
102
  install: { method: 'npm', cmd: 'bun add -g @beads/bd' },
103
103
  },
104
- openspec: {
105
- name: 'OpenSpec',
106
- type: 'cli',
107
- tier: 'free',
108
- stage: 'plan',
109
- description: 'Spec-driven development proposals',
110
- detectWhen: [],
111
- install: { method: 'npm', cmd: 'bun add -g @fission-ai/openspec' },
112
- },
113
-
114
104
  // ── Dev ──
115
105
  'typescript-lsp': {
116
106
  name: 'TypeScript LSP',
@@ -155,7 +145,7 @@ const CATALOG = Object.freeze({
155
145
  name: 'ESLint',
156
146
  type: 'cli',
157
147
  tier: 'free',
158
- stage: 'check',
148
+ stage: 'validate',
159
149
  description: 'JavaScript/TypeScript linting',
160
150
  detectWhen: ['file:eslint.config.js', 'dep:eslint'],
161
151
  install: { method: 'npm', cmd: 'bun add -D eslint', dev: true },
@@ -164,7 +154,7 @@ const CATALOG = Object.freeze({
164
154
  name: 'Biome',
165
155
  type: 'cli',
166
156
  tier: 'free',
167
- stage: 'check',
157
+ stage: 'validate',
168
158
  description: 'Fast formatter and linter',
169
159
  detectWhen: ['file:biome.json'],
170
160
  install: { method: 'npm', cmd: 'bun add -D @biomejs/biome', dev: true },
@@ -173,7 +163,7 @@ const CATALOG = Object.freeze({
173
163
  name: 'Prettier',
174
164
  type: 'cli',
175
165
  tier: 'free',
176
- stage: 'check',
166
+ stage: 'validate',
177
167
  description: 'Opinionated code formatter',
178
168
  detectWhen: ['file:.prettierrc'],
179
169
  install: { method: 'npm', cmd: 'bun add -D prettier', dev: true },
@@ -182,7 +172,7 @@ const CATALOG = Object.freeze({
182
172
  name: 'ESLint Security Plugin',
183
173
  type: 'config',
184
174
  tier: 'free',
185
- stage: 'check',
175
+ stage: 'validate',
186
176
  description: 'Security-focused ESLint rules',
187
177
  detectWhen: ['dep:express', 'dep:fastify'],
188
178
  install: { method: 'npm', cmd: 'bun add -D eslint-plugin-security', dev: true },
@@ -191,7 +181,7 @@ const CATALOG = Object.freeze({
191
181
  name: 'SonarCloud Analysis',
192
182
  type: 'skill',
193
183
  tier: 'free-public',
194
- stage: 'check',
184
+ stage: 'validate',
195
185
  description: 'Code quality and security analysis via SonarCloud REST API',
196
186
  detectWhen: [],
197
187
  install: {
@@ -204,7 +194,7 @@ const CATALOG = Object.freeze({
204
194
  name: 'Sonar Scanner',
205
195
  type: 'cli',
206
196
  tier: 'free-public',
207
- stage: 'check',
197
+ stage: 'validate',
208
198
  description: 'SonarCloud/SonarQube CLI scanner',
209
199
  detectWhen: ['file:sonar-project.properties'],
210
200
  install: { method: 'npm', cmd: 'bun add -D sonarqube-scanner', dev: true },
@@ -214,7 +204,7 @@ const CATALOG = Object.freeze({
214
204
  name: 'CodeQL',
215
205
  type: 'cli',
216
206
  tier: 'free-public',
217
- stage: 'check',
207
+ stage: 'validate',
218
208
  description: 'Semantic code analysis by GitHub',
219
209
  detectWhen: [],
220
210
  install: { method: 'binary', cmd: 'GitHub-provided (github.com/github/codeql-action)' },
@@ -224,7 +214,7 @@ const CATALOG = Object.freeze({
224
214
  name: 'npm audit',
225
215
  type: 'cli',
226
216
  tier: 'free',
227
- stage: 'check',
217
+ stage: 'validate',
228
218
  description: 'Dependency vulnerability scanning',
229
219
  detectWhen: [],
230
220
  install: { method: 'npm', cmd: 'built-in (bun pm audit)' },
@@ -233,7 +223,7 @@ const CATALOG = Object.freeze({
233
223
  name: 'Vitest',
234
224
  type: 'cli',
235
225
  tier: 'free',
236
- stage: 'check',
226
+ stage: 'validate',
237
227
  description: 'Vite-native test runner',
238
228
  detectWhen: ['dep:vitest', 'file:vitest.config.ts'],
239
229
  install: { method: 'npm', cmd: 'bun add -D vitest', dev: true },
@@ -242,7 +232,7 @@ const CATALOG = Object.freeze({
242
232
  name: 'Jest',
243
233
  type: 'cli',
244
234
  tier: 'free',
245
- stage: 'check',
235
+ stage: 'validate',
246
236
  description: 'Delightful JavaScript testing',
247
237
  detectWhen: ['dep:jest', 'file:jest.config.js'],
248
238
  install: { method: 'npm', cmd: 'bun add -D jest', dev: true },
@@ -251,7 +241,7 @@ const CATALOG = Object.freeze({
251
241
  name: 'Playwright',
252
242
  type: 'cli',
253
243
  tier: 'free',
254
- stage: 'check',
244
+ stage: 'validate',
255
245
  description: 'End-to-end browser testing',
256
246
  detectWhen: ['dep:@playwright/test'],
257
247
  install: { method: 'npm', cmd: 'bun add -D @playwright/test', dev: true },
@@ -260,7 +250,7 @@ const CATALOG = Object.freeze({
260
250
  name: 'c8',
261
251
  type: 'cli',
262
252
  tier: 'free',
263
- stage: 'check',
253
+ stage: 'validate',
264
254
  description: 'Native V8 code coverage',
265
255
  detectWhen: [],
266
256
  install: { method: 'npm', cmd: 'bun add -D c8', dev: true },
@@ -269,7 +259,7 @@ const CATALOG = Object.freeze({
269
259
  name: 'Stryker',
270
260
  type: 'cli',
271
261
  tier: 'free',
272
- stage: 'check',
262
+ stage: 'validate',
273
263
  description: 'Mutation testing for JavaScript',
274
264
  detectWhen: [],
275
265
  install: { method: 'npm', cmd: 'bun add -D @stryker-mutator/core', dev: true },
@@ -278,7 +268,7 @@ const CATALOG = Object.freeze({
278
268
  name: 'Oxlint',
279
269
  type: 'cli',
280
270
  tier: 'free',
281
- stage: 'check',
271
+ stage: 'validate',
282
272
  description: 'Blazing-fast Rust-based linter',
283
273
  detectWhen: [],
284
274
  install: { method: 'npm', cmd: 'bun add -D oxlint', dev: true },
@@ -341,7 +331,7 @@ const CATALOG = Object.freeze({
341
331
  name: 'Changesets',
342
332
  type: 'cli',
343
333
  tier: 'free',
344
- stage: 'merge',
334
+ stage: 'premerge',
345
335
  description: 'Versioning and changelog management',
346
336
  detectWhen: ['file:package.json'],
347
337
  install: { method: 'npm', cmd: 'bun add -D @changesets/cli', dev: true },
@@ -350,7 +340,7 @@ const CATALOG = Object.freeze({
350
340
  name: 'Release Please',
351
341
  type: 'config',
352
342
  tier: 'free',
353
- stage: 'merge',
343
+ stage: 'premerge',
354
344
  description: 'Automated release PRs by Google',
355
345
  detectWhen: [],
356
346
  install: { method: 'config', cmd: 'GitHub Action (google-github-actions/release-please-action)' },
@@ -12,29 +12,26 @@
12
12
  const PROFILES = {
13
13
  critical: {
14
14
  name: 'Critical (Maximum Rigor)',
15
- stages: ['/status', '/research', '/plan', '/dev', '/validate', '/ship', '/review', '/merge', '/verify'],
15
+ stages: ['/status', '/plan', '/dev', '/validate', '/ship', '/review', '/premerge', '/verify'],
16
16
  tdd: 'strict',
17
17
  research: 'required',
18
- openspec: 'required-strategic',
19
18
  description: 'Security, auth, payments, data integrity, breaking changes',
20
19
  keywords: ['auth', 'security', 'payment', 'crypto', 'password', 'token', 'session', 'data-migration', 'breaking']
21
20
  },
22
21
 
23
22
  standard: {
24
23
  name: 'Standard (Full Workflow)',
25
- stages: ['/status', '/plan', '/dev', '/validate', '/ship', '/merge'],
24
+ stages: ['/status', '/plan', '/dev', '/validate', '/ship', '/review', '/premerge'],
26
25
  tdd: 'required',
27
26
  research: 'optional',
28
- openspec: 'strategic',
29
27
  description: 'Most features and non-critical enhancements'
30
28
  },
31
29
 
32
30
  simple: {
33
31
  name: 'Simple (Streamlined)',
34
- stages: ['/dev', '/validate', '/ship', '/merge'],
32
+ stages: ['/dev', '/validate', '/ship', '/premerge'],
35
33
  tdd: 'recommended',
36
34
  research: 'skip',
37
- openspec: 'no',
38
35
  description: 'UI tweaks, small improvements, minor fixes'
39
36
  },
40
37
 
@@ -43,26 +40,23 @@ const PROFILES = {
43
40
  stages: ['/dev', '/validate', '/ship'],
44
41
  tdd: 'required', // Must reproduce bug
45
42
  research: 'skip',
46
- openspec: 'no',
47
43
  description: 'Production bugs, urgent fixes',
48
44
  keywords: ['urgent', 'production', 'emergency', 'hotfix', 'critical']
49
45
  },
50
46
 
51
47
  docs: {
52
48
  name: 'Docs (Documentation Only)',
53
- stages: ['/verify', '/ship', '/merge'],
49
+ stages: ['/verify', '/ship', '/premerge'],
54
50
  tdd: 'no',
55
51
  research: 'no',
56
- openspec: 'no',
57
52
  description: 'Documentation updates, README changes'
58
53
  },
59
54
 
60
55
  refactor: {
61
56
  name: 'Refactor (Behavior-Preserving)',
62
- stages: ['/plan', '/dev', '/validate', '/ship', '/merge'],
57
+ stages: ['/plan', '/dev', '/validate', '/ship', '/premerge'],
63
58
  tdd: 'strict', // Must preserve behavior
64
59
  research: 'optional',
65
- openspec: 'architectural',
66
60
  description: 'Code cleanup, optimization, behavior-preserving improvements'
67
61
  }
68
62
  };
package/package.json CHANGED
@@ -1,11 +1,14 @@
1
1
  {
2
2
  "name": "forge-workflow",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "7-stage TDD workflow for ALL AI coding agents (Claude, Cursor, Cline, OpenCode, Copilot, Kilo Code, Roo Code, Codex)",
5
5
  "bin": {
6
6
  "forge": "bin/forge.js",
7
7
  "forge-validate": "bin/forge-validate.js"
8
8
  },
9
+ "workspaces": [
10
+ "packages/*"
11
+ ],
9
12
  "scripts": {
10
13
  "test": "bun test",
11
14
  "test:setup": "bash test-env/automation/setup-fixtures.sh",
@@ -36,7 +39,7 @@
36
39
  "@commitlint/config-conventional": "^20.4.1",
37
40
  "@eslint/js": "^10.0.1",
38
41
  "@microsoft/eslint-formatter-sarif": "^3.1.0",
39
- "@stryker-mutator/core": "^9.5.1",
42
+ "@stryker-mutator/core": "^9.6.0",
40
43
  "c8": "^11.0.0",
41
44
  "eslint": "^10.0.2",
42
45
  "globals": "^17.3.0",
@@ -73,12 +76,21 @@
73
76
  "files": [
74
77
  "bin/",
75
78
  "lib/",
79
+ "skills/",
76
80
  ".claude/commands/",
77
81
  ".claude/rules/",
78
82
  ".claude/scripts/",
79
- ".forge/hooks/",
83
+ ".claude/skills/",
84
+ ".cursor/",
85
+ ".cline/",
86
+ ".roo/",
87
+ ".codex/",
88
+ ".kilocode/",
89
+ ".opencode/",
90
+ ".github/prompts/",
80
91
  ".github/PLUGIN_TEMPLATE.json",
81
- "docs/",
92
+ ".forge/hooks/",
93
+ "docs/*.md",
82
94
  "install.sh",
83
95
  "lefthook.yml",
84
96
  "AGENTS.md",
@@ -86,10 +98,12 @@
86
98
  ".mcp.json.example"
87
99
  ],
88
100
  "dependencies": {
101
+ "@babel/parser": "^7.29.2",
89
102
  "fastest-levenshtein": "^1.0.16"
90
103
  },
91
104
  "overrides": {
92
105
  "eslint": "^10.0.2",
106
+ "flatted": "3.4.2",
93
107
  "minimatch": "10.2.4"
94
108
  },
95
109
  "c8": {