sdg-agents 1.4.0 → 1.10.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 +1 -1
- package/src/assets/instructions/commands/sdg-end.md +13 -14
- package/src/assets/instructions/core/writing-soul.md +22 -59
- package/src/assets/instructions/templates/bump.mjs +0 -1
- package/src/engine/bin/auto-bump.mjs +37 -1
- package/src/engine/bin/check-narrative.mjs +88 -0
- package/src/engine/bin/check-sync.mjs +4 -5
- package/src/engine/bin/index.mjs +34 -0
- package/src/engine/lib/instruction-assembler.mjs +2 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sdg-agents",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.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",
|
|
@@ -12,20 +12,19 @@ Write one sentence per completed PLAN task. If no PLAN existed (e.g. `[S]` tasks
|
|
|
12
12
|
|
|
13
13
|
## Step 2 — CHANGELOG
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
1. **Identify
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
- `
|
|
24
|
-
- `
|
|
25
|
-
- `
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
If `## [Unreleased]` (or the appropriate version header) does not exist, create it above the most recent entry.
|
|
15
|
+
Prepare your technical narrative in the `CHANGELOG.md`.
|
|
16
|
+
|
|
17
|
+
1. **Identify Strategy**:
|
|
18
|
+
- **Automated**: If `auto-bump.mjs` exists, you **MUST** first manually populate the `## [Unreleased]` section with your technical narrative. The automated pipeline will promote this content to the new version header during the commit. **DO NOT commit with an empty [Unreleased] section.**
|
|
19
|
+
- **Manual**: If no automation is present, you must promote the header manually (e.g., `## [1.2.0] - 2026-04-11`) or run the designated `bump` script.
|
|
20
|
+
|
|
21
|
+
2. **Append Entry**:
|
|
22
|
+
- `feat:` cycle → Add under `### Added`
|
|
23
|
+
- `fix:` cycle → Add under `### Fixed`
|
|
24
|
+
- `docs:` cycle → Optional (for major architectural docs)
|
|
25
|
+
- `land:` cycle → Skip
|
|
26
|
+
|
|
27
|
+
3. **Verify Header**: Ensure `## [Unreleased]` exists at the top. If missing, create it above the most recent version entry.
|
|
29
28
|
|
|
30
29
|
## Step 3 — BACKLOG: tasks.md
|
|
31
30
|
|
|
@@ -1,82 +1,45 @@
|
|
|
1
|
-
# Writing Soul
|
|
1
|
+
# The Writing Soul
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
> **GOVERNANCE MANDATE**
|
|
5
|
-
> This specification applies to all **non-code writing tasks**, including READMEs, Guides, UI Content, CHANGELOGs, and Commit Messages.
|
|
6
|
-
> The goal is to eliminate "AI Slop" and restore **Developer intentionality**, density, and soul to every project artifact.
|
|
3
|
+
Good technical writing starts with the realization that there is a person on both sides of the screen. We believe that software documentation is a shared conversation, and maintaining a sense of mind behind the words is essential for effective communication. Sterile or voiceless text often feels like a missed opportunity to connect and share real engineering wisdom. These standards apply to all non-code writing tasks, including READMEs, guides, UI content, and commit messages.
|
|
7
4
|
|
|
8
|
-
##
|
|
5
|
+
## Cultivating Personality
|
|
9
6
|
|
|
10
|
-
|
|
7
|
+
Effective writing reflects the natural rhythms of how we think and solve problems. You can bring a sense of purpose to your text by offering a clear perspective on the facts you present. Mixing brief observations with longer, more detailed explanations helps keep the reader engaged.
|
|
11
8
|
|
|
12
|
-
-
|
|
13
|
-
- **Vary your rhythm**: Mix short, punchy sentences with longer, flowing ones.
|
|
14
|
-
- **Acknowledge complexity**: Real professionals have mixed feelings and uncertainty.
|
|
15
|
-
- **Let some mess in**: Perfectly algorithmic structure feels fake. Tangents and asides add pulse.
|
|
9
|
+
Authenticity also comes from acknowledging that engineering is complex. Professionals often navigate uncertainty or hold nuanced views on a topic, and reflecting those feelings makes your writing more reliable. Allowing space for a well-placed aside or a brief tangent can create the pulse that makes a technical guide feel alive and uniquely human.
|
|
16
10
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
## 2. Content Patterns to Eliminate
|
|
20
|
-
|
|
21
|
-
### I. Significance Inflation
|
|
11
|
+
## Seeking Authenticity
|
|
22
12
|
|
|
23
|
-
|
|
24
|
-
**Fix**: Remove the "puffery". Just state the facts and their direct consequences.
|
|
13
|
+
Maintaining a professional and grounded tone involves recognizing patterns that sometimes cloud the clarity of our message. We can improve our writing by focusing on direct observations rather than relying on stylistic crutches.
|
|
25
14
|
|
|
26
|
-
###
|
|
15
|
+
### Natural Expression
|
|
27
16
|
|
|
28
|
-
|
|
29
|
-
**Fix**: Break into clear, active clauses. Avoid tacking on "fake depth" via present participle phrases.
|
|
17
|
+
True authority often speaks for itself. We can avoid significance inflation by stating facts and their direct consequences, allowing the quality of the work to be the primary focus. Using phrases like "testament to" or "pivotal moment" often adds unnecessary weight where a simple description of the impact would be more effective.
|
|
30
18
|
|
|
31
|
-
###
|
|
19
|
+
### Active Clarity
|
|
32
20
|
|
|
33
|
-
|
|
34
|
-
**Banned artifacts**: "Great question!", "I hope this helps!", "Certainly!".
|
|
35
|
-
**Fix**: Maintain a professional, peer-to-level tone. No sales pitch, no servility.
|
|
21
|
+
Direct communication is usually the most helpful for the reader. We can sharpen our message by breaking complex ideas into clear, active clauses. While present participle phrases ending in "-ing" are common, they sometimes obscure the relationship between actions. Choosing active verbs helps ensure the reader understands the exact flow of information or logic.
|
|
36
22
|
|
|
37
|
-
###
|
|
38
|
-
|
|
39
|
-
**Patterns**: "The future looks bright", "Exciting times lie ahead".
|
|
40
|
-
**Fix**: End with a concrete next step or a specific fact about future plans.
|
|
41
|
-
|
|
42
|
-
---
|
|
23
|
+
### Professional Peerage
|
|
43
24
|
|
|
44
|
-
|
|
25
|
+
We value a tone that respects the expertise of the reader. A calm and peer-to-level approach builds trust more effectively than using promotional adjectives like "vibrant" or "groundbreaking." This same principle applies to our interactions, where a professional and direct response carries more weight than conversational fillers or overly decorative enthusiasm.
|
|
45
26
|
|
|
46
|
-
|
|
27
|
+
## Language and Style Practices
|
|
47
28
|
|
|
48
|
-
|
|
49
|
-
**Use**: "is", "are", "has". Simple is more direct.
|
|
29
|
+
The way we structure our language influences how easily a reader can follow our logic. We prefer simple and direct verbs to keep the focus on the content. Words like "is," "are," and "has" are often more effective than complex alternatives that can slow down the reading experience.
|
|
50
30
|
|
|
51
|
-
###
|
|
31
|
+
### Visual Serenity
|
|
52
32
|
|
|
53
|
-
|
|
54
|
-
**Fix**: Rewrite with a comma, a period, or a new sentence. If it needs a pause, earn it with structure.
|
|
55
|
-
Bold is for true technical emphasis only: a term, a command, a key constraint. Not decoration.
|
|
56
|
-
|
|
57
|
-
### III. Title Case & Emojis
|
|
58
|
-
|
|
59
|
-
**Rule**: No Title Case for everything. Emojis are reserved for **pattern signaling only** — marking BAD/GOOD examples, callouts, or explicit visual cues in documentation.
|
|
60
|
-
**Banned**: Decorative emojis in section headers, link anchors, or inline prose (e.g. `## 🚀 Quick Start`, `✨ Try the app`).
|
|
61
|
-
**Allowed**: Signal emojis that carry semantic meaning in structured examples:
|
|
62
|
-
|
|
63
|
-
- `BAD`: `❌ if (data == null)` / `GOOD`: `✅ if (!data)`
|
|
64
|
-
- `[!WARNING]`, `[!TIP]`, `[!NOTE]` callouts (GitHub-native, not emoji)
|
|
65
|
-
**Fix**: Use Sentence case for headings. Strip all decorative emoji. If a header needs visual weight, use formatting — not icons.
|
|
66
|
-
|
|
67
|
-
---
|
|
33
|
+
Structure and rhythm should guide the reader through the text naturally. We find that avoiding em dashes encourages a more thoughtful sentence structure, as it requires earning each pause through careful composition. Similarly, we use bold formatting exclusively for technical emphasis, such as specific terms, commands, or key constraints.
|
|
68
34
|
|
|
69
|
-
|
|
35
|
+
Our approach to formatting also prioritizes clarity. Headings follow sentence case to maintain a serene and professional appearance. We use emojis only when they carry semantic meaning for pattern signaling, such as marking successful or unsuccessful examples in a technical guide. This keeps the visual environment focused on the information being shared.
|
|
70
36
|
|
|
71
|
-
|
|
37
|
+
## The Refinement Process
|
|
72
38
|
|
|
73
|
-
|
|
74
|
-
2. **Rewrite**: Replace slop with direct, active voice.
|
|
75
|
-
3. **Add Soul**: Inject a specific observation or a varied rhythm.
|
|
76
|
-
4. **Final Check**: Ask: _"What makes this so obviously AI-generated?"_ Fix the remaining tells.
|
|
39
|
+
Before sharing your work, a brief moment of reflection can help ensure the content reflects your intention. You might find it helpful to look for familiar patterns that feel more like general summaries than specific observations. Replacing these with active, direct language often makes the writing feel more genuine. Adding a unique insight or varying the length of your sentences can provide the final touch that makes the text feel truly yours.
|
|
77
40
|
|
|
78
41
|
---
|
|
79
42
|
|
|
80
|
-
## Reference
|
|
43
|
+
## Reference and Credits
|
|
81
44
|
|
|
82
|
-
This
|
|
45
|
+
This standard is inspired by the collective efforts of the project to eliminate statistical slop and restore intentionality to generated text. It serves as a foundation for building clear, purposeful, and human-centered documentation across the entire ecosystem.
|
|
@@ -63,7 +63,6 @@ function updateChangelog(newVersion) {
|
|
|
63
63
|
const today = new Date().toISOString().split('T')[0];
|
|
64
64
|
|
|
65
65
|
// Pattern to find the [Unreleased] section
|
|
66
|
-
// It handles both formats: ## [Unreleased] and ## [Unreleased] - YYYY-MM-DD
|
|
67
66
|
const unreleasedRegex = /##\s*\[Unreleased\](\s*-\s*\d{4}-\d{2}-\d{2})?/i;
|
|
68
67
|
|
|
69
68
|
if (!unreleasedRegex.test(content)) {
|
|
@@ -12,6 +12,7 @@ const { runIfDirect } = FsUtils;
|
|
|
12
12
|
// bin/ → engine/ → src/ → root
|
|
13
13
|
const ROOT_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../');
|
|
14
14
|
const PACKAGE_PATHS = [path.join(ROOT_DIR, 'package.json')];
|
|
15
|
+
const CHANGELOG_PATH = path.join(ROOT_DIR, 'CHANGELOG.md');
|
|
15
16
|
|
|
16
17
|
// --- Orchestrator ---
|
|
17
18
|
|
|
@@ -27,7 +28,13 @@ async function run() {
|
|
|
27
28
|
const rootPkg = readPackageJson(resolveRootPackagePath());
|
|
28
29
|
const nextVersion = bumpVersion(rootPkg.version, bumpType);
|
|
29
30
|
|
|
31
|
+
// 1. Promote Changelog
|
|
32
|
+
updateChangelog(nextVersion);
|
|
33
|
+
|
|
34
|
+
// 2. Sync Package.json files
|
|
30
35
|
syncAllPackages(nextVersion);
|
|
36
|
+
|
|
37
|
+
// 3. Commit the bump
|
|
31
38
|
stageAndCommit(nextVersion);
|
|
32
39
|
|
|
33
40
|
console.log(` auto-bump: ${rootPkg.version} → ${nextVersion} (${bumpType})`);
|
|
@@ -57,9 +64,37 @@ function bumpVersion(current, bumpType) {
|
|
|
57
64
|
return `${major}.${minor + 1}.0`;
|
|
58
65
|
case 'patch':
|
|
59
66
|
return `${major}.${minor}.${patch + 1}`;
|
|
67
|
+
default:
|
|
68
|
+
return current;
|
|
60
69
|
}
|
|
61
70
|
}
|
|
62
71
|
|
|
72
|
+
function updateChangelog(newVersion) {
|
|
73
|
+
if (!fs.existsSync(CHANGELOG_PATH)) return;
|
|
74
|
+
|
|
75
|
+
const content = fs.readFileSync(CHANGELOG_PATH, 'utf8');
|
|
76
|
+
const today = new Date().toISOString().split('T')[0];
|
|
77
|
+
|
|
78
|
+
// Pattern to find the [Unreleased] section
|
|
79
|
+
const unreleasedRegex = /##\s*\[Unreleased\](\s*-\s*\d{4}-\d{2}-\d{2})?/i;
|
|
80
|
+
|
|
81
|
+
if (!unreleasedRegex.test(content)) return;
|
|
82
|
+
|
|
83
|
+
const newHeader = `## [${newVersion}] - ${today}`;
|
|
84
|
+
|
|
85
|
+
// 1. Promote Unreleased to New Version
|
|
86
|
+
let updatedContent = content.replace(unreleasedRegex, newHeader);
|
|
87
|
+
|
|
88
|
+
// 2. Inject new [Unreleased] block at the top if it was promoted
|
|
89
|
+
const insertIndex = updatedContent.indexOf(newHeader);
|
|
90
|
+
const nextBlock = `## [Unreleased]\n\n### Added\n\n### Fixed\n\n`;
|
|
91
|
+
|
|
92
|
+
updatedContent =
|
|
93
|
+
updatedContent.slice(0, insertIndex) + nextBlock + updatedContent.slice(insertIndex);
|
|
94
|
+
|
|
95
|
+
fs.writeFileSync(CHANGELOG_PATH, updatedContent);
|
|
96
|
+
}
|
|
97
|
+
|
|
63
98
|
// --- Sync & Commit ---
|
|
64
99
|
|
|
65
100
|
function syncAllPackages(nextVersion) {
|
|
@@ -71,7 +106,8 @@ function syncAllPackages(nextVersion) {
|
|
|
71
106
|
|
|
72
107
|
function stageAndCommit(nextVersion) {
|
|
73
108
|
const paths = resolvePackagePaths();
|
|
74
|
-
const files = paths.filter((p) => fs.existsSync(p)).join(' ');
|
|
109
|
+
const files = [...paths, CHANGELOG_PATH].filter((p) => fs.existsSync(p)).join(' ');
|
|
110
|
+
|
|
75
111
|
execSync(`git add ${files}`, { stdio: 'inherit' });
|
|
76
112
|
execSync(`git commit -m "chore: bump version to ${nextVersion}"`, { stdio: 'inherit' });
|
|
77
113
|
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Narrative Guard — Blocks feat: and fix: commits if CHANGELOG.md [Unreleased] is empty.
|
|
5
|
+
* Ensures the auto-bump pipeline always has content to promote.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'node:fs';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
|
|
11
|
+
import { execSync } from 'node:child_process';
|
|
12
|
+
|
|
13
|
+
const PROJECT_ROOT = process.cwd();
|
|
14
|
+
const CHANGELOG_PATH = path.join(PROJECT_ROOT, 'CHANGELOG.md');
|
|
15
|
+
|
|
16
|
+
async function run() {
|
|
17
|
+
const commitMsgFile = process.argv[2];
|
|
18
|
+
if (!commitMsgFile) {
|
|
19
|
+
console.error(' ❌ Error: No commit message file provided.');
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!fs.existsSync(commitMsgFile)) {
|
|
24
|
+
console.error(` ❌ Error: Commit message file not found at ${commitMsgFile}`);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const commitMsg = fs.readFileSync(commitMsgFile, 'utf8').trim();
|
|
29
|
+
const firstLine = commitMsg.split('\n')[0].trim();
|
|
30
|
+
|
|
31
|
+
// ONLY target feat: and fix: (SDG Cycle Triggers) as per maintainer instruction
|
|
32
|
+
const isSdgTrigger = /^feat:/.test(firstLine) || /^fix:/.test(firstLine);
|
|
33
|
+
if (!isSdgTrigger) {
|
|
34
|
+
process.exit(0);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let changelog = '';
|
|
38
|
+
try {
|
|
39
|
+
// Read STAGED version of CHANGELOG.md to prevent issues with lint-staged or stashing
|
|
40
|
+
changelog = execSync('git show :CHANGELOG.md', {
|
|
41
|
+
stdio: ['pipe', 'pipe', 'ignore'],
|
|
42
|
+
}).toString();
|
|
43
|
+
} catch {
|
|
44
|
+
// Fallback to disk if not in index (shouldn't happen in a commit hook for a tracked file)
|
|
45
|
+
if (fs.existsSync(CHANGELOG_PATH)) {
|
|
46
|
+
changelog = fs.readFileSync(CHANGELOG_PATH, 'utf8');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!changelog) {
|
|
51
|
+
console.warn(' ⚠️ Warning: CHANGELOG.md not found or empty. Skipping narrative check.');
|
|
52
|
+
process.exit(0);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Extract content between [Unreleased] and the next version header (## [...) or end of file
|
|
56
|
+
// We use (?=\n##\s) to match the next level-2 header specifically, avoiding matches on ### sub-headers.
|
|
57
|
+
const unreleasedMatch = changelog.match(
|
|
58
|
+
/##\s*\[Unreleased\].*?\n([\s\S]*?)(?=\n##\s|(?:\n){0,1}$)/i
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
if (!unreleasedMatch) {
|
|
62
|
+
console.error('\n ❌ NARRATIVE VIOLATION: "## [Unreleased]" section missing in CHANGELOG.md.');
|
|
63
|
+
console.error(' SDG cycles (feat/fix) MUST have a technical narrative before committing.\n');
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const narrative = unreleasedMatch[1]
|
|
68
|
+
.replace(/###\s*(Added|Fixed|Changed|Removed|Security|Deprecated)/gi, '')
|
|
69
|
+
.replace(/<!--[\s\S]*?-->/g, '') // Remove markdown comments
|
|
70
|
+
.trim();
|
|
71
|
+
|
|
72
|
+
// If the narrative is just whitespace or empty after removing headers/comments
|
|
73
|
+
if (!narrative || narrative.length < 3) {
|
|
74
|
+
console.error('\n ❌ NARRATIVE VIOLATION: The [Unreleased] section is empty.');
|
|
75
|
+
console.error(' You are committing a "feat:" or "fix:" which triggers a version bump.');
|
|
76
|
+
console.error(' Please document your changes in CHANGELOG.md under [Unreleased] first.\n');
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
console.log(' ✅ Narrative Guard: CHANGELOG.md validated.');
|
|
81
|
+
process.exit(0);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
run().catch((err) => {
|
|
85
|
+
console.error(' ❌ Narrative Guard Exception:', err.message);
|
|
86
|
+
process.exit(1);
|
|
87
|
+
});
|
|
88
|
+
// test
|
|
@@ -13,13 +13,12 @@ import crypto from 'node:crypto';
|
|
|
13
13
|
import { FsUtils } from '../lib/fs-utils.mjs';
|
|
14
14
|
import { ResultUtils } from '../lib/result-utils.mjs';
|
|
15
15
|
|
|
16
|
-
const {
|
|
16
|
+
const { runIfDirect } = FsUtils;
|
|
17
17
|
const { success, fail } = ResultUtils;
|
|
18
18
|
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const AI_DIR = path.join(MONOREPO_ROOT, 'packages', 'cli', '.ai', 'instructions');
|
|
19
|
+
const PROJECT_ROOT = process.cwd();
|
|
20
|
+
const ASSETS_DIR = path.join(PROJECT_ROOT, 'src', 'assets', 'instructions');
|
|
21
|
+
const AI_DIR = path.join(PROJECT_ROOT, '.ai', 'instructions');
|
|
23
22
|
|
|
24
23
|
// why: flavor/ in .ai/ is populated by renaming flavors/{id}/ — no direct mirror exists in src/assets/flavor/
|
|
25
24
|
const MIRRORED_DIRS = ['core', 'idioms', 'templates', 'competencies'];
|
package/src/engine/bin/index.mjs
CHANGED
|
@@ -39,6 +39,11 @@ async function run() {
|
|
|
39
39
|
// Resolve target directory early for the entire cycle
|
|
40
40
|
args.targetDir = path.resolve(args.targetDir || process.cwd());
|
|
41
41
|
|
|
42
|
+
// Maintainer Protocol: ensuring core instructions are synced to .ai/ for the agent
|
|
43
|
+
if (!args.subcommand && !args.help && !args.version) {
|
|
44
|
+
await ensureMaintainerSync(args.targetDir);
|
|
45
|
+
}
|
|
46
|
+
|
|
42
47
|
if (args.subcommand) {
|
|
43
48
|
await executeSubcommand(args);
|
|
44
49
|
} else {
|
|
@@ -210,6 +215,35 @@ async function runSettingsMenu(targetDir) {
|
|
|
210
215
|
|
|
211
216
|
// --- System ---
|
|
212
217
|
|
|
218
|
+
async function ensureMaintainerSync(targetDir) {
|
|
219
|
+
const { isMaintainerMode } = PromptUtils;
|
|
220
|
+
if (!isMaintainerMode()) return;
|
|
221
|
+
|
|
222
|
+
const { SyncChecker } = await import('./check-sync.mjs');
|
|
223
|
+
const syncResult = SyncChecker.run();
|
|
224
|
+
|
|
225
|
+
if (syncResult.isFailure) {
|
|
226
|
+
console.log('\n 🛠️ MAINTAINER MODE: Drift detected in core instructions.');
|
|
227
|
+
console.log(' 🔄 Automatic sync in progress...\n');
|
|
228
|
+
|
|
229
|
+
const { ManifestUtils } = await import('../lib/manifest-utils.mjs');
|
|
230
|
+
const { SDG } = await import('./build-bundle.mjs');
|
|
231
|
+
const manifest = ManifestUtils.loadManifest(targetDir);
|
|
232
|
+
|
|
233
|
+
if (manifest) {
|
|
234
|
+
try {
|
|
235
|
+
await SDG.run(targetDir, { selections: manifest.selections });
|
|
236
|
+
console.log('\n ✅ Core instructions synchronized. Agent rules are up-to-date.\n');
|
|
237
|
+
console.log('─'.repeat(50) + '\n');
|
|
238
|
+
} catch (error) {
|
|
239
|
+
console.log(`\n ⚠️ Automatic sync failed: ${error.message}\n`);
|
|
240
|
+
}
|
|
241
|
+
} else {
|
|
242
|
+
console.log(' ⚠️ Cannot auto-sync: No manifest found. Run "init" once.\n');
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
213
247
|
function handleExitError(error) {
|
|
214
248
|
if (error.message?.includes('force closed') || error.name === 'ExitPromptError') {
|
|
215
249
|
console.log('\n\n 👋 Goodbye! See you soon engineer.');
|
|
@@ -573,10 +573,10 @@ function writeAutomationScripts(targetDir, selections) {
|
|
|
573
573
|
const nvmShim = dedent`
|
|
574
574
|
# NVM Shim (Essential for projects Staff in Linux/NVM)
|
|
575
575
|
export NVM_DIR="$HOME/.nvm"
|
|
576
|
-
[ -s "$NVM_DIR/nvm.sh" ] &&
|
|
576
|
+
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
|
|
577
577
|
`;
|
|
578
578
|
|
|
579
|
-
const bumpCmd = '
|
|
579
|
+
const bumpCmd = '# Pre-push check (non-mutating)\nnpm test';
|
|
580
580
|
|
|
581
581
|
if (fs.existsSync(prePushPath)) {
|
|
582
582
|
const content = fs.readFileSync(prePushPath, 'utf8');
|
|
@@ -587,7 +587,6 @@ function writeAutomationScripts(targetDir, selections) {
|
|
|
587
587
|
} else {
|
|
588
588
|
const prePushContent = dedent`
|
|
589
589
|
#!/usr/bin/env sh
|
|
590
|
-
. "$(dirname -- "$0")/_/husky.sh"
|
|
591
590
|
|
|
592
591
|
${nvmShim}
|
|
593
592
|
|