sdg-agents 1.0.5
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/LICENSE +15 -0
- package/README.md +161 -0
- package/package.json +88 -0
- package/src/assets/dev-guides/agent-deep-flow.md +139 -0
- package/src/assets/dev-guides/prompt-tracks/00-lite-mode/01-project-scope-and-mvp.md +45 -0
- package/src/assets/dev-guides/prompt-tracks/00-lite-mode/02-stack-and-data-model.md +56 -0
- package/src/assets/dev-guides/prompt-tracks/00-lite-mode/03-external-integrations.md +44 -0
- package/src/assets/dev-guides/prompt-tracks/00-lite-mode/04-launch-checklist.md +26 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/00-foundation/01-project-vision.md +77 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/00-foundation/02-problem-definition.md +63 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/00-foundation/03-success-metrics.md +48 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/00-foundation/05-engineering-culture-and-silos.md +41 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/00-foundation/06-foundation-approval.md +57 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/01-setup/01-tech-stack.md +62 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/01-setup/02-local-environment.md +50 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/01-setup/03-version-control-and-tracking.md +65 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/01-setup/04-hello-world-validation.md +37 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/01-setup/05-setup-approval.md +61 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/01-folder-structure.md +49 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/02-design-patterns.md +59 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/03-apis-and-communication.md +55 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/04-security-and-access.md +63 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/05-security-audit.md +64 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/06-design-system-ux.md +72 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/07-ai-strategy.md +72 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/08-observability.md +65 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/09-technical-documentation.md +64 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/10-infrastructure-and-costs.md +40 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/11-data-privacy-compliance.md +41 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/12-performance-and-scale.md +46 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/13-disaster-recovery.md +39 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/02-architecture/14-architecture-approval.md +81 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/03-delivery/01-ci-cd-pipeline.md +49 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/03-delivery/02-quality-standards.md +46 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/03-delivery/03-delivery-approval.md +58 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/04-features/00-feature-template.md +30 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/04-features/01-context-and-scope.md +46 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/04-features/02-business-rules.md +39 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/04-features/03-architecture-and-design.md +50 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/04-features/04-testing-strategy.md +48 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/04-features/05-implementation-plan.md +45 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/04-features/06-feature-approval.md +46 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/05-evolution/01-rfcs-and-changes.md +63 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/05-evolution/02-incident-postmortems.md +64 -0
- package/src/assets/dev-guides/prompt-tracks/01-new-evolution/05-evolution/03-adr-template.md +66 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/00-foundation/01-legacy-vision.md +73 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/00-foundation/02-foundation-approval.md +53 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/01-analysis/01-tech-debt-inventory.md +55 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/01-analysis/02-security-audit.md +63 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/01-analysis/03-regression-baseline.md +49 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/01-analysis/04-analysis-approval.md +65 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/02-strategy/01-modernization-approach.md +60 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/02-strategy/02-versioning-and-coexistence.md +57 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/02-strategy/03-new-demands-triage.md +41 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/02-strategy/04-strategy-approval.md +56 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/03-refactor/01-cleanup-backlog.md +45 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/03-refactor/02-refactor-approval.md +53 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/04-migration/01-migration-roadmap.md +47 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/04-migration/02-data-sync-strategy.md +56 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/04-migration/03-migration-approval.md +55 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/05-sunset/01-decommission-plan.md +47 -0
- package/src/assets/dev-guides/prompt-tracks/02-legacy-modernization/05-sunset/02-sunset-approval.md +60 -0
- package/src/assets/dev-guides/prompt-tracks/README.md +144 -0
- package/src/assets/dev-guides/software-development-lifecycle-sdlc.md +147 -0
- package/src/assets/dev-guides/spec-driven-dev-guide.md +81 -0
- package/src/assets/dev-guides/ui-prompt-guide.md +181 -0
- package/src/assets/img/sdg-agents-icon-dark.svg +55 -0
- package/src/assets/img/sdg-agents-icon-light.svg +55 -0
- package/src/assets/img/sdg-agents-menu-v1.png +0 -0
- package/src/assets/img/sdg-icon.svg +110 -0
- package/src/assets/instructions/commands/sdg-docs.md +69 -0
- package/src/assets/instructions/commands/sdg-feat.md +45 -0
- package/src/assets/instructions/commands/sdg-fix.md +41 -0
- package/src/assets/instructions/commands/sdg-land.md +128 -0
- package/src/assets/instructions/competencies/backend.md +353 -0
- package/src/assets/instructions/competencies/frontend.md +439 -0
- package/src/assets/instructions/core/agent-roles.md +104 -0
- package/src/assets/instructions/core/api-design.md +65 -0
- package/src/assets/instructions/core/ci-cd.md +144 -0
- package/src/assets/instructions/core/cloud.md +63 -0
- package/src/assets/instructions/core/code-style.md +144 -0
- package/src/assets/instructions/core/containers.md +115 -0
- package/src/assets/instructions/core/data-access.md +119 -0
- package/src/assets/instructions/core/engineering-standards.md +502 -0
- package/src/assets/instructions/core/naming.md +136 -0
- package/src/assets/instructions/core/observability.md +73 -0
- package/src/assets/instructions/core/security-pipeline.md +209 -0
- package/src/assets/instructions/core/security.md +45 -0
- package/src/assets/instructions/core/sql-style.md +138 -0
- package/src/assets/instructions/core/staff-dna.md +72 -0
- package/src/assets/instructions/core/testing-principles.md +212 -0
- package/src/assets/instructions/core/ui/architecture.md +171 -0
- package/src/assets/instructions/core/ui/design-thinking.md +319 -0
- package/src/assets/instructions/core/ui/presets.md +200 -0
- package/src/assets/instructions/core/ui/standards.md +144 -0
- package/src/assets/instructions/core/writing-soul.md +82 -0
- package/src/assets/instructions/flavors/legacy/principles.md +64 -0
- package/src/assets/instructions/flavors/lite/principles.md +39 -0
- package/src/assets/instructions/flavors/mvc/principles.md +124 -0
- package/src/assets/instructions/flavors/vertical-slice/principles.md +178 -0
- package/src/assets/instructions/idioms/csharp/patterns.md +367 -0
- package/src/assets/instructions/idioms/flutter/patterns.md +97 -0
- package/src/assets/instructions/idioms/go/patterns.md +193 -0
- package/src/assets/instructions/idioms/java/patterns.md +233 -0
- package/src/assets/instructions/idioms/javascript/patterns.md +223 -0
- package/src/assets/instructions/idioms/kotlin/patterns.md +94 -0
- package/src/assets/instructions/idioms/python/patterns.md +185 -0
- package/src/assets/instructions/idioms/rust/patterns.md +189 -0
- package/src/assets/instructions/idioms/scripts/patterns.md +81 -0
- package/src/assets/instructions/idioms/sql/patterns.md +109 -0
- package/src/assets/instructions/idioms/swift/patterns.md +97 -0
- package/src/assets/instructions/idioms/typescript/patterns.md +304 -0
- package/src/assets/instructions/idioms/vbnet/patterns.md +96 -0
- package/src/assets/instructions/idioms/vbnet-legacy/patterns.md +104 -0
- package/src/assets/instructions/templates/workflow.md +244 -0
- package/src/assets/instructions/workflows/governance.md +162 -0
- package/src/engine/bin/add-idiom.mjs +186 -0
- package/src/engine/bin/index.mjs +226 -0
- package/src/engine/config/stack-display.mjs +75 -0
- package/src/engine/config/stack-versions.mjs +76 -0
- package/src/engine/lib/cli-parser.mjs +80 -0
- package/src/engine/lib/display-utils.mjs +20 -0
- package/src/engine/lib/fs-utils.mjs +99 -0
- package/src/engine/lib/instruction-assembler.mjs +487 -0
- package/src/engine/lib/manifest-utils.mjs +128 -0
- package/src/engine/lib/prompt-utils.mjs +137 -0
- package/src/engine/lib/result-utils.mjs +14 -0
- package/src/engine/lib/ruleset-injector.mjs +183 -0
- package/src/engine/lib/ui-utils.mjs +216 -0
- package/src/engine/lib/wizard.mjs +472 -0
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Instruction Assembler — Builds and writes the final master instructions.
|
|
3
|
+
* Assembles agent config files and the project manifest.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import dedent from 'dedent';
|
|
9
|
+
|
|
10
|
+
import { STACK_DISPLAY_NAMES } from '../config/stack-display.mjs';
|
|
11
|
+
import { DisplayUtils } from './display-utils.mjs';
|
|
12
|
+
import { ManifestUtils } from './manifest-utils.mjs';
|
|
13
|
+
import { FsUtils } from './fs-utils.mjs';
|
|
14
|
+
|
|
15
|
+
const { displayName } = DisplayUtils;
|
|
16
|
+
const { computeHashes } = ManifestUtils;
|
|
17
|
+
const { getDirname } = FsUtils;
|
|
18
|
+
|
|
19
|
+
const __dirname = getDirname(import.meta.url);
|
|
20
|
+
const SOURCE_INSTRUCTIONS = path.join(__dirname, '..', '..', 'assets', 'instructions');
|
|
21
|
+
|
|
22
|
+
function computeStackMetrics(idioms) {
|
|
23
|
+
const hasBackend = idioms.some((idiomId) => STACK_DISPLAY_NAMES[idiomId]?.isBackend);
|
|
24
|
+
const hasFrontend = idioms.some((idiomId) => STACK_DISPLAY_NAMES[idiomId]?.isFrontend);
|
|
25
|
+
|
|
26
|
+
const metrics = {
|
|
27
|
+
hasBackend,
|
|
28
|
+
hasFrontend,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
return metrics;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Assembles the full master instruction content from inline laws + workflow + links.
|
|
36
|
+
*/
|
|
37
|
+
function buildMasterInstructions(selections) {
|
|
38
|
+
const templatesDir = path.join(SOURCE_INSTRUCTIONS, 'templates');
|
|
39
|
+
const workflow = fs.readFileSync(path.join(templatesDir, 'workflow.md'), 'utf8');
|
|
40
|
+
|
|
41
|
+
const manifesto = buildStaffManifesto();
|
|
42
|
+
const instructionLinks = buildInstructionLinks(selections);
|
|
43
|
+
|
|
44
|
+
const isMultiAgent = selections.ide === 'claude' || selections.ide === 'all';
|
|
45
|
+
const agentRolesBlock = isMultiAgent ? buildAgentRolesBlock() : '';
|
|
46
|
+
const agentRolesSeparator = isMultiAgent ? '\n\n' : '';
|
|
47
|
+
|
|
48
|
+
const fullInstructionContent = `${manifesto}\n\n${workflow}${agentRolesSeparator}${agentRolesBlock}\n\n${instructionLinks}`;
|
|
49
|
+
|
|
50
|
+
return fullInstructionContent;
|
|
51
|
+
|
|
52
|
+
function buildAgentRolesBlock() {
|
|
53
|
+
const agentRolesString = dedent`
|
|
54
|
+
## Agent Roles
|
|
55
|
+
|
|
56
|
+
> [!NOTE]
|
|
57
|
+
> Multi-agent execution is active. Read \`.ai/instructions/core/agent-roles.md\` for the full protocol.
|
|
58
|
+
|
|
59
|
+
| Role | Phases | Model |
|
|
60
|
+
| :----------- | :---------------------- | :------------------------- |
|
|
61
|
+
| **Planning** | SPEC, PLAN, Review, END | claude-sonnet-4-6 thinking |
|
|
62
|
+
| **Fast** | CODE, TEST | claude-sonnet-4-6 |
|
|
63
|
+
|
|
64
|
+
**Handoff:** Planning spawns Fast via the Agent tool when PLAN is approved. Fast returns a structured report. Planning reviews before END.`;
|
|
65
|
+
|
|
66
|
+
return agentRolesString;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function buildStaffManifesto() {
|
|
70
|
+
const manifestoString = dedent`
|
|
71
|
+
# Staff Engineer — Governance Command Center
|
|
72
|
+
|
|
73
|
+
<ruleset name="StaffManifesto">
|
|
74
|
+
|
|
75
|
+
## The 4 Laws of the SDG Constitution
|
|
76
|
+
|
|
77
|
+
### 1. The Law of Hardening (Security-First)
|
|
78
|
+
> <rule name="LawOfHardening">
|
|
79
|
+
> Total configuration isolation. Zero runtime surprises. Fail fast if the environment is incomplete. Default to deny at every boundary.
|
|
80
|
+
> </rule>
|
|
81
|
+
|
|
82
|
+
### 2. The Law of Resilience (Stability)
|
|
83
|
+
> <rule name="LawOfResilience">
|
|
84
|
+
> Defensive dominance. Software must withstand both failure and repetition. Idempotency and graceful degradation are non-negotiable.
|
|
85
|
+
> </rule>
|
|
86
|
+
|
|
87
|
+
### 3. The Law of the Cascade (Narrative)
|
|
88
|
+
> <rule name="NarrativeCascade">
|
|
89
|
+
> [!IMPORTANT]
|
|
90
|
+
> **Code should be like a short story, a complete and meaningful narrative.**
|
|
91
|
+
>
|
|
92
|
+
> **The Principles:**
|
|
93
|
+
> - **Stepdown Rule**: Callers sitting at the top. The file reads top-down from headline to details.
|
|
94
|
+
> - **Rich Object Flow**: Peer elements receive the same rich object, maintaining consistent contracts.
|
|
95
|
+
> - **Explaining Returns**: The return reflects the final task or a named result. Avoid large anonymous objects.
|
|
96
|
+
> - **SLA (Single Level of Abstraction)**: Orchestrate or implement — never both in the same body.
|
|
97
|
+
> - **Shallow Boundaries**: Destructure Level 1/2. Stop deep navigation dead in its tracks.
|
|
98
|
+
> - **Vertical Density**: Visual grouping of related variables/logic with single blank lines (para-logical grouping).
|
|
99
|
+
> - **Revealing Module Pattern**: Define functions/logic first, create a named object at the end, then export only that object.
|
|
100
|
+
> - **Lexical Scoping**: One-off helpers must be encapsulated inside their parent's scope.
|
|
101
|
+
> - **Humanized Writing**: Apply \`.ai/instructions/core/writing-soul.md\` to all documentation, UI text, and communication. Eliminate "AI-isms" and promotional slop to maintain a pulse in every technical artifact.
|
|
102
|
+
>
|
|
103
|
+
> *Comments explain "why", never "what". If naming is right, comments disappear.*
|
|
104
|
+
> </rule>
|
|
105
|
+
|
|
106
|
+
### 4. The Law of Visual Excellence (Aesthetics)
|
|
107
|
+
> <rule name="LawOfVisualExcellence">
|
|
108
|
+
> Premium aesthetics by default. High contrast, modern typography, and meaningful micro-interactions. Maintain the chosen design language with absolute rigor.
|
|
109
|
+
> </rule>
|
|
110
|
+
|
|
111
|
+
</ruleset>`;
|
|
112
|
+
|
|
113
|
+
return manifestoString;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function buildInstructionLinks(currentSelections) {
|
|
117
|
+
const isMultiAgentLinks = currentSelections.ide === 'claude' || currentSelections.ide === 'all';
|
|
118
|
+
|
|
119
|
+
const blocks = [
|
|
120
|
+
buildContextRoutingHeader(),
|
|
121
|
+
buildProjectContextRouting(),
|
|
122
|
+
buildCoreGovernanceRouting(isMultiAgentLinks),
|
|
123
|
+
buildArchitecturalContextRouting(currentSelections.flavor),
|
|
124
|
+
buildTechnicalExecutionRouting(currentSelections.idioms),
|
|
125
|
+
buildUIUXDesignRouting(currentSelections),
|
|
126
|
+
buildWorkingCyclesRouting(),
|
|
127
|
+
];
|
|
128
|
+
|
|
129
|
+
const finalContextRouting = blocks.filter((block) => block !== null).join('\n\n');
|
|
130
|
+
|
|
131
|
+
return finalContextRouting;
|
|
132
|
+
|
|
133
|
+
function buildProjectContextRouting() {
|
|
134
|
+
const routingString = dedent`
|
|
135
|
+
**Project Context**
|
|
136
|
+
|
|
137
|
+
| File | Purpose |
|
|
138
|
+
| :--- | :------ |
|
|
139
|
+
| \`.ai-backlog/context.md\` | Project Brief — read before anything else |
|
|
140
|
+
| \`.ai-backlog/tasks.md\` | Active tasks and handoff state |`;
|
|
141
|
+
|
|
142
|
+
return routingString;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function buildContextRoutingHeader() {
|
|
146
|
+
const headerString = dedent`
|
|
147
|
+
## Project Command Center
|
|
148
|
+
|
|
149
|
+
> [!IMPORTANT]
|
|
150
|
+
> Load only what the current task requires. Start with Project Context, then add stack-specific files as needed. Read these before executing complex tasks — do not assume project structure.`;
|
|
151
|
+
|
|
152
|
+
return headerString;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function buildCoreGovernanceRouting(includeAgentRoles) {
|
|
156
|
+
const agentRolesRow = includeAgentRoles
|
|
157
|
+
? `\n| \`.ai/instructions/core/agent-roles.md\` | Multi-Agent Roles & Handoff Protocol |`
|
|
158
|
+
: '';
|
|
159
|
+
|
|
160
|
+
const governanceString = dedent`
|
|
161
|
+
**Universal Governance**
|
|
162
|
+
|
|
163
|
+
| File | Purpose |
|
|
164
|
+
| :--- | :------ |
|
|
165
|
+
| \`.ai/instructions/core/staff-dna.md\` | Staff DNA / Core Principles |
|
|
166
|
+
| \`.ai/instructions/core/security.md\` | Security Strategy |
|
|
167
|
+
| \`.ai/instructions/core/security-pipeline.md\` | Security Pipeline |
|
|
168
|
+
| \`.ai/instructions/core/engineering-standards.md\` | Engineering Standards |
|
|
169
|
+
| \`.ai/instructions/core/naming.md\` | Naming Discipline |
|
|
170
|
+
| \`.ai/instructions/core/code-style.md\` | Code Style & Scannability |
|
|
171
|
+
| \`.ai/instructions/core/writing-soul.md\` | Writing Soul (Docs & UI) |
|
|
172
|
+
| \`.ai/instructions/core/testing-principles.md\` | Testing Principles |
|
|
173
|
+
| \`.ai/instructions/core/observability.md\` | Observability |${agentRolesRow}`;
|
|
174
|
+
|
|
175
|
+
return governanceString;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function buildArchitecturalContextRouting(flavor) {
|
|
179
|
+
const architectureString = dedent`
|
|
180
|
+
**Architectural Context**
|
|
181
|
+
|
|
182
|
+
| File | Purpose |
|
|
183
|
+
| :--- | :------ |
|
|
184
|
+
| \`.ai/instructions/flavor/principles.md\` | Flavor: ${displayName(flavor)} |`;
|
|
185
|
+
|
|
186
|
+
return architectureString;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function buildTechnicalExecutionRouting(idioms) {
|
|
190
|
+
const { hasBackend, hasFrontend } = computeStackMetrics(idioms);
|
|
191
|
+
|
|
192
|
+
const idiomRows = idioms.map((idiomId) => {
|
|
193
|
+
const label = STACK_DISPLAY_NAMES[idiomId]?.name ?? idiomId;
|
|
194
|
+
const tableRow = `| \`.ai/instructions/idioms/${idiomId}/patterns.md\` | ${label} Idioms & Patterns |`;
|
|
195
|
+
return tableRow;
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
const backendRows = hasBackend
|
|
199
|
+
? [
|
|
200
|
+
`| \`.ai/instructions/competencies/backend.md\` | BFF + API Strategy |`,
|
|
201
|
+
`| \`.ai/instructions/core/data-access.md\` | Data Access |`,
|
|
202
|
+
`| \`.ai/instructions/core/sql-style.md\` | SQL Style |`,
|
|
203
|
+
`| \`.ai/instructions/core/api-design.md\` | API Design |`,
|
|
204
|
+
`| \`.ai/instructions/core/ci-cd.md\` | CI/CD |`,
|
|
205
|
+
`| \`.ai/instructions/core/cloud.md\` | Cloud & Containers |`,
|
|
206
|
+
]
|
|
207
|
+
: [];
|
|
208
|
+
|
|
209
|
+
const frontendRows = hasFrontend
|
|
210
|
+
? [`| \`.ai/instructions/competencies/frontend.md\` | Contract-Based UI System |`]
|
|
211
|
+
: [];
|
|
212
|
+
|
|
213
|
+
const allRows = [...idiomRows, ...backendRows, ...frontendRows].join('\n');
|
|
214
|
+
|
|
215
|
+
const technicalRoutingBlock = [
|
|
216
|
+
`**Technical Execution**`,
|
|
217
|
+
``,
|
|
218
|
+
`| File | Purpose |`,
|
|
219
|
+
`| :--- | :------ |`,
|
|
220
|
+
allRows,
|
|
221
|
+
].join('\n');
|
|
222
|
+
|
|
223
|
+
return technicalRoutingBlock;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function buildUIUXDesignRouting(selectionsObj) {
|
|
227
|
+
const { idioms } = selectionsObj;
|
|
228
|
+
const designPreset = selectionsObj.designPreset ?? 'UNIVERSAL';
|
|
229
|
+
const { hasFrontend } = computeStackMetrics(idioms);
|
|
230
|
+
|
|
231
|
+
if (!hasFrontend) return null;
|
|
232
|
+
|
|
233
|
+
const designLabel = designPreset.toUpperCase();
|
|
234
|
+
|
|
235
|
+
const uiuxRoutingBlock = dedent`
|
|
236
|
+
**UI/UX Design System (Target: ${designLabel})**
|
|
237
|
+
|
|
238
|
+
| File | Purpose |
|
|
239
|
+
| :--- | :------ |
|
|
240
|
+
| \`.ai/instructions/core/ui/standards.md\` | Visual Standards |
|
|
241
|
+
| \`.ai/instructions/core/ui/architecture.md\` | Component Architecture |
|
|
242
|
+
| \`.ai/instructions/core/ui/presets.md\` | Interface Presets |
|
|
243
|
+
| \`.ai/instructions/core/ui/design-thinking.md\` | Visual Contracts (Phase 0) |`;
|
|
244
|
+
|
|
245
|
+
return uiuxRoutingBlock;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function buildWorkingCyclesRouting() {
|
|
249
|
+
const workingCyclesString = dedent`
|
|
250
|
+
**Working Cycles**
|
|
251
|
+
|
|
252
|
+
| File | Purpose |
|
|
253
|
+
| :--- | :------ |
|
|
254
|
+
| \`.ai/commands/sdg-land.md\` | Land Cycle (Project Inception & Backlog) |
|
|
255
|
+
| \`.ai/commands/sdg-feat.md\` | Feature Cycle (Specs & Implementation) |
|
|
256
|
+
| \`.ai/commands/sdg-fix.md\` | Fix Cycle (Forensics & Regression) |
|
|
257
|
+
| \`.ai/commands/sdg-docs.md\` | Documentation Cycle (ADRs & Logs) |`;
|
|
258
|
+
|
|
259
|
+
return workingCyclesString;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Builds the stub content for projects initialized in Prompts Only mode.
|
|
266
|
+
*/
|
|
267
|
+
function buildPromptModeStub() {
|
|
268
|
+
const stubString = dedent`
|
|
269
|
+
# Project Specification Prompts
|
|
270
|
+
|
|
271
|
+
> [!NOTE]
|
|
272
|
+
> This project has been initialized with SDG-Agents in **Prompts Only** mode.
|
|
273
|
+
> The AI Agents governance rules (Staff-level engineering instructions) were NOT injected into the workspace.
|
|
274
|
+
>
|
|
275
|
+
> If you are an AI Agent operating in this project, you will only have access to the specification templates located at \`.ai/prompts/\`.
|
|
276
|
+
>
|
|
277
|
+
> **To Developers:**
|
|
278
|
+
> If you want to augment this repository with full AI Governance instructions, run the following command in the terminal:
|
|
279
|
+
> \`npx sdg-agents init\``;
|
|
280
|
+
|
|
281
|
+
return stubString;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Writes .ai-backlog/context.md and .ai-backlog/tasks.md at the project root.
|
|
286
|
+
* Only writes each file if it does not already exist — never overwrites user content.
|
|
287
|
+
*/
|
|
288
|
+
function writeBacklogFiles(targetDir, selections) {
|
|
289
|
+
const backlogDir = path.join(targetDir, '.ai-backlog');
|
|
290
|
+
if (!fs.existsSync(backlogDir)) fs.mkdirSync(backlogDir, { recursive: true });
|
|
291
|
+
|
|
292
|
+
writeContextFile(backlogDir, targetDir, selections);
|
|
293
|
+
writeTasksFile(backlogDir);
|
|
294
|
+
|
|
295
|
+
function writeContextFile(backlogDirPath, projectDir, currentSelections) {
|
|
296
|
+
const contextPath = path.join(backlogDirPath, 'context.md');
|
|
297
|
+
if (fs.existsSync(contextPath)) return;
|
|
298
|
+
|
|
299
|
+
const stackLine = (currentSelections.idioms ?? [])
|
|
300
|
+
.map((id) => STACK_DISPLAY_NAMES[id]?.name ?? id)
|
|
301
|
+
.join(', ');
|
|
302
|
+
|
|
303
|
+
const contextContent = dedent`
|
|
304
|
+
# ${path.basename(projectDir)} — [what this project does in one sentence]
|
|
305
|
+
|
|
306
|
+
stack: ${stackLine}
|
|
307
|
+
pattern: [architecture pattern]
|
|
308
|
+
entry: [main entry point file]
|
|
309
|
+
|
|
310
|
+
## Decisions
|
|
311
|
+
- [decision]: [rationale]
|
|
312
|
+
|
|
313
|
+
## Now
|
|
314
|
+
[what is actively being worked on]
|
|
315
|
+
|
|
316
|
+
## Engineering Insights
|
|
317
|
+
- [topic]: [lesson learned or research finding]
|
|
318
|
+
`;
|
|
319
|
+
|
|
320
|
+
fs.writeFileSync(contextPath, contextContent);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function writeTasksFile(backlogDirPath) {
|
|
324
|
+
const tasksPath = path.join(backlogDirPath, 'tasks.md');
|
|
325
|
+
if (fs.existsSync(tasksPath)) return;
|
|
326
|
+
|
|
327
|
+
const tasksContent = dedent`
|
|
328
|
+
# Tasks
|
|
329
|
+
|
|
330
|
+
> Managed by AI agents. Update status after each atomic task.
|
|
331
|
+
> Recovery: if lost, ask the agent to read recent git commits and reconstruct.
|
|
332
|
+
|
|
333
|
+
## Active
|
|
334
|
+
<!-- [IN_PROGRESS] description — context of where it stopped and what comes next -->
|
|
335
|
+
|
|
336
|
+
## Backlog
|
|
337
|
+
<!-- [TODO] description -->
|
|
338
|
+
|
|
339
|
+
## Done
|
|
340
|
+
<!-- [DONE] description -->
|
|
341
|
+
`;
|
|
342
|
+
|
|
343
|
+
fs.writeFileSync(tasksPath, tasksContent);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Builds the Claude Code-specific CLAUDE.md content.
|
|
349
|
+
* Uses @-import syntax so Claude Code loads governance files natively on session start.
|
|
350
|
+
* Distinct from AGENTS.md: references rather than duplicates content.
|
|
351
|
+
*/
|
|
352
|
+
function buildClaudeContent() {
|
|
353
|
+
const claudeContent = dedent`
|
|
354
|
+
# SDG Agents — Claude Code Governance
|
|
355
|
+
|
|
356
|
+
> [!IMPORTANT]
|
|
357
|
+
> This file is read automatically by Claude Code on every session start.
|
|
358
|
+
> Do not edit manually — regenerate with \`npx sdg-agents init --claude\`.
|
|
359
|
+
|
|
360
|
+
## Auto-Load: Governance Context
|
|
361
|
+
|
|
362
|
+
@.ai/skill/AGENTS.md
|
|
363
|
+
|
|
364
|
+
## Session Start Protocol
|
|
365
|
+
|
|
366
|
+
On every new session, execute in order:
|
|
367
|
+
|
|
368
|
+
1. **Check backlog**: Read \`.ai-backlog/context.md\` — understand the project brief. **Priority**: Always check the local directory first to avoid redundant scans.
|
|
369
|
+
2. **Check tasks**: Read \`.ai-backlog/tasks.md\` — resume any \`[IN_PROGRESS]\` task before accepting new work.
|
|
370
|
+
3. **Bootstrap if missing**: If \`.ai-backlog/context.md\` does not exist, follow the **Context Bootstrap** defined in the Working Protocol (loaded via \`@.ai/skill/AGENTS.md\` above).
|
|
371
|
+
|
|
372
|
+
## Intent Routing (quick reference)
|
|
373
|
+
|
|
374
|
+
| Prefix | Action |
|
|
375
|
+
| :----- | :----- |
|
|
376
|
+
| \`land: ...\` | Land Cycle — read \`.ai/commands/sdg-land.md\` |
|
|
377
|
+
| \`feat: ...\` | Feature Cycle — read \`.ai/commands/sdg-feat.md\` |
|
|
378
|
+
| \`fix: ...\` | Fix Cycle — read \`.ai/commands/sdg-fix.md\` |
|
|
379
|
+
| \`docs: ...\` | Docs Cycle — read \`.ai/commands/sdg-docs.md\` |
|
|
380
|
+
`;
|
|
381
|
+
|
|
382
|
+
return claudeContent;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Writes the universal agent config file inside .ai/skill/.
|
|
387
|
+
* A single AGENTS.md serves as the entry point for all AI Agents —
|
|
388
|
+
* it references only the rules relevant to the project's stack.
|
|
389
|
+
* If agents/ides are selected, it will also dump the rules to the native target.
|
|
390
|
+
*/
|
|
391
|
+
function writeAgentConfig(targetDir, content, requestedAgents = []) {
|
|
392
|
+
// Always create the generic fallback AGENTS.md
|
|
393
|
+
const skillDir = path.join(targetDir, '.ai', 'skill');
|
|
394
|
+
if (!fs.existsSync(skillDir)) fs.mkdirSync(skillDir, { recursive: true });
|
|
395
|
+
fs.writeFileSync(path.join(skillDir, 'AGENTS.md'), content);
|
|
396
|
+
|
|
397
|
+
if (!requestedAgents || requestedAgents.length === 0) return;
|
|
398
|
+
|
|
399
|
+
const ideTargets = {
|
|
400
|
+
cursor: { dir: '.cursor/rules', file: 'sdg-agents.mdc' },
|
|
401
|
+
windsurf: { dir: '.', file: '.windsurfrules' },
|
|
402
|
+
vscode: { dir: '.github', file: 'copilot-instructions.md' },
|
|
403
|
+
copilot: { dir: '.github', file: 'copilot-instructions.md' },
|
|
404
|
+
claude: { dir: '.', file: 'CLAUDE.md' },
|
|
405
|
+
roocode: { dir: '.', file: '.clinerules' },
|
|
406
|
+
gemini: { dir: '.', file: 'AI_INSTRUCTIONS.md' },
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
const expandedAgents = requestedAgents.includes('all')
|
|
410
|
+
? Object.keys(ideTargets)
|
|
411
|
+
: requestedAgents;
|
|
412
|
+
|
|
413
|
+
for (const agent of expandedAgents) {
|
|
414
|
+
if (agent === 'none' || agent === 'antigravity') continue;
|
|
415
|
+
|
|
416
|
+
const target = ideTargets[agent];
|
|
417
|
+
if (!target) continue;
|
|
418
|
+
|
|
419
|
+
const fullDir = path.join(targetDir, target.dir);
|
|
420
|
+
if (!fs.existsSync(fullDir)) fs.mkdirSync(fullDir, { recursive: true });
|
|
421
|
+
|
|
422
|
+
let finalContent = content;
|
|
423
|
+
if (agent === 'cursor') {
|
|
424
|
+
finalContent = `---\ndescription: Project Governance and Architectural Rules\nglob: *\n---\n\n${content}`;
|
|
425
|
+
} else if (agent === 'claude') {
|
|
426
|
+
finalContent = buildClaudeContent();
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
fs.writeFileSync(path.join(fullDir, target.file), finalContent);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Writes or updates .gitignore to block env files from being committed.
|
|
435
|
+
* Idempotent — only appends entries that are not already present.
|
|
436
|
+
*/
|
|
437
|
+
function writeGitignore(targetDir) {
|
|
438
|
+
const gitignorePath = path.join(targetDir, '.gitignore');
|
|
439
|
+
|
|
440
|
+
const SDG_BLOCK_HEADER = '# Environment files — never commit secrets (managed by SDG Agents)';
|
|
441
|
+
const REQUIRED_ENTRIES = ['.env', '.env.*'];
|
|
442
|
+
|
|
443
|
+
const existingContent = fs.existsSync(gitignorePath)
|
|
444
|
+
? fs.readFileSync(gitignorePath, 'utf8')
|
|
445
|
+
: '';
|
|
446
|
+
|
|
447
|
+
const existingLines = existingContent.split('\n').map((line) => line.trim());
|
|
448
|
+
const missingEntries = REQUIRED_ENTRIES.filter((entry) => !existingLines.includes(entry));
|
|
449
|
+
|
|
450
|
+
if (missingEntries.length === 0) return;
|
|
451
|
+
|
|
452
|
+
const alreadyHasHeader = existingContent.includes(SDG_BLOCK_HEADER);
|
|
453
|
+
const entriesToAppend = alreadyHasHeader ? missingEntries : [SDG_BLOCK_HEADER, ...missingEntries];
|
|
454
|
+
|
|
455
|
+
const separator = existingContent.length > 0 && !existingContent.endsWith('\n') ? '\n' : '';
|
|
456
|
+
const appendBlock = `${separator}\n${entriesToAppend.join('\n')}\n`;
|
|
457
|
+
|
|
458
|
+
fs.appendFileSync(gitignorePath, appendBlock);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Writes the .sdg-manifest.json with content hashes for future diff checks.
|
|
463
|
+
*/
|
|
464
|
+
function writeManifest(targetDir, selections, pkgVersion) {
|
|
465
|
+
const manifest = {
|
|
466
|
+
generatedAt: new Date().toISOString(),
|
|
467
|
+
sdgAgentVersion: pkgVersion,
|
|
468
|
+
selections,
|
|
469
|
+
contentHashes: computeHashes(selections, SOURCE_INSTRUCTIONS),
|
|
470
|
+
};
|
|
471
|
+
|
|
472
|
+
const aiDir = path.join(targetDir, '.ai');
|
|
473
|
+
if (!fs.existsSync(aiDir)) fs.mkdirSync(aiDir, { recursive: true });
|
|
474
|
+
fs.writeFileSync(path.join(aiDir, '.sdg-manifest.json'), JSON.stringify(manifest, null, 2));
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
const InstructionAssembler = {
|
|
478
|
+
buildMasterInstructions,
|
|
479
|
+
buildClaudeContent,
|
|
480
|
+
buildPromptModeStub,
|
|
481
|
+
writeAgentConfig,
|
|
482
|
+
writeBacklogFiles,
|
|
483
|
+
writeGitignore,
|
|
484
|
+
writeManifest,
|
|
485
|
+
};
|
|
486
|
+
|
|
487
|
+
export { InstructionAssembler };
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { FsUtils } from './fs-utils.mjs';
|
|
5
|
+
|
|
6
|
+
const { getDirname } = FsUtils;
|
|
7
|
+
|
|
8
|
+
const __dirname = getDirname(import.meta.url);
|
|
9
|
+
const INSTRUCTIONS_DIR = path.join(__dirname, '..', '..', 'assets', 'instructions');
|
|
10
|
+
|
|
11
|
+
function hashFile(filePath) {
|
|
12
|
+
if (!fs.existsSync(filePath)) return null;
|
|
13
|
+
const content = fs.readFileSync(filePath);
|
|
14
|
+
return crypto.createHash('sha256').update(content).digest('hex');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function computeHashes(selections, instructionsDir = INSTRUCTIONS_DIR) {
|
|
18
|
+
const { flavor, idioms } = selections;
|
|
19
|
+
const hashes = {};
|
|
20
|
+
|
|
21
|
+
const coreDir = path.join(instructionsDir, 'core');
|
|
22
|
+
if (fs.existsSync(coreDir)) {
|
|
23
|
+
scanDir(coreDir, 'core', hashes);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (flavor) {
|
|
27
|
+
const flavorDir = path.join(instructionsDir, 'flavors', flavor);
|
|
28
|
+
if (fs.existsSync(flavorDir)) {
|
|
29
|
+
scanDir(flavorDir, 'flavor', hashes);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (idioms && Array.isArray(idioms)) {
|
|
34
|
+
for (const idiom of idioms) {
|
|
35
|
+
const idiomDir = path.join(instructionsDir, 'idioms', idiom);
|
|
36
|
+
if (fs.existsSync(idiomDir)) {
|
|
37
|
+
scanDir(idiomDir, `idioms/${idiom}`, hashes);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const templatesDir = path.join(instructionsDir, 'templates');
|
|
43
|
+
if (fs.existsSync(templatesDir)) {
|
|
44
|
+
scanDir(templatesDir, 'templates', hashes);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const competenciesDir = path.join(instructionsDir, 'competencies');
|
|
48
|
+
if (fs.existsSync(competenciesDir)) {
|
|
49
|
+
scanDir(competenciesDir, 'competencies', hashes);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const workflowsDir = path.join(instructionsDir, 'workflows');
|
|
53
|
+
if (fs.existsSync(workflowsDir)) {
|
|
54
|
+
scanDir(workflowsDir, 'workflows', hashes);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const commandsDir = path.join(instructionsDir, 'commands');
|
|
58
|
+
if (fs.existsSync(commandsDir)) {
|
|
59
|
+
scanDir(commandsDir, 'commands', hashes);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return hashes;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function scanDir(dir, relPrefix, hashes) {
|
|
66
|
+
if (!fs.existsSync(dir)) return;
|
|
67
|
+
|
|
68
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
69
|
+
for (const entry of entries) {
|
|
70
|
+
const fullPath = path.join(dir, entry.name);
|
|
71
|
+
const relPath = path.join(relPrefix, entry.name);
|
|
72
|
+
|
|
73
|
+
if (entry.isDirectory()) {
|
|
74
|
+
scanDir(fullPath, relPath, hashes);
|
|
75
|
+
} else if (entry.isFile() && (entry.name.endsWith('.md') || entry.name.endsWith('.mjs'))) {
|
|
76
|
+
hashes[relPath] = hashFile(fullPath);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function compareHashes(stored, current) {
|
|
82
|
+
const changed = [];
|
|
83
|
+
const unchanged = [];
|
|
84
|
+
const added = [];
|
|
85
|
+
|
|
86
|
+
for (const [relPath, currentHash] of Object.entries(current)) {
|
|
87
|
+
if (!(relPath in stored)) {
|
|
88
|
+
added.push(relPath);
|
|
89
|
+
} else if (stored[relPath] !== currentHash) {
|
|
90
|
+
changed.push(relPath);
|
|
91
|
+
} else {
|
|
92
|
+
unchanged.push(relPath);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return { changed, unchanged, added };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function daysAgo(isoDate) {
|
|
100
|
+
const ms = Date.now() - new Date(isoDate).getTime();
|
|
101
|
+
const days = Math.floor(ms / (1000 * 60 * 60 * 24));
|
|
102
|
+
if (days < 0) return 'just now';
|
|
103
|
+
if (days === 0) return 'today';
|
|
104
|
+
if (days === 1) return '1 day ago';
|
|
105
|
+
return `${days} days ago`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function loadManifest(projectRoot) {
|
|
109
|
+
const manifestPath = path.join(projectRoot, '.ai', '.sdg-manifest.json');
|
|
110
|
+
|
|
111
|
+
if (!fs.existsSync(manifestPath)) return null;
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
return JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
115
|
+
} catch {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const ManifestUtils = {
|
|
121
|
+
hashFile,
|
|
122
|
+
computeHashes,
|
|
123
|
+
compareHashes,
|
|
124
|
+
daysAgo,
|
|
125
|
+
loadManifest,
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
export { ManifestUtils };
|