forge-workflow 0.0.4 → 0.0.6

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 (248) hide show
  1. package/.claude/commands/dev.md +345 -340
  2. package/.claude/commands/plan.md +566 -521
  3. package/.claude/commands/premerge.md +186 -176
  4. package/.claude/commands/research.md +42 -42
  5. package/.claude/commands/review.md +448 -442
  6. package/.claude/commands/rollback.md +721 -721
  7. package/.claude/commands/ship.md +212 -164
  8. package/.claude/commands/sonarcloud.md +152 -152
  9. package/.claude/commands/status.md +90 -48
  10. package/.claude/commands/validate.md +288 -282
  11. package/.claude/commands/verify.md +269 -221
  12. package/.claude/rules/greptile-review-process.md +285 -285
  13. package/.claude/rules/workflow.md +121 -105
  14. package/.claude/scripts/greptile-resolve.sh +558 -526
  15. package/.claude/scripts/load-env.sh +32 -32
  16. package/.cline/workflows/dev.md +342 -337
  17. package/.cline/workflows/plan.md +563 -518
  18. package/.cline/workflows/premerge.md +183 -173
  19. package/.cline/workflows/research.md +39 -39
  20. package/.cline/workflows/review.md +445 -439
  21. package/.cline/workflows/rollback.md +718 -718
  22. package/.cline/workflows/ship.md +209 -161
  23. package/.cline/workflows/sonarcloud.md +146 -146
  24. package/.cline/workflows/status.md +87 -45
  25. package/.cline/workflows/validate.md +285 -279
  26. package/.cline/workflows/verify.md +266 -218
  27. package/.codex/config.toml +11 -11
  28. package/.codex/skills/dev/SKILL.md +345 -340
  29. package/.codex/skills/plan/SKILL.md +566 -521
  30. package/.codex/skills/premerge/SKILL.md +186 -176
  31. package/.codex/skills/research/SKILL.md +42 -42
  32. package/.codex/skills/review/SKILL.md +448 -442
  33. package/.codex/skills/rollback/SKILL.md +721 -721
  34. package/.codex/skills/ship/SKILL.md +212 -164
  35. package/.codex/skills/sonarcloud/SKILL.md +149 -149
  36. package/.codex/skills/status/SKILL.md +90 -48
  37. package/.codex/skills/validate/SKILL.md +288 -282
  38. package/.codex/skills/verify/SKILL.md +269 -221
  39. package/.cursor/commands/dev.md +342 -337
  40. package/.cursor/commands/plan.md +563 -518
  41. package/.cursor/commands/premerge.md +183 -173
  42. package/.cursor/commands/research.md +39 -39
  43. package/.cursor/commands/review.md +445 -439
  44. package/.cursor/commands/rollback.md +718 -718
  45. package/.cursor/commands/ship.md +209 -161
  46. package/.cursor/commands/sonarcloud.md +146 -146
  47. package/.cursor/commands/status.md +87 -45
  48. package/.cursor/commands/validate.md +285 -279
  49. package/.cursor/commands/verify.md +266 -218
  50. package/.cursor/rules/permissions-guidance.mdc +37 -37
  51. package/.forge/hooks/check-tdd.js +240 -240
  52. package/.github/PLUGIN_TEMPLATE.json +32 -32
  53. package/.github/prompts/dev.prompt.md +347 -342
  54. package/.github/prompts/plan.prompt.md +568 -523
  55. package/.github/prompts/premerge.prompt.md +188 -178
  56. package/.github/prompts/research.prompt.md +44 -44
  57. package/.github/prompts/review.prompt.md +450 -444
  58. package/.github/prompts/rollback.prompt.md +723 -723
  59. package/.github/prompts/ship.prompt.md +214 -166
  60. package/.github/prompts/sonarcloud.prompt.md +151 -151
  61. package/.github/prompts/status.prompt.md +92 -50
  62. package/.github/prompts/validate.prompt.md +290 -284
  63. package/.github/prompts/verify.prompt.md +271 -223
  64. package/.github/workflows/beads-to-github.yml +56 -0
  65. package/.github/workflows/github-to-beads.yml +97 -0
  66. package/.kilocode/workflows/dev.md +346 -341
  67. package/.kilocode/workflows/plan.md +567 -522
  68. package/.kilocode/workflows/premerge.md +187 -177
  69. package/.kilocode/workflows/research.md +43 -43
  70. package/.kilocode/workflows/review.md +449 -443
  71. package/.kilocode/workflows/rollback.md +722 -722
  72. package/.kilocode/workflows/ship.md +213 -165
  73. package/.kilocode/workflows/sonarcloud.md +150 -150
  74. package/.kilocode/workflows/status.md +91 -49
  75. package/.kilocode/workflows/validate.md +289 -283
  76. package/.kilocode/workflows/verify.md +270 -222
  77. package/.mcp.json.example +12 -12
  78. package/.opencode/commands/dev.md +345 -340
  79. package/.opencode/commands/plan.md +566 -521
  80. package/.opencode/commands/premerge.md +186 -176
  81. package/.opencode/commands/research.md +42 -42
  82. package/.opencode/commands/review.md +448 -442
  83. package/.opencode/commands/rollback.md +721 -721
  84. package/.opencode/commands/ship.md +212 -164
  85. package/.opencode/commands/sonarcloud.md +149 -149
  86. package/.opencode/commands/status.md +90 -48
  87. package/.opencode/commands/validate.md +288 -282
  88. package/.opencode/commands/verify.md +269 -221
  89. package/.roo/commands/dev.md +346 -341
  90. package/.roo/commands/plan.md +567 -522
  91. package/.roo/commands/premerge.md +187 -177
  92. package/.roo/commands/research.md +43 -43
  93. package/.roo/commands/review.md +449 -443
  94. package/.roo/commands/rollback.md +722 -722
  95. package/.roo/commands/ship.md +213 -165
  96. package/.roo/commands/sonarcloud.md +150 -150
  97. package/.roo/commands/status.md +91 -49
  98. package/.roo/commands/validate.md +289 -283
  99. package/.roo/commands/verify.md +270 -222
  100. package/AGENTS.md +272 -175
  101. package/CLAUDE.md +110 -100
  102. package/README.md +429 -416
  103. package/bin/forge-cmd.js +317 -313
  104. package/bin/forge-preflight.js +322 -309
  105. package/bin/forge.js +4765 -4303
  106. package/docs/AGENT_INSTALL_PROMPT.md +342 -342
  107. package/docs/BEADS_GITHUB_SYNC.md +251 -251
  108. package/docs/ENHANCED_ONBOARDING.md +612 -602
  109. package/docs/EXAMPLES.md +482 -482
  110. package/docs/GREPTILE_SETUP.md +400 -400
  111. package/docs/MANUAL_REVIEW_GUIDE.md +106 -106
  112. package/docs/ROADMAP.md +359 -359
  113. package/docs/SETUP.md +663 -631
  114. package/docs/TOOLCHAIN.md +653 -630
  115. package/docs/VALIDATION.md +363 -363
  116. package/install.sh +40 -1056
  117. package/lefthook.yml +50 -39
  118. package/lib/agents/README.md +198 -198
  119. package/lib/agents/claude.plugin.json +28 -28
  120. package/lib/agents/cline.plugin.json +22 -22
  121. package/lib/agents/codex.plugin.json +19 -19
  122. package/lib/agents/copilot.plugin.json +24 -24
  123. package/lib/agents/cursor.plugin.json +25 -25
  124. package/lib/agents/kilocode.plugin.json +22 -22
  125. package/lib/agents/opencode.plugin.json +20 -20
  126. package/lib/agents/roo.plugin.json +23 -23
  127. package/lib/agents-config.js +2112 -2112
  128. package/lib/beads-health-check.js +143 -0
  129. package/lib/beads-setup.js +341 -0
  130. package/lib/beads-sync-scaffold.js +260 -0
  131. package/lib/commands/_registry.js +134 -0
  132. package/lib/commands/clean.js +181 -0
  133. package/lib/commands/dev.js +571 -513
  134. package/lib/commands/plan.js +692 -692
  135. package/lib/commands/push.js +196 -0
  136. package/lib/commands/recommend.js +119 -119
  137. package/lib/commands/ship.js +377 -377
  138. package/lib/commands/status.js +378 -378
  139. package/lib/commands/sync.js +55 -0
  140. package/lib/commands/team.js +37 -0
  141. package/lib/commands/test.js +207 -0
  142. package/lib/commands/validate.js +602 -602
  143. package/lib/commands/worktree.js +310 -0
  144. package/lib/context-merge.js +359 -359
  145. package/lib/dep-guard/analyzer.js +294 -294
  146. package/lib/dep-guard/behavior-detector.js +98 -98
  147. package/lib/dep-guard/contract-detector.js +162 -162
  148. package/lib/dep-guard/import-detector.js +498 -498
  149. package/lib/dep-guard/path-utils.js +13 -13
  150. package/lib/dep-guard/rubric.js +120 -120
  151. package/lib/dep-guard/task-parser.js +318 -318
  152. package/lib/detect-agent.js +191 -191
  153. package/lib/detect-worktree.js +47 -47
  154. package/lib/docs-command.js +51 -0
  155. package/lib/docs-copy.js +50 -0
  156. package/lib/file-hash.js +26 -26
  157. package/lib/freshness-token.js +148 -0
  158. package/lib/greptile-match.js +80 -0
  159. package/lib/husky-migration.js +450 -0
  160. package/lib/lefthook-check.js +65 -0
  161. package/lib/pat-setup.js +207 -0
  162. package/lib/plugin-catalog.js +350 -350
  163. package/lib/plugin-manager.js +166 -166
  164. package/lib/plugin-recommender.js +141 -141
  165. package/lib/project-discovery.js +491 -491
  166. package/lib/reset.js +309 -0
  167. package/lib/setup-action-log.js +139 -139
  168. package/lib/setup-summary-renderer.js +106 -106
  169. package/lib/setup-utils.js +96 -0
  170. package/lib/setup.js +192 -192
  171. package/lib/smart-merge.js +64 -0
  172. package/lib/symlink-utils.js +81 -0
  173. package/lib/task-ownership.js +117 -0
  174. package/lib/workflow-profiles.js +197 -197
  175. package/package.json +131 -128
  176. package/scripts/beads-context.sh +426 -0
  177. package/scripts/beads-context.test.js +567 -0
  178. package/scripts/behavioral-judge.sh +378 -0
  179. package/scripts/benchmark.js +85 -0
  180. package/scripts/branch-protection.js +183 -0
  181. package/scripts/check-agents.js +172 -0
  182. package/scripts/check-forge-token.js +98 -0
  183. package/scripts/commitlint.js +42 -0
  184. package/scripts/conflict-detect.sh +323 -0
  185. package/scripts/dep-guard-analyze.js +71 -0
  186. package/scripts/dep-guard.sh +789 -0
  187. package/scripts/eval_win.py +249 -0
  188. package/scripts/file-index.sh +493 -0
  189. package/scripts/forge-team/index.sh +86 -0
  190. package/scripts/forge-team/lib/agent-prompt.sh +52 -0
  191. package/scripts/forge-team/lib/claim.sh +256 -0
  192. package/scripts/forge-team/lib/dashboard.sh +341 -0
  193. package/scripts/forge-team/lib/epic.sh +332 -0
  194. package/scripts/forge-team/lib/hooks.sh +253 -0
  195. package/scripts/forge-team/lib/identity.sh +235 -0
  196. package/scripts/forge-team/lib/sync-github.sh +317 -0
  197. package/scripts/forge-team/lib/verify.sh +284 -0
  198. package/scripts/forge-team/lib/workload.sh +296 -0
  199. package/scripts/forge-team/tests/agent-prompt.test.sh +72 -0
  200. package/scripts/forge-team/tests/claim.test.sh +179 -0
  201. package/scripts/forge-team/tests/dashboard.test.sh +170 -0
  202. package/scripts/forge-team/tests/dispatcher.test.sh +79 -0
  203. package/scripts/forge-team/tests/epic.test.sh +176 -0
  204. package/scripts/forge-team/tests/hooks.test.sh +239 -0
  205. package/scripts/forge-team/tests/identity.test.sh +176 -0
  206. package/scripts/forge-team/tests/integration.test.sh +371 -0
  207. package/scripts/forge-team/tests/sync-github.test.sh +209 -0
  208. package/scripts/forge-team/tests/verify.test.sh +314 -0
  209. package/scripts/forge-team/tests/workflow-integration.test.sh +43 -0
  210. package/scripts/forge-team/tests/workload.test.sh +209 -0
  211. package/scripts/github-beads-sync/comment.mjs +64 -0
  212. package/scripts/github-beads-sync/config.mjs +148 -0
  213. package/scripts/github-beads-sync/github-api.mjs +131 -0
  214. package/scripts/github-beads-sync/index.mjs +332 -0
  215. package/scripts/github-beads-sync/label-mapper.mjs +54 -0
  216. package/scripts/github-beads-sync/mapping.mjs +78 -0
  217. package/scripts/github-beads-sync/reverse-sync-cli.mjs +31 -0
  218. package/scripts/github-beads-sync/reverse-sync.mjs +138 -0
  219. package/scripts/github-beads-sync/run-bd.mjs +159 -0
  220. package/scripts/github-beads-sync/sanitize.mjs +121 -0
  221. package/scripts/github-beads-sync.config.json +26 -0
  222. package/scripts/improve-command.js +375 -0
  223. package/scripts/lib/eval-runner.js +268 -0
  224. package/scripts/lib/eval-schema.js +135 -0
  225. package/scripts/lib/eval-storage.js +78 -0
  226. package/scripts/lib/grading.js +203 -0
  227. package/scripts/lib/jsonl-lock.sh +48 -0
  228. package/scripts/lib/sanitize.sh +116 -0
  229. package/scripts/lib/transcript-parser.js +63 -0
  230. package/scripts/lint.js +47 -0
  231. package/scripts/migrate-to-bun-test.js +412 -0
  232. package/scripts/pr-coordinator.sh +706 -0
  233. package/scripts/run-command-eval.js +236 -0
  234. package/scripts/smart-status.sh +809 -0
  235. package/scripts/sync-commands.js +571 -0
  236. package/scripts/sync-utils.sh +455 -0
  237. package/scripts/test-dashboard.js +123 -0
  238. package/scripts/test.js +46 -0
  239. package/scripts/validate.sh +94 -0
  240. package/skills/parallel-deep-research/SKILL.md +108 -108
  241. package/skills/parallel-deep-research/evals/README.md +27 -27
  242. package/skills/parallel-deep-research/evals/evals.json +62 -62
  243. package/skills/sonarcloud-analysis/SKILL.md +171 -171
  244. package/skills/sonarcloud-analysis/evals/README.md +27 -27
  245. package/skills/sonarcloud-analysis/evals/evals.json +50 -50
  246. package/skills/sonarcloud-analysis/references/api-reference.md +466 -466
  247. package/.cursor/hooks/state/continual-learning-index.json +0 -19
  248. package/.cursor/hooks/state/continual-learning.json +0 -8
package/lib/reset.js ADDED
@@ -0,0 +1,309 @@
1
+ const fs = require('node:fs');
2
+ const path = require('node:path');
3
+
4
+ /**
5
+ * Known forge-created file patterns.
6
+ * Used to distinguish forge files from user-created files.
7
+ */
8
+ const FORGE_COMMANDS = [
9
+ '.claude/commands/plan.md',
10
+ '.claude/commands/dev.md',
11
+ '.claude/commands/validate.md',
12
+ '.claude/commands/ship.md',
13
+ '.claude/commands/review.md',
14
+ '.claude/commands/premerge.md',
15
+ '.claude/commands/verify.md',
16
+ '.claude/commands/status.md',
17
+ '.claude/commands/preflight.md',
18
+ ];
19
+
20
+ const FORGE_RULES = [
21
+ '.claude/rules/workflow.md',
22
+ '.claude/rules/greptile-review-process.md',
23
+ ];
24
+
25
+ const FORGE_SCRIPTS = [
26
+ '.claude/scripts/greptile-resolve.sh',
27
+ '.claude/scripts/validate.sh',
28
+ ];
29
+
30
+ const FORGE_WORKFLOWS = [
31
+ '.github/workflows/beads-to-github.yml',
32
+ '.github/workflows/github-to-beads.yml',
33
+ ];
34
+
35
+ const FORGE_DOCS = [
36
+ 'docs/forge/TOOLCHAIN.md',
37
+ 'docs/forge/VALIDATION.md',
38
+ ];
39
+
40
+ const FORGE_AGENT_DIRS = [
41
+ '.cursor',
42
+ '.cline',
43
+ '.roo',
44
+ '.codex',
45
+ '.kilocode',
46
+ '.opencode',
47
+ ];
48
+
49
+ const FORGE_SYNC_SCRIPTS_DIR = 'scripts/github-beads-sync';
50
+
51
+ /**
52
+ * Get categorized lists of forge-managed files in a project.
53
+ * Only returns files that actually exist on disk and are known forge templates.
54
+ * User-created files are excluded.
55
+ *
56
+ * @param {string} projectRoot - Project root directory
57
+ * @returns {{
58
+ * config: string[],
59
+ * commands: string[],
60
+ * rules: string[],
61
+ * scripts: string[],
62
+ * docs: string[],
63
+ * agentDirs: string[],
64
+ * workflows: string[],
65
+ * syncScripts: string[]
66
+ * }}
67
+ */
68
+ function getForgeFiles(projectRoot) {
69
+ const result = {
70
+ config: [],
71
+ commands: [],
72
+ rules: [],
73
+ scripts: [],
74
+ docs: [],
75
+ agentDirs: [],
76
+ workflows: [],
77
+ syncScripts: [],
78
+ };
79
+
80
+ // .forge/ directory
81
+ if (fs.existsSync(path.join(projectRoot, '.forge'))) {
82
+ result.config.push('.forge');
83
+ }
84
+
85
+ // Known forge commands (static list)
86
+ for (const f of FORGE_COMMANDS) {
87
+ if (fs.existsSync(path.join(projectRoot, f))) {
88
+ result.commands.push(f);
89
+ }
90
+ }
91
+
92
+ // Dynamic discovery: scan the package's .claude/commands/ directory to find
93
+ // any commands that setup may have copied but aren't in the static list.
94
+ // Only files that exist in both the package source AND the project are included.
95
+ const packageCommandsDir = path.join(__dirname, '..', '.claude', 'commands');
96
+ try {
97
+ const packageEntries = fs.readdirSync(packageCommandsDir);
98
+ for (const entry of packageEntries) {
99
+ if (entry.endsWith('.md')) {
100
+ const relPath = `.claude/commands/${entry}`;
101
+ if (!result.commands.includes(relPath) &&
102
+ fs.existsSync(path.join(projectRoot, relPath))) {
103
+ result.commands.push(relPath);
104
+ }
105
+ }
106
+ }
107
+ } catch (_error) {
108
+ // Package commands directory read failed, skip
109
+ }
110
+
111
+ // Known forge rules
112
+ for (const f of FORGE_RULES) {
113
+ if (fs.existsSync(path.join(projectRoot, f))) {
114
+ result.rules.push(f);
115
+ }
116
+ }
117
+
118
+ // Known forge scripts
119
+ for (const f of FORGE_SCRIPTS) {
120
+ if (fs.existsSync(path.join(projectRoot, f))) {
121
+ result.scripts.push(f);
122
+ }
123
+ }
124
+
125
+ // Known forge docs
126
+ for (const f of FORGE_DOCS) {
127
+ if (fs.existsSync(path.join(projectRoot, f))) {
128
+ result.docs.push(f);
129
+ }
130
+ }
131
+
132
+ // Agent directories (only if they exist)
133
+ for (const dir of FORGE_AGENT_DIRS) {
134
+ if (fs.existsSync(path.join(projectRoot, dir))) {
135
+ result.agentDirs.push(dir);
136
+ }
137
+ }
138
+
139
+ // Beads workflow files
140
+ for (const f of FORGE_WORKFLOWS) {
141
+ if (fs.existsSync(path.join(projectRoot, f))) {
142
+ result.workflows.push(f);
143
+ }
144
+ }
145
+
146
+ // Sync scripts directory
147
+ const syncDir = path.join(projectRoot, FORGE_SYNC_SCRIPTS_DIR);
148
+ if (fs.existsSync(syncDir)) {
149
+ try {
150
+ const entries = fs.readdirSync(syncDir);
151
+ for (const entry of entries) {
152
+ const relPath = [FORGE_SYNC_SCRIPTS_DIR, entry].join('/');
153
+ result.syncScripts.push(relPath);
154
+ }
155
+ } catch (_error) {
156
+ // Directory read failed, skip
157
+ }
158
+ }
159
+
160
+ return result;
161
+ }
162
+
163
+ /**
164
+ * Remove .forge/ directory only (soft reset).
165
+ * Requires force=true to proceed.
166
+ *
167
+ * @param {string} projectRoot - Project root directory
168
+ * @param {{ force?: boolean }} options
169
+ * @returns {{ removed: string[], preserved: string[] }}
170
+ */
171
+ function resetSoft(projectRoot, { force = false } = {}) {
172
+ if (!force) {
173
+ throw new Error('Soft reset requires --force flag. This will remove .forge/ directory.');
174
+ }
175
+
176
+ const removed = [];
177
+ const preserved = [];
178
+
179
+ const forgePath = path.join(projectRoot, '.forge');
180
+ if (fs.existsSync(forgePath)) {
181
+ fs.rmSync(forgePath, { recursive: true, force: true });
182
+ removed.push('.forge');
183
+ }
184
+
185
+ // Record what was preserved
186
+ const inventory = getForgeFiles(projectRoot);
187
+ for (const f of [...inventory.commands, ...inventory.rules, ...inventory.scripts]) {
188
+ preserved.push(f);
189
+ }
190
+ for (const dir of inventory.agentDirs) {
191
+ preserved.push(dir);
192
+ }
193
+
194
+ return { removed, preserved };
195
+ }
196
+
197
+ /**
198
+ * Remove ALL forge-managed files (hard reset).
199
+ * Preserves user-created files not in forge template list.
200
+ * Requires force=true to proceed.
201
+ *
202
+ * @param {string} projectRoot - Project root directory
203
+ * @param {{ force?: boolean }} options
204
+ * @returns {{ removed: string[], preserved: string[] }}
205
+ */
206
+ function resetHard(projectRoot, { force = false } = {}) {
207
+ if (!force) {
208
+ throw new Error('Hard reset requires --force flag. This will remove ALL forge files.');
209
+ }
210
+
211
+ const removed = [];
212
+ const preserved = [];
213
+ const inventory = getForgeFiles(projectRoot);
214
+
215
+ // Remove .forge/ directory
216
+ for (const dir of inventory.config) {
217
+ const fullPath = path.join(projectRoot, dir);
218
+ if (fs.existsSync(fullPath)) {
219
+ fs.rmSync(fullPath, { recursive: true, force: true });
220
+ removed.push(dir);
221
+ }
222
+ }
223
+
224
+ // Remove individual forge files
225
+ const allFiles = [
226
+ ...inventory.commands,
227
+ ...inventory.rules,
228
+ ...inventory.scripts,
229
+ ...inventory.docs,
230
+ ...inventory.workflows,
231
+ ...inventory.syncScripts,
232
+ ];
233
+
234
+ for (const f of allFiles) {
235
+ const fullPath = path.join(projectRoot, f);
236
+ if (fs.existsSync(fullPath)) {
237
+ fs.rmSync(fullPath, { recursive: true, force: true });
238
+ removed.push(f);
239
+ }
240
+ }
241
+
242
+ // Remove sync scripts directory if it still exists (empty after file removal)
243
+ const syncDir = path.join(projectRoot, FORGE_SYNC_SCRIPTS_DIR);
244
+ if (fs.existsSync(syncDir)) {
245
+ fs.rmSync(syncDir, { recursive: true, force: true });
246
+ removed.push(FORGE_SYNC_SCRIPTS_DIR);
247
+ }
248
+
249
+ // Remove docs/forge/ directory if empty after file removal
250
+ const docsForgeDir = path.join(projectRoot, 'docs', 'forge');
251
+ if (fs.existsSync(docsForgeDir)) {
252
+ try {
253
+ const remaining = fs.readdirSync(docsForgeDir);
254
+ if (remaining.length === 0) {
255
+ fs.rmSync(docsForgeDir, { recursive: true, force: true });
256
+ removed.push('docs/forge');
257
+ }
258
+ } catch (_error) {
259
+ // Directory read failed, skip
260
+ }
261
+ }
262
+
263
+ // Remove agent directories
264
+ for (const dir of inventory.agentDirs) {
265
+ const fullPath = path.join(projectRoot, dir);
266
+ if (fs.existsSync(fullPath)) {
267
+ fs.rmSync(fullPath, { recursive: true, force: true });
268
+ removed.push(dir);
269
+ }
270
+ }
271
+
272
+ return { removed, preserved };
273
+ }
274
+
275
+ /**
276
+ * Remove all forge files and re-run setup (reinstall).
277
+ * Accepts a setupFn parameter for testability.
278
+ *
279
+ * @param {string} projectRoot - Project root directory
280
+ * @param {{ force?: boolean, setupFn?: Function }} options
281
+ * @returns {Promise<{ resetResult: object, setupResult: any }>}
282
+ */
283
+ async function reinstall(projectRoot, { force = false, setupFn } = {}) {
284
+ if (!force) {
285
+ throw new Error('Reinstall requires --force flag.');
286
+ }
287
+
288
+ const resetResult = resetHard(projectRoot, { force: true });
289
+
290
+ let setupResult = null;
291
+ if (setupFn) {
292
+ setupResult = await setupFn(projectRoot);
293
+ }
294
+
295
+ return { resetResult, setupResult };
296
+ }
297
+
298
+ module.exports = {
299
+ getForgeFiles,
300
+ resetSoft,
301
+ resetHard,
302
+ reinstall,
303
+ FORGE_COMMANDS,
304
+ FORGE_RULES,
305
+ FORGE_SCRIPTS,
306
+ FORGE_DOCS,
307
+ FORGE_WORKFLOWS,
308
+ FORGE_AGENT_DIRS,
309
+ };
@@ -1,139 +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 };
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 };