arkgate 2.12.0 → 3.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.
Files changed (88) hide show
  1. package/CHANGELOG.md +122 -0
  2. package/README.md +90 -51
  3. package/bin/ark-check.mjs +156 -39
  4. package/bin/ark-mcp.mjs +119 -6
  5. package/bin/ark-shared.mjs +216 -129
  6. package/bin/ark.mjs +134 -34
  7. package/bin/lib/adapter-contract.mjs +93 -0
  8. package/bin/lib/agent-gates.mjs +13 -0
  9. package/bin/lib/analysis-engine.mjs +1171 -0
  10. package/bin/lib/architecture-scan.mjs +84 -127
  11. package/bin/lib/ci-and-commands.mjs +40 -3
  12. package/bin/lib/codex-home.mjs +7 -0
  13. package/bin/lib/config-contract.mjs +331 -0
  14. package/bin/lib/config-warnings.mjs +7 -205
  15. package/bin/lib/doctor-plan.mjs +43 -16
  16. package/bin/lib/enforcement-profiles.mjs +97 -0
  17. package/bin/lib/field-install.mjs +67 -10
  18. package/bin/lib/gate-files.mjs +42 -3
  19. package/bin/lib/graph-cycles.mjs +4 -54
  20. package/bin/lib/hook-templates.mjs +33 -1
  21. package/bin/lib/host-support-matrix.mjs +83 -0
  22. package/bin/lib/install-migrate.mjs +99 -30
  23. package/bin/lib/mcp-adoption.mjs +35 -3
  24. package/bin/lib/open-html.mjs +75 -0
  25. package/bin/lib/presets.mjs +45 -4
  26. package/bin/lib/safety-diagnostics.mjs +36 -15
  27. package/bin/lib/scan-files.mjs +12 -1
  28. package/bin/lib/skill-install.mjs +72 -1
  29. package/bin/lib/source-policy.mjs +36 -0
  30. package/bin/lib/start-preview.mjs +271 -0
  31. package/bin/lib/ts-resolve.mjs +13 -3
  32. package/bin/lib/weakest-link.mjs +417 -0
  33. package/bin/lib/write-path-capabilities.mjs +186 -0
  34. package/bin/lib/write-path-detect.mjs +62 -99
  35. package/compat/nestjs.cjs +2 -0
  36. package/compat/nestjs.d.ts +2 -0
  37. package/compat/nestjs.js +1 -0
  38. package/compat/runtime.cjs +2 -0
  39. package/compat/runtime.d.ts +2 -0
  40. package/compat/runtime.js +1 -0
  41. package/dist/configContract-BxSIwVRo.d.cts +259 -0
  42. package/dist/configContract-BxSIwVRo.d.ts +259 -0
  43. package/dist/eslint/index.cjs +500 -61
  44. package/dist/eslint/index.d.cts +36 -20
  45. package/dist/eslint/index.d.ts +36 -20
  46. package/dist/eslint/index.js +500 -61
  47. package/dist/index.cjs +1349 -2741
  48. package/dist/index.d.cts +449 -483
  49. package/dist/index.d.ts +449 -483
  50. package/dist/index.js +1325 -2687
  51. package/docs/agent-guide.md +58 -34
  52. package/docs/ai-gates.md +79 -21
  53. package/docs/configuration.md +97 -0
  54. package/docs/enthusiast/README.md +3 -3
  55. package/docs/enthusiast/how-to-agent-gates.md +7 -3
  56. package/docs/migrate-from-ark-runtime-kernel.md +5 -3
  57. package/docs/package-surface.md +19 -19
  58. package/docs/production-hardening.md +31 -5
  59. package/docs/threat-model.md +65 -0
  60. package/docs/typescript-support.md +30 -3
  61. package/package.json +46 -11
  62. package/schemas/ark.analysis-result.schema.json +91 -0
  63. package/schemas/ark.config.schema.json +750 -0
  64. package/server.json +2 -2
  65. package/templates/hooks/pre-commit-ark +37 -0
  66. package/templates/skills/ark-architect.md +3 -2
  67. package/templates/skills/ark-coverage.md +2 -2
  68. package/templates/skills/ark-runtime.md +8 -5
  69. package/templates/skills/ark-upgrade.md +36 -16
  70. package/tests/fixtures/ts-consumer/ark.config.json +2 -0
  71. package/dist/eslint/index.cjs.map +0 -1
  72. package/dist/eslint/index.js.map +0 -1
  73. package/dist/index.cjs.map +0 -1
  74. package/dist/index.js.map +0 -1
  75. package/dist/nestjs/index.cjs +0 -2498
  76. package/dist/nestjs/index.cjs.map +0 -1
  77. package/dist/nestjs/index.d.cts +0 -22
  78. package/dist/nestjs/index.d.ts +0 -22
  79. package/dist/nestjs/index.js +0 -2474
  80. package/dist/nestjs/index.js.map +0 -1
  81. package/dist/runtime/index.cjs +0 -3352
  82. package/dist/runtime/index.cjs.map +0 -1
  83. package/dist/runtime/index.d.cts +0 -2
  84. package/dist/runtime/index.d.ts +0 -2
  85. package/dist/runtime/index.js +0 -3270
  86. package/dist/runtime/index.js.map +0 -1
  87. package/dist/types-BZ17b9i5.d.cts +0 -1068
  88. package/dist/types-BZ17b9i5.d.ts +0 -1068
package/bin/ark.mjs CHANGED
@@ -19,6 +19,9 @@ import {
19
19
  resolveOperatingMode,
20
20
  } from './ark-shared.mjs';
21
21
  import { pinArkgateDevDependency, FALSE_GREEN_GAP_ID } from './lib/field-install.mjs';
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';
22
25
 
23
26
  const here = path.dirname(fileURLToPath(import.meta.url));
24
27
  const arkCheck = path.join(here, 'ark-check.mjs');
@@ -57,6 +60,13 @@ function parseArgs(argv) {
57
60
  force: false,
58
61
  strict: true,
59
62
  install: true,
63
+ installExplicit: false,
64
+ apply: false,
65
+ json: false,
66
+ internalApply: false,
67
+ skipPackageManager: false,
68
+ removeHost: undefined,
69
+ requireWriteHook: undefined,
60
70
  help: false,
61
71
  version: false,
62
72
  };
@@ -77,10 +87,25 @@ function parseArgs(argv) {
77
87
  else if (arg === '--yes' || arg === '-y') args.yes = true;
78
88
  else if (arg === '--force') args.force = true;
79
89
  else if (arg === '--no-strict') args.strict = false;
80
- 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();
81
103
  else if (arg === '--preset') args.preset = requireValue(arg, i++);
82
104
  else if (arg === '--archetype') args.archetype = requireValue(arg, i++);
83
105
  else if (arg === '--tools') args.tools = requireValue(arg, i++);
106
+ else if (arg === '--require-write-hook') {
107
+ args.requireWriteHook = requireValue(arg, i++).trim().toLowerCase();
108
+ }
84
109
  else if (arg === '--help' || arg === '-h' || arg === 'help') args.help = true;
85
110
  else if (arg === '--version' || arg === '-V') args.version = true;
86
111
  else if (!arg.startsWith('-') && args.command === undefined) args.command = arg;
@@ -92,14 +117,13 @@ function parseArgs(argv) {
92
117
 
93
118
  function usage() {
94
119
  return `Usage:
95
- ark start [--root <project>] [--yes]
120
+ ark start [--root <project>] [--tools <host>] [--require-write-hook <host>] [--install] [--apply] [--json]
96
121
  ark init [--root <project>] [--preset hexagonal|layered|feature-sliced|monorepo|ui-surface|vertical-slice|ddd-bounded-contexts|clean-architecture|onion-architecture]
97
- [--archetype <playbook-id>] [--tools <list>] [--yes] [--force] [--no-strict]
122
+ [--archetype <playbook-id>] [--tools <list>] [--require-write-hook <host>] [--yes] [--force] [--no-strict]
98
123
  ark upgrade [--root <project>] [--no-install] [--no-strict]
99
124
 
100
125
  Commands:
101
- start New here? The guided setup. Looks at your project, suggests a shape in
102
- 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.
103
127
  init Configure Ark project enforcement with explicit prompts.
104
128
  upgrade One command to update Ark: bump the package to @latest, refresh gate
105
129
  templates + /ark-* skills (and Codex home prompts), migrate command
@@ -111,14 +135,22 @@ Options:
111
135
  (Also the implicit default when stdin/stdout are not a TTY — agents never hang on prompts.)
112
136
  --force Allow generated files to overwrite existing files.
113
137
  --no-strict Skip the final strict ark-check run.
138
+ --install Add arkgate to package.json explicitly before applying a start plan.
114
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.
115
142
  --preset Start from a named architecture preset instead of detection.
116
143
  --archetype Application shape from templates/architecture-playbook.json (maps to the matching preset).
117
144
  Valid ids: crud-product, api-backend, frontend-surface, library-sdk, cli-utility,
118
145
  worker-pipeline, event-coordinator, integration-bridge, multi-app-workspace, prototype-spike,
119
146
  vertical-slice-product, ddd-bounded-contexts.
120
- --tools Comma-separated agents to gate (claude,cursor,codex,grok,windsurf,cline,copilot,kiro,roo,continue,gemini).
121
- 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>.
151
+ --require-write-hook <host>
152
+ Require and verify a hard local write hook for Claude or Grok. Cursor/Codex are
153
+ advisory-write plus hard CI merge only; impossible requests fail before any write.
122
154
 
123
155
  Interactive mode (TTY, no --yes): asks what application shape you are building and maps it to a preset.
124
156
  Non-interactive (no TTY): uses the same defaults as --yes — never calls readline on a null interface.
@@ -203,7 +235,7 @@ async function upgrade(args) {
203
235
  }
204
236
  console.log('\n4/4 Verifying architecture…');
205
237
  return runArkCheck(
206
- ['--root', root, '--config', 'ark.config.json', '--strict-config'],
238
+ ['--root', root, '--config', 'ark.config.json', '--strict-merge'],
207
239
  { cwd: root }
208
240
  );
209
241
  }
@@ -221,6 +253,25 @@ function isInteractiveTty() {
221
253
  return Boolean(process.stdin.isTTY && process.stdout.isTTY);
222
254
  }
223
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
+
224
275
  /**
225
276
  * True when prompts should be skipped and guided defaults applied.
226
277
  * Agents typically have no TTY — never call readline on a null interface.
@@ -365,6 +416,9 @@ async function init(args) {
365
416
  if (installGates) {
366
417
  const gateArgs = ['--root', root, '--install-agent-gates'];
367
418
  if (args.tools) gateArgs.push('--tools', args.tools);
419
+ if (args.requireWriteHook) {
420
+ gateArgs.push('--require-write-hook', args.requireWriteHook);
421
+ }
368
422
  if (args.force) gateArgs.push('--force');
369
423
  const status = runArkCheck(gateArgs, { cwd: root });
370
424
  if (status !== 0) return status;
@@ -374,14 +428,15 @@ async function init(args) {
374
428
  args.strict &&
375
429
  (nonInteractive || (await askYesNo(rl, 'Run strict architecture check now?', true)));
376
430
  if (runStrict) {
377
- return runArkCheck(
378
- ['--root', root, '--config', 'ark.config.json', '--strict-config'],
379
- { cwd: root }
380
- );
431
+ const strictArgs = ['--root', root, '--config', 'ark.config.json', '--strict-merge'];
432
+ if (args.requireWriteHook) {
433
+ strictArgs.push('--require-write-hook', args.requireWriteHook);
434
+ }
435
+ return runArkCheck(strictArgs, { cwd: root });
381
436
  }
382
437
 
383
438
  console.log(
384
- `Ark init complete. Run \`${arkCommand(root, 'ark-check', '--root . --config ark.config.json --strict-config')}\` before merging.`
439
+ `Ark init complete. Run \`${arkCommand(root, 'ark-check', '--root . --config ark.config.json --strict-merge')}\` before merging.`
385
440
  );
386
441
  if (archetype) {
387
442
  console.log(`Shape: ${archetype}. Plan: ${arkCommand(root, 'ark-check', '--recommend')}`);
@@ -398,9 +453,24 @@ async function init(args) {
398
453
 
399
454
  // `ark start` — the guided entry point (co-pilot Phase G). One command takes a newcomer from
400
455
  // "I have a project" to "governed, with a plan" in plain language, without knowing any skill
401
- // 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
402
457
  // orchestrates existing steps (recommend → init → --plan) and frames each in outcome terms.
403
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
+ }
404
474
  const root = args.root;
405
475
  const nonInteractive = shouldUseNonInteractiveDefaults(args);
406
476
  const interactive = !nonInteractive;
@@ -411,10 +481,10 @@ async function start(args) {
411
481
  try {
412
482
  console.log("Let's set up Ark for your project.");
413
483
  console.log(
414
- "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."
415
485
  );
416
486
  console.log(
417
- '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.'
418
488
  );
419
489
  if (nonInteractive && !args.yes) {
420
490
  console.log(
@@ -453,10 +523,10 @@ async function start(args) {
453
523
  }
454
524
 
455
525
  // 2b) Pin arkgate as a project devDependency so CI/npx do not depend on a stale global.
456
- if (args.install !== false && fs.existsSync(path.join(root, 'package.json'))) {
526
+ if (args.installExplicit && args.install && fs.existsSync(path.join(root, 'package.json'))) {
457
527
  const { pinned, installStatus } = ensureProjectArkgateDependency(root, {
458
528
  install: true,
459
- runPackageManager: true,
529
+ runPackageManager: !args.skipPackageManager,
460
530
  });
461
531
  if (pinned.changed) {
462
532
  console.log(` Pinned arkgate@${pinned.version} in package.json devDependencies.`);
@@ -468,7 +538,7 @@ async function start(args) {
468
538
  } else if (pinned.reason === 'already-present') {
469
539
  console.log(` arkgate already in package.json (${pinned.version}).`);
470
540
  }
471
- } else if (args.install === false) {
541
+ } else if (args.installExplicit && args.install === false) {
472
542
  console.log(' Skipping arkgate package pin (--no-install).');
473
543
  }
474
544
 
@@ -482,10 +552,11 @@ async function start(args) {
482
552
  const preset = archetype ? resolveArchetypePreset(archetype).preset : undefined;
483
553
  const includeRoots = resolveIncludeRoots(root);
484
554
  const tsPackages = detectTsPackageRoots(root);
555
+ const nestedTsPackages = tsPackages.filter((entry) => entry !== '.');
485
556
  const workspaces = detectWorkspaces(root);
486
557
  const looksLikeMonorepo =
487
558
  includeRoots.length > 0 ||
488
- tsPackages.length > 0 ||
559
+ nestedTsPackages.length > 0 ||
489
560
  workspaces.length > 0 ||
490
561
  fs.existsSync(path.join(root, 'rush.json')) ||
491
562
  fs.existsSync(path.join(root, 'pnpm-workspace.yaml')) ||
@@ -498,9 +569,9 @@ async function start(args) {
498
569
  const useUi =
499
570
  rec?.preset === 'feature-sliced' ||
500
571
  rec?.archetype === 'frontend-surface' ||
501
- (tsPackages.length > 0 && includeRoots.length === 0 && !rec?.mature);
502
- initArgs.push('--preset', useUi && tsPackages.length <= 3 ? 'ui-surface' : 'monorepo');
503
- 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;
504
575
  console.log(
505
576
  shown.length > 0
506
577
  ? ` Multi-package / TS package layout detected — profile include: ${shown.join(', ')}.`
@@ -515,19 +586,23 @@ async function start(args) {
515
586
  console.log(' Found an existing ark.config.json — keeping it.');
516
587
  }
517
588
 
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).
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.
520
591
  console.log('');
521
- freezeDayZeroOrigin(root);
592
+ console.log('Skipping day-zero report archive in compact start (run ark-check --report later if wanted).');
522
593
 
523
- // 5) Agent + CI gate templates (docs, hooks, skills) after origin is frozen.
594
+ // 5) Agent + CI gate templates for exactly one active host.
524
595
  console.log('');
525
596
  console.log('Installing agent and CI gate templates…');
526
597
  {
527
- const gateArgs = ['--root', root, '--install-agent-gates'];
598
+ const gateArgs = ['--root', root, '--install-agent-gates', '--compact'];
528
599
  if (args.tools) gateArgs.push('--tools', args.tools);
600
+ if (args.requireWriteHook) {
601
+ gateArgs.push('--require-write-hook', args.requireWriteHook);
602
+ }
529
603
  if (args.force) gateArgs.push('--force');
530
- runArkCheck(gateArgs, { cwd: root });
604
+ const status = runArkCheck(gateArgs, { cwd: root });
605
+ if (status !== 0) return status;
531
606
  }
532
607
 
533
608
  // 6) Show the plan: what's safe to auto-fix vs what needs a decision.
@@ -664,14 +739,14 @@ async function start(args) {
664
739
  console.log(' → explore first, dual plan (remediation + pattern bets), safe fixes, leave gates on.');
665
740
  }
666
741
  console.log(` 2. Status anytime: ${arkCommand(root, 'ark-check', '--doctor')}`);
667
- console.log(` 3. After edits: ${arkCommand(root, 'ark-check', '--root . --config ark.config.json --strict-config')}`);
742
+ console.log(` 3. After edits: ${arkCommand(root, 'ark-check', '--root . --config ark.config.json --strict-merge')}`);
668
743
  if (mode === 'adapt' && planOk && !falseGreenGap) {
669
744
  console.log(
670
745
  ` 4. When green but cores still optional: ${arkCommand(root, 'ark-check', '--ratchet-cores')} → honest ENFORCE`
671
746
  );
672
747
  }
673
748
  console.log('');
674
- 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).');
675
750
  console.log('Optional later: --plan · --coverage · /ark-explore · /ark-fix · /ark-place · ark upgrade');
676
751
  return 0;
677
752
  } finally {
@@ -696,6 +771,25 @@ async function main() {
696
771
  return 0;
697
772
  }
698
773
 
774
+ if (args.requireWriteHook && !['start', 'init'].includes(args.command)) {
775
+ console.error('--require-write-hook is supported by ark start and ark init.');
776
+ return 2;
777
+ }
778
+ const enforcement = validateHardWriteRequest({
779
+ root: args.root,
780
+ host: args.requireWriteHook,
781
+ tools: args.tools,
782
+ force: args.force,
783
+ });
784
+ if (!enforcement.ok) {
785
+ console.error(enforcement.error);
786
+ return 2;
787
+ }
788
+ if (enforcement.host) {
789
+ args.requireWriteHook = enforcement.host;
790
+ if (!args.tools) args.tools = enforcement.tools.join(',');
791
+ }
792
+
699
793
  if (args.command === 'start') {
700
794
  try {
701
795
  return await start(args);
@@ -729,9 +823,15 @@ async function main() {
729
823
  }
730
824
 
731
825
  // Only run when executed as the CLI entry (not when imported by unit tests).
732
- const isMain =
733
- Boolean(process.argv[1]) &&
734
- 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));
735
835
  if (isMain) {
736
836
  process.exitCode = await main();
737
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,
@@ -76,6 +77,8 @@ export {
76
77
  normalizeToolsList,
77
78
  resolveTools,
78
79
  KNOWN_TOOLS,
80
+ detectActiveAgentHost,
81
+ codexConcernIsActive,
79
82
  arkPackageVersion,
80
83
  stampSkill,
81
84
  installedSkillVersion,
@@ -95,6 +98,16 @@ export {
95
98
  collectAdoptionGaps,
96
99
  } from './mcp-adoption.mjs';
97
100
 
101
+ export {
102
+ detectPreCommitArk,
103
+ detectCiEnforcement,
104
+ detectConfigGateDrift,
105
+ jobIdsThatRunArkCheck,
106
+ isArkRequiredStatusCheck,
107
+ reportGithubBranchProtection,
108
+ collectWeakestLinkGaps,
109
+ } from './weakest-link.mjs';
110
+
98
111
  export {
99
112
  staleRunnerGateFiles,
100
113
  warnLockfileConflict,