great-cto 2.22.3 → 2.24.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/dist/adapt.js +225 -9
- package/dist/agentshield/index.js +1 -1
- package/dist/agentshield/rules-loader.js +38 -0
- package/dist/bootstrap.js +86 -0
- package/package.json +1 -1
package/dist/adapt.js
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
1
|
-
// great-cto adapt —
|
|
1
|
+
// great-cto adapt — AI config generator.
|
|
2
2
|
//
|
|
3
3
|
// Writes CLAUDE.md + AGENTS.md from .great_cto/PROJECT.md context.
|
|
4
|
+
// Also generates cross-AI configs for Cursor, Copilot, Windsurf, Aider
|
|
5
|
+
// when ai_tools is set in PROJECT.md.
|
|
4
6
|
//
|
|
5
7
|
// Usage:
|
|
6
|
-
// great-cto adapt Generate
|
|
8
|
+
// great-cto adapt Generate all configs (reads ai_tools from PROJECT.md)
|
|
7
9
|
// great-cto adapt --dry-run Show what would be written
|
|
8
10
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
9
11
|
import { dirname, join } from "node:path";
|
|
12
|
+
const VALID_AI_TOOLS = ["claude-code", "cursor", "copilot", "windsurf", "aider"];
|
|
13
|
+
function parseAiTools(text) {
|
|
14
|
+
const line = text.match(/^ai_tools:\s*(.+)$/m)?.[1] ?? "";
|
|
15
|
+
// Support: [claude-code, cursor] or claude-code, cursor or claude-code
|
|
16
|
+
const raw = line.replace(/[\[\]]/g, "").split(/[,\s]+/).map(s => s.trim()).filter(Boolean);
|
|
17
|
+
return raw.filter((t) => VALID_AI_TOOLS.includes(t));
|
|
18
|
+
}
|
|
10
19
|
function readProjectMeta(cwd) {
|
|
11
20
|
const projectMd = join(cwd, ".great_cto", "PROJECT.md");
|
|
12
21
|
if (!existsSync(projectMd)) {
|
|
13
|
-
return { archetype: "unknown", compliance: [], owners: "", hasGreatCto: false };
|
|
22
|
+
return { archetype: "unknown", compliance: [], owners: "", hasGreatCto: false, aiTools: ["claude-code"], projectName: "project" };
|
|
14
23
|
}
|
|
15
24
|
const text = readFileSync(projectMd, "utf8");
|
|
16
25
|
const archetype = (text.match(/^primary:\s*(\S+)/m)?.[1] ?? "unknown").trim();
|
|
@@ -20,7 +29,63 @@ function readProjectMeta(cwd) {
|
|
|
20
29
|
.map(s => s.trim())
|
|
21
30
|
.filter(Boolean);
|
|
22
31
|
const owners = (text.match(/^owners?:\s*(.+)$/m)?.[1] ?? "").trim();
|
|
23
|
-
|
|
32
|
+
const aiTools = parseAiTools(text);
|
|
33
|
+
// Infer project name from first H1 heading
|
|
34
|
+
const projectName = (text.match(/^#\s+(.+)$/m)?.[1] ?? "project").trim();
|
|
35
|
+
return { archetype, compliance, owners, hasGreatCto: true, aiTools: aiTools.length > 0 ? aiTools : ["claude-code"], projectName };
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Project Constitution — universal hard constraints injected into every AI config.
|
|
39
|
+
* Inspired by Spec Driven Development (FredAntB/Spec-Driven-Development).
|
|
40
|
+
*/
|
|
41
|
+
function getConstitutionBlock(meta) {
|
|
42
|
+
const hasSpec = existsSync(join(process.cwd(), "requirements.md"));
|
|
43
|
+
const specSection = hasSpec ? `
|
|
44
|
+
## Spec files (source of truth)
|
|
45
|
+
|
|
46
|
+
requirements.md — What the system must do
|
|
47
|
+
design.md — How the system is structured
|
|
48
|
+
tasks.md — Ordered implementation plan
|
|
49
|
+
|
|
50
|
+
MANDATORY BEFORE ANY ACTION:
|
|
51
|
+
0. If CONTEXT.md exists, read it first — it has session state
|
|
52
|
+
1. Read requirements.md — understand what is in scope
|
|
53
|
+
2. Read design.md — understand how it is structured
|
|
54
|
+
3. Read tasks.md — find the next incomplete [ ] task
|
|
55
|
+
` : `
|
|
56
|
+
## Session context
|
|
57
|
+
|
|
58
|
+
0. If CONTEXT.md exists, read it first — it has the active task and session state
|
|
59
|
+
1. Read .great_cto/PROJECT.md — archetype, compliance gates, team mode
|
|
60
|
+
2. Read .great_cto/lessons.md if present — past mistakes and patterns
|
|
61
|
+
`;
|
|
62
|
+
return `
|
|
63
|
+
═══════════════════════════════════════════════════════════
|
|
64
|
+
PROJECT CONSTITUTION — ${meta.projectName.toUpperCase()}
|
|
65
|
+
Generated by great-cto adapt · ${new Date().toISOString().slice(0, 10)}
|
|
66
|
+
═══════════════════════════════════════════════════════════
|
|
67
|
+
${specSection}
|
|
68
|
+
HARD CONSTRAINTS (no exceptions):
|
|
69
|
+
✗ Never implement requirements not explicitly defined in requirements.md
|
|
70
|
+
✗ Never alter the data model without updating design.md first
|
|
71
|
+
✗ Never mark a task [x] without verifying its acceptance criterion
|
|
72
|
+
✗ Never create files not listed or implied in design.md
|
|
73
|
+
✗ Never guess when a requirement is ambiguous — ask instead
|
|
74
|
+
✗ Never skip gates (security-officer, qa-engineer) — use /waiver with reason
|
|
75
|
+
|
|
76
|
+
AFTER COMPLETING A TASK:
|
|
77
|
+
1. Run the verification step listed in tasks.md (or acceptance criterion)
|
|
78
|
+
2. Mark the task [x] in tasks.md
|
|
79
|
+
3. Update CONTEXT.md resume block with the next active task
|
|
80
|
+
4. Record any divergences from design.md in CONTEXT.md
|
|
81
|
+
|
|
82
|
+
IF IMPLEMENTATION MUST DEVIATE FROM DESIGN:
|
|
83
|
+
1. Stop immediately
|
|
84
|
+
2. Describe the conflict clearly
|
|
85
|
+
3. Wait for explicit approval
|
|
86
|
+
4. Update design.md FIRST, then implement
|
|
87
|
+
═══════════════════════════════════════════════════════════
|
|
88
|
+
`;
|
|
24
89
|
}
|
|
25
90
|
/**
|
|
26
91
|
* AGENTS.md — Claude Code reads this as the cross-tool agent contract.
|
|
@@ -29,7 +94,9 @@ function getAgentsCore(meta) {
|
|
|
29
94
|
const compliance = meta.compliance.length > 0
|
|
30
95
|
? meta.compliance.map(c => `- ${c}`).join("\n")
|
|
31
96
|
: "_(none auto-detected — set in .great_cto/PROJECT.md)_";
|
|
97
|
+
const constitution = getConstitutionBlock(meta);
|
|
32
98
|
return `# AGENTS.md
|
|
99
|
+
${constitution}
|
|
33
100
|
|
|
34
101
|
> Project agent contract for Claude Code. Generated by \`great-cto adapt\` —
|
|
35
102
|
> re-run after editing \`.great_cto/PROJECT.md\`.
|
|
@@ -133,6 +200,117 @@ gates "because it's just a small change" — that's the path to incidents.
|
|
|
133
200
|
_Generated by \`great-cto@${getCliVersion()}\` adapt at ${new Date().toISOString().slice(0, 10)}_
|
|
134
201
|
`;
|
|
135
202
|
}
|
|
203
|
+
// ── Cross-AI config generators ─────────────────────────────────────────────
|
|
204
|
+
function getCursorrules(meta) {
|
|
205
|
+
const constitution = getConstitutionBlock(meta);
|
|
206
|
+
return `${constitution}
|
|
207
|
+
|
|
208
|
+
# .cursorrules — Cursor AI configuration for ${meta.projectName}
|
|
209
|
+
# Generated by great-cto adapt · ${new Date().toISOString().slice(0, 10)}
|
|
210
|
+
|
|
211
|
+
## Project context
|
|
212
|
+
|
|
213
|
+
- **Archetype:** ${meta.archetype}
|
|
214
|
+
- **Compliance:** ${meta.compliance.join(", ") || "none"}
|
|
215
|
+
- **Stack:** See .great_cto/PROJECT.md
|
|
216
|
+
|
|
217
|
+
## Coding conventions
|
|
218
|
+
|
|
219
|
+
- TypeScript strict mode — no \`any\` without explanatory comment
|
|
220
|
+
- ESM modules only (.mjs / "type":"module")
|
|
221
|
+
- Conventional commits: feat / fix / docs / refactor / test / chore
|
|
222
|
+
- Tests before implementation (RED → GREEN → REFACTOR), 80%+ coverage
|
|
223
|
+
- One concern per PR — refactors and behaviour changes are separate PRs
|
|
224
|
+
|
|
225
|
+
## Agent routing
|
|
226
|
+
|
|
227
|
+
For complex tasks, prefer the great_cto agent pipeline (via Claude Code):
|
|
228
|
+
- Architecture decisions → \`architect\` agent
|
|
229
|
+
- Feature planning → \`pm\` agent
|
|
230
|
+
- Implementation → \`senior-dev\` agent
|
|
231
|
+
- Security review → \`security-officer\` agent
|
|
232
|
+
|
|
233
|
+
See AGENTS.md for the full routing table.
|
|
234
|
+
`;
|
|
235
|
+
}
|
|
236
|
+
function getCopilotInstructions(meta) {
|
|
237
|
+
const constitution = getConstitutionBlock(meta);
|
|
238
|
+
return `${constitution}
|
|
239
|
+
|
|
240
|
+
# GitHub Copilot Instructions for ${meta.projectName}
|
|
241
|
+
# Generated by great-cto adapt · ${new Date().toISOString().slice(0, 10)}
|
|
242
|
+
|
|
243
|
+
## Project context
|
|
244
|
+
|
|
245
|
+
- **Archetype:** ${meta.archetype}
|
|
246
|
+
- **Compliance gates:** ${meta.compliance.join(", ") || "none"}
|
|
247
|
+
- **Read first:** CONTEXT.md (session state), requirements.md (scope)
|
|
248
|
+
|
|
249
|
+
## Code style
|
|
250
|
+
|
|
251
|
+
- TypeScript strict mode, ESM-only
|
|
252
|
+
- Conventional commits
|
|
253
|
+
- Test-first development — write tests before implementation
|
|
254
|
+
- No secrets in code — great-cto scan blocks ~13 patterns
|
|
255
|
+
|
|
256
|
+
## What NOT to do
|
|
257
|
+
|
|
258
|
+
- Do not implement features not in requirements.md
|
|
259
|
+
- Do not alter data models without updating design.md
|
|
260
|
+
- Do not generate placeholder files with TODO tokens
|
|
261
|
+
- Do not guess ambiguous requirements — surface them for human decision
|
|
262
|
+
`;
|
|
263
|
+
}
|
|
264
|
+
function getWindsurfrules(meta) {
|
|
265
|
+
const constitution = getConstitutionBlock(meta);
|
|
266
|
+
return `${constitution}
|
|
267
|
+
|
|
268
|
+
# .windsurfrules — Windsurf AI configuration for ${meta.projectName}
|
|
269
|
+
# Generated by great-cto adapt · ${new Date().toISOString().slice(0, 10)}
|
|
270
|
+
|
|
271
|
+
## Session startup
|
|
272
|
+
|
|
273
|
+
1. Read CONTEXT.md — resume active task from resume block
|
|
274
|
+
2. Read requirements.md — understand scope
|
|
275
|
+
3. Read design.md — understand structure
|
|
276
|
+
4. Find next incomplete [ ] task in tasks.md
|
|
277
|
+
|
|
278
|
+
## Project context
|
|
279
|
+
|
|
280
|
+
- Archetype: ${meta.archetype}
|
|
281
|
+
- Compliance: ${meta.compliance.join(", ") || "none"}
|
|
282
|
+
- Spec files: requirements.md / design.md / tasks.md
|
|
283
|
+
|
|
284
|
+
## Hard rules
|
|
285
|
+
|
|
286
|
+
- Never implement beyond requirements.md scope
|
|
287
|
+
- Never skip the CONTEXT.md update at session end
|
|
288
|
+
- Run verification step before marking any task complete
|
|
289
|
+
`;
|
|
290
|
+
}
|
|
291
|
+
function getAiderConf(meta) {
|
|
292
|
+
return `# .aider.conf.yml — Aider configuration for ${meta.projectName}
|
|
293
|
+
# Generated by great-cto adapt · ${new Date().toISOString().slice(0, 10)}
|
|
294
|
+
|
|
295
|
+
# Read these files as context at startup
|
|
296
|
+
read:
|
|
297
|
+
- CONTEXT.md
|
|
298
|
+
- requirements.md
|
|
299
|
+
- design.md
|
|
300
|
+
- tasks.md
|
|
301
|
+
- .great_cto/PROJECT.md
|
|
302
|
+
|
|
303
|
+
# Commit message format
|
|
304
|
+
commit-prompt: |
|
|
305
|
+
Write a conventional commit message (feat/fix/docs/refactor/test/chore).
|
|
306
|
+
Be specific about what changed and why.
|
|
307
|
+
Never mention private project names — use <private-project> placeholder.
|
|
308
|
+
|
|
309
|
+
# Conventions
|
|
310
|
+
auto-commits: false
|
|
311
|
+
dirty-commits: false
|
|
312
|
+
`;
|
|
313
|
+
}
|
|
136
314
|
function getCliVersion() {
|
|
137
315
|
try {
|
|
138
316
|
const here = dirname(new URL(import.meta.url).pathname);
|
|
@@ -171,8 +349,8 @@ conventions. Generated by \`great-cto adapt\`.
|
|
|
171
349
|
|
|
172
350
|
## Claude Code specifics
|
|
173
351
|
|
|
174
|
-
- The great_cto plugin orchestrates
|
|
175
|
-
through \`/start\`, \`/inbox\`, \`/save\`, etc.
|
|
352
|
+
- The great_cto plugin orchestrates specialist agents — pipeline runs
|
|
353
|
+
through \`/start\`, \`/inbox\`, \`/save\`, \`/spec\`, etc.
|
|
176
354
|
- Memory is layered: \`.great_cto/PROJECT.md\` (L1) → \`lessons.md\` (L3) →
|
|
177
355
|
\`~/.great_cto/decisions.md\` (L4 cross-project ADR log).
|
|
178
356
|
- Run \`great-cto board\` (\`http://localhost:3141\`) for the kanban + metrics
|
|
@@ -180,6 +358,7 @@ conventions. Generated by \`great-cto adapt\`.
|
|
|
180
358
|
|
|
181
359
|
## Quick links
|
|
182
360
|
|
|
361
|
+
- \`/spec\` — interview → requirements.md + design.md + tasks.md
|
|
183
362
|
- \`/start "..."\` — kick off a feature pipeline
|
|
184
363
|
- \`/inbox\` — see what needs your decision
|
|
185
364
|
- \`/agent-review\` — performance scorecard for agents
|
|
@@ -189,6 +368,38 @@ conventions. Generated by \`great-cto adapt\`.
|
|
|
189
368
|
out.push("AGENTS.md");
|
|
190
369
|
if (writeFile(join(cwd, "CLAUDE.md"), claudeBody, dryRun))
|
|
191
370
|
out.push("CLAUDE.md");
|
|
371
|
+
// Cross-AI config files based on ai_tools in PROJECT.md
|
|
372
|
+
for (const tool of meta.aiTools) {
|
|
373
|
+
switch (tool) {
|
|
374
|
+
case "cursor": {
|
|
375
|
+
const path = join(cwd, ".cursorrules");
|
|
376
|
+
if (writeFile(path, getCursorrules(meta), dryRun))
|
|
377
|
+
out.push(".cursorrules");
|
|
378
|
+
break;
|
|
379
|
+
}
|
|
380
|
+
case "copilot": {
|
|
381
|
+
const path = join(cwd, ".github", "copilot-instructions.md");
|
|
382
|
+
if (writeFile(path, getCopilotInstructions(meta), dryRun))
|
|
383
|
+
out.push(".github/copilot-instructions.md");
|
|
384
|
+
break;
|
|
385
|
+
}
|
|
386
|
+
case "windsurf": {
|
|
387
|
+
const path = join(cwd, ".windsurfrules");
|
|
388
|
+
if (writeFile(path, getWindsurfrules(meta), dryRun))
|
|
389
|
+
out.push(".windsurfrules");
|
|
390
|
+
break;
|
|
391
|
+
}
|
|
392
|
+
case "aider": {
|
|
393
|
+
const path = join(cwd, ".aider.conf.yml");
|
|
394
|
+
if (writeFile(path, getAiderConf(meta), dryRun))
|
|
395
|
+
out.push(".aider.conf.yml");
|
|
396
|
+
break;
|
|
397
|
+
}
|
|
398
|
+
case "claude-code":
|
|
399
|
+
// Already handled above (CLAUDE.md + AGENTS.md)
|
|
400
|
+
break;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
192
403
|
return out;
|
|
193
404
|
}
|
|
194
405
|
// ── Main entry ─────────────────────────────────────────────────────────────
|
|
@@ -199,14 +410,19 @@ export async function runAdapt(args) {
|
|
|
199
410
|
console.error("Run `npx great-cto init` first to bootstrap the project.");
|
|
200
411
|
return 1;
|
|
201
412
|
}
|
|
202
|
-
|
|
413
|
+
const toolsList = meta.aiTools.join(", ");
|
|
414
|
+
console.log(`great-cto adapt → ${toolsList}${args.dryRun ? " (dry-run)" : ""}`);
|
|
203
415
|
console.log(` archetype: ${meta.archetype} compliance: ${meta.compliance.join(", ") || "none"}`);
|
|
416
|
+
console.log(` ai_tools: ${toolsList}`);
|
|
204
417
|
console.log("");
|
|
205
418
|
const written = adaptClaude(args.cwd, meta, args.dryRun);
|
|
206
419
|
if (!args.dryRun) {
|
|
207
420
|
console.log("");
|
|
208
|
-
console.log(`✓ generated ${written.length} file(s)
|
|
209
|
-
console.log(` Re-run after editing .great_cto/PROJECT.md to refresh.`);
|
|
421
|
+
console.log(`✓ generated ${written.length} file(s): ${written.join(", ")}`);
|
|
422
|
+
console.log(` Re-run after editing .great_cto/PROJECT.md or adding tools to ai_tools: [] to refresh.`);
|
|
423
|
+
if (!meta.aiTools.includes("cursor") && !meta.aiTools.includes("copilot") && !meta.aiTools.includes("windsurf")) {
|
|
424
|
+
console.log(` Tip: add other AI tools to ai_tools: [] in PROJECT.md to generate .cursorrules, copilot-instructions.md, etc.`);
|
|
425
|
+
}
|
|
210
426
|
}
|
|
211
427
|
return 0;
|
|
212
428
|
}
|
|
@@ -11,5 +11,5 @@
|
|
|
11
11
|
* writeFileSync('agentshield.sarif', JSON.stringify(toSarif(report)));
|
|
12
12
|
*/
|
|
13
13
|
export { scan, scanFile } from './scanner.js';
|
|
14
|
-
export { loadRules, parseRulesFile } from './rules-loader.js';
|
|
14
|
+
export { loadRules, parseRulesFile, loadUserRules, userGuardrailsPath } from './rules-loader.js';
|
|
15
15
|
export { SEVERITY_ORDER, severityRank } from './types.js';
|
|
@@ -5,9 +5,14 @@
|
|
|
5
5
|
* each rule file is a list of dash-prefixed entries with key/value lines.
|
|
6
6
|
* If we ever need real YAML (anchors, complex nesting), we'll add `yaml` as
|
|
7
7
|
* a dep then.
|
|
8
|
+
*
|
|
9
|
+
* User-defined rules are loaded from ~/.great_cto/guardrails.yml and merged
|
|
10
|
+
* with the built-in rules. User rules use the same YAML format but include
|
|
11
|
+
* an optional `action: block | audit | redact` field.
|
|
8
12
|
*/
|
|
9
13
|
import { readdirSync, readFileSync, existsSync } from 'node:fs';
|
|
10
14
|
import { join, dirname } from 'node:path';
|
|
15
|
+
import { homedir } from 'node:os';
|
|
11
16
|
import { fileURLToPath } from 'node:url';
|
|
12
17
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
13
18
|
/**
|
|
@@ -40,8 +45,39 @@ export function loadRules(rulesDir = defaultRulesDir()) {
|
|
|
40
45
|
const text = readFileSync(join(rulesDir, f), 'utf8');
|
|
41
46
|
rules.push(...parseRulesFile(text, f));
|
|
42
47
|
}
|
|
48
|
+
// Merge user-defined rules from ~/.great_cto/guardrails.yml
|
|
49
|
+
const userRules = loadUserRules();
|
|
50
|
+
rules.push(...userRules);
|
|
43
51
|
return rules;
|
|
44
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Default path for user-defined guardrail rules.
|
|
55
|
+
* Falls back to legacy .great_cto/guardrails.yml in the current project.
|
|
56
|
+
*/
|
|
57
|
+
export function userGuardrailsPath() {
|
|
58
|
+
return join(homedir(), '.great_cto', 'guardrails.yml');
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Load user-defined rules from ~/.great_cto/guardrails.yml.
|
|
62
|
+
* Returns [] silently if the file does not exist.
|
|
63
|
+
* Errors in user rules are surfaced as warnings (console.warn) but do not
|
|
64
|
+
* abort the scan — broken user rules should not block CI.
|
|
65
|
+
*/
|
|
66
|
+
export function loadUserRules(path) {
|
|
67
|
+
const guardrailsPath = path ?? userGuardrailsPath();
|
|
68
|
+
if (!existsSync(guardrailsPath))
|
|
69
|
+
return [];
|
|
70
|
+
try {
|
|
71
|
+
const text = readFileSync(guardrailsPath, 'utf8');
|
|
72
|
+
const parsed = parseRulesFile(text, guardrailsPath);
|
|
73
|
+
// Mark all user rules as userDefined so scanners can handle action correctly
|
|
74
|
+
return parsed.map(r => ({ ...r, userDefined: true }));
|
|
75
|
+
}
|
|
76
|
+
catch (e) {
|
|
77
|
+
console.warn(`agentshield: warning — failed to load user guardrails from ${guardrailsPath}: ${e.message}`);
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
45
81
|
/**
|
|
46
82
|
* Parse a minimal YAML format:
|
|
47
83
|
*
|
|
@@ -160,6 +196,7 @@ function parseBlock(block, filename) {
|
|
|
160
196
|
throw new Error(`missing required field "${required}" in rule (block from ${filename})\nparsed: ${JSON.stringify(out)}`);
|
|
161
197
|
}
|
|
162
198
|
}
|
|
199
|
+
const action = out.action;
|
|
163
200
|
return {
|
|
164
201
|
id: out.id,
|
|
165
202
|
scanner: out.scanner,
|
|
@@ -171,5 +208,6 @@ function parseBlock(block, filename) {
|
|
|
171
208
|
patterns: out.patterns,
|
|
172
209
|
file_globs: out.file_globs,
|
|
173
210
|
negate: out.negate,
|
|
211
|
+
action: (action === 'block' || action === 'audit' || action === 'redact') ? action : undefined,
|
|
174
212
|
};
|
|
175
213
|
}
|
package/dist/bootstrap.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Safe: will NOT overwrite an existing PROJECT.md.
|
|
3
3
|
import { existsSync, mkdirSync, writeFileSync, readFileSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
+
import { homedir } from "node:os";
|
|
5
6
|
import { dim, success, warn } from "./ui.js";
|
|
6
7
|
import { suggestJurisdictions } from "./jurisdictions.js";
|
|
7
8
|
import { compileFlow, renderFlowMd } from "./flow.js";
|
|
@@ -22,6 +23,38 @@ export function bootstrap(dir, detection, archetype, compliance, detectionMeta)
|
|
|
22
23
|
: "unknown";
|
|
23
24
|
const teamSize = 1; // MVP default — user edits later
|
|
24
25
|
const approvalLevel = "gates-only"; // default per README
|
|
26
|
+
// Write CONTEXT.md in the project root (resume-block for all AI agents).
|
|
27
|
+
const contextMdPath = join(dir, "CONTEXT.md");
|
|
28
|
+
if (!existsSync(contextMdPath)) {
|
|
29
|
+
const contextContent = `# CONTEXT.md
|
|
30
|
+
|
|
31
|
+
> AI session state for all agents. Updated at the end of each session.
|
|
32
|
+
> All AI coding tools (Claude Code, Cursor, Copilot, Windsurf, Aider) read this
|
|
33
|
+
> before starting work.
|
|
34
|
+
|
|
35
|
+
## Resume block
|
|
36
|
+
|
|
37
|
+
**Current task:** _(none — project just initialized)_
|
|
38
|
+
**Last session:** ${new Date().toISOString().slice(0, 10)}
|
|
39
|
+
**Status:** 🟢 ready to start
|
|
40
|
+
|
|
41
|
+
## Session log
|
|
42
|
+
|
|
43
|
+
| Session | Date | Summary | Files changed |
|
|
44
|
+
|---------|------|---------|---------------|
|
|
45
|
+
| 1 | ${new Date().toISOString().slice(0, 10)} | Project initialized | CONTEXT.md, .great_cto/PROJECT.md |
|
|
46
|
+
|
|
47
|
+
## Open questions
|
|
48
|
+
|
|
49
|
+
_(none yet — add questions that need founder/team input before implementation can proceed)_
|
|
50
|
+
|
|
51
|
+
## Divergences from spec
|
|
52
|
+
|
|
53
|
+
_(none — record any deviations from design.md here with rationale)_
|
|
54
|
+
`;
|
|
55
|
+
writeFileSync(contextMdPath, contextContent, "utf-8");
|
|
56
|
+
success(`created CONTEXT.md ${dim("(session resume block for all AI tools)")}`);
|
|
57
|
+
}
|
|
25
58
|
const content = `# ${title}
|
|
26
59
|
|
|
27
60
|
> Auto-generated by \`great-cto init\` on ${new Date().toISOString().slice(0, 10)}.
|
|
@@ -55,8 +88,12 @@ phase: implementation
|
|
|
55
88
|
team-size: ${teamSize}
|
|
56
89
|
mode: solo
|
|
57
90
|
approval-level: ${approvalLevel}
|
|
91
|
+
ai_tools: [claude-code]
|
|
58
92
|
|
|
59
93
|
> \`team-size:\` is read at root level (not nested) by /rfc and other commands.
|
|
94
|
+
> \`ai_tools:\` controls which config files \`great-cto adapt\` generates.
|
|
95
|
+
> Supported values: claude-code, cursor, copilot, windsurf, aider
|
|
96
|
+
> Example: ai_tools: [claude-code, cursor, copilot]
|
|
60
97
|
|
|
61
98
|
## Compliance
|
|
62
99
|
|
|
@@ -109,6 +146,55 @@ when you actually start work:
|
|
|
109
146
|
`;
|
|
110
147
|
writeFileSync(projectMd, content, "utf-8");
|
|
111
148
|
success(`created .great_cto/PROJECT.md ${dim(`(archetype: ${archetype})`)}`);
|
|
149
|
+
// Write ~/.great_cto/guardrails.yml example if it doesn't exist yet.
|
|
150
|
+
// This is the user-level agentshield config — applies across all projects.
|
|
151
|
+
const globalGreatCtoDir = join(homedir(), ".great_cto");
|
|
152
|
+
const guardrailsPath = join(globalGreatCtoDir, "guardrails.yml");
|
|
153
|
+
if (!existsSync(guardrailsPath)) {
|
|
154
|
+
mkdirSync(globalGreatCtoDir, { recursive: true });
|
|
155
|
+
const guardrailsContent = `# ~/.great_cto/guardrails.yml
|
|
156
|
+
# User-defined agentshield rules. Loaded and merged with built-in rules on every scan.
|
|
157
|
+
# Rules use the same YAML format as agentshield-rules/*.yaml.
|
|
158
|
+
#
|
|
159
|
+
# action field (user rules only):
|
|
160
|
+
# block — scan fails (treated as critical finding; blocks gate:ship)
|
|
161
|
+
# audit — finding reported but scan continues (warning only)
|
|
162
|
+
# redact — same as audit; marks pattern for future content redaction
|
|
163
|
+
#
|
|
164
|
+
# Example rule — customize with your org's patterns:
|
|
165
|
+
#
|
|
166
|
+
# - id: UG-001
|
|
167
|
+
# scanner: secrets-in-prompts
|
|
168
|
+
# title: Internal API token pattern
|
|
169
|
+
# severity: critical
|
|
170
|
+
# description: |
|
|
171
|
+
# Detects hardcoded internal API tokens matching the org pattern mytoken_*.
|
|
172
|
+
# remediation: |
|
|
173
|
+
# Move to environment variable. Reference via process.env.MY_TOKEN.
|
|
174
|
+
# patterns:
|
|
175
|
+
# - 'mytoken_[a-z0-9]{32}'
|
|
176
|
+
# action: block
|
|
177
|
+
#
|
|
178
|
+
# - id: UG-002
|
|
179
|
+
# scanner: prompt-injection
|
|
180
|
+
# title: Audit use of customer data in prompts
|
|
181
|
+
# severity: high
|
|
182
|
+
# description: |
|
|
183
|
+
# Flags when customer PII fields (email, phone, address) are embedded in prompts.
|
|
184
|
+
# remediation: |
|
|
185
|
+
# Replace PII with anonymized tokens before embedding in prompts.
|
|
186
|
+
# patterns:
|
|
187
|
+
# - 'customer\.(email|phone|address|ssn)'
|
|
188
|
+
# action: audit
|
|
189
|
+
# file_globs:
|
|
190
|
+
# - "**/*.ts"
|
|
191
|
+
# - "**/*.py"
|
|
192
|
+
#
|
|
193
|
+
# Add your own rules below:
|
|
194
|
+
`;
|
|
195
|
+
writeFileSync(guardrailsPath, guardrailsContent, "utf-8");
|
|
196
|
+
success(`created ~/.great_cto/guardrails.yml ${dim("(user-level agentshield rules — edit to add org-specific patterns)")}`);
|
|
197
|
+
}
|
|
112
198
|
// Write FLOW.md — compiled delivery flow for agents and user
|
|
113
199
|
const confidence = detectionMeta?.confidence ?? "medium";
|
|
114
200
|
const size = (detection.projectSize ?? "medium");
|
package/package.json
CHANGED