arkgate 2.10.0 → 2.12.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.
Files changed (55) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/README.md +21 -12
  3. package/SECURITY.md +3 -4
  4. package/bin/ark-check.mjs +41 -16
  5. package/bin/ark-mcp.mjs +54 -10
  6. package/bin/ark.mjs +87 -24
  7. package/bin/lib/agent-gates.mjs +68 -2090
  8. package/bin/lib/architecture-scan.mjs +4 -1
  9. package/bin/lib/baseline-key.mjs +17 -0
  10. package/bin/lib/ci-and-commands.mjs +386 -0
  11. package/bin/lib/config-warnings.mjs +22 -0
  12. package/bin/lib/core-layers.mjs +7 -0
  13. package/bin/lib/core-ratchet.mjs +3 -7
  14. package/bin/lib/deploy-path.mjs +205 -0
  15. package/bin/lib/doctor-plan.mjs +29 -5
  16. package/bin/lib/gate-files.mjs +223 -0
  17. package/bin/lib/hook-templates.mjs +99 -0
  18. package/bin/lib/install-migrate.mjs +442 -0
  19. package/bin/lib/mcp-adoption.mjs +423 -0
  20. package/bin/lib/presets.mjs +3 -0
  21. package/bin/lib/safety-diagnostics.mjs +263 -0
  22. package/bin/lib/scan-files.mjs +51 -6
  23. package/bin/lib/skill-install.mjs +259 -0
  24. package/bin/lib/typescript-host.mjs +88 -0
  25. package/bin/lib/violations.mjs +3 -3
  26. package/bin/lib/write-path-detect.mjs +138 -0
  27. package/dist/index.cjs +103 -8
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.cts +5 -3
  30. package/dist/index.d.ts +5 -3
  31. package/dist/index.js +103 -8
  32. package/dist/index.js.map +1 -1
  33. package/dist/nestjs/index.cjs +18 -5
  34. package/dist/nestjs/index.cjs.map +1 -1
  35. package/dist/nestjs/index.d.cts +1 -1
  36. package/dist/nestjs/index.d.ts +1 -1
  37. package/dist/nestjs/index.js +18 -5
  38. package/dist/nestjs/index.js.map +1 -1
  39. package/dist/runtime/index.cjs +103 -8
  40. package/dist/runtime/index.cjs.map +1 -1
  41. package/dist/runtime/index.d.cts +1 -1
  42. package/dist/runtime/index.d.ts +1 -1
  43. package/dist/runtime/index.js +103 -8
  44. package/dist/runtime/index.js.map +1 -1
  45. package/dist/{types-D6Q8WHes.d.cts → types-BZ17b9i5.d.cts} +5 -1
  46. package/dist/{types-D6Q8WHes.d.ts → types-BZ17b9i5.d.ts} +5 -1
  47. package/docs/agent-guide.md +12 -2
  48. package/docs/ai-gates.md +20 -2
  49. package/docs/package-surface.md +10 -3
  50. package/docs/production-hardening.md +5 -0
  51. package/package.json +5 -2
  52. package/server.json +2 -2
  53. package/templates/skills/ark-autopilot.md +77 -45
  54. package/templates/skills/ark-explain.md +2 -1
  55. package/templates/skills/ark-explore.md +135 -34
package/bin/ark.mjs CHANGED
@@ -23,6 +23,32 @@ import { pinArkgateDevDependency, FALSE_GREEN_GAP_ID } from './lib/field-install
23
23
  const here = path.dirname(fileURLToPath(import.meta.url));
24
24
  const arkCheck = path.join(here, 'ark-check.mjs');
25
25
 
26
+ /**
27
+ * Day-zero architecture picture: freeze origin under `.ark/reports/` as soon as
28
+ * `ark.config.json` exists — **before** agent docs, skills, CI templates, or cleanups.
29
+ * Idempotent: origin is written only once (`--report` archive semantics).
30
+ */
31
+ function freezeDayZeroOrigin(root) {
32
+ const configPath = path.join(root, 'ark.config.json');
33
+ if (!fs.existsSync(configPath)) {
34
+ console.log(
35
+ ` Skip origin freeze — no ark.config.json yet. After init: ${arkCommand(root, 'ark-check', '--report ark-report.html')}`
36
+ );
37
+ return;
38
+ }
39
+ const originJson = path.join(root, '.ark', 'reports', 'origin.json');
40
+ const already = fs.existsSync(originJson);
41
+ console.log(
42
+ already
43
+ ? 'Architecture origin already frozen (.ark/reports/origin.*) — leaving it untouched.'
44
+ : 'Freezing day-zero architecture picture (origin) before agent docs / gates…'
45
+ );
46
+ runArkCheck(
47
+ ['--root', root, '--config', 'ark.config.json', '--report', 'ark-report.html'],
48
+ { cwd: root }
49
+ );
50
+ }
51
+
26
52
  function parseArgs(argv) {
27
53
  const args = {
28
54
  command: undefined,
@@ -32,22 +58,33 @@ function parseArgs(argv) {
32
58
  strict: true,
33
59
  install: true,
34
60
  help: false,
61
+ version: false,
62
+ };
63
+
64
+ const requireValue = (flag, index) => {
65
+ const value = argv[index + 1];
66
+ if (value === undefined || value.startsWith('-')) {
67
+ throw new Error(`Missing value for ${flag}. Run ark --help for usage.`);
68
+ }
69
+ return value;
35
70
  };
36
71
 
37
72
  // Scan from the first user token (index 2) so a leading flag like `ark --help` is
38
73
  // recognized: the command is the first NON-dash argument, not blindly argv[2].
39
74
  for (let i = 2; i < argv.length; i += 1) {
40
75
  const arg = argv[i];
41
- if (arg === '--root') args.root = path.resolve(argv[++i]);
76
+ if (arg === '--root') args.root = path.resolve(requireValue(arg, i++));
42
77
  else if (arg === '--yes' || arg === '-y') args.yes = true;
43
78
  else if (arg === '--force') args.force = true;
44
79
  else if (arg === '--no-strict') args.strict = false;
45
80
  else if (arg === '--no-install') args.install = false;
46
- else if (arg === '--preset') args.preset = argv[++i];
47
- else if (arg === '--archetype') args.archetype = argv[++i];
48
- else if (arg === '--tools') args.tools = argv[++i];
81
+ else if (arg === '--preset') args.preset = requireValue(arg, i++);
82
+ else if (arg === '--archetype') args.archetype = requireValue(arg, i++);
83
+ else if (arg === '--tools') args.tools = requireValue(arg, i++);
49
84
  else if (arg === '--help' || arg === '-h' || arg === 'help') args.help = true;
85
+ else if (arg === '--version' || arg === '-V') args.version = true;
50
86
  else if (!arg.startsWith('-') && args.command === undefined) args.command = arg;
87
+ else throw new Error(`Unknown argument: ${arg}. Run ark --help for usage.`);
51
88
  }
52
89
 
53
90
  return args;
@@ -88,6 +125,15 @@ Non-interactive (no TTY): uses the same defaults as --yes — never calls readli
88
125
  `;
89
126
  }
90
127
 
128
+ function cliVersion() {
129
+ try {
130
+ const pkg = JSON.parse(fs.readFileSync(path.join(here, '..', 'package.json'), 'utf8'));
131
+ return typeof pkg.version === 'string' ? pkg.version : 'unknown';
132
+ } catch {
133
+ return 'unknown';
134
+ }
135
+ }
136
+
91
137
  // The package-manager command that adds arkgate as a dev dependency.
92
138
  // Prefer an explicit version/range when pin already chose one (avoid pin=^2.9.0 then
93
139
  // `npm i arkgate@latest` rewriting package.json to a different range).
@@ -310,6 +356,10 @@ async function init(args) {
310
356
  console.log('Skipped ark.config.json generation.');
311
357
  }
312
358
 
359
+ // Origin first: contract-on-tree picture before AGENTS.md / skills / CI templates.
360
+ console.log('');
361
+ freezeDayZeroOrigin(root);
362
+
313
363
  const installGates =
314
364
  nonInteractive || (await askYesNo(rl, 'Configure agent and CI gate templates?', true));
315
365
  if (installGates) {
@@ -337,7 +387,7 @@ async function init(args) {
337
387
  console.log(`Shape: ${archetype}. Plan: ${arkCommand(root, 'ark-check', '--recommend')}`);
338
388
  }
339
389
  console.log(
340
- `Freeze day-one architecture snapshot: ${arkCommand(root, 'ark-check', '--report ark-report.html')} (writes .ark/reports/origin.* once).`
390
+ `Day-zero origin: .ark/reports/origin.* (frozen once; later --report shows evolution vs origin).`
341
391
  );
342
392
  console.log(`Adoption health: ${arkCommand(root, 'ark-check', '--doctor')}`);
343
393
  return 0;
@@ -361,9 +411,11 @@ async function start(args) {
361
411
  try {
362
412
  console.log("Let's set up Ark for your project.");
363
413
  console.log(
364
- "I'll look at your code, suggest a shape, set up the guardrails, and show you a plan."
414
+ "I'll walk the tree, freeze a day-zero architecture picture, then set up guardrails and show a plan."
415
+ );
416
+ console.log(
417
+ 'Nothing in your product code is changed — only Ark config, then origin snapshot, then agent/CI templates.'
365
418
  );
366
- console.log('Nothing in your code is changed — this only adds Ark configuration.');
367
419
  if (nonInteractive && !args.yes) {
368
420
  console.log(
369
421
  'Non-interactive session (no TTY) — using guided defaults (same as --yes). Pass flags to override.'
@@ -420,10 +472,10 @@ async function start(args) {
420
472
  console.log(' Skipping arkgate package pin (--no-install).');
421
473
  }
422
474
 
423
- // 3) Set up config + gates. Greenfield → the shape's preset; an established repo → detection,
424
- // so the contract anchors to the directories you already have instead of aspirational globs.
475
+ // 3) Contract first (config only). Greenfield → shape preset; established repo → detection,
476
+ // so the contract anchors to directories you already have instead of aspirational globs.
425
477
  console.log('');
426
- console.log('Setting up Ark…');
478
+ console.log('Setting up Ark contract…');
427
479
  const configPath = path.join(root, 'ark.config.json');
428
480
  if (!fs.existsSync(configPath)) {
429
481
  const initArgs = ['--root', root, '--init'];
@@ -462,6 +514,15 @@ async function start(args) {
462
514
  } else {
463
515
  console.log(' Found an existing ark.config.json — keeping it.');
464
516
  }
517
+
518
+ // 4) Day-zero origin — freeze the architecture picture *before* agent docs / CI / skills.
519
+ // Later --report runs show evolution vs this snapshot. Idempotent (origin once).
520
+ console.log('');
521
+ freezeDayZeroOrigin(root);
522
+
523
+ // 5) Agent + CI gate templates (docs, hooks, skills) — after origin is frozen.
524
+ console.log('');
525
+ console.log('Installing agent and CI gate templates…');
465
526
  {
466
527
  const gateArgs = ['--root', root, '--install-agent-gates'];
467
528
  if (args.tools) gateArgs.push('--tools', args.tools);
@@ -469,7 +530,7 @@ async function start(args) {
469
530
  runArkCheck(gateArgs, { cwd: root });
470
531
  }
471
532
 
472
- // 4) Show the plan: what's safe to auto-fix vs what needs a decision.
533
+ // 6) Show the plan: what's safe to auto-fix vs what needs a decision.
473
534
  console.log('');
474
535
  console.log('Your architecture plan:');
475
536
  runArkCheck(['--root', root, '--config', 'ark.config.json', '--plan'], { cwd: root });
@@ -551,7 +612,7 @@ async function start(args) {
551
612
  planOk = false;
552
613
  }
553
614
 
554
- // 5) Plain-language wrap-up — one next step, status light only.
615
+ // 7) Plain-language wrap-up — one next step, status light only.
555
616
  // Modes are detected (Suggest/Adapt/Enforce), not user-picked settings.
556
617
  // Soft-block false-green using the same doctor adoption gap (no second detector).
557
618
  let falseGreenGap = null;
@@ -600,7 +661,7 @@ async function start(args) {
600
661
  console.log(' → reclassify I/O dirs out of Application; then /ark-autopilot for residual debt.');
601
662
  } else {
602
663
  console.log(' 1. In your agent: /ark-autopilot');
603
- console.log(' → origin report, adoption, plan, safe fixes, leave gates on.');
664
+ console.log(' → explore first, dual plan (remediation + pattern bets), safe fixes, leave gates on.');
604
665
  }
605
666
  console.log(` 2. Status anytime: ${arkCommand(root, 'ark-check', '--doctor')}`);
606
667
  console.log(` 3. After edits: ${arkCommand(root, 'ark-check', '--root . --config ark.config.json --strict-config')}`);
@@ -610,16 +671,8 @@ async function start(args) {
610
671
  );
611
672
  }
612
673
  console.log('');
613
- console.log('Optional later: --plan · --coverage · /ark-fix · /ark-place · ark upgrade');
614
-
615
- // 6) First architecture report — freezes an origin snapshot under .ark/reports/
616
- // so later --report runs can show evolution. Idempotent: origin is written only once.
617
- console.log('');
618
- console.log('Capturing architecture report (origin snapshot on first run)…');
619
- runArkCheck(
620
- ['--root', root, '--config', 'ark.config.json', '--report', 'ark-report.html'],
621
- { cwd: root }
622
- );
674
+ console.log('Day-zero origin is under .ark/reports/origin.* re-run --report later for evolution.');
675
+ console.log('Optional later: --plan · --coverage · /ark-explore · /ark-fix · /ark-place · ark upgrade');
623
676
  return 0;
624
677
  } finally {
625
678
  rl?.close();
@@ -627,7 +680,17 @@ async function start(args) {
627
680
  }
628
681
 
629
682
  async function main() {
630
- const args = parseArgs(process.argv);
683
+ let args;
684
+ try {
685
+ args = parseArgs(process.argv);
686
+ } catch (error) {
687
+ console.error(error instanceof Error ? error.message : String(error));
688
+ return 2;
689
+ }
690
+ if (args.version) {
691
+ console.log(cliVersion());
692
+ return 0;
693
+ }
631
694
  if (args.help || !args.command) {
632
695
  console.log(usage());
633
696
  return 0;