cto-ai-cli 3.1.0 → 4.0.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.
@@ -935,8 +935,8 @@ var analyzeCommand = new Command2("analyze").description("Analyze project struct
935
935
  // src/cli/v2/interact.ts
936
936
  import { Command as Command3 } from "commander";
937
937
  import chalk3 from "chalk";
938
- import { resolve as resolve8, join as join8 } from "path";
939
- import { writeFile as writeFile3 } from "fs/promises";
938
+ import { resolve as resolve8, join as join9 } from "path";
939
+ import { writeFile as writeFile4 } from "fs/promises";
940
940
 
941
941
  // src/engine/cache.ts
942
942
  import { createHash as createHash2 } from "crypto";
@@ -1307,11 +1307,13 @@ function emptyResult(baseBranch) {
1307
1307
  import { randomUUID as randomUUID2 } from "crypto";
1308
1308
 
1309
1309
  // src/engine/selector.ts
1310
- import { createHash as createHash3 } from "crypto";
1310
+ import { createHash as createHash4 } from "crypto";
1311
1311
 
1312
1312
  // src/govern/secrets.ts
1313
1313
  import { readFile as readFile4 } from "fs/promises";
1314
- import { resolve as resolve7, relative as relative4 } from "path";
1314
+ import { readFileSync, existsSync as existsSync3, mkdirSync, writeFileSync } from "fs";
1315
+ import { resolve as resolve7, relative as relative4, join as join6, dirname as dirname2 } from "path";
1316
+ import { createHash as createHash3 } from "crypto";
1315
1317
  var BUILTIN_PATTERNS = [
1316
1318
  // API Keys
1317
1319
  { type: "api-key", source: `(?:api[_-]?key|apikey)\\s*[:=]\\s*['"]?([a-zA-Z0-9_\\-]{20,})['"]?`, flags: "gi", severity: "critical", description: "API Key" },
@@ -1336,15 +1338,66 @@ var BUILTIN_PATTERNS = [
1336
1338
  { type: "connection-string", source: `(?:mongodb(?:\\+srv)?|postgres(?:ql)?|mysql|redis|amqp):\\/\\/[^\\s'"]+:[^\\s'"]+@[^\\s'"]+`, flags: "gi", severity: "critical", description: "Database Connection String" },
1337
1339
  { type: "connection-string", source: `(?:DATABASE_URL|REDIS_URL|MONGODB_URI)\\s*[:=]\\s*['"]?([^\\s'"]{10,})['"]?`, flags: "gi", severity: "high", description: "Database URL" },
1338
1340
  // Environment variables with secrets
1339
- { type: "env-variable", source: `(?:SECRET|PRIVATE|ENCRYPTION)[_-]?(?:KEY|TOKEN|PASS)\\s*[:=]\\s*['"]?([^\\s'"]{8,})['"]?`, flags: "gi", severity: "high", description: "Secret Environment Variable" }
1341
+ { type: "env-variable", source: `(?:SECRET|PRIVATE|ENCRYPTION)[_-]?(?:KEY|TOKEN|PASS)\\s*[:=]\\s*['"]?([^\\s'"]{8,})['"]?`, flags: "gi", severity: "high", description: "Secret Environment Variable" },
1342
+ // Stripe
1343
+ { type: "api-key", source: "sk_live_[a-zA-Z0-9]{24,}", flags: "g", severity: "critical", description: "Stripe Live Secret Key" },
1344
+ { type: "api-key", source: "pk_live_[a-zA-Z0-9]{24,}", flags: "g", severity: "high", description: "Stripe Live Publishable Key" },
1345
+ { type: "api-key", source: "rk_live_[a-zA-Z0-9]{24,}", flags: "g", severity: "critical", description: "Stripe Restricted Key" },
1346
+ // Slack
1347
+ { type: "token", source: "xoxb-[0-9]{10,}-[0-9]{10,}-[a-zA-Z0-9]{24,}", flags: "g", severity: "critical", description: "Slack Bot Token" },
1348
+ { type: "token", source: "xoxp-[0-9]{10,}-[0-9]{10,}-[a-zA-Z0-9]{24,}", flags: "g", severity: "critical", description: "Slack User Token" },
1349
+ { type: "api-key", source: "https://hooks\\.slack\\.com/services/T[a-zA-Z0-9_]+/B[a-zA-Z0-9_]+/[a-zA-Z0-9_]+", flags: "g", severity: "high", description: "Slack Webhook URL" },
1350
+ // Google
1351
+ { type: "api-key", source: "AIza[0-9A-Za-z_-]{35}", flags: "g", severity: "high", description: "Google API Key" },
1352
+ { type: "token", source: "ya29\\.[0-9A-Za-z_-]+", flags: "g", severity: "high", description: "Google OAuth Token" },
1353
+ // Azure
1354
+ { type: "api-key", source: "(?:AccountKey|SharedAccessKey)\\s*=\\s*[a-zA-Z0-9+/=]{40,}", flags: "g", severity: "critical", description: "Azure Storage Key" },
1355
+ // Twilio
1356
+ { type: "api-key", source: "AC[a-f0-9]{32}", flags: "g", severity: "high", description: "Twilio Account SID" },
1357
+ // SendGrid
1358
+ { type: "api-key", source: "SG\\.[a-zA-Z0-9_-]{22}\\.[a-zA-Z0-9_-]{43}", flags: "g", severity: "critical", description: "SendGrid API Key" },
1359
+ // JWT
1360
+ { type: "token", source: "eyJ[a-zA-Z0-9_-]{10,}\\.eyJ[a-zA-Z0-9_-]{10,}\\.[a-zA-Z0-9_-]{10,}", flags: "g", severity: "high", description: "JSON Web Token" },
1361
+ // Datadog
1362
+ { type: "api-key", source: `(?:DD_API_KEY|DATADOG_API_KEY)\\s*[:=]\\s*['"]?([a-f0-9]{32})['"]?`, flags: "gi", severity: "critical", description: "Datadog API Key" },
1363
+ { type: "api-key", source: `(?:DD_APP_KEY|DATADOG_APP_KEY)\\s*[:=]\\s*['"]?([a-f0-9]{40})['"]?`, flags: "gi", severity: "critical", description: "Datadog App Key" },
1364
+ // Sentry
1365
+ { type: "connection-string", source: "https://[a-f0-9]{32}@[a-z0-9]+\\.ingest\\.sentry\\.io/[0-9]+", flags: "g", severity: "high", description: "Sentry DSN" },
1366
+ // Firebase
1367
+ { type: "api-key", source: `(?:FIREBASE_API_KEY|FIREBASE_KEY)\\s*[:=]\\s*['"]?([a-zA-Z0-9_\\-]{30,})['"]?`, flags: "gi", severity: "high", description: "Firebase API Key" },
1368
+ { type: "connection-string", source: `firebase[a-z]*:\\/\\/[^\\s'"]+`, flags: "gi", severity: "high", description: "Firebase URL" },
1369
+ // Supabase
1370
+ { type: "api-key", source: "sbp_[a-f0-9]{40}", flags: "g", severity: "critical", description: "Supabase Service Key" },
1371
+ { type: "token", source: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\\.[a-zA-Z0-9_-]{20,}\\.[a-zA-Z0-9_-]{20,}", flags: "g", severity: "high", description: "Supabase Anon/Service JWT" },
1372
+ // Vercel
1373
+ { type: "token", source: `(?:VERCEL_TOKEN|VERCEL_API_TOKEN)\\s*[:=]\\s*['"]?([a-zA-Z0-9]{24,})['"]?`, flags: "gi", severity: "critical", description: "Vercel Token" },
1374
+ // Heroku
1375
+ { type: "api-key", source: `(?:HEROKU_API_KEY|HEROKU_TOKEN)\\s*[:=]\\s*['"]?([a-f0-9\\-]{36,})['"]?`, flags: "gi", severity: "critical", description: "Heroku API Key" },
1376
+ // DigitalOcean
1377
+ { type: "token", source: "dop_v1_[a-f0-9]{64}", flags: "g", severity: "critical", description: "DigitalOcean Personal Access Token" },
1378
+ { type: "token", source: "doo_v1_[a-f0-9]{64}", flags: "g", severity: "critical", description: "DigitalOcean OAuth Token" },
1379
+ // Mailgun
1380
+ { type: "api-key", source: "key-[a-zA-Z0-9]{32}", flags: "g", severity: "high", description: "Mailgun API Key" },
1381
+ // PII
1382
+ { type: "pii", source: "\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b", flags: "g", severity: "medium", description: "Email Address (PII)" },
1383
+ { type: "pii", source: "\\b(?!000|666|9\\d{2})(\\d{3})[-.]?(?!00)(\\d{2})[-.]?(?!0000)(\\d{4})\\b", flags: "g", severity: "high", description: "Possible SSN (PII)" }
1340
1384
  ];
1385
+ var _cachedBuiltinPatterns = null;
1386
+ function getBuiltinPatterns() {
1387
+ if (!_cachedBuiltinPatterns) {
1388
+ _cachedBuiltinPatterns = BUILTIN_PATTERNS.map((def) => ({
1389
+ type: def.type,
1390
+ pattern: new RegExp(def.source, def.flags),
1391
+ severity: def.severity,
1392
+ description: def.description
1393
+ }));
1394
+ }
1395
+ return _cachedBuiltinPatterns;
1396
+ }
1341
1397
  function buildPatterns(customPatterns = []) {
1342
- const patterns = BUILTIN_PATTERNS.map((def) => ({
1343
- type: def.type,
1344
- pattern: new RegExp(def.source, def.flags),
1345
- severity: def.severity,
1346
- description: def.description
1347
- }));
1398
+ const builtins = getBuiltinPatterns();
1399
+ if (customPatterns.length === 0) return builtins;
1400
+ const patterns = [...builtins];
1348
1401
  for (const custom of customPatterns) {
1349
1402
  try {
1350
1403
  patterns.push({
@@ -1358,7 +1411,7 @@ function buildPatterns(customPatterns = []) {
1358
1411
  }
1359
1412
  return patterns;
1360
1413
  }
1361
- function scanContentForSecrets(content, filePath, customPatterns = []) {
1414
+ function scanContentForSecrets(content, filePath, customPatterns = [], extraPiiSafeDomains) {
1362
1415
  const findings = [];
1363
1416
  const lines = content.split("\n");
1364
1417
  const allPatterns = buildPatterns(customPatterns);
@@ -1370,6 +1423,7 @@ function scanContentForSecrets(content, filePath, customPatterns = []) {
1370
1423
  while ((match = secretPattern.pattern.exec(line)) !== null) {
1371
1424
  const matchText = match[0];
1372
1425
  if (isTemplateOrPlaceholder(matchText)) continue;
1426
+ if (secretPattern.type === "pii" && isSafeEmail(matchText, extraPiiSafeDomains)) continue;
1373
1427
  findings.push({
1374
1428
  type: secretPattern.type,
1375
1429
  file: filePath,
@@ -1417,6 +1471,36 @@ function isTemplateOrPlaceholder(value) {
1417
1471
  ];
1418
1472
  return placeholders.some((p) => p.test(value));
1419
1473
  }
1474
+ var PII_SAFE_EMAIL_DOMAINS = /* @__PURE__ */ new Set([
1475
+ "example.com",
1476
+ "example.org",
1477
+ "example.net",
1478
+ "test.com",
1479
+ "test.org",
1480
+ "test.net",
1481
+ "localhost",
1482
+ "localhost.localdomain",
1483
+ "email.com",
1484
+ "mail.com",
1485
+ "foo.com",
1486
+ "bar.com",
1487
+ "baz.com",
1488
+ "acme.com",
1489
+ "company.com",
1490
+ "corp.com",
1491
+ "noreply.com",
1492
+ "no-reply.com",
1493
+ "users.noreply.github.com",
1494
+ "placeholder.com"
1495
+ ]);
1496
+ function isSafeEmail(value, extraDomains) {
1497
+ const match = value.match(/@([a-zA-Z0-9.-]+)$/);
1498
+ if (!match) return false;
1499
+ const domain = match[1].toLowerCase();
1500
+ if (PII_SAFE_EMAIL_DOMAINS.has(domain)) return true;
1501
+ if (extraDomains && extraDomains.has(domain)) return true;
1502
+ return false;
1503
+ }
1420
1504
  function deduplicateFindings(findings) {
1421
1505
  const seen = /* @__PURE__ */ new Set();
1422
1506
  return findings.filter((f) => {
@@ -1430,8 +1514,8 @@ function deduplicateFindings(findings) {
1430
1514
  // src/engine/pruner.ts
1431
1515
  import { Project as Project2, SyntaxKind as SyntaxKind2 } from "ts-morph";
1432
1516
  import { readFile as readFile5 } from "fs/promises";
1433
- import { existsSync as existsSync3 } from "fs";
1434
- import { join as join6 } from "path";
1517
+ import { existsSync as existsSync4 } from "fs";
1518
+ import { join as join7 } from "path";
1435
1519
  var TS_EXTENSIONS2 = /* @__PURE__ */ new Set(["ts", "tsx", "js", "jsx", "mts", "mjs"]);
1436
1520
  async function pruneFile(file, level) {
1437
1521
  if (level === "excluded") {
@@ -1677,9 +1761,9 @@ function addJSDoc(node, parts) {
1677
1761
  function findTsConfig(filePath) {
1678
1762
  let dir = filePath;
1679
1763
  for (let i = 0; i < 10; i++) {
1680
- dir = join6(dir, "..");
1681
- const candidate = join6(dir, "tsconfig.json");
1682
- if (existsSync3(candidate)) return candidate;
1764
+ dir = join7(dir, "..");
1765
+ const candidate = join7(dir, "tsconfig.json");
1766
+ if (existsSync4(candidate)) return candidate;
1683
1767
  }
1684
1768
  return void 0;
1685
1769
  }
@@ -1893,7 +1977,7 @@ async function selectContext(input) {
1893
1977
  );
1894
1978
  const excludedRisk = excludedFiles.length > 0 ? Math.round(excludedFiles.reduce((s, f) => s + f.riskScore, 0) / excludedFiles.length) : 0;
1895
1979
  const hashInput = selectedFiles.map((f) => `${f.relativePath}:${f.pruneLevel}`).sort().join("|") + `|budget:${budget}`;
1896
- const hash = createHash3("sha256").update(hashInput).digest("hex").substring(0, 16);
1980
+ const hash = createHash4("sha256").update(hashInput).digest("hex").substring(0, 16);
1897
1981
  return {
1898
1982
  files: selectedFiles,
1899
1983
  totalTokens: usedTokens,
@@ -2386,20 +2470,20 @@ function makeSection(id, role, content) {
2386
2470
  }
2387
2471
 
2388
2472
  // src/govern/audit.ts
2389
- import { randomUUID, createHash as createHash4 } from "crypto";
2473
+ import { randomUUID, createHash as createHash5 } from "crypto";
2390
2474
  import { readdir as readdir3, chmod } from "fs/promises";
2391
- import { join as join7 } from "path";
2475
+ import { join as join8 } from "path";
2392
2476
  import { userInfo } from "os";
2393
2477
  import { homedir } from "os";
2394
2478
  var CTO_DIR = ".cto-ai";
2395
2479
  var AUDIT_DIR = "audit";
2396
2480
  var MAX_ENTRIES_PER_FILE = 500;
2397
2481
  function getAuditDir() {
2398
- return join7(homedir(), CTO_DIR, AUDIT_DIR);
2482
+ return join8(homedir(), CTO_DIR, AUDIT_DIR);
2399
2483
  }
2400
2484
  function getCurrentAuditFile() {
2401
2485
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0].replace(/-/g, "");
2402
- return join7(getAuditDir(), `audit_${date}.json`);
2486
+ return join8(getAuditDir(), `audit_${date}.json`);
2403
2487
  }
2404
2488
  function computeIntegrityHash(entry) {
2405
2489
  const payload = JSON.stringify({
@@ -2410,7 +2494,7 @@ function computeIntegrityHash(entry) {
2410
2494
  projectPath: entry.projectPath,
2411
2495
  details: entry.details
2412
2496
  });
2413
- return createHash4("sha256").update(payload).digest("hex");
2497
+ return createHash5("sha256").update(payload).digest("hex");
2414
2498
  }
2415
2499
  async function ensureDir(dirPath) {
2416
2500
  const { mkdir: mkdir5 } = await import("fs/promises");
@@ -2426,9 +2510,9 @@ async function readJSON(filePath) {
2426
2510
  }
2427
2511
  }
2428
2512
  async function writeJSON(filePath, data) {
2429
- const { writeFile: writeFile6 } = await import("fs/promises");
2430
- await ensureDir(join7(filePath, ".."));
2431
- await writeFile6(filePath, JSON.stringify(data, null, 2), "utf-8");
2513
+ const { writeFile: writeFile7 } = await import("fs/promises");
2514
+ await ensureDir(join8(filePath, ".."));
2515
+ await writeFile7(filePath, JSON.stringify(data, null, 2), "utf-8");
2432
2516
  }
2433
2517
  async function logAudit(action, projectPath, details = {}) {
2434
2518
  const auditDir = getAuditDir();
@@ -2477,7 +2561,7 @@ async function getAuditEntries(options = {}) {
2477
2561
  const limit = options.limit ?? 100;
2478
2562
  for (const file of auditFiles) {
2479
2563
  if (allEntries.length >= limit) break;
2480
- const entries = await readJSON(join7(auditDir, file));
2564
+ const entries = await readJSON(join8(auditDir, file));
2481
2565
  if (!entries) continue;
2482
2566
  for (const entry of entries.reverse()) {
2483
2567
  if (allEntries.length >= limit) break;
@@ -2526,7 +2610,7 @@ async function purgeOldAuditEntries(retentionDays) {
2526
2610
  const dateStr = file.replace("audit_", "").replace(".json", "");
2527
2611
  if (dateStr < cutoffStr) {
2528
2612
  try {
2529
- await unlink(join7(auditDir, file));
2613
+ await unlink(join8(auditDir, file));
2530
2614
  purged++;
2531
2615
  } catch {
2532
2616
  }
@@ -2753,8 +2837,8 @@ var interactCommand = new Command3("interact").description("Build optimized AI c
2753
2837
  console.log("");
2754
2838
  }
2755
2839
  if (opts.output === "file") {
2756
- const outPath = join8(resolve8(opts.path ?? "."), ".cto", `prompt-${plan.id}.md`);
2757
- await writeFile3(outPath, plan.prompt.rendered, "utf-8");
2840
+ const outPath = join9(resolve8(opts.path ?? "."), ".cto", `prompt-${plan.id}.md`);
2841
+ await writeFile4(outPath, plan.prompt.rendered, "utf-8");
2758
2842
  console.log(chalk3.green(` \u{1F4BE} Prompt saved to ${outPath}`));
2759
2843
  console.log("");
2760
2844
  } else if (opts.output === "clipboard") {
@@ -2783,11 +2867,11 @@ var interactCommand = new Command3("interact").description("Build optimized AI c
2783
2867
  // src/cli/v2/snapshot.ts
2784
2868
  import { Command as Command4 } from "commander";
2785
2869
  import chalk4 from "chalk";
2786
- import { resolve as resolve9, join as join9 } from "path";
2787
- import { readFile as readFile7, writeFile as writeFile4, readdir as readdir4, mkdir as mkdir3 } from "fs/promises";
2870
+ import { resolve as resolve9, join as join10 } from "path";
2871
+ import { readFile as readFile7, writeFile as writeFile5, readdir as readdir4, mkdir as mkdir3 } from "fs/promises";
2788
2872
 
2789
2873
  // src/govern/snapshot.ts
2790
- import { randomUUID as randomUUID3, createHash as createHash5 } from "crypto";
2874
+ import { randomUUID as randomUUID3, createHash as createHash6 } from "crypto";
2791
2875
  import "fs/promises";
2792
2876
  function createSnapshot(name, analysis, selection, metadata = {}) {
2793
2877
  const files = selection.files.map((f) => ({
@@ -2880,13 +2964,13 @@ function compareSnapshots(older, newer) {
2880
2964
  };
2881
2965
  }
2882
2966
  function hashString(input) {
2883
- return createHash5("sha256").update(input).digest("hex").substring(0, 16);
2967
+ return createHash6("sha256").update(input).digest("hex").substring(0, 16);
2884
2968
  }
2885
2969
 
2886
2970
  // src/cli/v2/snapshot.ts
2887
2971
  var SNAPSHOTS_DIR = ".cto/snapshots";
2888
2972
  async function ensureSnapshotsDir(projectPath) {
2889
- const dir = join9(projectPath, SNAPSHOTS_DIR);
2973
+ const dir = join10(projectPath, SNAPSHOTS_DIR);
2890
2974
  await mkdir3(dir, { recursive: true });
2891
2975
  return dir;
2892
2976
  }
@@ -2914,8 +2998,8 @@ var snapshotCommand = new Command4("snapshot").description("Create and manage re
2914
2998
  createdBy: process.env.USER ?? "unknown"
2915
2999
  });
2916
3000
  const dir = await ensureSnapshotsDir(projectPath);
2917
- const filePath = join9(dir, `${name}.json`);
2918
- await writeFile4(filePath, JSON.stringify(snap, null, 2), "utf-8");
3001
+ const filePath = join10(dir, `${name}.json`);
3002
+ await writeFile5(filePath, JSON.stringify(snap, null, 2), "utf-8");
2919
3003
  console.log("");
2920
3004
  console.log(chalk4.bold.cyan(` \u{1F4F8} Snapshot "${name}" created`));
2921
3005
  console.log(` Files: ${snap.files.length}`);
@@ -2937,7 +3021,7 @@ var snapshotCommand = new Command4("snapshot").description("Create and manage re
2937
3021
  try {
2938
3022
  const projectPath = resolve9(opts.path ?? ".");
2939
3023
  const budget = parseInt(opts.budget ?? "50000", 10);
2940
- const snapPath = join9(projectPath, SNAPSHOTS_DIR, `${name}.json`);
3024
+ const snapPath = join10(projectPath, SNAPSHOTS_DIR, `${name}.json`);
2941
3025
  const snap = await loadSnapshot(snapPath);
2942
3026
  console.log(chalk4.dim(`
2943
3027
  Re-analyzing project...`));
@@ -2974,9 +3058,9 @@ var snapshotCommand = new Command4("snapshot").description("Create and manage re
2974
3058
  new Command4("compare").description("Compare two snapshots").argument("<older>", "Older snapshot name").argument("<newer>", "Newer snapshot name").option("-p, --path <path>", "Project path", ".").action(async (older, newer, opts) => {
2975
3059
  try {
2976
3060
  const projectPath = resolve9(opts.path ?? ".");
2977
- const dir = join9(projectPath, SNAPSHOTS_DIR);
2978
- const snap1 = await loadSnapshot(join9(dir, `${older}.json`));
2979
- const snap2 = await loadSnapshot(join9(dir, `${newer}.json`));
3061
+ const dir = join10(projectPath, SNAPSHOTS_DIR);
3062
+ const snap1 = await loadSnapshot(join10(dir, `${older}.json`));
3063
+ const snap2 = await loadSnapshot(join10(dir, `${newer}.json`));
2980
3064
  const diff = compareSnapshots(snap1, snap2);
2981
3065
  console.log("");
2982
3066
  console.log(chalk4.bold.cyan(` \u{1F4CA} Snapshot Comparison: ${older} \u2192 ${newer}`));
@@ -3009,7 +3093,7 @@ var snapshotCommand = new Command4("snapshot").description("Create and manage re
3009
3093
  new Command4("list").description("List saved snapshots").option("-p, --path <path>", "Project path", ".").action(async (opts) => {
3010
3094
  try {
3011
3095
  const projectPath = resolve9(opts.path ?? ".");
3012
- const dir = join9(projectPath, SNAPSHOTS_DIR);
3096
+ const dir = join10(projectPath, SNAPSHOTS_DIR);
3013
3097
  let files;
3014
3098
  try {
3015
3099
  files = await readdir4(dir);
@@ -3027,7 +3111,7 @@ var snapshotCommand = new Command4("snapshot").description("Create and manage re
3027
3111
  console.log("");
3028
3112
  for (const file of snapFiles) {
3029
3113
  try {
3030
- const snap = await loadSnapshot(join9(dir, file));
3114
+ const snap = await loadSnapshot(join10(dir, file));
3031
3115
  const date = new Date(snap.createdAt).toLocaleString();
3032
3116
  console.log(` ${chalk4.bold(snap.name)} \u2014 ${snap.files.length} files, ~${Math.round(snap.totalTokens / 1e3)}K tokens, coverage ${snap.coverageScore}%`);
3033
3117
  console.log(` ${chalk4.dim(`Created: ${date} | Hash: ${snap.hash}`)}`);
@@ -3138,8 +3222,8 @@ var auditCommand = new Command5("audit").description("View and manage the audit
3138
3222
  // src/cli/v2/policy.ts
3139
3223
  import { Command as Command6 } from "commander";
3140
3224
  import chalk6 from "chalk";
3141
- import { resolve as resolve10, join as join10 } from "path";
3142
- import { readFile as readFile8, writeFile as writeFile5, mkdir as mkdir4 } from "fs/promises";
3225
+ import { resolve as resolve10, join as join11 } from "path";
3226
+ import { readFile as readFile8, writeFile as writeFile6, mkdir as mkdir4 } from "fs/promises";
3143
3227
 
3144
3228
  // src/govern/policy.ts
3145
3229
  var DEFAULT_POLICY = {
@@ -3293,16 +3377,16 @@ function fileMatchesCategory(path, category) {
3293
3377
  var POLICY_FILE2 = ".cto/policy.json";
3294
3378
  async function loadPolicy(projectPath) {
3295
3379
  try {
3296
- const content = await readFile8(join10(projectPath, POLICY_FILE2), "utf-8");
3380
+ const content = await readFile8(join11(projectPath, POLICY_FILE2), "utf-8");
3297
3381
  return JSON.parse(content);
3298
3382
  } catch {
3299
3383
  return DEFAULT_POLICY;
3300
3384
  }
3301
3385
  }
3302
3386
  async function savePolicy(projectPath, policy) {
3303
- const dir = join10(projectPath, ".cto");
3387
+ const dir = join11(projectPath, ".cto");
3304
3388
  await mkdir4(dir, { recursive: true });
3305
- await writeFile5(join10(projectPath, POLICY_FILE2), JSON.stringify(policy, null, 2), "utf-8");
3389
+ await writeFile6(join11(projectPath, POLICY_FILE2), JSON.stringify(policy, null, 2), "utf-8");
3306
3390
  }
3307
3391
  var policyCommand = new Command6("policy").description("Manage context selection policies").addCommand(
3308
3392
  new Command6("show").description("Show current policy rules").option("-p, --path <path>", "Project path", ".").option("--json", "Output as JSON").action(async (opts) => {
@@ -3446,7 +3530,7 @@ var policyCommand = new Command6("policy").description("Manage context selection
3446
3530
  const projectPath = resolve10(opts.path ?? ".");
3447
3531
  await savePolicy(projectPath, DEFAULT_POLICY);
3448
3532
  console.log(chalk6.green(`
3449
- \u2705 Default policy initialized at ${join10(projectPath, POLICY_FILE2)}
3533
+ \u2705 Default policy initialized at ${join11(projectPath, POLICY_FILE2)}
3450
3534
  `));
3451
3535
  } catch (err) {
3452
3536
  console.error(chalk6.red(`