@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.
- package/README.md +322 -0
- package/bin/cli.js +21 -0
- package/flows/aidlc/README.md +372 -0
- package/flows/aidlc/agents/construction-agent.md +80 -0
- package/flows/aidlc/agents/inception-agent.md +97 -0
- package/flows/aidlc/agents/master-agent.md +61 -0
- package/flows/aidlc/agents/operations-agent.md +89 -0
- package/flows/aidlc/commands/construction-agent.md +67 -0
- package/flows/aidlc/commands/inception-agent.md +59 -0
- package/flows/aidlc/commands/master-agent.md +51 -0
- package/flows/aidlc/commands/operations-agent.md +81 -0
- package/flows/aidlc/context-config.yaml +67 -0
- package/flows/aidlc/memory-bank.yaml +105 -0
- package/flows/aidlc/quick-start.md +322 -0
- package/flows/aidlc/scripts/artifact-validator.cjs +594 -0
- package/flows/aidlc/scripts/bolt-complete.cjs +606 -0
- package/flows/aidlc/scripts/status-integrity.cjs +598 -0
- package/flows/aidlc/skills/construction/bolt-list.md +163 -0
- package/flows/aidlc/skills/construction/bolt-replan.md +345 -0
- package/flows/aidlc/skills/construction/bolt-start.md +442 -0
- package/flows/aidlc/skills/construction/bolt-status.md +185 -0
- package/flows/aidlc/skills/construction/navigator.md +196 -0
- package/flows/aidlc/skills/construction/prototype-apply.md +311 -0
- package/flows/aidlc/skills/inception/bolt-plan.md +372 -0
- package/flows/aidlc/skills/inception/context.md +171 -0
- package/flows/aidlc/skills/inception/intent-create.md +211 -0
- package/flows/aidlc/skills/inception/intent-list.md +124 -0
- package/flows/aidlc/skills/inception/navigator.md +207 -0
- package/flows/aidlc/skills/inception/requirements.md +227 -0
- package/flows/aidlc/skills/inception/review.md +248 -0
- package/flows/aidlc/skills/inception/story-create.md +304 -0
- package/flows/aidlc/skills/inception/units.md +278 -0
- package/flows/aidlc/skills/inception/vibe-to-spec.md +410 -0
- package/flows/aidlc/skills/master/analyze-context.md +239 -0
- package/flows/aidlc/skills/master/answer-question.md +141 -0
- package/flows/aidlc/skills/master/explain-flow.md +158 -0
- package/flows/aidlc/skills/master/project-init.md +281 -0
- package/flows/aidlc/skills/master/route-request.md +126 -0
- package/flows/aidlc/skills/operations/build.md +237 -0
- package/flows/aidlc/skills/operations/deploy.md +259 -0
- package/flows/aidlc/skills/operations/monitor.md +265 -0
- package/flows/aidlc/skills/operations/navigator.md +209 -0
- package/flows/aidlc/skills/operations/verify.md +224 -0
- package/flows/aidlc/templates/construction/bolt-template.md +226 -0
- package/flows/aidlc/templates/construction/bolt-types/ddd-construction-bolt/adr-template.md +49 -0
- package/flows/aidlc/templates/construction/bolt-types/ddd-construction-bolt/ddd-01-domain-model-template.md +55 -0
- package/flows/aidlc/templates/construction/bolt-types/ddd-construction-bolt/ddd-02-technical-design-template.md +67 -0
- package/flows/aidlc/templates/construction/bolt-types/ddd-construction-bolt/ddd-03-test-report-template.md +62 -0
- package/flows/aidlc/templates/construction/bolt-types/ddd-construction-bolt.md +590 -0
- package/flows/aidlc/templates/construction/bolt-types/simple-construction-bolt.md +347 -0
- package/flows/aidlc/templates/construction/bolt-types/spike-bolt.md +240 -0
- package/flows/aidlc/templates/construction/construction-log-template.md +129 -0
- package/flows/aidlc/templates/construction/standards/coding-standards.md +29 -0
- package/flows/aidlc/templates/construction/standards/system-architecture.md +22 -0
- package/flows/aidlc/templates/construction/standards/tech-stack.md +19 -0
- package/flows/aidlc/templates/inception/inception-log-template.md +134 -0
- package/flows/aidlc/templates/inception/project/README.md +55 -0
- package/flows/aidlc/templates/inception/requirements-template.md +144 -0
- package/flows/aidlc/templates/inception/stories-template.md +38 -0
- package/flows/aidlc/templates/inception/story-template.md +147 -0
- package/flows/aidlc/templates/inception/system-context-template.md +29 -0
- package/flows/aidlc/templates/inception/unit-brief-template.md +177 -0
- package/flows/aidlc/templates/inception/units-template.md +52 -0
- package/flows/aidlc/templates/standards/catalog.yaml +345 -0
- package/flows/aidlc/templates/standards/coding-standards.guide.md +553 -0
- package/flows/aidlc/templates/standards/data-stack.guide.md +162 -0
- package/flows/aidlc/templates/standards/decision-index-template.md +32 -0
- package/flows/aidlc/templates/standards/tech-stack.guide.md +280 -0
- package/flows/fire/README.md +19 -0
- package/flows/fire/agents/builder/agent.md +254 -0
- package/flows/fire/agents/builder/skills/code-review/SKILL.md +257 -0
- package/flows/fire/agents/builder/skills/code-review/references/auto-fix-rules.md +218 -0
- package/flows/fire/agents/builder/skills/code-review/references/review-categories.md +154 -0
- package/flows/fire/agents/builder/skills/code-review/templates/review-report.md.hbs +120 -0
- package/flows/fire/agents/builder/skills/commit-changes/SKILL.md +232 -0
- package/flows/fire/agents/builder/skills/commit-changes/scripts/commit-changes.cjs +447 -0
- package/flows/fire/agents/builder/skills/run-execute/SKILL.md +700 -0
- package/flows/fire/agents/builder/skills/run-execute/scripts/complete-run.cjs +748 -0
- package/flows/fire/agents/builder/skills/run-execute/scripts/init-run.cjs +457 -0
- package/flows/fire/agents/builder/skills/run-execute/scripts/update-phase.cjs +239 -0
- package/flows/fire/agents/builder/skills/run-execute/templates/plan.md.hbs +61 -0
- package/flows/fire/agents/builder/skills/run-execute/templates/test-report.md.hbs +81 -0
- package/flows/fire/agents/builder/skills/run-plan/SKILL.md +366 -0
- package/flows/fire/agents/builder/skills/run-status/SKILL.md +96 -0
- package/flows/fire/agents/builder/skills/walkthrough-generate/SKILL.md +181 -0
- package/flows/fire/agents/builder/skills/walkthrough-generate/templates/walkthrough.md.hbs +108 -0
- package/flows/fire/agents/orchestrator/agent.md +144 -0
- package/flows/fire/agents/orchestrator/skills/project-init/SKILL.md +226 -0
- package/flows/fire/agents/orchestrator/skills/project-init/templates/coding-standards.md.hbs +149 -0
- package/flows/fire/agents/orchestrator/skills/project-init/templates/constitution.md.hbs +43 -0
- package/flows/fire/agents/orchestrator/skills/project-init/templates/system-architecture.md.hbs +101 -0
- package/flows/fire/agents/orchestrator/skills/project-init/templates/tech-stack.md.hbs +136 -0
- package/flows/fire/agents/orchestrator/skills/project-init/templates/testing-standards.md.hbs +94 -0
- package/flows/fire/agents/orchestrator/skills/route/SKILL.md +146 -0
- package/flows/fire/agents/orchestrator/skills/status/SKILL.md +696 -0
- package/flows/fire/agents/planner/agent.md +143 -0
- package/flows/fire/agents/planner/skills/design-doc-generate/SKILL.md +156 -0
- package/flows/fire/agents/planner/skills/design-doc-generate/templates/design.md.hbs +124 -0
- package/flows/fire/agents/planner/skills/intent-capture/SKILL.md +125 -0
- package/flows/fire/agents/planner/skills/intent-capture/templates/brief.md.hbs +40 -0
- package/flows/fire/agents/planner/skills/work-item-decompose/SKILL.md +166 -0
- package/flows/fire/agents/planner/skills/work-item-decompose/templates/work-item.md.hbs +40 -0
- package/flows/fire/commands/fire-builder.md +56 -0
- package/flows/fire/commands/fire-planner.md +48 -0
- package/flows/fire/commands/fire.md +46 -0
- package/flows/fire/memory-bank.yaml +240 -0
- package/flows/fire/quick-start.md +146 -0
- package/flows/simple/README.md +190 -0
- package/flows/simple/agents/agent.md +404 -0
- package/flows/simple/commands/agent.md +60 -0
- package/flows/simple/context-config.yaml +34 -0
- package/flows/simple/memory-bank.yaml +66 -0
- package/flows/simple/quick-start.md +231 -0
- package/flows/simple/skills/design.md +96 -0
- package/flows/simple/skills/execute.md +190 -0
- package/flows/simple/skills/requirements.md +94 -0
- package/flows/simple/skills/tasks.md +136 -0
- package/flows/simple/templates/design-template.md +138 -0
- package/flows/simple/templates/requirements-template.md +85 -0
- package/flows/simple/templates/tasks-template.md +104 -0
- package/lib/InstallerFactory.js +36 -0
- package/lib/analytics/env-detector.js +92 -0
- package/lib/analytics/index.js +22 -0
- package/lib/analytics/machine-id.js +33 -0
- package/lib/analytics/tracker.js +232 -0
- package/lib/cli-utils.js +342 -0
- package/lib/constants.js +44 -0
- package/lib/installer.js +406 -0
- package/lib/installers/AntigravityInstaller.js +22 -0
- package/lib/installers/ClaudeInstaller.js +85 -0
- package/lib/installers/ClineInstaller.js +21 -0
- package/lib/installers/CodexInstaller.js +21 -0
- package/lib/installers/CopilotInstaller.js +113 -0
- package/lib/installers/CursorInstaller.js +63 -0
- package/lib/installers/GeminiInstaller.js +75 -0
- package/lib/installers/KiroInstaller.js +77 -0
- package/lib/installers/OpenCodeInstaller.js +30 -0
- package/lib/installers/RooInstaller.js +22 -0
- package/lib/installers/ToolInstaller.js +76 -0
- package/lib/installers/WindsurfInstaller.js +22 -0
- package/lib/markdown-validator.ts +175 -0
- package/lib/yaml-validator.ts +99 -0
- 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 };
|