@ydtb/specsmd 0.1.22

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 (143) hide show
  1. package/README.md +322 -0
  2. package/bin/cli.js +21 -0
  3. package/flows/aidlc/README.md +372 -0
  4. package/flows/aidlc/agents/construction-agent.md +80 -0
  5. package/flows/aidlc/agents/inception-agent.md +97 -0
  6. package/flows/aidlc/agents/master-agent.md +61 -0
  7. package/flows/aidlc/agents/operations-agent.md +89 -0
  8. package/flows/aidlc/commands/construction-agent.md +67 -0
  9. package/flows/aidlc/commands/inception-agent.md +59 -0
  10. package/flows/aidlc/commands/master-agent.md +51 -0
  11. package/flows/aidlc/commands/operations-agent.md +81 -0
  12. package/flows/aidlc/context-config.yaml +67 -0
  13. package/flows/aidlc/memory-bank.yaml +105 -0
  14. package/flows/aidlc/quick-start.md +322 -0
  15. package/flows/aidlc/scripts/artifact-validator.cjs +594 -0
  16. package/flows/aidlc/scripts/bolt-complete.cjs +606 -0
  17. package/flows/aidlc/scripts/status-integrity.cjs +598 -0
  18. package/flows/aidlc/skills/construction/bolt-list.md +163 -0
  19. package/flows/aidlc/skills/construction/bolt-replan.md +345 -0
  20. package/flows/aidlc/skills/construction/bolt-start.md +442 -0
  21. package/flows/aidlc/skills/construction/bolt-status.md +185 -0
  22. package/flows/aidlc/skills/construction/navigator.md +196 -0
  23. package/flows/aidlc/skills/construction/prototype-apply.md +311 -0
  24. package/flows/aidlc/skills/inception/bolt-plan.md +372 -0
  25. package/flows/aidlc/skills/inception/context.md +171 -0
  26. package/flows/aidlc/skills/inception/intent-create.md +211 -0
  27. package/flows/aidlc/skills/inception/intent-list.md +124 -0
  28. package/flows/aidlc/skills/inception/navigator.md +207 -0
  29. package/flows/aidlc/skills/inception/requirements.md +227 -0
  30. package/flows/aidlc/skills/inception/review.md +248 -0
  31. package/flows/aidlc/skills/inception/story-create.md +304 -0
  32. package/flows/aidlc/skills/inception/units.md +278 -0
  33. package/flows/aidlc/skills/inception/vibe-to-spec.md +410 -0
  34. package/flows/aidlc/skills/master/analyze-context.md +239 -0
  35. package/flows/aidlc/skills/master/answer-question.md +141 -0
  36. package/flows/aidlc/skills/master/explain-flow.md +158 -0
  37. package/flows/aidlc/skills/master/project-init.md +281 -0
  38. package/flows/aidlc/skills/master/route-request.md +126 -0
  39. package/flows/aidlc/skills/operations/build.md +237 -0
  40. package/flows/aidlc/skills/operations/deploy.md +259 -0
  41. package/flows/aidlc/skills/operations/monitor.md +265 -0
  42. package/flows/aidlc/skills/operations/navigator.md +209 -0
  43. package/flows/aidlc/skills/operations/verify.md +224 -0
  44. package/flows/aidlc/templates/construction/bolt-template.md +226 -0
  45. package/flows/aidlc/templates/construction/bolt-types/ddd-construction-bolt/adr-template.md +49 -0
  46. package/flows/aidlc/templates/construction/bolt-types/ddd-construction-bolt/ddd-01-domain-model-template.md +55 -0
  47. package/flows/aidlc/templates/construction/bolt-types/ddd-construction-bolt/ddd-02-technical-design-template.md +67 -0
  48. package/flows/aidlc/templates/construction/bolt-types/ddd-construction-bolt/ddd-03-test-report-template.md +62 -0
  49. package/flows/aidlc/templates/construction/bolt-types/ddd-construction-bolt.md +590 -0
  50. package/flows/aidlc/templates/construction/bolt-types/simple-construction-bolt.md +347 -0
  51. package/flows/aidlc/templates/construction/bolt-types/spike-bolt.md +240 -0
  52. package/flows/aidlc/templates/construction/construction-log-template.md +129 -0
  53. package/flows/aidlc/templates/construction/standards/coding-standards.md +29 -0
  54. package/flows/aidlc/templates/construction/standards/system-architecture.md +22 -0
  55. package/flows/aidlc/templates/construction/standards/tech-stack.md +19 -0
  56. package/flows/aidlc/templates/inception/inception-log-template.md +134 -0
  57. package/flows/aidlc/templates/inception/project/README.md +55 -0
  58. package/flows/aidlc/templates/inception/requirements-template.md +144 -0
  59. package/flows/aidlc/templates/inception/stories-template.md +38 -0
  60. package/flows/aidlc/templates/inception/story-template.md +147 -0
  61. package/flows/aidlc/templates/inception/system-context-template.md +29 -0
  62. package/flows/aidlc/templates/inception/unit-brief-template.md +177 -0
  63. package/flows/aidlc/templates/inception/units-template.md +52 -0
  64. package/flows/aidlc/templates/standards/catalog.yaml +345 -0
  65. package/flows/aidlc/templates/standards/coding-standards.guide.md +553 -0
  66. package/flows/aidlc/templates/standards/data-stack.guide.md +162 -0
  67. package/flows/aidlc/templates/standards/decision-index-template.md +32 -0
  68. package/flows/aidlc/templates/standards/tech-stack.guide.md +280 -0
  69. package/flows/fire/README.md +19 -0
  70. package/flows/fire/agents/builder/agent.md +254 -0
  71. package/flows/fire/agents/builder/skills/code-review/SKILL.md +257 -0
  72. package/flows/fire/agents/builder/skills/code-review/references/auto-fix-rules.md +218 -0
  73. package/flows/fire/agents/builder/skills/code-review/references/review-categories.md +154 -0
  74. package/flows/fire/agents/builder/skills/code-review/templates/review-report.md.hbs +120 -0
  75. package/flows/fire/agents/builder/skills/commit-changes/SKILL.md +232 -0
  76. package/flows/fire/agents/builder/skills/commit-changes/scripts/commit-changes.cjs +447 -0
  77. package/flows/fire/agents/builder/skills/run-execute/SKILL.md +700 -0
  78. package/flows/fire/agents/builder/skills/run-execute/scripts/complete-run.cjs +748 -0
  79. package/flows/fire/agents/builder/skills/run-execute/scripts/init-run.cjs +457 -0
  80. package/flows/fire/agents/builder/skills/run-execute/scripts/update-phase.cjs +239 -0
  81. package/flows/fire/agents/builder/skills/run-execute/templates/plan.md.hbs +61 -0
  82. package/flows/fire/agents/builder/skills/run-execute/templates/test-report.md.hbs +81 -0
  83. package/flows/fire/agents/builder/skills/run-plan/SKILL.md +366 -0
  84. package/flows/fire/agents/builder/skills/run-status/SKILL.md +96 -0
  85. package/flows/fire/agents/builder/skills/walkthrough-generate/SKILL.md +181 -0
  86. package/flows/fire/agents/builder/skills/walkthrough-generate/templates/walkthrough.md.hbs +108 -0
  87. package/flows/fire/agents/orchestrator/agent.md +144 -0
  88. package/flows/fire/agents/orchestrator/skills/project-init/SKILL.md +226 -0
  89. package/flows/fire/agents/orchestrator/skills/project-init/templates/coding-standards.md.hbs +149 -0
  90. package/flows/fire/agents/orchestrator/skills/project-init/templates/constitution.md.hbs +43 -0
  91. package/flows/fire/agents/orchestrator/skills/project-init/templates/system-architecture.md.hbs +101 -0
  92. package/flows/fire/agents/orchestrator/skills/project-init/templates/tech-stack.md.hbs +136 -0
  93. package/flows/fire/agents/orchestrator/skills/project-init/templates/testing-standards.md.hbs +94 -0
  94. package/flows/fire/agents/orchestrator/skills/route/SKILL.md +146 -0
  95. package/flows/fire/agents/orchestrator/skills/status/SKILL.md +696 -0
  96. package/flows/fire/agents/planner/agent.md +143 -0
  97. package/flows/fire/agents/planner/skills/design-doc-generate/SKILL.md +156 -0
  98. package/flows/fire/agents/planner/skills/design-doc-generate/templates/design.md.hbs +124 -0
  99. package/flows/fire/agents/planner/skills/intent-capture/SKILL.md +125 -0
  100. package/flows/fire/agents/planner/skills/intent-capture/templates/brief.md.hbs +40 -0
  101. package/flows/fire/agents/planner/skills/work-item-decompose/SKILL.md +166 -0
  102. package/flows/fire/agents/planner/skills/work-item-decompose/templates/work-item.md.hbs +40 -0
  103. package/flows/fire/commands/fire-builder.md +56 -0
  104. package/flows/fire/commands/fire-planner.md +48 -0
  105. package/flows/fire/commands/fire.md +46 -0
  106. package/flows/fire/memory-bank.yaml +240 -0
  107. package/flows/fire/quick-start.md +146 -0
  108. package/flows/simple/README.md +190 -0
  109. package/flows/simple/agents/agent.md +404 -0
  110. package/flows/simple/commands/agent.md +60 -0
  111. package/flows/simple/context-config.yaml +34 -0
  112. package/flows/simple/memory-bank.yaml +66 -0
  113. package/flows/simple/quick-start.md +231 -0
  114. package/flows/simple/skills/design.md +96 -0
  115. package/flows/simple/skills/execute.md +190 -0
  116. package/flows/simple/skills/requirements.md +94 -0
  117. package/flows/simple/skills/tasks.md +136 -0
  118. package/flows/simple/templates/design-template.md +138 -0
  119. package/flows/simple/templates/requirements-template.md +85 -0
  120. package/flows/simple/templates/tasks-template.md +104 -0
  121. package/lib/InstallerFactory.js +36 -0
  122. package/lib/analytics/env-detector.js +92 -0
  123. package/lib/analytics/index.js +22 -0
  124. package/lib/analytics/machine-id.js +33 -0
  125. package/lib/analytics/tracker.js +232 -0
  126. package/lib/cli-utils.js +342 -0
  127. package/lib/constants.js +44 -0
  128. package/lib/installer.js +406 -0
  129. package/lib/installers/AntigravityInstaller.js +22 -0
  130. package/lib/installers/ClaudeInstaller.js +85 -0
  131. package/lib/installers/ClineInstaller.js +21 -0
  132. package/lib/installers/CodexInstaller.js +21 -0
  133. package/lib/installers/CopilotInstaller.js +113 -0
  134. package/lib/installers/CursorInstaller.js +63 -0
  135. package/lib/installers/GeminiInstaller.js +75 -0
  136. package/lib/installers/KiroInstaller.js +77 -0
  137. package/lib/installers/OpenCodeInstaller.js +30 -0
  138. package/lib/installers/RooInstaller.js +22 -0
  139. package/lib/installers/ToolInstaller.js +76 -0
  140. package/lib/installers/WindsurfInstaller.js +22 -0
  141. package/lib/markdown-validator.ts +175 -0
  142. package/lib/yaml-validator.ts +99 -0
  143. package/package.json +69 -0
@@ -0,0 +1,447 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * FIRE Commit Changes Script
5
+ *
6
+ * Commits changes to git after code review completes.
7
+ * Generates conventional commit messages based on work item context.
8
+ *
9
+ * Usage:
10
+ * node scripts/commit-changes.cjs <rootPath> <runId> [options]
11
+ *
12
+ * Options:
13
+ * --files-created=JSON - JSON array of {path, purpose}
14
+ * --files-modified=JSON - JSON array of {path, changes}
15
+ * --work-item-id=ID - Work item ID
16
+ * --work-item-title=STR - Work item title
17
+ * --intent-id=ID - Intent ID
18
+ */
19
+
20
+ const fs = require('fs');
21
+ const path = require('path');
22
+ const { execSync } = require('child_process');
23
+
24
+ // =============================================================================
25
+ // Error Helper
26
+ // =============================================================================
27
+
28
+ function fireError(message, code, suggestion) {
29
+ const err = new Error(`FIRE Error [${code}]: ${message} ${suggestion}`);
30
+ err.code = code;
31
+ err.suggestion = suggestion;
32
+ return err;
33
+ }
34
+
35
+ // =============================================================================
36
+ // Validation
37
+ // =============================================================================
38
+
39
+ function validateInputs(rootPath, runId) {
40
+ if (!rootPath || typeof rootPath !== 'string' || rootPath.trim() === '') {
41
+ throw fireError('rootPath is required.', 'COMMIT_001', 'Provide a valid project root path.');
42
+ }
43
+
44
+ if (!runId || typeof runId !== 'string' || runId.trim() === '') {
45
+ throw fireError('runId is required.', 'COMMIT_002', 'Provide the run ID.');
46
+ }
47
+
48
+ if (!fs.existsSync(rootPath)) {
49
+ throw fireError(
50
+ `Project root not found: "${rootPath}".`,
51
+ 'COMMIT_003',
52
+ 'Ensure the path exists and is accessible.'
53
+ );
54
+ }
55
+ }
56
+
57
+ // =============================================================================
58
+ // Git Operations
59
+ // =============================================================================
60
+
61
+ /**
62
+ * Check if git is available and repository is initialized.
63
+ */
64
+ function checkGitAvailable(rootPath) {
65
+ try {
66
+ // Check if .git directory exists
67
+ const gitDir = path.join(rootPath, '.git');
68
+ if (!fs.existsSync(gitDir)) {
69
+ return { available: false, reason: 'git_not_initialized' };
70
+ }
71
+
72
+ // Check if git command works
73
+ execSync('git --version', { cwd: rootPath, stdio: 'ignore' });
74
+ execSync('git rev-parse --git-dir', { cwd: rootPath, stdio: 'ignore' });
75
+
76
+ return { available: true };
77
+ } catch (err) {
78
+ return { available: false, reason: 'git_not_available' };
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Get short commit hash of HEAD.
84
+ */
85
+ function getShortCommitHash(rootPath) {
86
+ try {
87
+ return execSync('git rev-parse --short HEAD', { cwd: rootPath, encoding: 'utf-8' }).trim();
88
+ } catch (err) {
89
+ return null;
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Stage a specific file.
95
+ */
96
+ function stageFile(rootPath, filePath) {
97
+ try {
98
+ execSync(`git add -- "${filePath}"`, { cwd: rootPath, stdio: 'ignore' });
99
+ return true;
100
+ } catch (err) {
101
+ console.error(`Warning: Failed to stage ${filePath}: ${err.message}`);
102
+ return false;
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Create commit with message.
108
+ */
109
+ function createCommit(rootPath, message) {
110
+ try {
111
+ // Use --allow-empty so we can distinguish between "no changes" and actual errors
112
+ // We'll check the output to see if anything was actually committed
113
+ const result = execSync(`git commit -m "${message.replace(/"/g, '\\"')}"`, {
114
+ cwd: rootPath,
115
+ encoding: 'utf-8',
116
+ stdio: 'pipe'
117
+ });
118
+ return { success: true, output: result };
119
+ } catch (err) {
120
+ const stderr = err.stderr?.toString() || err.message || '';
121
+
122
+ // Check for "nothing to commit" - this means files didn't actually change
123
+ if (stderr.includes('nothing to commit')) {
124
+ return { success: false, reason: 'nothing_to_commit', stderr };
125
+ }
126
+
127
+ // Check for pre-commit hook failure
128
+ if (stderr.includes('pre-commit hook')) {
129
+ return { success: false, reason: 'pre_commit_hook_failed', stderr };
130
+ }
131
+
132
+ // Unknown error
133
+ return { success: false, reason: 'unknown_error', stderr };
134
+ }
135
+ }
136
+
137
+ // =============================================================================
138
+ // Commit Message Generation
139
+ // =============================================================================
140
+
141
+ /**
142
+ * Determine commit type from work item title.
143
+ */
144
+ function determineCommitType(workItemTitle) {
145
+ const title = (workItemTitle || '').toLowerCase();
146
+
147
+ if (/^(new|add|create|implement|introduce|build)/.test(title)) return 'feat';
148
+ if (/^(fix|repair|resolve|correct|bug|patch)/.test(title)) return 'fix';
149
+ if (/test|testing|spec|coverage/.test(title)) return 'test';
150
+ if (/doc|document|readme|changelog|comment/.test(title)) return 'docs';
151
+ if (/refactor|cleanup|restructure|reorganize/.test(title)) return 'refactor';
152
+ if (/performance|optimize|speed/.test(title)) return 'perf';
153
+ if (/style|format|lint|whitespace/.test(title)) return 'style';
154
+
155
+ return 'chore';
156
+ }
157
+
158
+ /**
159
+ * Generate scope from work item ID or intent ID.
160
+ * Extracts a meaningful scope if possible, otherwise returns null.
161
+ */
162
+ function generateScope(workItemId, intentId) {
163
+ // Use intent ID as scope if it's descriptive enough
164
+ if (intentId && intentId.length > 0 && intentId !== 'default') {
165
+ // Convert kebab-case or snake_case to lowercase words
166
+ return intentId.replace(/[_-]/g, ' ').replace(/\s+/g, '-').toLowerCase();
167
+ }
168
+
169
+ return null;
170
+ }
171
+
172
+ /**
173
+ * Format commit subject from work item title.
174
+ */
175
+ function formatSubject(workItemTitle) {
176
+ if (!workItemTitle) return 'Update code';
177
+
178
+ // Remove leading type/colons if present
179
+ let subject = workItemTitle
180
+ .replace(/^(feat|fix|test|docs|refactor|perf|style|chore)(\(.+\))?:\s*/i, '')
181
+ .toLowerCase();
182
+
183
+ // Capitalize first letter
184
+ subject = subject.charAt(0).toUpperCase() + subject.slice(1);
185
+
186
+ // Limit to 72 chars
187
+ if (subject.length > 72) {
188
+ subject = subject.substring(0, 69) + '...';
189
+ }
190
+
191
+ return subject;
192
+ }
193
+
194
+ /**
195
+ * Generate conventional commit message.
196
+ */
197
+ function generateCommitMessage(workItemId, workItemTitle, intentId, runId, filesCount) {
198
+ const type = determineCommitType(workItemTitle);
199
+ const scope = generateScope(workItemId, intentId);
200
+ const subject = formatSubject(workItemTitle);
201
+
202
+ // Build header
203
+ const header = scope ? `${type}(${scope}): ${subject}` : `${type}: ${subject}`;
204
+
205
+ // Build body
206
+ const body = [
207
+ '',
208
+ `Work Item: ${workItemId} - ${workItemTitle}`,
209
+ `Run: ${runId}`,
210
+ '',
211
+ `${filesCount} file(s) changed`
212
+ ].join('\n');
213
+
214
+ return header + body;
215
+ }
216
+
217
+ // =============================================================================
218
+ // File Filtering
219
+ // =============================================================================
220
+
221
+ /**
222
+ * Filter files to only those that should be committed.
223
+ * Excludes .specs-fire/ artifacts and non-existent files.
224
+ */
225
+ function filterFilesForCommit(rootPath, filesCreated, filesModified) {
226
+ const toCommit = [];
227
+
228
+ const processFile = (file, list) => {
229
+ // Skip .specs-fire/ files (documentation artifacts)
230
+ if (file.path.startsWith('.specs-fire/') || file.path.includes('/.specs-fire/')) {
231
+ return false;
232
+ }
233
+
234
+ // Skip if file doesn't exist
235
+ const fullPath = path.join(rootPath, file.path);
236
+ if (!fs.existsSync(fullPath)) {
237
+ console.warn(`Warning: File not found, skipping: ${file.path}`);
238
+ return false;
239
+ }
240
+
241
+ toCommit.push(file.path);
242
+ return true;
243
+ };
244
+
245
+ if (Array.isArray(filesCreated)) {
246
+ filesCreated.forEach(f => processFile(f, 'created'));
247
+ }
248
+
249
+ if (Array.isArray(filesModified)) {
250
+ filesModified.forEach(f => processFile(f, 'modified'));
251
+ }
252
+
253
+ return toCommit;
254
+ }
255
+
256
+ // =============================================================================
257
+ // Main Function
258
+ // =============================================================================
259
+
260
+ function commitChanges(rootPath, runId, params = {}) {
261
+ const {
262
+ filesCreated = [],
263
+ filesModified = [],
264
+ workItemId = 'unknown',
265
+ workItemTitle = 'Code changes',
266
+ intentId = '',
267
+ } = params;
268
+
269
+ // Validate inputs
270
+ validateInputs(rootPath, runId);
271
+
272
+ // Check git availability
273
+ const gitCheck = checkGitAvailable(rootPath);
274
+ if (!gitCheck.available) {
275
+ return {
276
+ success: false,
277
+ skipped: true,
278
+ reason: gitCheck.reason
279
+ };
280
+ }
281
+
282
+ // Filter files for commit
283
+ const filesToCommit = filterFilesForCommit(rootPath, filesCreated, filesModified);
284
+
285
+ if (filesToCommit.length === 0) {
286
+ return {
287
+ success: false,
288
+ skipped: true,
289
+ reason: 'no_code_changes'
290
+ };
291
+ }
292
+
293
+ // Stage files
294
+ let stagedCount = 0;
295
+ for (const filePath of filesToCommit) {
296
+ if (stageFile(rootPath, filePath)) {
297
+ stagedCount++;
298
+ }
299
+ }
300
+
301
+ if (stagedCount === 0) {
302
+ return {
303
+ success: false,
304
+ skipped: true,
305
+ reason: 'failed_to_stage_files'
306
+ };
307
+ }
308
+
309
+ // Generate commit message
310
+ const commitMessage = generateCommitMessage(
311
+ workItemId,
312
+ workItemTitle,
313
+ intentId,
314
+ runId,
315
+ stagedCount
316
+ );
317
+
318
+ // Create commit
319
+ const commitResult = createCommit(rootPath, commitMessage);
320
+
321
+ if (!commitResult.success) {
322
+ return {
323
+ success: false,
324
+ skipped: true,
325
+ reason: commitResult.reason,
326
+ stderr: commitResult.stderr
327
+ };
328
+ }
329
+
330
+ // Get commit hash
331
+ const commitHash = getShortCommitHash(rootPath);
332
+ const commitType = determineCommitType(workItemTitle);
333
+
334
+ return {
335
+ success: true,
336
+ committed: true,
337
+ commit_hash: commitHash,
338
+ commit_type: commitType,
339
+ files_count: stagedCount,
340
+ run_id: runId
341
+ };
342
+ }
343
+
344
+ // =============================================================================
345
+ // CLI Argument Parsing
346
+ // =============================================================================
347
+
348
+ function parseArgs(args) {
349
+ const result = {
350
+ rootPath: args[0],
351
+ runId: args[1],
352
+ filesCreated: [],
353
+ filesModified: [],
354
+ workItemId: '',
355
+ workItemTitle: '',
356
+ intentId: '',
357
+ };
358
+
359
+ for (let i = 2; i < args.length; i++) {
360
+ const arg = args[i];
361
+ if (arg.startsWith('--files-created=')) {
362
+ try {
363
+ result.filesCreated = JSON.parse(arg.substring('--files-created='.length));
364
+ } catch (e) {
365
+ console.error('Warning: Could not parse --files-created JSON');
366
+ }
367
+ } else if (arg.startsWith('--files-modified=')) {
368
+ try {
369
+ result.filesModified = JSON.parse(arg.substring('--files-modified='.length));
370
+ } catch (e) {
371
+ console.error('Warning: Could not parse --files-modified JSON');
372
+ }
373
+ } else if (arg.startsWith('--work-item-id=')) {
374
+ result.workItemId = arg.substring('--work-item-id='.length);
375
+ } else if (arg.startsWith('--work-item-title=')) {
376
+ result.workItemTitle = arg.substring('--work-item-title='.length);
377
+ } else if (arg.startsWith('--intent-id=')) {
378
+ result.intentId = arg.substring('--intent-id='.length);
379
+ }
380
+ }
381
+
382
+ return result;
383
+ }
384
+
385
+ function printUsage() {
386
+ console.error('Usage:');
387
+ console.error(' node scripts/commit-changes.cjs <rootPath> <runId> [options]');
388
+ console.error('');
389
+ console.error('Arguments:');
390
+ console.error(' rootPath - Project root directory');
391
+ console.error(' runId - Run ID (e.g., run-001)');
392
+ console.error('');
393
+ console.error('Options:');
394
+ console.error(' --files-created=JSON - JSON array of {path, purpose}');
395
+ console.error(' --files-modified=JSON - JSON array of {path, changes}');
396
+ console.error(' --work-item-id=ID - Work item ID');
397
+ console.error(' --work-item-title=STR - Work item title');
398
+ console.error(' --intent-id=ID - Intent ID');
399
+ console.error('');
400
+ console.error('Example:');
401
+ console.error(' node scripts/commit-changes.cjs /project run-001 \\');
402
+ console.error(' --files-created=\'[{"path":"src/new.ts","purpose":"New feature"}]\' \\');
403
+ console.error(' --files-modified=\'[{"path":"src/old.ts","changes":"Added import"}]\' \\');
404
+ console.error(' --work-item-id=login-endpoint \\');
405
+ console.error(' --work-item-title="Implement login endpoint" \\');
406
+ console.error(' --intent-id=user-auth');
407
+ }
408
+
409
+ // =============================================================================
410
+ // CLI Interface
411
+ // =============================================================================
412
+
413
+ if (require.main === module) {
414
+ const args = process.argv.slice(2);
415
+
416
+ if (args.length < 2) {
417
+ printUsage();
418
+ process.exit(1);
419
+ }
420
+
421
+ const params = parseArgs(args);
422
+
423
+ try {
424
+ const result = commitChanges(params.rootPath, params.runId, {
425
+ filesCreated: params.filesCreated,
426
+ filesModified: params.filesModified,
427
+ workItemId: params.workItemId,
428
+ workItemTitle: params.workItemTitle,
429
+ intentId: params.intentId,
430
+ });
431
+
432
+ console.log(JSON.stringify(result, null, 2));
433
+
434
+ // Exit 0 even if skipped (graceful handling)
435
+ // Exit 1 only on actual errors
436
+ if (result.success === false && !result.skipped) {
437
+ process.exit(1);
438
+ }
439
+
440
+ process.exit(0);
441
+ } catch (err) {
442
+ console.error(err.message);
443
+ process.exit(1);
444
+ }
445
+ }
446
+
447
+ module.exports = { commitChanges, checkGitAvailable, filterFilesForCommit };