great-cto 2.31.0 → 2.32.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/bootstrap.js CHANGED
@@ -2,7 +2,6 @@
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";
6
5
  import { dim, success, warn } from "./ui.js";
7
6
  import { suggestJurisdictions } from "./jurisdictions.js";
8
7
  import { compileFlow, renderFlowMd } from "./flow.js";
@@ -146,55 +145,6 @@ when you actually start work:
146
145
  `;
147
146
  writeFileSync(projectMd, content, "utf-8");
148
147
  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
- }
198
148
  // Write FLOW.md — compiled delivery flow for agents and user
199
149
  const confidence = detectionMeta?.confidence ?? "medium";
200
150
  const size = (detection.projectSize ?? "medium");
package/dist/ci.js CHANGED
@@ -1,95 +1,22 @@
1
1
  // great-cto ci — single-command CI gate.
2
2
  //
3
- // Runs scan + budget-check + archetype-validate with output formats matching
4
- // CI conventions. Designed to be the only great_cto invocation in a CI step.
3
+ // Runs archetype-validate + budget-check. Designed to be the only great_cto
4
+ // invocation in a CI step.
5
5
  //
6
6
  // Outputs:
7
7
  // - human-readable to stderr (always)
8
- // - GitHub Actions annotations (auto when $GITHUB_ACTIONS is set, or --annotations)
9
- // - SARIF 2.1.0 JSON (--sarif <path>) — uploadable to GitHub Security tab
10
- // - JUnit XML (--junit <path>) — for test reporters / pipeline UIs
11
8
  //
12
9
  // Exit codes:
13
10
  // 0 = clean, all gates pass
14
- // 1 = findings at/above --fail-on threshold (CI should fail)
15
- // 2 = scan/setup error (not a finding — infrastructure problem)
11
+ // 1 = archetype drift (CI should fail)
12
+ // 2 = setup error (not a finding — infrastructure problem)
16
13
  //
17
14
  // Example workflow:
18
15
  // - run: npx great-cto@latest ci ./
19
16
  // env:
20
17
  // GREAT_CTO_NO_TELEMETRY: "1"
21
- import { existsSync, readFileSync, writeFileSync } from "node:fs";
18
+ import { existsSync, readFileSync } from "node:fs";
22
19
  import { resolve } from "node:path";
23
- const SEVERITY_ORDER = {
24
- info: 0,
25
- low: 1,
26
- medium: 2,
27
- high: 3,
28
- critical: 4,
29
- };
30
- function severityAtLeast(sev, threshold) {
31
- return (SEVERITY_ORDER[sev] ?? 0) >= (SEVERITY_ORDER[threshold] ?? 0);
32
- }
33
- /**
34
- * Emit GitHub Actions annotation lines. These appear inline on PR diffs.
35
- * Format: ::error file=path,line=N,col=M,title=T::message
36
- * ::warning file=...
37
- * ::notice file=...
38
- */
39
- function emitGitHubAnnotation(finding) {
40
- const sev = finding.rule.severity;
41
- const level = sev === "critical" || sev === "high" ? "error"
42
- : sev === "medium" ? "warning" : "notice";
43
- const file = finding.location.file;
44
- const line = finding.location.line;
45
- const title = `${finding.rule.id}: ${finding.rule.title}`;
46
- const message = finding.rule.owasp
47
- ? `${finding.rule.title} (${finding.rule.owasp})`
48
- : finding.rule.title;
49
- // Escape GHA-special characters
50
- const escape = (s) => s.replace(/%/g, "%25").replace(/\r/g, "%0D").replace(/\n/g, "%0A");
51
- console.log(`::${level} file=${escape(file)},line=${line},title=${escape(title)}::${escape(message)}`);
52
- }
53
- /**
54
- * Emit JUnit XML report. One <testcase> per scanned file, fails recorded as
55
- * <failure> elements. Format compatible with most CI test reporters.
56
- */
57
- function buildJunitXml(report) {
58
- const findingsByFile = new Map();
59
- for (const f of report.findings) {
60
- const arr = findingsByFile.get(f.location.file) || [];
61
- arr.push(f);
62
- findingsByFile.set(f.location.file, arr);
63
- }
64
- const totalTests = Math.max(report.filesScanned, findingsByFile.size);
65
- const failures = report.findings.length;
66
- const escape = (s) => String(s)
67
- .replace(/&/g, "&amp;")
68
- .replace(/</g, "&lt;")
69
- .replace(/>/g, "&gt;")
70
- .replace(/"/g, "&quot;")
71
- .replace(/'/g, "&apos;");
72
- const cases = Array.from(findingsByFile.entries())
73
- .map(([file, findings]) => {
74
- const failureBody = findings
75
- .map(f => ` <failure type="${escape(f.rule.severity)}" message="${escape(f.rule.id + ': ' + f.rule.title)}">
76
- ${escape(f.location.snippet || '')}
77
- ${escape(f.rule.owasp || '')}
78
- </failure>`)
79
- .join("\n");
80
- return ` <testcase classname="agentshield" name="${escape(file)}" time="0">
81
- ${failureBody}
82
- </testcase>`;
83
- })
84
- .join("\n");
85
- return `<?xml version="1.0" encoding="UTF-8"?>
86
- <testsuites>
87
- <testsuite name="great-cto ci" tests="${totalTests}" failures="${failures}" errors="0" time="${(report.durationMs / 1000).toFixed(3)}">
88
- ${cases}
89
- </testsuite>
90
- </testsuites>
91
- `;
92
- }
93
20
  /**
94
21
  * Quick archetype-detection sanity check. Fails CI if the archetype changed
95
22
  * from what's pinned in .great_cto/PROJECT.md (signals undeclared
@@ -147,66 +74,26 @@ function budgetCheck(cwd, quiet) {
147
74
  }
148
75
  export async function runCi(args) {
149
76
  const startTs = Date.now();
150
- const inGitHubActions = process.env.GITHUB_ACTIONS === "true";
151
- const wantAnnotations = args.annotations || inGitHubActions;
152
77
  if (!args.quiet) {
153
- console.error(`\ngreat-cto ci — gate threshold: ${args.failOn}, scan: ${args.severity}+`);
78
+ console.error(`\ngreat-cto ci — archetype + budget gate`);
154
79
  console.error(` path: ${resolve(args.path)}`);
155
- if (inGitHubActions)
156
- console.error(` env: GitHub Actions detected — emitting annotations`);
157
80
  console.error("");
158
81
  }
159
- // 1. Scan
160
- let scan;
161
- let toSarif;
162
- try {
163
- ({ scan } = await import("./agentshield/scanner.js"));
164
- ({ toSarif } = await import("./agentshield/sarif.js"));
165
- }
166
- catch (e) {
167
- console.error(`ci: failed to load scanner: ${e.message}`);
168
- return 2;
169
- }
170
- const report = scan(resolve(args.path), {
171
- minSeverity: args.severity,
172
- });
173
- // 2. Archetype check
82
+ // 1. Archetype check
174
83
  let archResult = { ok: true, msg: "skipped" };
175
84
  if (!args.noArchetype) {
176
85
  archResult = await archetypeCheck(args.path, args.quiet);
177
86
  }
178
- // 3. Budget check (warn-only)
87
+ // 2. Budget check (warn-only)
179
88
  let budgetResult = { ok: true, msg: "skipped" };
180
89
  if (!args.noBudget) {
181
90
  budgetResult = budgetCheck(args.path, args.quiet);
182
91
  }
183
- // 4. Emit annotations
184
- if (wantAnnotations) {
185
- for (const f of report.findings) {
186
- if (severityAtLeast(f.rule.severity, args.failOn)) {
187
- emitGitHubAnnotation(f);
188
- }
189
- }
190
- }
191
- // 5. Emit SARIF
192
- if (args.sarifPath) {
193
- writeFileSync(args.sarifPath, JSON.stringify(toSarif(report), null, 2));
194
- if (!args.quiet)
195
- console.error(` ✓ SARIF → ${args.sarifPath}`);
196
- }
197
- // 6. Emit JUnit XML
198
- if (args.junitPath) {
199
- writeFileSync(args.junitPath, buildJunitXml(report));
200
- if (!args.quiet)
201
- console.error(` ✓ JUnit XML → ${args.junitPath}`);
202
- }
203
- // 7. Summary
204
- const blockingFindings = report.findings.filter((f) => severityAtLeast(f.rule.severity, args.failOn));
205
- const passed = blockingFindings.length === 0 && archResult.ok;
92
+ // 3. Summary
93
+ const passed = archResult.ok;
206
94
  if (!args.quiet) {
207
95
  const dur = ((Date.now() - startTs) / 1000).toFixed(1);
208
96
  console.error("");
209
- console.error(` scan: ${report.findings.length} finding(s) (${blockingFindings.length} at/above ${args.failOn})`);
210
97
  console.error(` archetype: ${archResult.ok ? "✓" : "✗"} ${archResult.msg}`);
211
98
  console.error(` budget: ${budgetResult.msg}`);
212
99
  console.error(` duration: ${dur}s`);
@@ -216,15 +103,6 @@ export async function runCi(args) {
216
103
  }
217
104
  else {
218
105
  console.error("\x1b[31m✗ great-cto ci: failed\x1b[0m");
219
- if (blockingFindings.length) {
220
- console.error(` ${blockingFindings.length} finding(s) at/above ${args.failOn}:`);
221
- for (const f of blockingFindings.slice(0, 10)) {
222
- console.error(` [${f.rule.severity}] ${f.rule.id} — ${f.location.file}:${f.location.line}`);
223
- }
224
- if (blockingFindings.length > 10) {
225
- console.error(` ... +${blockingFindings.length - 10} more`);
226
- }
227
- }
228
106
  if (!archResult.ok)
229
107
  console.error(` ${archResult.msg}`);
230
108
  }
@@ -236,10 +114,6 @@ export async function runCi(args) {
236
114
  */
237
115
  export function parseCiArgs(rawArgv) {
238
116
  const flag = (n) => rawArgv.includes(`--${n}`);
239
- const value = (n, def) => {
240
- const i = rawArgv.indexOf(`--${n}`);
241
- return i >= 0 && i < rawArgv.length - 1 ? rawArgv[i + 1] : def;
242
- };
243
117
  const ciIdx = rawArgv.indexOf("ci");
244
118
  let path = ".";
245
119
  for (let i = ciIdx + 1; i < rawArgv.length; i++) {
@@ -250,11 +124,6 @@ export function parseCiArgs(rawArgv) {
250
124
  }
251
125
  return {
252
126
  path,
253
- severity: value("severity", "high"),
254
- failOn: value("fail-on", "critical"),
255
- sarifPath: value("sarif") ?? null,
256
- junitPath: value("junit") ?? null,
257
- annotations: flag("annotations"),
258
127
  noBudget: flag("no-budget"),
259
128
  noArchetype: flag("no-archetype"),
260
129
  quiet: flag("quiet"),
package/dist/main.js CHANGED
@@ -82,10 +82,6 @@ function parseArgs(argv) {
82
82
  args.command = "board";
83
83
  else if (a === "register")
84
84
  args.command = "register";
85
- else if (a === "scan")
86
- args.command = "scan";
87
- else if (a === "list-rules")
88
- args.command = "list-rules";
89
85
  else if (a === "ci")
90
86
  args.command = "ci";
91
87
  else if (a === "mcp")
@@ -140,136 +136,6 @@ function parseArgs(argv) {
140
136
  args.positional = rest;
141
137
  return args;
142
138
  }
143
- /**
144
- * `great-cto scan [path]` — AI-specific security scanner (formerly @great-cto/agentshield).
145
- *
146
- * Detects OWASP LLM Top 10 patterns: prompt injection vectors, secrets in
147
- * prompts, SSRF in tool definitions, RAG poisoning, cost-runaway loops.
148
- *
149
- * Flags (parsed from raw argv since they're scan-specific):
150
- * --severity <lvl> info|low|medium|high|critical (default: info)
151
- * --scanner <name> prompt-injection | secrets-in-prompts | ssrf-in-tools |
152
- * rag-poisoning | cost-runaway (repeatable)
153
- * --sarif <file> emit SARIF 2.1.0 to file
154
- * --json emit JSON to stdout
155
- * --quiet suppress human-readable output
156
- * --max <n> stop after N findings
157
- * --exclude <regex> add path exclude (repeatable)
158
- *
159
- * Exit codes:
160
- * 0 = no findings (or all below severity threshold)
161
- * 1 = findings at/above threshold (CI-friendly)
162
- * 2 = scan failed
163
- */
164
- async function runScan(args, rawArgv) {
165
- const { writeFileSync } = await import("node:fs");
166
- const { resolve: resolvePath } = await import("node:path");
167
- // Lazy import compiled scanner — keeps cold start fast for `init` flow.
168
- let scan;
169
- let toSarif;
170
- try {
171
- ({ scan } = await import("./agentshield/scanner.js"));
172
- ({ toSarif } = await import("./agentshield/sarif.js"));
173
- }
174
- catch (e) {
175
- error(`scan: failed to load scanner: ${e.message}`);
176
- return 2;
177
- }
178
- // Parse scan-specific flags from raw argv
179
- const flag = (n) => rawArgv.includes(`--${n}`);
180
- const value = (n, def) => {
181
- const i = rawArgv.indexOf(`--${n}`);
182
- return i >= 0 && i < rawArgv.length - 1 ? rawArgv[i + 1] : def;
183
- };
184
- const scanners = rawArgv
185
- .map((a, i) => (a === "--scanner" ? rawArgv[i + 1] : null))
186
- .filter(Boolean);
187
- const exclude = rawArgv
188
- .map((a, i) => (a === "--exclude" ? rawArgv[i + 1] : null))
189
- .filter(Boolean);
190
- // Path: first non-flag arg after `scan`, default cwd
191
- const scanIdx = rawArgv.indexOf("scan");
192
- let root = ".";
193
- for (let i = scanIdx + 1; i < rawArgv.length; i++) {
194
- if (rawArgv[i] && !rawArgv[i].startsWith("--")) {
195
- root = rawArgv[i];
196
- break;
197
- }
198
- }
199
- const opts = {
200
- scanners: scanners.length > 0 ? scanners : undefined,
201
- minSeverity: value("severity", "info"),
202
- exclude: exclude.length > 0 ? exclude : undefined,
203
- maxFindings: value("max") ? parseInt(value("max"), 10) : undefined,
204
- };
205
- const sarifPath = value("sarif");
206
- const wantsJson = flag("json");
207
- const quiet = flag("quiet");
208
- const report = scan(resolvePath(root), opts);
209
- if (sarifPath) {
210
- writeFileSync(sarifPath, JSON.stringify(toSarif(report), null, 2));
211
- if (!quiet)
212
- console.error(`✓ SARIF written → ${sarifPath}`);
213
- }
214
- if (wantsJson) {
215
- console.log(JSON.stringify(report, null, 2));
216
- }
217
- else if (!quiet) {
218
- const COLORS = {
219
- critical: "\x1b[1;31m", high: "\x1b[31m", medium: "\x1b[33m",
220
- low: "\x1b[36m", info: "\x1b[2m", reset: "\x1b[0m",
221
- };
222
- const useColor = process.stdout.isTTY;
223
- const c = (sev, s) => (useColor ? `${COLORS[sev] || ""}${s}${COLORS.reset}` : s);
224
- console.error(`\ngreat-cto scan ${getCliVersion()} — scanned ${report.filesScanned} file(s) in ${report.durationMs}ms\n`);
225
- if (report.errors.length > 0) {
226
- console.error(`\x1b[33m⚠ ${report.errors.length} error(s):\x1b[0m`);
227
- for (const e of report.errors)
228
- console.error(` ${e}`);
229
- console.error("");
230
- }
231
- if (report.findings.length === 0) {
232
- console.error("\x1b[32m✓ No findings.\x1b[0m\n");
233
- }
234
- else {
235
- for (const f of report.findings) {
236
- const tag = c(f.rule.severity, `[${f.rule.severity.toUpperCase()}]`);
237
- console.error(`${tag} ${f.rule.id} ${f.location.file}:${f.location.line}`);
238
- console.error(` ${f.rule.title}`);
239
- console.error(` ${c("info", f.location.snippet)}`);
240
- if (f.rule.owasp)
241
- console.error(` ${c("info", f.rule.owasp)}`);
242
- console.error("");
243
- }
244
- const counts = {};
245
- for (const f of report.findings)
246
- counts[f.rule.severity] = (counts[f.rule.severity] || 0) + 1;
247
- const order = ["critical", "high", "medium", "low", "info"];
248
- const parts = order.filter((s) => counts[s]).map((s) => c(s, `${counts[s]} ${s}`));
249
- console.error(`\x1b[1m${report.findings.length} finding(s)\x1b[0m — ${parts.join(", ")}\n`);
250
- }
251
- }
252
- return report.findings.length > 0 ? 1 : 0;
253
- }
254
- /**
255
- * `great-cto list-rules` — print the rule catalog.
256
- */
257
- async function runListRules() {
258
- let loadRules;
259
- try {
260
- ({ loadRules } = await import("./agentshield/rules-loader.js"));
261
- }
262
- catch (e) {
263
- error(`list-rules: failed: ${e.message}`);
264
- return 2;
265
- }
266
- const rules = loadRules();
267
- for (const r of rules) {
268
- console.log(`${r.id.padEnd(8)} ${r.severity.padEnd(8)} ${r.scanner.padEnd(20)} ${r.title}`);
269
- }
270
- console.log(`\n${rules.length} rule(s) loaded.`);
271
- return 0;
272
- }
273
139
  async function runRegister(args) {
274
140
  const { join } = await import("node:path");
275
141
  const { existsSync, readFileSync, writeFileSync, mkdirSync, statSync } = await import("node:fs");
@@ -464,9 +330,7 @@ ${bold("Usage:")}
464
330
  npx great-cto [init] [options] Detect + bootstrap
465
331
  npx great-cto board [--port 3141] [--no-open]
466
332
  npx great-cto register [--dir PATH]
467
- npx great-cto scan [path] [--severity LVL] [--scanner NAME] [--sarif FILE]
468
- npx great-cto list-rules
469
- npx great-cto ci [path] [--fail-on LVL] [--sarif F] [--junit F]
333
+ npx great-cto ci [path] [--no-archetype] [--no-budget]
470
334
  npx great-cto mcp [--sse --port N]
471
335
  npx great-cto adapt [--dry-run]
472
336
  npx great-cto serve [--port 3142]
@@ -490,27 +354,18 @@ ${bold("Upgrade:")}
490
354
  great-cto upgrade beads Upgrade beads only
491
355
  ${dim("(Safe to run any time — idempotent if already on latest)")}
492
356
 
493
- ${bold("Scan (AI-security):")}
494
- great-cto scan AI-specific scan of cwd (OWASP LLM Top 10)
495
- great-cto scan ./src --severity high Filter by minimum severity
496
- great-cto scan --scanner ssrf-in-tools Run only one scanner
497
- great-cto scan --sarif out.sarif Emit SARIF for GitHub Code Scanning
498
- great-cto scan --json JSON output for CI pipelines
499
- great-cto list-rules Print rule catalog
500
- ${dim("(exits 1 if findings ≥ severity threshold; CI-friendly)")}
501
-
502
357
  ${bold("CI gate:")}
503
- great-cto ci Single-command CI gate (scan + archetype check)
504
- great-cto ci --fail-on critical Exit 1 only on critical findings (default)
505
- great-cto ci --sarif out.sarif Emit SARIF (uploadable to GitHub Security)
506
- great-cto ci --junit out.xml Emit JUnit XML for test reporters
507
- ${dim("(auto-detects \$GITHUB_ACTIONS → emits ::error:: annotations)")}
358
+ great-cto ci Single-command CI gate (archetype + budget check)
359
+ great-cto ci --no-archetype Skip the archetype-drift check
360
+ great-cto ci --no-budget Skip the monthly-budget sanity check
361
+ ${dim("(exits 1 on archetype drift; budget is warn-only)")}
508
362
 
509
363
  ${bold("MCP server (cross-platform):")}
510
364
  great-cto mcp Stdio MCP server — works in Claude Code /
511
365
  Claude Desktop / any MCP host
512
366
  great-cto mcp --sse --port 8765 SSE mode for remote / multi-client (TODO v2.5)
513
- ${dim("Tools exposed: scan, list_rules, detect_archetype, estimate_cost, query_decisions")}
367
+ ${dim("Tools exposed: detect_archetype, estimate_cost, query_decisions,")}
368
+ ${dim(" project_status, cost_summary, pipeline_stages, recent_verdicts")}
514
369
 
515
370
  ${bold("Claude Code adapter:")}
516
371
  great-cto adapt Generate AGENTS.md + CLAUDE.md
@@ -997,26 +852,6 @@ async function main() {
997
852
  log(`Run ${cyan("great-cto --help")} for usage.`);
998
853
  process.exit(2);
999
854
  }
1000
- if (args.command === "scan") {
1001
- try {
1002
- const code = await runScan(args, rawArgv);
1003
- process.exit(code);
1004
- }
1005
- catch (e) {
1006
- error(e.message);
1007
- process.exit(2);
1008
- }
1009
- }
1010
- if (args.command === "list-rules") {
1011
- try {
1012
- const code = await runListRules();
1013
- process.exit(code);
1014
- }
1015
- catch (e) {
1016
- error(e.message);
1017
- process.exit(2);
1018
- }
1019
- }
1020
855
  if (args.command === "board") {
1021
856
  try {
1022
857
  const code = await runBoard(args);
@@ -1128,8 +963,8 @@ async function main() {
1128
963
  log(` ${cyan("/" + tried)} ${dim("[args]")}`);
1129
964
  log("");
1130
965
  log(`The CLI surface (this command) only exposes:`);
1131
- log(` ${cyan("init")} · ${cyan("scan")} · ${cyan("list-rules")} · ${cyan("ci")} · ${cyan("mcp")} ·`);
1132
- log(` ${cyan("adapt")} · ${cyan("serve")} · ${cyan("webhook")} · ${cyan("report")} · ${cyan("board")} · ${cyan("register")}`);
966
+ log(` ${cyan("init")} · ${cyan("ci")} · ${cyan("mcp")} · ${cyan("adapt")} ·`);
967
+ log(` ${cyan("serve")} · ${cyan("webhook")} · ${cyan("report")} · ${cyan("board")} · ${cyan("register")} · ${cyan("upgrade")}`);
1133
968
  log("");
1134
969
  log(`Run ${cyan("npx great-cto --help")} for the full CLI reference.`);
1135
970
  process.exit(2);
package/dist/mcp.js CHANGED
@@ -10,11 +10,13 @@
10
10
  // sse — long-running HTTP/SSE for remote / multi-client access
11
11
  //
12
12
  // Tools exposed:
13
- // scan OWASP LLM Top 10 + 24 rules → findings
14
- // list_rules full rule catalogue
15
13
  // detect_archetype archetype + compliance for a path
16
14
  // estimate_cost LLM/human time for a task
17
15
  // query_decisions search ~/.great_cto/decisions.md
16
+ // project_status board project state
17
+ // cost_summary board cost rollup
18
+ // pipeline_stages board pipeline stage breakdown
19
+ // recent_verdicts board recent agent verdicts
18
20
  //
19
21
  // Protocol: minimal MCP 2024-11-05 implementation. We hand-roll because
20
22
  // adding @modelcontextprotocol/sdk would balloon install size for what is
@@ -28,41 +30,6 @@ const SERVER_INFO = {
28
30
  version: "", // populated at startup
29
31
  };
30
32
  // ── Tool implementations ────────────────────────────────────────────────────
31
- async function toolScan(args) {
32
- const { scan } = await import("./agentshield/scanner.js");
33
- const path = args.path ?? ".";
34
- const report = scan(resolve(path), {
35
- minSeverity: (args.severity ?? "info"),
36
- scanners: args.scanner,
37
- });
38
- return {
39
- files_scanned: report.filesScanned,
40
- duration_ms: report.durationMs,
41
- findings: report.findings.map((f) => ({
42
- rule_id: f.rule.id,
43
- severity: f.rule.severity,
44
- title: f.rule.title,
45
- owasp: f.rule.owasp,
46
- file: f.location.file,
47
- line: f.location.line,
48
- snippet: f.location.snippet,
49
- })),
50
- };
51
- }
52
- async function toolListRules() {
53
- const { loadRules } = await import("./agentshield/rules-loader.js");
54
- const rules = loadRules();
55
- return {
56
- count: rules.length,
57
- rules: rules.map((r) => ({
58
- id: r.id,
59
- severity: r.severity,
60
- scanner: r.scanner,
61
- title: r.title,
62
- owasp: r.owasp ?? null,
63
- })),
64
- };
65
- }
66
33
  async function toolDetectArchetype(args) {
67
34
  const { detect } = await import("./detect.js");
68
35
  const { pickArchetype, suggestCompliance } = await import("./archetypes.js");
@@ -234,36 +201,6 @@ async function toolRecentVerdicts(args) {
234
201
  }
235
202
  // ── Tool dispatch table ────────────────────────────────────────────────────
236
203
  const TOOLS = [
237
- {
238
- name: "scan",
239
- description: "Scan code for AI/LLM-specific security issues (OWASP LLM Top 10, 24 rules). Returns findings with severity, file, line, OWASP mapping.",
240
- inputSchema: {
241
- type: "object",
242
- properties: {
243
- path: { type: "string", description: "File or directory to scan (default: cwd)" },
244
- severity: {
245
- type: "string",
246
- enum: ["info", "low", "medium", "high", "critical"],
247
- description: "Minimum severity to report",
248
- },
249
- scanner: {
250
- type: "array",
251
- items: {
252
- type: "string",
253
- enum: ["prompt-injection", "secrets-in-prompts", "ssrf-in-tools", "rag-poisoning", "cost-runaway"],
254
- },
255
- description: "Limit to specific scanner categories",
256
- },
257
- },
258
- },
259
- handler: toolScan,
260
- },
261
- {
262
- name: "list_rules",
263
- description: "List all 24 AgentShield security rules with severity and OWASP LLM Top 10 mapping.",
264
- inputSchema: { type: "object", properties: {} },
265
- handler: toolListRules,
266
- },
267
204
  {
268
205
  name: "detect_archetype",
269
206
  description: "Detect the project archetype (one of 25: fintech, healthcare, commerce, agent-product, mlops, edtech, gov-public, insurance, ...) and the compliance gates that apply.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "great-cto",
3
- "version": "2.31.0",
3
+ "version": "2.32.0",
4
4
  "description": "One command install for the great_cto Claude Code plugin. Auto-detects your stack, picks the right archetype, bootstraps PROJECT.md.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -70,7 +70,6 @@
70
70
  "index.mjs",
71
71
  "dist/",
72
72
  "assets/",
73
- "agentshield-rules/",
74
73
  "postinstall.mjs",
75
74
  "README.md"
76
75
  ],