forge-workflow 0.0.2 → 0.0.4

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 (175) hide show
  1. package/.claude/commands/dev.md +26 -0
  2. package/.claude/commands/plan.md +141 -9
  3. package/.claude/commands/premerge.md +0 -3
  4. package/.claude/commands/rollback.md +4 -4
  5. package/.claude/commands/ship.md +71 -41
  6. package/.claude/commands/status.md +9 -38
  7. package/.claude/commands/validate.md +47 -2
  8. package/.cline/workflows/dev.md +337 -0
  9. package/.cline/workflows/plan.md +518 -0
  10. package/.cline/workflows/premerge.md +173 -0
  11. package/.cline/workflows/research.md +39 -0
  12. package/.cline/workflows/review.md +439 -0
  13. package/.cline/workflows/rollback.md +718 -0
  14. package/.cline/workflows/ship.md +161 -0
  15. package/.cline/workflows/sonarcloud.md +146 -0
  16. package/.cline/workflows/status.md +45 -0
  17. package/.cline/workflows/validate.md +279 -0
  18. package/.cline/workflows/verify.md +218 -0
  19. package/.codex/config.toml +11 -0
  20. package/.codex/skills/dev/SKILL.md +340 -0
  21. package/.codex/skills/plan/SKILL.md +521 -0
  22. package/.codex/skills/premerge/SKILL.md +176 -0
  23. package/.codex/skills/research/SKILL.md +42 -0
  24. package/.codex/skills/review/SKILL.md +442 -0
  25. package/.codex/skills/rollback/SKILL.md +721 -0
  26. package/.codex/skills/ship/SKILL.md +164 -0
  27. package/.codex/skills/sonarcloud/SKILL.md +149 -0
  28. package/.codex/skills/status/SKILL.md +48 -0
  29. package/.codex/skills/validate/SKILL.md +282 -0
  30. package/.codex/skills/verify/SKILL.md +221 -0
  31. package/.cursor/commands/dev.md +337 -0
  32. package/.cursor/commands/plan.md +518 -0
  33. package/.cursor/commands/premerge.md +173 -0
  34. package/.cursor/commands/research.md +39 -0
  35. package/.cursor/commands/review.md +439 -0
  36. package/.cursor/commands/rollback.md +718 -0
  37. package/.cursor/commands/ship.md +161 -0
  38. package/.cursor/commands/sonarcloud.md +146 -0
  39. package/.cursor/commands/status.md +45 -0
  40. package/.cursor/commands/validate.md +279 -0
  41. package/.cursor/commands/verify.md +218 -0
  42. package/.cursor/hooks/state/continual-learning-index.json +19 -0
  43. package/.cursor/hooks/state/continual-learning.json +8 -0
  44. package/.cursor/rules/permissions-guidance.mdc +37 -0
  45. package/.github/prompts/dev.prompt.md +342 -0
  46. package/.github/prompts/plan.prompt.md +523 -0
  47. package/.github/prompts/premerge.prompt.md +178 -0
  48. package/.github/prompts/research.prompt.md +44 -0
  49. package/.github/prompts/review.prompt.md +444 -0
  50. package/.github/prompts/rollback.prompt.md +723 -0
  51. package/.github/prompts/ship.prompt.md +166 -0
  52. package/.github/prompts/sonarcloud.prompt.md +151 -0
  53. package/.github/prompts/status.prompt.md +50 -0
  54. package/.github/prompts/validate.prompt.md +284 -0
  55. package/.github/prompts/verify.prompt.md +223 -0
  56. package/.kilocode/workflows/dev.md +341 -0
  57. package/.kilocode/workflows/plan.md +522 -0
  58. package/.kilocode/workflows/premerge.md +177 -0
  59. package/.kilocode/workflows/research.md +43 -0
  60. package/.kilocode/workflows/review.md +443 -0
  61. package/.kilocode/workflows/rollback.md +722 -0
  62. package/.kilocode/workflows/ship.md +165 -0
  63. package/.kilocode/workflows/sonarcloud.md +150 -0
  64. package/.kilocode/workflows/status.md +49 -0
  65. package/.kilocode/workflows/validate.md +283 -0
  66. package/.kilocode/workflows/verify.md +222 -0
  67. package/.opencode/commands/dev.md +340 -0
  68. package/.opencode/commands/plan.md +521 -0
  69. package/.opencode/commands/premerge.md +176 -0
  70. package/.opencode/commands/research.md +42 -0
  71. package/.opencode/commands/review.md +442 -0
  72. package/.opencode/commands/rollback.md +721 -0
  73. package/.opencode/commands/ship.md +164 -0
  74. package/.opencode/commands/sonarcloud.md +149 -0
  75. package/.opencode/commands/status.md +48 -0
  76. package/.opencode/commands/validate.md +282 -0
  77. package/.opencode/commands/verify.md +221 -0
  78. package/.roo/commands/dev.md +341 -0
  79. package/.roo/commands/plan.md +522 -0
  80. package/.roo/commands/premerge.md +177 -0
  81. package/.roo/commands/research.md +43 -0
  82. package/.roo/commands/review.md +443 -0
  83. package/.roo/commands/rollback.md +722 -0
  84. package/.roo/commands/ship.md +165 -0
  85. package/.roo/commands/sonarcloud.md +150 -0
  86. package/.roo/commands/status.md +49 -0
  87. package/.roo/commands/validate.md +283 -0
  88. package/.roo/commands/verify.md +222 -0
  89. package/AGENTS.md +7 -1
  90. package/CLAUDE.md +5 -4
  91. package/README.md +21 -19
  92. package/bin/{forge-validate.js → forge-preflight.js} +21 -15
  93. package/bin/forge.js +209 -138
  94. package/docs/AGENT_INSTALL_PROMPT.md +1 -1
  95. package/docs/BEADS_GITHUB_SYNC.md +251 -0
  96. package/docs/ENHANCED_ONBOARDING.md +8 -8
  97. package/docs/EXAMPLES.md +4 -4
  98. package/docs/GREPTILE_SETUP.md +1 -1
  99. package/docs/MANUAL_REVIEW_GUIDE.md +1 -1
  100. package/docs/ROADMAP.md +6 -6
  101. package/docs/SETUP.md +1 -2
  102. package/docs/TOOLCHAIN.md +15 -234
  103. package/docs/VALIDATION.md +11 -11
  104. package/install.sh +33 -39
  105. package/lib/agents-config.js +3 -3
  106. package/lib/commands/plan.js +11 -15
  107. package/lib/commands/recommend.js +2 -2
  108. package/lib/dep-guard/analyzer.js +294 -0
  109. package/lib/dep-guard/behavior-detector.js +98 -0
  110. package/lib/dep-guard/contract-detector.js +162 -0
  111. package/lib/dep-guard/import-detector.js +498 -0
  112. package/lib/dep-guard/path-utils.js +13 -0
  113. package/lib/dep-guard/rubric.js +120 -0
  114. package/lib/dep-guard/task-parser.js +318 -0
  115. package/lib/detect-agent.js +191 -0
  116. package/lib/detect-worktree.js +47 -0
  117. package/lib/file-hash.js +26 -0
  118. package/lib/plugin-catalog.js +18 -28
  119. package/lib/setup-action-log.js +139 -0
  120. package/lib/setup-summary-renderer.js +106 -0
  121. package/lib/setup.js +75 -1
  122. package/lib/workflow-profiles.js +5 -11
  123. package/package.json +17 -7
  124. package/skills/parallel-deep-research/SKILL.md +108 -0
  125. package/skills/parallel-deep-research/evals/README.md +27 -0
  126. package/skills/parallel-deep-research/evals/evals.json +62 -0
  127. package/skills/sonarcloud-analysis/SKILL.md +171 -0
  128. package/skills/sonarcloud-analysis/evals/README.md +27 -0
  129. package/skills/sonarcloud-analysis/evals/evals.json +50 -0
  130. package/skills/sonarcloud-analysis/references/api-reference.md +466 -0
  131. package/docs/WORKFLOW.md +0 -400
  132. package/docs/planning/PROGRESS.md +0 -396
  133. package/docs/plans/.gitkeep +0 -0
  134. package/docs/plans/2026-02-27-forge-test-suite-v2-decisions.md +0 -21
  135. package/docs/plans/2026-02-27-forge-test-suite-v2-design.md +0 -362
  136. package/docs/plans/2026-02-27-forge-test-suite-v2-tasks.md +0 -343
  137. package/docs/plans/2026-03-02-superpowers-gaps-decisions.md +0 -26
  138. package/docs/plans/2026-03-02-superpowers-gaps-design.md +0 -239
  139. package/docs/plans/2026-03-02-superpowers-gaps-tasks.md +0 -260
  140. package/docs/plans/2026-03-04-agent-command-parity-design.md +0 -163
  141. package/docs/plans/2026-03-04-verify-worktree-cleanup-decisions.md +0 -7
  142. package/docs/plans/2026-03-04-verify-worktree-cleanup-design.md +0 -165
  143. package/docs/plans/2026-03-05-forge-uto-decisions.md +0 -6
  144. package/docs/plans/2026-03-05-forge-uto-design.md +0 -116
  145. package/docs/plans/2026-03-05-forge-uto-tasks.md +0 -244
  146. package/docs/plans/2026-03-10-command-creator-and-eval-decisions.md +0 -52
  147. package/docs/plans/2026-03-10-command-creator-and-eval-design.md +0 -350
  148. package/docs/plans/2026-03-10-command-creator-and-eval-tasks.md +0 -426
  149. package/docs/plans/2026-03-10-stale-workflow-refs-decisions.md +0 -8
  150. package/docs/plans/2026-03-10-stale-workflow-refs-design.md +0 -80
  151. package/docs/plans/2026-03-10-stale-workflow-refs-tasks.md +0 -90
  152. package/docs/plans/2026-03-14-beads-plan-context-decisions.md +0 -9
  153. package/docs/plans/2026-03-14-beads-plan-context-design.md +0 -171
  154. package/docs/plans/2026-03-14-beads-plan-context-tasks.md +0 -160
  155. package/docs/plans/2026-03-14-skill-eval-loop-decisions.md +0 -33
  156. package/docs/plans/2026-03-14-skill-eval-loop-design.md +0 -118
  157. package/docs/plans/2026-03-14-skill-eval-loop-results.md +0 -78
  158. package/docs/plans/2026-03-14-skill-eval-loop-tasks.md +0 -160
  159. package/docs/plans/2026-03-15-agent-command-parity-v2-decisions.md +0 -11
  160. package/docs/plans/2026-03-15-agent-command-parity-v2-design.md +0 -145
  161. package/docs/plans/2026-03-15-agent-command-parity-v2-tasks.md +0 -211
  162. package/docs/research/TEMPLATE.md +0 -292
  163. package/docs/research/advanced-testing.md +0 -297
  164. package/docs/research/agent-permissions.md +0 -167
  165. package/docs/research/dependency-chain.md +0 -328
  166. package/docs/research/forge-workflow-v2.md +0 -550
  167. package/docs/research/plugin-architecture.md +0 -772
  168. package/docs/research/pr4-cli-automation.md +0 -326
  169. package/docs/research/premerge-verify-restructure.md +0 -205
  170. package/docs/research/skills-restructure.md +0 -508
  171. package/docs/research/sonarcloud-perfection-plan.md +0 -166
  172. package/docs/research/sonarcloud-quality-gate.md +0 -184
  173. package/docs/research/superpowers-integration.md +0 -403
  174. package/docs/research/superpowers.md +0 -319
  175. package/docs/research/test-environment.md +0 -519
@@ -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)' },
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Centralized action log for setup operations.
3
+ *
4
+ * Collects file-level actions (created, skipped, merged, etc.) during
5
+ * `forge setup` so the CLI can display a structured summary at the end.
6
+ *
7
+ * @module setup-action-log
8
+ */
9
+
10
+ /** Map of directory prefixes to human-readable agent names. */
11
+ const AGENT_PREFIXES = {
12
+ '.claude/': 'Claude Code',
13
+ '.cursor/': 'Cursor',
14
+ '.windsurf/': 'Windsurf',
15
+ '.cline/': 'Cline',
16
+ '.codex/': 'Codex',
17
+ '.opencode/': 'OpenCode',
18
+ '.kilocode/': 'Kilocode',
19
+ '.roo/': 'Roo Code',
20
+ '.github/prompts/': 'GitHub Copilot'
21
+ };
22
+
23
+ /**
24
+ * Detect the agent name from a file path.
25
+ *
26
+ * @param {string} filePath - Relative file path (e.g. `.claude/settings.json`)
27
+ * @returns {string} Agent name or `'General'` if no agent prefix matches
28
+ */
29
+ function detectAgent(filePath) {
30
+ const normalized = filePath.replace(/\\/g, '/');
31
+ for (const [prefix, name] of Object.entries(AGENT_PREFIXES)) {
32
+ if (normalized.startsWith(prefix)) {
33
+ return name;
34
+ }
35
+ }
36
+ return 'General';
37
+ }
38
+
39
+ /**
40
+ * Strip the agent directory prefix from a file path.
41
+ *
42
+ * @param {string} filePath - Relative file path
43
+ * @returns {string} Path with the leading agent directory removed
44
+ */
45
+ function stripAgentPrefix(filePath) {
46
+ const normalized = filePath.replace(/\\/g, '/');
47
+ for (const prefix of Object.keys(AGENT_PREFIXES)) {
48
+ if (normalized.startsWith(prefix)) {
49
+ return normalized.slice(prefix.length);
50
+ }
51
+ }
52
+ return normalized;
53
+ }
54
+
55
+ class SetupActionLog {
56
+ constructor() {
57
+ /** @type {Array<{file: string, action: string, detail: string|null}>} */
58
+ this.actions = [];
59
+ }
60
+
61
+ /**
62
+ * Record a setup action.
63
+ *
64
+ * @param {string} file - Relative file path that was acted on
65
+ * @param {string} action - One of: created, skipped, merged, conflict, removed, force-created
66
+ * @param {string|null} [detail=null] - Optional human-readable detail
67
+ */
68
+ add(file, action, detail = null) {
69
+ this.actions.push({ file, action, detail });
70
+ }
71
+
72
+ /**
73
+ * Get counts grouped by action type.
74
+ *
75
+ * @returns {Record<string, number>} e.g. `{ created: 4, skipped: 2 }`
76
+ */
77
+ getSummary() {
78
+ const counts = {};
79
+ for (const { action } of this.actions) {
80
+ counts[action] = (counts[action] || 0) + 1;
81
+ }
82
+ return counts;
83
+ }
84
+
85
+ /**
86
+ * Get the full ordered list of actions.
87
+ *
88
+ * @returns {Array<{file: string, action: string, detail: string|null}>}
89
+ */
90
+ getVerbose() {
91
+ return this.actions;
92
+ }
93
+
94
+ /**
95
+ * Group files by detected agent name.
96
+ *
97
+ * Each agent key maps to an object whose keys are action types and whose
98
+ * values are arrays of file paths (with the agent prefix stripped).
99
+ *
100
+ * @returns {Record<string, Record<string, string[]>>}
101
+ */
102
+ getAgentSummary() {
103
+ const agents = {};
104
+ for (const { file, action } of this.actions) {
105
+ const agent = detectAgent(file);
106
+ const stripped = stripAgentPrefix(file);
107
+
108
+ if (!agents[agent]) {
109
+ agents[agent] = {};
110
+ }
111
+ if (!agents[agent][action]) {
112
+ agents[agent][action] = [];
113
+ }
114
+ agents[agent][action].push(stripped);
115
+ }
116
+ return agents;
117
+ }
118
+
119
+ /**
120
+ * Filter actions by type.
121
+ *
122
+ * @param {string} action - The action type to filter on
123
+ * @returns {Array<{file: string, action: string, detail: string|null}>}
124
+ */
125
+ getByAction(action) {
126
+ return this.actions.filter(a => a.action === action);
127
+ }
128
+
129
+ /**
130
+ * Total number of recorded actions.
131
+ *
132
+ * @returns {number}
133
+ */
134
+ get length() {
135
+ return this.actions.length;
136
+ }
137
+ }
138
+
139
+ module.exports = { SetupActionLog };
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Renders a clean summary from SetupActionLog data.
3
+ *
4
+ * Default mode: 3-line concise output.
5
+ * Verbose mode: file-by-file detail grouped by agent.
6
+ *
7
+ * @module setup-summary-renderer
8
+ */
9
+
10
+ /**
11
+ * Capitalize the first letter of a string.
12
+ *
13
+ * @param {string} str
14
+ * @returns {string}
15
+ */
16
+ function capitalize(str) {
17
+ if (!str) return str;
18
+ return str.charAt(0).toUpperCase() + str.slice(1);
19
+ }
20
+
21
+ /**
22
+ * Render setup summary as a string.
23
+ *
24
+ * @param {import('./setup-action-log').SetupActionLog} actionLog - The populated action log
25
+ * @param {string[]} agentNames - List of configured agent slugs (e.g. ['claude', 'cursor'])
26
+ * @param {boolean} verbose - Whether to show file-by-file detail
27
+ * @returns {string} The formatted summary output
28
+ */
29
+ function renderSetupSummary(actionLog, agentNames, verbose) {
30
+ if (verbose) {
31
+ return renderVerbose(actionLog, agentNames);
32
+ }
33
+ return renderDefault(actionLog, agentNames);
34
+ }
35
+
36
+ /**
37
+ * Render the default 3-line concise summary.
38
+ *
39
+ * @param {import('./setup-action-log').SetupActionLog} actionLog
40
+ * @param {string[]} agentNames
41
+ * @returns {string}
42
+ */
43
+ function renderDefault(actionLog, agentNames) {
44
+ const agentCount = agentNames.length;
45
+ const agentLabel = agentCount === 1 ? '1 agent' : `${agentCount} agents`;
46
+ const agentList = agentNames.length > 0 ? ` (${agentNames.join(', ')})` : '';
47
+
48
+ const summary = actionLog.getSummary();
49
+
50
+ // Build counts line — only include non-zero actions
51
+ const displayOrder = ['created', 'skipped', 'merged', 'force-created', 'updated', 'conflict', 'removed'];
52
+ const parts = [];
53
+ for (const action of displayOrder) {
54
+ if (summary[action] && summary[action] > 0) {
55
+ parts.push(`${capitalize(action)}: ${summary[action]} ${summary[action] === 1 ? 'file' : 'files'}`);
56
+ }
57
+ }
58
+ // Include any actions not in displayOrder
59
+ for (const [action, count] of Object.entries(summary)) {
60
+ if (!displayOrder.includes(action) && count > 0) {
61
+ parts.push(`${capitalize(action)}: ${count} ${count === 1 ? 'file' : 'files'}`);
62
+ }
63
+ }
64
+
65
+ const lines = [];
66
+ lines.push(`Forge setup complete — ${agentLabel} configured${agentList}`);
67
+
68
+ if (parts.length > 0) {
69
+ lines.push(` ${parts.join(' | ')}`);
70
+ } else {
71
+ lines.push(' 0 files changed');
72
+ }
73
+
74
+ lines.push(' Run forge setup --verbose to see all files');
75
+
76
+ return lines.join('\n');
77
+ }
78
+
79
+ /**
80
+ * Render verbose file-by-file output grouped by agent.
81
+ *
82
+ * @param {import('./setup-action-log').SetupActionLog} actionLog
83
+ * @param {string[]} _agentNames - Not used in verbose (agents come from log data)
84
+ * @returns {string}
85
+ */
86
+ function renderVerbose(actionLog, _agentNames) {
87
+ const agentSummary = actionLog.getAgentSummary();
88
+ const lines = [];
89
+
90
+ for (const [agent, actions] of Object.entries(agentSummary)) {
91
+ for (const [action, files] of Object.entries(actions)) {
92
+ const fileCount = files.length;
93
+ const fileLabel = fileCount === 1 ? '1 file' : `${fileCount} files`;
94
+ const fileList = files.join(', ');
95
+ lines.push(`${agent}: ${fileList} (${fileLabel}) [${action}]`);
96
+ }
97
+ }
98
+
99
+ if (lines.length === 0) {
100
+ return 'No file operations recorded.';
101
+ }
102
+
103
+ return lines.join('\n');
104
+ }
105
+
106
+ module.exports = { renderSetupSummary };
package/lib/setup.js CHANGED
@@ -109,10 +109,84 @@ async function markStepComplete(projectPath, stepName) {
109
109
  await saveSetupState(projectPath, state);
110
110
  }
111
111
 
112
+ /**
113
+ * Scaffold GitHub-Beads sync files into a target project.
114
+ * Copies workflow, config, and mapping template — never overwrites existing files.
115
+ *
116
+ * @param {string} projectPath - Target project root
117
+ * @param {string} pkgDir - Forge package directory (source of template files)
118
+ * @returns {Promise<{created: string[], skipped: string[]}>}
119
+ */
120
+ async function scaffoldGithubBeadsSync(projectPath, pkgDir) {
121
+ const created = [];
122
+ const skipped = [];
123
+
124
+ // Sync script modules that workflows depend on at runtime
125
+ const syncScripts = [
126
+ 'config.mjs', 'mapping.mjs', 'comment.mjs', 'github-api.mjs',
127
+ 'sanitize.mjs', 'run-bd.mjs', 'label-mapper.mjs', 'index.mjs',
128
+ 'reverse-sync.mjs', 'reverse-sync-cli.mjs',
129
+ ];
130
+
131
+ const files = [
132
+ // Phase 1: GitHub → Beads
133
+ {
134
+ src: path.join(pkgDir, '.github', 'workflows', 'github-to-beads.yml'),
135
+ dest: path.join('.github', 'workflows', 'github-to-beads.yml'),
136
+ },
137
+ // Phase 2: Beads → GitHub
138
+ {
139
+ src: path.join(pkgDir, '.github', 'workflows', 'beads-to-github.yml'),
140
+ dest: path.join('.github', 'workflows', 'beads-to-github.yml'),
141
+ },
142
+ {
143
+ src: path.join(pkgDir, 'scripts', 'github-beads-sync.config.json'),
144
+ dest: path.join('scripts', 'github-beads-sync.config.json'),
145
+ },
146
+ {
147
+ src: null, // generated in-place (empty mapping template)
148
+ dest: path.join('.github', 'beads-mapping.json'),
149
+ content: '{}',
150
+ },
151
+ // Sync script modules
152
+ ...syncScripts.map((name) => ({
153
+ src: path.join(pkgDir, 'scripts', 'github-beads-sync', name),
154
+ dest: path.join('scripts', 'github-beads-sync', name),
155
+ })),
156
+ ];
157
+
158
+ for (const file of files) {
159
+ const destPath = path.join(projectPath, file.dest);
160
+
161
+ // Never overwrite existing files — preserve user customizations
162
+ if (fs.existsSync(destPath)) {
163
+ skipped.push(file.dest);
164
+ continue;
165
+ }
166
+
167
+ // Ensure parent directory exists
168
+ const destDir = path.dirname(destPath);
169
+ await fs.promises.mkdir(destDir, { recursive: true });
170
+
171
+ if (file.src) {
172
+ // Copy from template
173
+ await fs.promises.copyFile(file.src, destPath);
174
+ } else {
175
+ // Write generated content
176
+ await fs.promises.writeFile(destPath, file.content, 'utf-8');
177
+ }
178
+
179
+ created.push(file.dest);
180
+ }
181
+
182
+ return { created, skipped };
183
+ }
184
+
112
185
  module.exports = {
113
186
  saveSetupState,
114
187
  loadSetupState,
115
188
  isSetupComplete,
116
189
  getNextStep,
117
- markStepComplete
190
+ markStepComplete,
191
+ scaffoldGithubBeadsSync
118
192
  };
@@ -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,10 +1,10 @@
1
1
  {
2
2
  "name": "forge-workflow",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
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
- "forge-validate": "bin/forge-validate.js"
7
+ "forge-preflight": "bin/forge-preflight.js"
8
8
  },
9
9
  "workspaces": [
10
10
  "packages/*"
@@ -15,9 +15,8 @@
15
15
  "test:all": "bun run test:setup && bun test",
16
16
  "test:coverage": "c8 --check-coverage bun test",
17
17
  "typecheck": "echo 'No TypeScript in project - skipping type check'",
18
- "lint": "eslint .",
18
+ "lint": "eslint . --max-warnings 0",
19
19
  "check": "bash scripts/validate.sh",
20
- "postinstall": "node ./bin/forge.js",
21
20
  "prepare": "lefthook install || echo 'Note: lefthook not installed. Git hooks disabled. Run: bun add -d lefthook'",
22
21
  "setup": "node ./bin/forge.js setup",
23
22
  "help": "node ./bin/forge.js --help",
@@ -39,7 +38,7 @@
39
38
  "@commitlint/config-conventional": "^20.4.1",
40
39
  "@eslint/js": "^10.0.1",
41
40
  "@microsoft/eslint-formatter-sarif": "^3.1.0",
42
- "@stryker-mutator/core": "^9.5.1",
41
+ "@stryker-mutator/core": "^9.6.0",
43
42
  "c8": "^11.0.0",
44
43
  "eslint": "^10.0.2",
45
44
  "globals": "^17.3.0",
@@ -76,12 +75,21 @@
76
75
  "files": [
77
76
  "bin/",
78
77
  "lib/",
78
+ "skills/",
79
79
  ".claude/commands/",
80
80
  ".claude/rules/",
81
81
  ".claude/scripts/",
82
- ".forge/hooks/",
82
+ ".claude/skills/",
83
+ ".cursor/",
84
+ ".cline/",
85
+ ".roo/",
86
+ ".codex/",
87
+ ".kilocode/",
88
+ ".opencode/",
89
+ ".github/prompts/",
83
90
  ".github/PLUGIN_TEMPLATE.json",
84
- "docs/",
91
+ ".forge/hooks/",
92
+ "docs/*.md",
85
93
  "install.sh",
86
94
  "lefthook.yml",
87
95
  "AGENTS.md",
@@ -89,10 +97,12 @@
89
97
  ".mcp.json.example"
90
98
  ],
91
99
  "dependencies": {
100
+ "@babel/parser": "^7.29.2",
92
101
  "fastest-levenshtein": "^1.0.16"
93
102
  },
94
103
  "overrides": {
95
104
  "eslint": "^10.0.2",
105
+ "flatted": "3.4.2",
96
106
  "minimatch": "10.2.4"
97
107
  },
98
108
  "c8": {