sdg-agents 1.2.6 → 1.4.0
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/package.json +2 -2
- package/src/assets/instructions/templates/bump.mjs +90 -0
- package/src/assets/instructions/templates/workflow.md +2 -1
- package/src/engine/bin/build-bundle.mjs +3 -0
- package/src/engine/lib/cli-parser.mjs +1 -0
- package/src/engine/lib/instruction-assembler.mjs +71 -0
- package/src/engine/lib/wizard.mjs +23 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sdg-agents",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Structured working protocol and engineering rules for AI agents. Works with Claude Code, Antigravity, Codex, and others.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/engine/bin/index.mjs",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"dev": "node src/engine/bin/index.mjs",
|
|
15
15
|
"build": "node src/engine/bin/build-bundle.mjs",
|
|
16
16
|
"review": "node src/engine/bin/review-bundle.mjs",
|
|
17
|
-
"bump": "node
|
|
17
|
+
"bump": "node scripts/bump.mjs",
|
|
18
18
|
"add-idiom": "node src/engine/bin/add-idiom.mjs",
|
|
19
19
|
"clear": "node src/engine/bin/clear-bundle.mjs",
|
|
20
20
|
"test": "node --test src/engine/lib/*.test.mjs",
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SDG-Agents: Bump & Changelog Automation
|
|
3
|
+
* Automates semantic versioning and promotes Unreleased changes in CHANGELOG.md.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { execSync } from 'node:child_process';
|
|
9
|
+
|
|
10
|
+
const ROOT_DIR = process.cwd();
|
|
11
|
+
const PACKAGE_JSON_PATH = path.join(ROOT_DIR, 'package.json');
|
|
12
|
+
const CHANGELOG_PATH = path.join(ROOT_DIR, 'CHANGELOG.md');
|
|
13
|
+
|
|
14
|
+
function run() {
|
|
15
|
+
const args = process.argv.slice(2);
|
|
16
|
+
const bumpType = args[0]; // feat, fix, major
|
|
17
|
+
|
|
18
|
+
if (!['feat', 'fix', 'major'].includes(bumpType)) {
|
|
19
|
+
console.error('❌ Error: Please specify bump type (feat, fix, or major).');
|
|
20
|
+
console.log('Usage: npm run bump <feat|fix|major>');
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const typeMap = {
|
|
25
|
+
feat: 'minor',
|
|
26
|
+
fix: 'patch',
|
|
27
|
+
major: 'major',
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const npmType = typeMap[bumpType];
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
// 1. Get current version
|
|
34
|
+
const pkg = JSON.parse(fs.readFileSync(PACKAGE_JSON_PATH, 'utf8'));
|
|
35
|
+
const oldVersion = pkg.version;
|
|
36
|
+
|
|
37
|
+
// 2. Bump version in package.json (no git tag/commit yet)
|
|
38
|
+
console.log(`🚀 Bumping version (${npmType})...`);
|
|
39
|
+
execSync(`npm version ${npmType} --no-git-tag-version`, { stdio: 'inherit' });
|
|
40
|
+
|
|
41
|
+
// 3. Get new version
|
|
42
|
+
const newPkg = JSON.parse(fs.readFileSync(PACKAGE_JSON_PATH, 'utf8'));
|
|
43
|
+
const newVersion = newPkg.version;
|
|
44
|
+
|
|
45
|
+
// 4. Update CHANGELOG.md
|
|
46
|
+
updateChangelog(newVersion);
|
|
47
|
+
|
|
48
|
+
console.log(`✅ Success: ${oldVersion} → ${newVersion}`);
|
|
49
|
+
console.log('🔗 CHANGELOG.md updated and promoted to current date.');
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.error('❌ Error during bump strategy:', error.message);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function updateChangelog(newVersion) {
|
|
57
|
+
if (!fs.existsSync(CHANGELOG_PATH)) {
|
|
58
|
+
console.warn('⚠️ CHANGELOG.md not found. Skipping changelog update.');
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const content = fs.readFileSync(CHANGELOG_PATH, 'utf8');
|
|
63
|
+
const today = new Date().toISOString().split('T')[0];
|
|
64
|
+
|
|
65
|
+
// Pattern to find the [Unreleased] section
|
|
66
|
+
// It handles both formats: ## [Unreleased] and ## [Unreleased] - YYYY-MM-DD
|
|
67
|
+
const unreleasedRegex = /##\s*\[Unreleased\](\s*-\s*\d{4}-\d{2}-\d{2})?/i;
|
|
68
|
+
|
|
69
|
+
if (!unreleasedRegex.test(content)) {
|
|
70
|
+
console.warn('⚠️ Could not find "## [Unreleased]" header in CHANGELOG.md.');
|
|
71
|
+
console.log('Skipping content promotion.');
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const newHeader = `## [${newVersion}] - ${today}`;
|
|
76
|
+
|
|
77
|
+
// 1. Promote Unreleased to New Version
|
|
78
|
+
let updatedContent = content.replace(unreleasedRegex, newHeader);
|
|
79
|
+
|
|
80
|
+
// 2. Inject new [Unreleased] block at the top
|
|
81
|
+
const insertIndex = updatedContent.indexOf(newHeader);
|
|
82
|
+
const nextBlock = `## [Unreleased]\n\n### Added\n\n### Fixed\n\n`;
|
|
83
|
+
|
|
84
|
+
updatedContent =
|
|
85
|
+
updatedContent.slice(0, insertIndex) + nextBlock + updatedContent.slice(insertIndex);
|
|
86
|
+
|
|
87
|
+
fs.writeFileSync(CHANGELOG_PATH, updatedContent);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
run();
|
|
@@ -140,7 +140,8 @@ On every request, classify intent before acting:
|
|
|
140
140
|
### END Checklist (mandatory — execute in order, mark each before proceeding)
|
|
141
141
|
|
|
142
142
|
- [ ] **SUMMARIZE** — one sentence per completed PLAN task written in response
|
|
143
|
-
- [ ] **
|
|
143
|
+
- [ ] **BUMP** — run \`npm run bump <feat|fix>\` to promote CHANGELOG and package.json version. Skip if not applicable.
|
|
144
|
+
- [ ] **CHANGELOG** — Verify [Unreleased] content was promoted. Append any manual notes if needed.
|
|
144
145
|
- [ ] **BACKLOG: tasks.md** — all completed tasks moved to `## Done` with `[DONE]` status
|
|
145
146
|
- [ ] **BACKLOG: context.md** — \`## Now\` updated with next objective or cleared
|
|
146
147
|
- [ ] **KNOWLEDGE** — Log any patterns, findings, or rework discovered during this cycle. Update \`.ai-backlog/learned.md\` (for successful feats) or \`.ai-backlog/troubleshoot.md\` (for fixed incidents). Curate stale or irrelevant items.
|
|
@@ -21,6 +21,7 @@ const {
|
|
|
21
21
|
writeBacklogFiles,
|
|
22
22
|
writeGitignore,
|
|
23
23
|
writeManifest,
|
|
24
|
+
writeAutomationScripts,
|
|
24
25
|
} = InstructionAssembler;
|
|
25
26
|
const { success } = ResultUtils;
|
|
26
27
|
const { runIfDirect } = FsUtils;
|
|
@@ -207,6 +208,7 @@ function executeQuickPipeline(targetDir, selections, { noDevGuides = false } = {
|
|
|
207
208
|
|
|
208
209
|
printStep(5, 5, 'Injecting spec templates...');
|
|
209
210
|
injectPrompts(targetDir, selections.track);
|
|
211
|
+
writeAutomationScripts(targetDir, selections);
|
|
210
212
|
writeManifest(targetDir, selections, packageJson.version);
|
|
211
213
|
}
|
|
212
214
|
|
|
@@ -239,6 +241,7 @@ function executeAgentsPipeline(targetDir, selections, { noDevGuides = false } =
|
|
|
239
241
|
writeGitignore(targetDir);
|
|
240
242
|
|
|
241
243
|
printStep(5, 5, 'Finalizing manifest...');
|
|
244
|
+
writeAutomationScripts(targetDir, selections);
|
|
242
245
|
writeManifest(targetDir, selections, packageJson.version);
|
|
243
246
|
}
|
|
244
247
|
|
|
@@ -528,6 +528,76 @@ function writeManifest(targetDir, selections, pkgVersion) {
|
|
|
528
528
|
fs.writeFileSync(path.join(aiDir, '.sdg-manifest.json'), JSON.stringify(manifest, null, 2));
|
|
529
529
|
}
|
|
530
530
|
|
|
531
|
+
/**
|
|
532
|
+
* Injects automation scripts and configurations (Bump, Husky) if enabled.
|
|
533
|
+
* Idempotent: skips if scripts/bump.mjs exists or if selections.bump is false.
|
|
534
|
+
*/
|
|
535
|
+
function writeAutomationScripts(targetDir, selections) {
|
|
536
|
+
if (selections.bump === false) return;
|
|
537
|
+
|
|
538
|
+
const scriptsDir = path.join(targetDir, 'scripts');
|
|
539
|
+
const bumpScriptPath = path.join(scriptsDir, 'bump.mjs');
|
|
540
|
+
|
|
541
|
+
// 1. Check for existing bump script in package.json to avoid collision
|
|
542
|
+
const pkgPath = path.join(targetDir, 'package.json');
|
|
543
|
+
if (!fs.existsSync(pkgPath)) return;
|
|
544
|
+
|
|
545
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
546
|
+
const hasExistingBump =
|
|
547
|
+
pkg.scripts && (pkg.scripts.bump || pkg.scripts.release || pkg.scripts.version);
|
|
548
|
+
|
|
549
|
+
if (hasExistingBump && !fs.existsSync(bumpScriptPath)) {
|
|
550
|
+
// If they have a script but not our file, we respect their script
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// 2. Write bump.mjs template
|
|
555
|
+
if (!fs.existsSync(bumpScriptPath)) {
|
|
556
|
+
if (!fs.existsSync(scriptsDir)) fs.mkdirSync(scriptsDir, { recursive: true });
|
|
557
|
+
const templatePath = path.join(SOURCE_INSTRUCTIONS, 'templates', 'bump.mjs');
|
|
558
|
+
const templateContent = fs.readFileSync(templatePath, 'utf8');
|
|
559
|
+
fs.writeFileSync(bumpScriptPath, templateContent);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// 3. Update package.json scripts
|
|
563
|
+
if (!pkg.scripts) pkg.scripts = {};
|
|
564
|
+
if (!pkg.scripts.bump) {
|
|
565
|
+
pkg.scripts.bump = 'node scripts/bump.mjs';
|
|
566
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// 4. Configure Husky if .husky exists
|
|
570
|
+
const huskyDir = path.join(targetDir, '.husky');
|
|
571
|
+
if (fs.existsSync(huskyDir)) {
|
|
572
|
+
const prePushPath = path.join(huskyDir, 'pre-push');
|
|
573
|
+
const nvmShim = dedent`
|
|
574
|
+
# NVM Shim (Essential for projects Staff in Linux/NVM)
|
|
575
|
+
export NVM_DIR="$HOME/.nvm"
|
|
576
|
+
[ -s "$NVM_DIR/nvm.sh" ] && \\. "$NVM_DIR/nvm.sh"
|
|
577
|
+
`;
|
|
578
|
+
|
|
579
|
+
const bumpCmd = 'npm run bump fix';
|
|
580
|
+
|
|
581
|
+
if (fs.existsSync(prePushPath)) {
|
|
582
|
+
const content = fs.readFileSync(prePushPath, 'utf8');
|
|
583
|
+
if (!content.includes('npm run bump')) {
|
|
584
|
+
const separator = content.endsWith('\n') ? '' : '\n';
|
|
585
|
+
fs.appendFileSync(prePushPath, `${separator}\n${nvmShim}\n${bumpCmd}\n`);
|
|
586
|
+
}
|
|
587
|
+
} else {
|
|
588
|
+
const prePushContent = dedent`
|
|
589
|
+
#!/usr/bin/env sh
|
|
590
|
+
. "$(dirname -- "$0")/_/husky.sh"
|
|
591
|
+
|
|
592
|
+
${nvmShim}
|
|
593
|
+
|
|
594
|
+
${bumpCmd}
|
|
595
|
+
`;
|
|
596
|
+
fs.writeFileSync(prePushPath, prePushContent, { mode: 0o755 });
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
531
601
|
const InstructionAssembler = {
|
|
532
602
|
buildMasterInstructions,
|
|
533
603
|
buildClaudeContent,
|
|
@@ -536,6 +606,7 @@ const InstructionAssembler = {
|
|
|
536
606
|
writeBacklogFiles,
|
|
537
607
|
writeGitignore,
|
|
538
608
|
writeManifest,
|
|
609
|
+
writeAutomationScripts,
|
|
539
610
|
};
|
|
540
611
|
|
|
541
612
|
export { InstructionAssembler };
|
|
@@ -33,7 +33,7 @@ async function gatherUserSelections(targetDir = process.cwd()) {
|
|
|
33
33
|
let scope = 'fullstack';
|
|
34
34
|
let step = 0;
|
|
35
35
|
|
|
36
|
-
const finalStep = () => (selections.mode === 'prompts' ? 2 :
|
|
36
|
+
const finalStep = () => (selections.mode === 'prompts' ? 2 : 9);
|
|
37
37
|
|
|
38
38
|
while (step < finalStep()) {
|
|
39
39
|
const context = {
|
|
@@ -74,6 +74,7 @@ async function gatherUserSelections(targetDir = process.cwd()) {
|
|
|
74
74
|
if (stepValue.ide) currentSelections.ide = stepValue.ide;
|
|
75
75
|
if (stepValue.undoLastIdiom) currentSelections.idioms.pop();
|
|
76
76
|
if (stepValue.resetIdioms) currentSelections.idioms = [];
|
|
77
|
+
if (stepValue.bump !== undefined) currentSelections.bump = stepValue.bump;
|
|
77
78
|
return nextScope;
|
|
78
79
|
}
|
|
79
80
|
}
|
|
@@ -98,8 +99,10 @@ async function executeWizardStep(step, context) {
|
|
|
98
99
|
return promptDesignPreset(context);
|
|
99
100
|
case 7:
|
|
100
101
|
return promptIdeSelection();
|
|
102
|
+
case 8:
|
|
103
|
+
return promptBumpAutomation(context);
|
|
101
104
|
default: {
|
|
102
|
-
const defaultResult = success({ nextStep:
|
|
105
|
+
const defaultResult = success({ nextStep: 9 });
|
|
103
106
|
return defaultResult;
|
|
104
107
|
}
|
|
105
108
|
}
|
|
@@ -393,6 +396,24 @@ async function promptIdeSelection() {
|
|
|
393
396
|
return ideResult;
|
|
394
397
|
}
|
|
395
398
|
|
|
399
|
+
async function promptBumpAutomation(context) {
|
|
400
|
+
const { selections } = context;
|
|
401
|
+
|
|
402
|
+
const hasJsTs = selections.idioms.some((id) => id === 'javascript' || id === 'typescript');
|
|
403
|
+
|
|
404
|
+
if (!hasJsTs) {
|
|
405
|
+
return success({ nextStep: 9, bump: false });
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const result = await safeConfirm({
|
|
409
|
+
message: 'Enable automated versioning (Bump & Changelog)?',
|
|
410
|
+
default: true,
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
const bumpResult = success({ nextStep: 9, bump: result });
|
|
414
|
+
return bumpResult;
|
|
415
|
+
}
|
|
416
|
+
|
|
396
417
|
function validateSelections(selections) {
|
|
397
418
|
if (selections.mode === 'quick') {
|
|
398
419
|
selections.flavor = selections.flavor || 'lite';
|