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.
- package/CHANGELOG.md +122 -0
- package/README.md +90 -51
- package/bin/ark-check.mjs +156 -39
- package/bin/ark-mcp.mjs +119 -6
- package/bin/ark-shared.mjs +216 -129
- package/bin/ark.mjs +134 -34
- package/bin/lib/adapter-contract.mjs +93 -0
- package/bin/lib/agent-gates.mjs +13 -0
- package/bin/lib/analysis-engine.mjs +1171 -0
- package/bin/lib/architecture-scan.mjs +84 -127
- package/bin/lib/ci-and-commands.mjs +40 -3
- package/bin/lib/codex-home.mjs +7 -0
- package/bin/lib/config-contract.mjs +331 -0
- package/bin/lib/config-warnings.mjs +7 -205
- package/bin/lib/doctor-plan.mjs +43 -16
- package/bin/lib/enforcement-profiles.mjs +97 -0
- package/bin/lib/field-install.mjs +67 -10
- package/bin/lib/gate-files.mjs +42 -3
- package/bin/lib/graph-cycles.mjs +4 -54
- package/bin/lib/hook-templates.mjs +33 -1
- package/bin/lib/host-support-matrix.mjs +83 -0
- package/bin/lib/install-migrate.mjs +99 -30
- package/bin/lib/mcp-adoption.mjs +35 -3
- package/bin/lib/open-html.mjs +75 -0
- package/bin/lib/presets.mjs +45 -4
- package/bin/lib/safety-diagnostics.mjs +36 -15
- package/bin/lib/scan-files.mjs +12 -1
- package/bin/lib/skill-install.mjs +72 -1
- package/bin/lib/source-policy.mjs +36 -0
- package/bin/lib/start-preview.mjs +271 -0
- package/bin/lib/ts-resolve.mjs +13 -3
- package/bin/lib/weakest-link.mjs +417 -0
- package/bin/lib/write-path-capabilities.mjs +186 -0
- package/bin/lib/write-path-detect.mjs +62 -99
- package/compat/nestjs.cjs +2 -0
- package/compat/nestjs.d.ts +2 -0
- package/compat/nestjs.js +1 -0
- package/compat/runtime.cjs +2 -0
- package/compat/runtime.d.ts +2 -0
- package/compat/runtime.js +1 -0
- package/dist/configContract-BxSIwVRo.d.cts +259 -0
- package/dist/configContract-BxSIwVRo.d.ts +259 -0
- package/dist/eslint/index.cjs +500 -61
- package/dist/eslint/index.d.cts +36 -20
- package/dist/eslint/index.d.ts +36 -20
- package/dist/eslint/index.js +500 -61
- package/dist/index.cjs +1349 -2741
- package/dist/index.d.cts +449 -483
- package/dist/index.d.ts +449 -483
- package/dist/index.js +1325 -2687
- package/docs/agent-guide.md +58 -34
- package/docs/ai-gates.md +79 -21
- package/docs/configuration.md +97 -0
- package/docs/enthusiast/README.md +3 -3
- package/docs/enthusiast/how-to-agent-gates.md +7 -3
- package/docs/migrate-from-ark-runtime-kernel.md +5 -3
- package/docs/package-surface.md +19 -19
- package/docs/production-hardening.md +31 -5
- package/docs/threat-model.md +65 -0
- package/docs/typescript-support.md +30 -3
- package/package.json +46 -11
- package/schemas/ark.analysis-result.schema.json +91 -0
- package/schemas/ark.config.schema.json +750 -0
- package/server.json +2 -2
- package/templates/hooks/pre-commit-ark +37 -0
- package/templates/skills/ark-architect.md +3 -2
- package/templates/skills/ark-coverage.md +2 -2
- package/templates/skills/ark-runtime.md +8 -5
- package/templates/skills/ark-upgrade.md +36 -16
- package/tests/fixtures/ts-consumer/ark.config.json +2 -0
- package/dist/eslint/index.cjs.map +0 -1
- package/dist/eslint/index.js.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/nestjs/index.cjs +0 -2498
- package/dist/nestjs/index.cjs.map +0 -1
- package/dist/nestjs/index.d.cts +0 -22
- package/dist/nestjs/index.d.ts +0 -22
- package/dist/nestjs/index.js +0 -2474
- package/dist/nestjs/index.js.map +0 -1
- package/dist/runtime/index.cjs +0 -3352
- package/dist/runtime/index.cjs.map +0 -1
- package/dist/runtime/index.d.cts +0 -2
- package/dist/runtime/index.d.ts +0 -2
- package/dist/runtime/index.js +0 -3270
- package/dist/runtime/index.js.map +0 -1
- package/dist/types-BZ17b9i5.d.cts +0 -1068
- 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 === '--
|
|
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>] [--
|
|
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?
|
|
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
|
|
121
|
-
Omit to
|
|
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-
|
|
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
|
-
|
|
378
|
-
|
|
379
|
-
|
|
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-
|
|
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
|
|
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,
|
|
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,
|
|
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.
|
|
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:
|
|
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
|
-
|
|
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
|
-
(
|
|
502
|
-
initArgs.push('--preset', useUi &&
|
|
503
|
-
const shown = includeRoots.length > 0 ? includeRoots :
|
|
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)
|
|
519
|
-
//
|
|
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
|
-
|
|
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
|
|
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-
|
|
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('
|
|
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
|
-
|
|
733
|
-
|
|
734
|
-
|
|
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
|
+
};
|
package/bin/lib/agent-gates.mjs
CHANGED
|
@@ -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,
|