forge-workflow 0.0.2 → 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 (147) 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/docs/ENHANCED_ONBOARDING.md +2 -2
  83. package/docs/TOOLCHAIN.md +15 -234
  84. package/install.sh +32 -36
  85. package/lib/commands/plan.js +11 -15
  86. package/lib/commands/recommend.js +2 -2
  87. package/lib/dep-guard/analyzer.js +294 -0
  88. package/lib/dep-guard/behavior-detector.js +98 -0
  89. package/lib/dep-guard/contract-detector.js +162 -0
  90. package/lib/dep-guard/import-detector.js +498 -0
  91. package/lib/dep-guard/path-utils.js +13 -0
  92. package/lib/dep-guard/rubric.js +120 -0
  93. package/lib/dep-guard/task-parser.js +318 -0
  94. package/lib/plugin-catalog.js +18 -28
  95. package/lib/workflow-profiles.js +5 -11
  96. package/package.json +15 -4
  97. package/skills/parallel-deep-research/SKILL.md +108 -0
  98. package/skills/parallel-deep-research/evals/README.md +27 -0
  99. package/skills/parallel-deep-research/evals/evals.json +62 -0
  100. package/skills/sonarcloud-analysis/SKILL.md +171 -0
  101. package/skills/sonarcloud-analysis/evals/README.md +27 -0
  102. package/skills/sonarcloud-analysis/evals/evals.json +50 -0
  103. package/skills/sonarcloud-analysis/references/api-reference.md +466 -0
  104. package/docs/planning/PROGRESS.md +0 -396
  105. package/docs/plans/.gitkeep +0 -0
  106. package/docs/plans/2026-02-27-forge-test-suite-v2-decisions.md +0 -21
  107. package/docs/plans/2026-02-27-forge-test-suite-v2-design.md +0 -362
  108. package/docs/plans/2026-02-27-forge-test-suite-v2-tasks.md +0 -343
  109. package/docs/plans/2026-03-02-superpowers-gaps-decisions.md +0 -26
  110. package/docs/plans/2026-03-02-superpowers-gaps-design.md +0 -239
  111. package/docs/plans/2026-03-02-superpowers-gaps-tasks.md +0 -260
  112. package/docs/plans/2026-03-04-agent-command-parity-design.md +0 -163
  113. package/docs/plans/2026-03-04-verify-worktree-cleanup-decisions.md +0 -7
  114. package/docs/plans/2026-03-04-verify-worktree-cleanup-design.md +0 -165
  115. package/docs/plans/2026-03-05-forge-uto-decisions.md +0 -6
  116. package/docs/plans/2026-03-05-forge-uto-design.md +0 -116
  117. package/docs/plans/2026-03-05-forge-uto-tasks.md +0 -244
  118. package/docs/plans/2026-03-10-command-creator-and-eval-decisions.md +0 -52
  119. package/docs/plans/2026-03-10-command-creator-and-eval-design.md +0 -350
  120. package/docs/plans/2026-03-10-command-creator-and-eval-tasks.md +0 -426
  121. package/docs/plans/2026-03-10-stale-workflow-refs-decisions.md +0 -8
  122. package/docs/plans/2026-03-10-stale-workflow-refs-design.md +0 -80
  123. package/docs/plans/2026-03-10-stale-workflow-refs-tasks.md +0 -90
  124. package/docs/plans/2026-03-14-beads-plan-context-decisions.md +0 -9
  125. package/docs/plans/2026-03-14-beads-plan-context-design.md +0 -171
  126. package/docs/plans/2026-03-14-beads-plan-context-tasks.md +0 -160
  127. package/docs/plans/2026-03-14-skill-eval-loop-decisions.md +0 -33
  128. package/docs/plans/2026-03-14-skill-eval-loop-design.md +0 -118
  129. package/docs/plans/2026-03-14-skill-eval-loop-results.md +0 -78
  130. package/docs/plans/2026-03-14-skill-eval-loop-tasks.md +0 -160
  131. package/docs/plans/2026-03-15-agent-command-parity-v2-decisions.md +0 -11
  132. package/docs/plans/2026-03-15-agent-command-parity-v2-design.md +0 -145
  133. package/docs/plans/2026-03-15-agent-command-parity-v2-tasks.md +0 -211
  134. package/docs/research/TEMPLATE.md +0 -292
  135. package/docs/research/advanced-testing.md +0 -297
  136. package/docs/research/agent-permissions.md +0 -167
  137. package/docs/research/dependency-chain.md +0 -328
  138. package/docs/research/forge-workflow-v2.md +0 -550
  139. package/docs/research/plugin-architecture.md +0 -772
  140. package/docs/research/pr4-cli-automation.md +0 -326
  141. package/docs/research/premerge-verify-restructure.md +0 -205
  142. package/docs/research/skills-restructure.md +0 -508
  143. package/docs/research/sonarcloud-perfection-plan.md +0 -166
  144. package/docs/research/sonarcloud-quality-gate.md +0 -184
  145. package/docs/research/superpowers-integration.md +0 -403
  146. package/docs/research/superpowers.md +0 -319
  147. package/docs/research/test-environment.md +0 -519
@@ -0,0 +1,120 @@
1
+ const WEIGHTS = {
2
+ importCallChain: 3,
3
+ contractDependencies: 3,
4
+ behavioralDependencies: 2,
5
+ };
6
+
7
+ const CONFLICT_MESSAGES = {
8
+ materialDisagreement: 'Detector scores disagree materially across dependency categories.',
9
+ behavioralUncertainty: 'Behavioral signals are heuristic and need manual review.',
10
+ };
11
+ const MAX_DETECTOR_STRENGTH = 3;
12
+ const MAX_TOTAL = Object.values(WEIGHTS)
13
+ .reduce((sum, weight) => sum + (weight * MAX_DETECTOR_STRENGTH), 0);
14
+
15
+ function aggregateRubricScores(detectors, options = {}) {
16
+ const confidenceThreshold = options.confidenceThreshold ?? 0.7;
17
+ const dimensions = {};
18
+ let total = 0;
19
+ let hasMaterialDisagreement = false;
20
+ let hasUncertainty = false;
21
+
22
+ for (const [name, weight] of Object.entries(WEIGHTS)) {
23
+ const raw = detectors[name]?.score ?? 0;
24
+ const normalized = Math.min(raw, MAX_DETECTOR_STRENGTH);
25
+ const weighted = normalized * weight;
26
+ dimensions[name] = { raw, normalized, weighted };
27
+ total += weighted;
28
+ if (detectors[name]?.uncertain) {
29
+ hasUncertainty = true;
30
+ }
31
+ }
32
+
33
+ if (total === 0) {
34
+ return {
35
+ result: 'PASS',
36
+ score: 0,
37
+ total: 0,
38
+ dimensions,
39
+ weights: { ...WEIGHTS },
40
+ confidence: {
41
+ score: 1,
42
+ threshold: confidenceThreshold,
43
+ belowThreshold: false,
44
+ reasons: [],
45
+ },
46
+ detectorConflicts: [],
47
+ summary: 'No detector findings yet.',
48
+ reasons: [],
49
+ };
50
+ }
51
+
52
+ const rawScores = Object.values(dimensions).map((dimension) => dimension.raw);
53
+ const nonZeroScores = rawScores.filter((score) => score > 0);
54
+ const detectorConflicts = [];
55
+
56
+ if (nonZeroScores.length >= 2 && (Math.max(...nonZeroScores) - Math.min(...nonZeroScores) >= 2)) {
57
+ hasMaterialDisagreement = true;
58
+ detectorConflicts.push(CONFLICT_MESSAGES.materialDisagreement);
59
+ }
60
+
61
+ if (hasUncertainty) {
62
+ detectorConflicts.push(CONFLICT_MESSAGES.behavioralUncertainty);
63
+ }
64
+
65
+ let confidenceScore = total / MAX_TOTAL;
66
+ if (hasMaterialDisagreement) {
67
+ confidenceScore -= 0.2;
68
+ }
69
+ if (hasUncertainty) {
70
+ confidenceScore -= 0.15;
71
+ }
72
+ confidenceScore = Math.max(0, Math.min(1, confidenceScore));
73
+ const belowThreshold = confidenceScore < confidenceThreshold;
74
+
75
+ const reasons = [];
76
+ if (detectorConflicts.length > 0) {
77
+ reasons.push(...detectorConflicts);
78
+ }
79
+ if (belowThreshold) {
80
+ reasons.push(`Confidence ${confidenceScore.toFixed(2)} is below the ${(confidenceThreshold * 100).toFixed(0)}% threshold.`);
81
+ }
82
+
83
+ let result = 'PASS';
84
+ if (detectorConflicts.length > 0) {
85
+ result = 'INCONCLUSIVE';
86
+ } else if (belowThreshold) {
87
+ result = 'WEAK';
88
+ }
89
+
90
+ return {
91
+ result,
92
+ score: total,
93
+ total,
94
+ dimensions,
95
+ weights: { ...WEIGHTS },
96
+ confidence: {
97
+ score: confidenceScore,
98
+ threshold: confidenceThreshold,
99
+ belowThreshold,
100
+ reasons,
101
+ },
102
+ detectorConflicts,
103
+ summary: reasons.join(' ') || 'Detector findings are consistent.',
104
+ reasons,
105
+ };
106
+ }
107
+
108
+ function needsUserEscalation(rubricResult) {
109
+ return (
110
+ rubricResult.result === 'INCONCLUSIVE'
111
+ || rubricResult.confidence.belowThreshold
112
+ || rubricResult.detectorConflicts.length > 0
113
+ );
114
+ }
115
+
116
+ module.exports = {
117
+ WEIGHTS,
118
+ aggregateRubricScores,
119
+ needsUserEscalation,
120
+ };
@@ -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
  };