arkgate 2.13.0 → 3.0.1

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 (85) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/README.md +53 -36
  3. package/bin/ark-check.mjs +72 -6
  4. package/bin/ark-mcp.mjs +108 -1
  5. package/bin/ark-shared.mjs +204 -149
  6. package/bin/ark.mjs +90 -25
  7. package/bin/lib/adapter-contract.mjs +93 -0
  8. package/bin/lib/agent-gates.mjs +1 -0
  9. package/bin/lib/analysis-engine.mjs +1171 -0
  10. package/bin/lib/architecture-scan.mjs +84 -135
  11. package/bin/lib/ci-and-commands.mjs +51 -15
  12. package/bin/lib/config-warnings.mjs +7 -205
  13. package/bin/lib/design-smells.mjs +434 -0
  14. package/bin/lib/doctor-plan.mjs +149 -16
  15. package/bin/lib/field-install.mjs +67 -10
  16. package/bin/lib/gate-files.mjs +42 -3
  17. package/bin/lib/graph-cycles.mjs +4 -54
  18. package/bin/lib/hook-templates.mjs +33 -1
  19. package/bin/lib/host-support-matrix.mjs +7 -1
  20. package/bin/lib/install-migrate.mjs +54 -16
  21. package/bin/lib/presets.mjs +42 -2
  22. package/bin/lib/safety-diagnostics.mjs +18 -17
  23. package/bin/lib/scan-files.mjs +12 -1
  24. package/bin/lib/skill-install.mjs +8 -1
  25. package/bin/lib/source-policy.mjs +36 -0
  26. package/bin/lib/start-preview.mjs +271 -0
  27. package/bin/lib/ts-resolve.mjs +11 -2
  28. package/bin/lib/write-path-capabilities.mjs +4 -0
  29. package/compat/nestjs.cjs +2 -0
  30. package/compat/nestjs.d.ts +2 -0
  31. package/compat/nestjs.js +1 -0
  32. package/compat/runtime.cjs +2 -0
  33. package/compat/runtime.d.ts +2 -0
  34. package/compat/runtime.js +1 -0
  35. package/dist/configContract-BxSIwVRo.d.cts +259 -0
  36. package/dist/configContract-BxSIwVRo.d.ts +259 -0
  37. package/dist/eslint/index.cjs +125 -48
  38. package/dist/eslint/index.d.cts +7 -1
  39. package/dist/eslint/index.d.ts +7 -1
  40. package/dist/eslint/index.js +125 -48
  41. package/dist/index.cjs +1248 -3302
  42. package/dist/index.d.cts +359 -483
  43. package/dist/index.d.ts +359 -483
  44. package/dist/index.js +1231 -3248
  45. package/docs/agent-guide.md +34 -16
  46. package/docs/ai-gates.md +30 -7
  47. package/docs/brownfield-adoption.md +52 -1
  48. package/docs/migrate-from-ark-runtime-kernel.md +2 -3
  49. package/docs/package-surface.md +10 -13
  50. package/docs/production-hardening.md +17 -4
  51. package/docs/typescript-support.md +27 -0
  52. package/package.json +33 -11
  53. package/schemas/ark.analysis-result.schema.json +91 -0
  54. package/server.json +2 -2
  55. package/templates/skills/ark-adopt.md +26 -3
  56. package/templates/skills/ark-architect.md +10 -2
  57. package/templates/skills/ark-autopilot.md +37 -20
  58. package/templates/skills/ark-contract.md +7 -0
  59. package/templates/skills/ark-coverage.md +44 -45
  60. package/templates/skills/ark-explain.md +8 -0
  61. package/templates/skills/ark-explore.md +117 -47
  62. package/templates/skills/ark-fix.md +22 -0
  63. package/templates/skills/ark-loop.md +15 -1
  64. package/templates/skills/ark-place.md +7 -0
  65. package/templates/skills/ark-think.md +24 -20
  66. package/dist/configContract-iBLxx5Tz.d.cts +0 -53
  67. package/dist/configContract-iBLxx5Tz.d.ts +0 -53
  68. package/dist/eslint/index.cjs.map +0 -1
  69. package/dist/eslint/index.js.map +0 -1
  70. package/dist/index.cjs.map +0 -1
  71. package/dist/index.js.map +0 -1
  72. package/dist/nestjs/index.cjs +0 -2606
  73. package/dist/nestjs/index.cjs.map +0 -1
  74. package/dist/nestjs/index.d.cts +0 -23
  75. package/dist/nestjs/index.d.ts +0 -23
  76. package/dist/nestjs/index.js +0 -2582
  77. package/dist/nestjs/index.js.map +0 -1
  78. package/dist/runtime/index.cjs +0 -4014
  79. package/dist/runtime/index.cjs.map +0 -1
  80. package/dist/runtime/index.d.cts +0 -3
  81. package/dist/runtime/index.d.ts +0 -3
  82. package/dist/runtime/index.js +0 -3925
  83. package/dist/runtime/index.js.map +0 -1
  84. package/dist/types-BxBwnBpC.d.cts +0 -1041
  85. package/dist/types-Wcs_l1_J.d.ts +0 -1041
package/bin/ark.mjs CHANGED
@@ -20,6 +20,8 @@ import {
20
20
  } from './ark-shared.mjs';
21
21
  import { pinArkgateDevDependency, FALSE_GREEN_GAP_ID } from './lib/field-install.mjs';
22
22
  import { validateHardWriteRequest } from './lib/enforcement-profiles.mjs';
23
+ import { applyStartPreview, planStart, renderStartPreview } from './lib/start-preview.mjs';
24
+ import { detectActiveAgentHost } from './lib/skill-install.mjs';
23
25
 
24
26
  const here = path.dirname(fileURLToPath(import.meta.url));
25
27
  const arkCheck = path.join(here, 'ark-check.mjs');
@@ -58,6 +60,12 @@ function parseArgs(argv) {
58
60
  force: false,
59
61
  strict: true,
60
62
  install: true,
63
+ installExplicit: false,
64
+ apply: false,
65
+ json: false,
66
+ internalApply: false,
67
+ skipPackageManager: false,
68
+ removeHost: undefined,
61
69
  requireWriteHook: undefined,
62
70
  help: false,
63
71
  version: false,
@@ -79,7 +87,19 @@ function parseArgs(argv) {
79
87
  else if (arg === '--yes' || arg === '-y') args.yes = true;
80
88
  else if (arg === '--force') args.force = true;
81
89
  else if (arg === '--no-strict') args.strict = false;
82
- else if (arg === '--no-install') args.install = false;
90
+ else if (arg === '--install') {
91
+ args.install = true;
92
+ args.installExplicit = true;
93
+ }
94
+ else if (arg === '--no-install') {
95
+ args.install = false;
96
+ args.installExplicit = true;
97
+ }
98
+ else if (arg === '--apply') args.apply = true;
99
+ else if (arg === '--json') args.json = true;
100
+ else if (arg === '--internal-apply') args.internalApply = true;
101
+ else if (arg === '--skip-package-manager') args.skipPackageManager = true;
102
+ else if (arg === '--remove-host') args.removeHost = requireValue(arg, i++).trim().toLowerCase();
83
103
  else if (arg === '--preset') args.preset = requireValue(arg, i++);
84
104
  else if (arg === '--archetype') args.archetype = requireValue(arg, i++);
85
105
  else if (arg === '--tools') args.tools = requireValue(arg, i++);
@@ -97,14 +117,13 @@ function parseArgs(argv) {
97
117
 
98
118
  function usage() {
99
119
  return `Usage:
100
- ark start [--root <project>] [--tools <list>] [--require-write-hook <host>] [--yes]
120
+ ark start [--root <project>] [--tools <host>] [--require-write-hook <host>] [--install] [--apply] [--json]
101
121
  ark init [--root <project>] [--preset hexagonal|layered|feature-sliced|monorepo|ui-surface|vertical-slice|ddd-bounded-contexts|clean-architecture|onion-architecture]
102
122
  [--archetype <playbook-id>] [--tools <list>] [--require-write-hook <host>] [--yes] [--force] [--no-strict]
103
123
  ark upgrade [--root <project>] [--no-install] [--no-strict]
104
124
 
105
125
  Commands:
106
- start New here? The guided setup. Looks at your project, suggests a shape in
107
- plain language, sets up the guardrails, and shows a plan — no code changed.
126
+ start New here? Analyze and preview the complete setup. Read-only unless --apply.
108
127
  init Configure Ark project enforcement with explicit prompts.
109
128
  upgrade One command to update Ark: bump the package to @latest, refresh gate
110
129
  templates + /ark-* skills (and Codex home prompts), migrate command
@@ -116,14 +135,19 @@ Options:
116
135
  (Also the implicit default when stdin/stdout are not a TTY — agents never hang on prompts.)
117
136
  --force Allow generated files to overwrite existing files.
118
137
  --no-strict Skip the final strict ark-check run.
138
+ --install Add arkgate to package.json explicitly before applying a start plan.
119
139
  --no-install Skip adding/installing arkgate as a project devDependency (start/upgrade).
140
+ --apply Apply the mutations shown by the start preview.
141
+ --json Emit the start preview as deterministic machine-readable JSON.
120
142
  --preset Start from a named architecture preset instead of detection.
121
143
  --archetype Application shape from templates/architecture-playbook.json (maps to the matching preset).
122
144
  Valid ids: crud-product, api-backend, frontend-surface, library-sdk, cli-utility,
123
145
  worker-pipeline, event-coordinator, integration-bridge, multi-app-workspace, prototype-spike,
124
146
  vertical-slice-product, ddd-bounded-contexts.
125
- --tools Comma-separated agents to gate (claude,cursor,codex,grok,windsurf,cline,copilot,kiro,roo,continue,gemini).
126
- Omit to auto-detect from each tool's config dir, falling back to claude+cursor+codex+grok.
147
+ --tools One active agent host for start (claude,cursor,codex,grok,windsurf,cline,copilot,kiro,roo,continue,gemini).
148
+ Omit to use the active host; an unknown host creates only the shared compact router.
149
+ --remove-host <host>
150
+ Preview or apply removal of that compact host integration; re-add it with --tools <host>.
127
151
  --require-write-hook <host>
128
152
  Require and verify a hard local write hook for Claude or Grok. Cursor/Codex are
129
153
  advisory-write plus hard CI merge only; impossible requests fail before any write.
@@ -229,6 +253,25 @@ function isInteractiveTty() {
229
253
  return Boolean(process.stdin.isTTY && process.stdout.isTTY);
230
254
  }
231
255
 
256
+ async function resolveStartHost(args) {
257
+ if (args.tools || args.removeHost) return args.tools;
258
+ const active = detectActiveAgentHost();
259
+ if (!isInteractiveTty() || args.yes) return active ?? undefined;
260
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
261
+ try {
262
+ const answer = (
263
+ await rl.question(
264
+ `Which agent should receive Ark's compact router? [${active ?? 'skip for now'}] `
265
+ )
266
+ )
267
+ .trim()
268
+ .toLowerCase();
269
+ return answer || active || undefined;
270
+ } finally {
271
+ rl.close();
272
+ }
273
+ }
274
+
232
275
  /**
233
276
  * True when prompts should be skipped and guided defaults applied.
234
277
  * Agents typically have no TTY — never call readline on a null interface.
@@ -410,9 +453,24 @@ async function init(args) {
410
453
 
411
454
  // `ark start` — the guided entry point (co-pilot Phase G). One command takes a newcomer from
412
455
  // "I have a project" to "governed, with a plan" in plain language, without knowing any skill
413
- // names: look at the code → suggest a shape → set up the guardrails → show the plan. It only
456
+ // names: look at the code → suggest a shape → set up one compact host router → show the plan. It only
414
457
  // orchestrates existing steps (recommend → init → --plan) and frames each in outcome terms.
415
458
  async function start(args) {
459
+ if (!args.internalApply) {
460
+ args.tools = await resolveStartHost(args);
461
+ const preview = await planStart(args, {
462
+ arkCheck,
463
+ cliPath: fileURLToPath(import.meta.url),
464
+ cliVersion,
465
+ packageInstallArgv,
466
+ });
467
+ if (args.json) console.log(JSON.stringify(preview, null, 2));
468
+ else renderStartPreview(preview);
469
+ if (!args.apply) return 0;
470
+ applyStartPreview(args.root, preview);
471
+ if (!args.json) console.log(`Applied ${preview.changes.length} previewed mutation(s).`);
472
+ return 0;
473
+ }
416
474
  const root = args.root;
417
475
  const nonInteractive = shouldUseNonInteractiveDefaults(args);
418
476
  const interactive = !nonInteractive;
@@ -423,10 +481,10 @@ async function start(args) {
423
481
  try {
424
482
  console.log("Let's set up Ark for your project.");
425
483
  console.log(
426
- "I'll walk the tree, freeze a day-zero architecture picture, then set up guardrails and show a plan."
484
+ "I'll walk the tree, create a compact host-specific guardrail setup, then show a plan."
427
485
  );
428
486
  console.log(
429
- 'Nothing in your product code is changed — only Ark config, then origin snapshot, then agent/CI templates.'
487
+ 'Nothing in your product code is changed — only Ark config, one active-host router, and a CI template.'
430
488
  );
431
489
  if (nonInteractive && !args.yes) {
432
490
  console.log(
@@ -465,10 +523,10 @@ async function start(args) {
465
523
  }
466
524
 
467
525
  // 2b) Pin arkgate as a project devDependency so CI/npx do not depend on a stale global.
468
- if (args.install !== false && fs.existsSync(path.join(root, 'package.json'))) {
526
+ if (args.installExplicit && args.install && fs.existsSync(path.join(root, 'package.json'))) {
469
527
  const { pinned, installStatus } = ensureProjectArkgateDependency(root, {
470
528
  install: true,
471
- runPackageManager: true,
529
+ runPackageManager: !args.skipPackageManager,
472
530
  });
473
531
  if (pinned.changed) {
474
532
  console.log(` Pinned arkgate@${pinned.version} in package.json devDependencies.`);
@@ -480,7 +538,7 @@ async function start(args) {
480
538
  } else if (pinned.reason === 'already-present') {
481
539
  console.log(` arkgate already in package.json (${pinned.version}).`);
482
540
  }
483
- } else if (args.install === false) {
541
+ } else if (args.installExplicit && args.install === false) {
484
542
  console.log(' Skipping arkgate package pin (--no-install).');
485
543
  }
486
544
 
@@ -494,10 +552,11 @@ async function start(args) {
494
552
  const preset = archetype ? resolveArchetypePreset(archetype).preset : undefined;
495
553
  const includeRoots = resolveIncludeRoots(root);
496
554
  const tsPackages = detectTsPackageRoots(root);
555
+ const nestedTsPackages = tsPackages.filter((entry) => entry !== '.');
497
556
  const workspaces = detectWorkspaces(root);
498
557
  const looksLikeMonorepo =
499
558
  includeRoots.length > 0 ||
500
- tsPackages.length > 0 ||
559
+ nestedTsPackages.length > 0 ||
501
560
  workspaces.length > 0 ||
502
561
  fs.existsSync(path.join(root, 'rush.json')) ||
503
562
  fs.existsSync(path.join(root, 'pnpm-workspace.yaml')) ||
@@ -510,9 +569,9 @@ async function start(args) {
510
569
  const useUi =
511
570
  rec?.preset === 'feature-sliced' ||
512
571
  rec?.archetype === 'frontend-surface' ||
513
- (tsPackages.length > 0 && includeRoots.length === 0 && !rec?.mature);
514
- initArgs.push('--preset', useUi && tsPackages.length <= 3 ? 'ui-surface' : 'monorepo');
515
- const shown = includeRoots.length > 0 ? includeRoots : tsPackages;
572
+ (nestedTsPackages.length > 0 && includeRoots.length === 0 && !rec?.mature);
573
+ initArgs.push('--preset', useUi && nestedTsPackages.length <= 3 ? 'ui-surface' : 'monorepo');
574
+ const shown = includeRoots.length > 0 ? includeRoots : nestedTsPackages;
516
575
  console.log(
517
576
  shown.length > 0
518
577
  ? ` Multi-package / TS package layout detected — profile include: ${shown.join(', ')}.`
@@ -527,16 +586,16 @@ async function start(args) {
527
586
  console.log(' Found an existing ark.config.json — keeping it.');
528
587
  }
529
588
 
530
- // 4) Day-zero origin freeze the architecture picture *before* agent docs / CI / skills.
531
- // Later --report runs show evolution vs this snapshot. Idempotent (origin once).
589
+ // 4) Compact start deliberately avoids reports/history and copied skills. A report can
590
+ // be requested later, after the small host-specific setup is accepted.
532
591
  console.log('');
533
- freezeDayZeroOrigin(root);
592
+ console.log('Skipping day-zero report archive in compact start (run ark-check --report later if wanted).');
534
593
 
535
- // 5) Agent + CI gate templates (docs, hooks, skills) after origin is frozen.
594
+ // 5) Agent + CI gate templates for exactly one active host.
536
595
  console.log('');
537
596
  console.log('Installing agent and CI gate templates…');
538
597
  {
539
- const gateArgs = ['--root', root, '--install-agent-gates'];
598
+ const gateArgs = ['--root', root, '--install-agent-gates', '--compact'];
540
599
  if (args.tools) gateArgs.push('--tools', args.tools);
541
600
  if (args.requireWriteHook) {
542
601
  gateArgs.push('--require-write-hook', args.requireWriteHook);
@@ -687,7 +746,7 @@ async function start(args) {
687
746
  );
688
747
  }
689
748
  console.log('');
690
- console.log('Day-zero origin is under .ark/reports/origin.* — re-run --report later for evolution.');
749
+ console.log('Optional later: ark-check --report ark-report.html (captures a day-zero/evolution report).');
691
750
  console.log('Optional later: --plan · --coverage · /ark-explore · /ark-fix · /ark-place · ark upgrade');
692
751
  return 0;
693
752
  } finally {
@@ -764,9 +823,15 @@ async function main() {
764
823
  }
765
824
 
766
825
  // Only run when executed as the CLI entry (not when imported by unit tests).
767
- const isMain =
768
- Boolean(process.argv[1]) &&
769
- path.resolve(process.argv[1]) === path.resolve(fileURLToPath(import.meta.url));
826
+ function entryPath(file) {
827
+ try {
828
+ return fs.realpathSync(file);
829
+ } catch {
830
+ return path.resolve(file);
831
+ }
832
+ }
833
+
834
+ const isMain = Boolean(process.argv[1]) && entryPath(process.argv[1]) === entryPath(fileURLToPath(import.meta.url));
770
835
  if (isMain) {
771
836
  process.exitCode = await main();
772
837
  }
@@ -0,0 +1,93 @@
1
+ /**
2
+ * GENERATED FILE — do not edit by hand.
3
+ *
4
+ * Canonical algorithm: src/domain/adapterContract.ts
5
+ * Regenerate: node scripts/generate-cli-pure.mjs
6
+ * Drift check: node scripts/generate-cli-pure.mjs --check
7
+ *
8
+ * Pure CLI helper (bin/lib/adapter-contract.mjs). Zero Node I/O.
9
+ */
10
+
11
+ export const ARK_ANALYSIS_RESULT_SCHEMA_VERSION = '1.0';
12
+ function text(value) {
13
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
14
+ }
15
+ function positiveInteger(value, fallback) {
16
+ return Number.isInteger(value) && Number(value) > 0 ? Number(value) : fallback;
17
+ }
18
+ export function toAdapterDiagnostic(violation, fallbackSeverity = 'error') {
19
+ const ruleId = text(violation.ruleId) ?? text(violation.code) ?? 'ARK_UNKNOWN';
20
+ const severity = violation.severity === 'warning' ? 'warning' : fallbackSeverity;
21
+ const evidence = {
22
+ ...(text(violation.target) ? { target: text(violation.target) } : {}),
23
+ ...(text(violation.fromLayer) ? { fromLayer: text(violation.fromLayer) } : {}),
24
+ ...(text(violation.toLayer) ? { toLayer: text(violation.toLayer) } : {}),
25
+ ...(typeof violation.typeOnly === 'boolean' ? { typeOnly: violation.typeOnly } : {}),
26
+ };
27
+ return {
28
+ ruleId,
29
+ severity,
30
+ message: text(violation.message) ?? ruleId,
31
+ location: {
32
+ file: text(violation.file) ?? '<unknown>',
33
+ line: positiveInteger(violation.line, 1),
34
+ column: positiveInteger(violation.column, 1),
35
+ },
36
+ evidence,
37
+ };
38
+ }
39
+ export function createAdapterResult(input) {
40
+ return {
41
+ schemaVersion: ARK_ANALYSIS_RESULT_SCHEMA_VERSION,
42
+ valid: input.valid,
43
+ diagnostics: [
44
+ ...(input.violations ?? []).map((item) => toAdapterDiagnostic(item, 'error')),
45
+ ...(input.warnings ?? []).map((item) => toAdapterDiagnostic(item, 'warning')),
46
+ ],
47
+ };
48
+ }
49
+ export const ARK_ANALYSIS_RESULT_SCHEMA = {
50
+ $schema: 'https://json-schema.org/draft/2020-12/schema',
51
+ $id: 'https://unpkg.com/arkgate@2/schemas/ark.analysis-result.schema.json',
52
+ title: 'ArkGate analysis result',
53
+ type: 'object',
54
+ additionalProperties: false,
55
+ required: ['schemaVersion', 'valid', 'diagnostics'],
56
+ properties: {
57
+ schemaVersion: { const: ARK_ANALYSIS_RESULT_SCHEMA_VERSION },
58
+ valid: { type: 'boolean' },
59
+ diagnostics: {
60
+ type: 'array',
61
+ items: {
62
+ type: 'object',
63
+ additionalProperties: false,
64
+ required: ['ruleId', 'severity', 'message', 'location', 'evidence'],
65
+ properties: {
66
+ ruleId: { type: 'string', minLength: 1 },
67
+ severity: { enum: ['error', 'warning'] },
68
+ message: { type: 'string', minLength: 1 },
69
+ location: {
70
+ type: 'object',
71
+ additionalProperties: false,
72
+ required: ['file', 'line', 'column'],
73
+ properties: {
74
+ file: { type: 'string', minLength: 1 },
75
+ line: { type: 'integer', minimum: 1 },
76
+ column: { type: 'integer', minimum: 1 },
77
+ },
78
+ },
79
+ evidence: {
80
+ type: 'object',
81
+ additionalProperties: false,
82
+ properties: {
83
+ target: { type: 'string' },
84
+ fromLayer: { type: 'string' },
85
+ toLayer: { type: 'string' },
86
+ typeOnly: { type: 'boolean' },
87
+ },
88
+ },
89
+ },
90
+ },
91
+ },
92
+ },
93
+ };
@@ -45,6 +45,7 @@ export {
45
45
  packageScriptsHaveTypecheck,
46
46
  treeHasTypecheckScript,
47
47
  ensureTypecheckScript,
48
+ compactRouterHost,
48
49
  REQUIRED_GATE_FILES,
49
50
  hasArkWorkflow,
50
51
  missingGates,