arkgate 4.6.1 → 4.6.2
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 +30 -1
- package/README.md +5 -3
- package/bin/ark-check-runtime.mjs +23 -127
- package/bin/ark-mcp-runtime.mjs +70 -48
- package/bin/ark.mjs +19 -78
- package/bin/lib/doctor-next-actions.mjs +92 -0
- package/bin/lib/doctor-plan.mjs +59 -69
- package/bin/lib/first-run-help.mjs +221 -0
- package/bin/lib/start-preview.mjs +17 -10
- package/bin/lib/status-command.mjs +5 -0
- package/bin/lib/status-manifest.mjs +6 -0
- package/dist/index.cjs +19 -19
- package/dist/index.d.ts +6 -1
- package/dist/index.js +22 -22
- package/docs/README.md +5 -5
- package/docs/agent-guide.md +1 -1
- package/docs/enthusiast/how-to-agent-gates.md +1 -1
- package/docs/package-surface.md +3 -3
- package/docs/use.md +4 -4
- package/package.json +2 -2
- package/server.json +3 -3
- package/templates/agent-skills/README.md +1 -1
- package/templates/agent-skills/ark-adopt/SKILL.md +14 -5
- package/templates/agent-skills/ark-architect/SKILL.md +2 -2
- package/templates/agent-skills/ark-autopilot/SKILL.md +12 -5
- package/templates/agent-skills/ark-contract/SKILL.md +1 -1
- package/templates/agent-skills/ark-coverage/SKILL.md +6 -5
- package/templates/agent-skills/ark-explain/SKILL.md +3 -2
- package/templates/agent-skills/ark-explore/SKILL.md +13 -4
- package/templates/agent-skills/ark-fix/SKILL.md +1 -1
- package/templates/agent-skills/ark-loop/SKILL.md +1 -1
- package/templates/agent-skills/ark-place/SKILL.md +10 -1
- package/templates/agent-skills/ark-think/SKILL.md +3 -2
- package/templates/agent-skills/ark-upgrade/SKILL.md +10 -3
- package/templates/skills/ark-adopt.md +14 -5
- package/templates/skills/ark-architect.md +2 -2
- package/templates/skills/ark-autopilot.md +12 -5
- package/templates/skills/ark-contract.md +1 -1
- package/templates/skills/ark-coverage.md +6 -5
- package/templates/skills/ark-explain.md +3 -2
- package/templates/skills/ark-explore.md +13 -4
- package/templates/skills/ark-fix.md +1 -1
- package/templates/skills/ark-loop.md +1 -1
- package/templates/skills/ark-place.md +10 -1
- package/templates/skills/ark-think.md +3 -2
- package/templates/skills/ark-upgrade.md +10 -3
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,33 @@ in the immutable pre-2.0 archive linked below.
|
|
|
5
5
|
|
|
6
6
|
## Unreleased
|
|
7
7
|
|
|
8
|
+
## 4.6.2 — 2026-08-16
|
|
9
|
+
|
|
10
|
+
**Patch** over **4.6.1**. First-contact copy: a newcomer (human or coding agent) sees what
|
|
11
|
+
to do in a few lines — `arkgate` / `arkgate-check --help`, start wrap-up, doctor light +
|
|
12
|
+
#1, write-gate deny, SessionStart, MCP tool order, and the five doors. Same 13 skill names.
|
|
13
|
+
**No required config migration.**
|
|
14
|
+
|
|
15
|
+
**Status: prepared** (not yet on npm `latest`; see `docs/releases/4.6.2.md`).
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- **First-run help:** `arkgate --help` and `arkgate-check --help` are short; encyclopedia
|
|
20
|
+
text is `--help --all`. `arkgate upgrade --help` is preview vs apply.
|
|
21
|
+
- **Start wrap-up:** doctor → `/ark-adopt` session 0 (not `/ark-autopilot` as step 1).
|
|
22
|
+
- **Doctor:** operating-mode light + primary next action #1 print first.
|
|
23
|
+
- **Write-gate deny:** `blocked {file} — {reason}` then `Next:` (move the import / `/ark-place`).
|
|
24
|
+
Rule id on a following line. No “call ark_manifest”.
|
|
25
|
+
- **Agents:** SessionStart points at `/ark-adopt` or `arkgate-check --doctor`. `ark_identity`
|
|
26
|
+
is first. `ark_check` is a scan (pass/fail/incomplete), not a yes/no architecture score.
|
|
27
|
+
`server.json` first sentence is the layers definition.
|
|
28
|
+
- **Skills:** five doors open with when + steps. Shortcuts are not the first-run menu.
|
|
29
|
+
- **Status:** `nextAction` is `map-leftover-design` when leftover design work remains
|
|
30
|
+
(never `stay-enforced`).
|
|
31
|
+
- **npm `description`:** `One architecture config. One check. One coach.` (not “co-pilot”).
|
|
32
|
+
- **`docs/use.md`:** Cursor hard-blocks Write/StrReplace when hooks are trusted;
|
|
33
|
+
Codex/OpenCode stay advisory.
|
|
34
|
+
|
|
8
35
|
## 4.6.1 — 2026-08-14
|
|
9
36
|
|
|
10
37
|
**Patch** over **4.6.0**. Five-door autonomy (skills write or map in-turn; CLI is sensor +
|
|
@@ -12,7 +39,7 @@ gate) plus team parliament (law vs feature: stewards, mixed-PR deny, ratchet vs
|
|
|
12
39
|
base, cheap `--changed` check). Same 13 skill names. Steward identity is a GitHub handle or
|
|
13
40
|
email, not git `user.name`. **No required config migration.**
|
|
14
41
|
|
|
15
|
-
**Status:
|
|
42
|
+
**Status: published** (on npm `latest`; see `docs/releases/4.6.1.md`).
|
|
16
43
|
|
|
17
44
|
### Added
|
|
18
45
|
|
|
@@ -28,6 +55,8 @@ email, not git `user.name`. **No required config migration.**
|
|
|
28
55
|
|
|
29
56
|
- Doctor, compact router, and public lanes prefer the five doors. Historical changelogs stay
|
|
30
57
|
as shipped.
|
|
58
|
+
- Published 4.6.1 tarball `README.md` still banners 4.6.0 (packed at `1eadc96` before the
|
|
59
|
+
pointer flip). Tree README on `main` is current. No 4.6.2 for that banner.
|
|
31
60
|
|
|
32
61
|
## 4.6.0 — 2026-08-12
|
|
33
62
|
|
package/README.md
CHANGED
|
@@ -16,8 +16,9 @@ and makes sure a “green” check means something real.
|
|
|
16
16
|
|
|
17
17
|
</div>
|
|
18
18
|
|
|
19
|
-
> **ArkGate 4.6.
|
|
20
|
-
>
|
|
19
|
+
> **ArkGate 4.6.1** is on npm `latest` — five doors + a lock on the constitution.
|
|
20
|
+
> **4.6.2** is prepared on this tree (first-contact copy; not yet `latest`).
|
|
21
|
+
> [4.6.2 notes](docs/releases/4.6.2.md) · [4.6.1](docs/releases/4.6.1.md) · [4.6.0](docs/releases/4.6.0.md) · [Docs hub](docs/README.md) · [Product voice](docs/product-voice.md)
|
|
21
22
|
|
|
22
23
|
---
|
|
23
24
|
|
|
@@ -213,7 +214,8 @@ for real systems. Details: [docs/production-hardening.md](docs/production-harden
|
|
|
213
214
|
| Config · package surface · TS | [configuration](docs/configuration.md) · [package-surface](docs/package-surface.md) · [typescript-support](docs/typescript-support.md) |
|
|
214
215
|
| Brownfield | [docs/brownfield-adoption.md](docs/brownfield-adoption.md) |
|
|
215
216
|
| Security | [SECURITY.md](SECURITY.md) |
|
|
216
|
-
| Current release (4.6.
|
|
217
|
+
| Current release (4.6.1 on npm `latest`) | [docs/releases/4.6.1.md](docs/releases/4.6.1.md) · [CHANGELOG](CHANGELOG.md) |
|
|
218
|
+
| Prepared (4.6.2 — not yet `latest`) | [docs/releases/4.6.2.md](docs/releases/4.6.2.md) |
|
|
217
219
|
| Prior (4.5.7) | [docs/releases/4.5.7.md](docs/releases/4.5.7.md) |
|
|
218
220
|
| Prior (4.5.0) | [docs/releases/4.5.0.md](docs/releases/4.5.0.md) |
|
|
219
221
|
| Prior (4.4.0) | [docs/releases/4.4.0.md](docs/releases/4.4.0.md) |
|
|
@@ -95,6 +95,7 @@ import {
|
|
|
95
95
|
writeArkRulesTemplates,
|
|
96
96
|
} from './lib/presets.mjs';
|
|
97
97
|
import { loadArkConfigContract, parseArkConfigJson } from './lib/config-contract.mjs';
|
|
98
|
+
import { checkUsage, checkUsageAll } from './lib/first-run-help.mjs';
|
|
98
99
|
import { createAdapterResult } from './lib/adapter-contract.mjs';
|
|
99
100
|
import { collectGovernedFiles, normalize, walk } from './lib/scan-files.mjs';
|
|
100
101
|
import { configWarning } from './lib/config-warnings.mjs';
|
|
@@ -166,12 +167,13 @@ function parseArgs(argv) {
|
|
|
166
167
|
noOpenReport: false,
|
|
167
168
|
version: false,
|
|
168
169
|
help: false,
|
|
170
|
+
all: false,
|
|
169
171
|
followConfigRoot: false,
|
|
170
172
|
};
|
|
171
173
|
const requireValue = (flag, index) => {
|
|
172
174
|
const value = argv[index + 1];
|
|
173
175
|
if (value === undefined || value.startsWith('-')) {
|
|
174
|
-
throw new Error(`Missing value for ${flag}. Run
|
|
176
|
+
throw new Error(`Missing value for ${flag}. Run arkgate-check --help for usage.`);
|
|
175
177
|
}
|
|
176
178
|
return value;
|
|
177
179
|
};
|
|
@@ -269,8 +271,9 @@ function parseArgs(argv) {
|
|
|
269
271
|
else if (arg === '--print-config') args.printConfig = requireValue(arg, i++);
|
|
270
272
|
else if (arg === '--tsconfig') args.tsconfig = requireValue(arg, i++);
|
|
271
273
|
else if (arg === '--help' || arg === '-h') args.help = true;
|
|
274
|
+
else if (arg === '--all') args.all = true;
|
|
272
275
|
else if (arg === '--version' || arg === '-V') args.version = true;
|
|
273
|
-
else throw new Error(`Unknown argument: ${arg}. Run
|
|
276
|
+
else throw new Error(`Unknown argument: ${arg}. Run arkgate-check --help for usage.`);
|
|
274
277
|
}
|
|
275
278
|
return args;
|
|
276
279
|
}
|
|
@@ -283,116 +286,6 @@ function displayPathFromRoot(root, absPath) {
|
|
|
283
286
|
}
|
|
284
287
|
return rel.split(path.sep).join('/');
|
|
285
288
|
}
|
|
286
|
-
function usage() {
|
|
287
|
-
return [
|
|
288
|
-
'Usage: arkgate-check | ark-check (identical bins; product name ArkGate)',
|
|
289
|
-
' ark-check --version',
|
|
290
|
-
' ark-check --root <project> --config <ark.config.json> [--manifest <ark.manifest.json>] [--tsconfig <tsconfig.json>] [--strict-merge | --strict | --strict-config] [--policy-base <file> | --policy-base-ref <git-ref>] [--policy-ack <file>] [--fail-on-new-smells --base-ref <git-ref>] [--contract-diff] [--contract-session] [--changed] [--against <git-ref>] [--base <git-ref>] [--persona touch|contributor|agent|steward] [--author <id>] [--require-gates] [--require-write-hook <host>] [--json] [--baseline [file]] [--report [file.html]] [--no-cache]',
|
|
291
|
-
' ark-check --doctor [--json] [--resident] [--fail-on-new-smells --base-ref <git-ref>] read-only diagnosis; resident JSON falls back cold',
|
|
292
|
-
' ark-check --coverage [--json] per-layer file counts + full unclassified list (report only, exit 0)',
|
|
293
|
-
' ark-check --plan [--json] classified remediation plan (mechanical-safe / judgment / deferred) + goal; report only',
|
|
294
|
-
' ark-check --rules-inventory [--json] brownfield rules inventory (AR13; deterministic candidates, not a score)',
|
|
295
|
-
' ark-check --recommend [--json] [--write-plan] application-shape plan; --write-plan emits ark-adoption-plan.json',
|
|
296
|
-
' ark-check --list-policy-packs enthusiast packs (hexagonal, layered, feature-sliced, monorepo, ui-surface, vertical-slice, ddd-bounded-contexts)',
|
|
297
|
-
' ark-check --apply-policy-pack <id> [--force] write ark.config.json from templates/policy-packs/ (uses preset factory)',
|
|
298
|
-
' ark-check --suggest-include [--json] propose include roots (TS packages / workspaces)',
|
|
299
|
-
' ark-check --adopt-contract [--write] expand include + layer patterns from ungoverned dirs (never bare lib→Presentation)',
|
|
300
|
-
' ark-check --migrate-contract [--write] additive P0-A retrofit: inject app/api/** → Application when missing',
|
|
301
|
-
' ark-check --ratchet-cores when raw graph is green (0 violations; baseline ignored), set optional:false on populated cores only (writes ark.config.json)',
|
|
302
|
-
' ark-check --watch re-run the check when governed files change (debounced)',
|
|
303
|
-
' ark-check --report [file.html] [--beginner] [--reset-origin] [--no-archive] [--open|--no-open]',
|
|
304
|
-
' HTML report + snapshots under .ark/reports/ (origin once, latest each run, history JSON)',
|
|
305
|
-
' Best-effort open in browser (local TTY). No-op if open fails. --no-open / ARK_NO_OPEN_REPORT=1 to skip; --open forces open.',
|
|
306
|
-
' ark-check --init [--preset hexagonal|layered|feature-sliced|monorepo|ui-surface|vertical-slice|ddd-bounded-contexts|vite-vercel-spa|clean-architecture|onion-architecture] [--force] [--follow-config-root]',
|
|
307
|
-
' --follow-config-root On writes (init/install-agent-gates/migrate --write/…), adopt walked-up monorepo config root (default: keep explicit --root)',
|
|
308
|
-
' ark-check --install-agent-gates [--tools claude,cursor,codex,grok] [--require-write-hook <host>] [--skills-only] [--codex-home] [--claude-home] [--grok-home] [--agent-homes] [--force]',
|
|
309
|
-
' ark-check --update-baseline [file] freeze current violations (default .ark-baseline.json)',
|
|
310
|
-
' ark-check --print-config eleven-layer',
|
|
311
|
-
'',
|
|
312
|
-
'Adopting Ark in an existing codebase? Run --update-baseline once to freeze existing',
|
|
313
|
-
'violations, commit the baseline file, and gate CI with --baseline: only NEW violations',
|
|
314
|
-
'fail the check, so the ratchet only moves toward zero.',
|
|
315
|
-
'',
|
|
316
|
-
'Team parliament: law files (ark.config / arkrules / .ark-baseline.json) cannot ship in',
|
|
317
|
-
'the same diff as product source. --changed --base <ref> checks touched files only.',
|
|
318
|
-
'--against <ref> ratchets new keys vs that ref\'s baseline. --contract-session is a',
|
|
319
|
-
'steward law-only PR. Loosen / baseline-grow need stewards[] + --author when set.',
|
|
320
|
-
'',
|
|
321
|
-
'--init scans the project for the built-in layer directory conventions (src/domain,',
|
|
322
|
-
'src/application, src/adapters/persistence, ...) and writes an ark.config.json covering',
|
|
323
|
-
'only the layers that actually exist, with the default rules filtered to those layers.',
|
|
324
|
-
'Undetected profile layers are printed as suggestions with their conventional',
|
|
325
|
-
'directories. When nothing is detected, the full 11-layer starter profile is written',
|
|
326
|
-
'instead (all layers optional, anchored at src/), so the strict check passes today and',
|
|
327
|
-
'each layer starts being enforced as soon as its directory gains source files.',
|
|
328
|
-
'',
|
|
329
|
-
'Resolves relative, tsconfig path-alias, and package imports via the TypeScript',
|
|
330
|
-
'module resolver, then checks each resolved cross-layer import against the rules.',
|
|
331
|
-
'Path aliases resolve against the NEAREST tsconfig.json above each source file, so',
|
|
332
|
-
'monorepo packages with per-package configs work under a single --root. Pass',
|
|
333
|
-
'--tsconfig to force one config for every file. If no tsconfig is found, path',
|
|
334
|
-
'aliases are unavailable but relative/package imports still resolve.',
|
|
335
|
-
'',
|
|
336
|
-
'The correctness path resolves and parses one complete candidate on every invocation.',
|
|
337
|
-
'Legacy node_modules/.cache/ark-check.json files are ignored. --no-cache remains an',
|
|
338
|
-
'accepted compatibility no-op; the identity-keyed warm snapshot is introduced in Z07.',
|
|
339
|
-
'',
|
|
340
|
-
'Config shape:',
|
|
341
|
-
'{',
|
|
342
|
-
' "include": ["src"],',
|
|
343
|
-
' // optional: "exclude": ["**/vendor/**"], "excludeGenerated": false (default skips *.gen.ts / *.generated.ts)',
|
|
344
|
-
' "layers": [',
|
|
345
|
-
' { "name": "DomainModel", "patterns": ["src/domain/**"], "intentPrefixes": ["Domain."],',
|
|
346
|
-
' "forbiddenGlobals": ["fetch", "process", "Date.now", "Math.random"] }',
|
|
347
|
-
' ],',
|
|
348
|
-
' "rules": [{ "from": "DomainModel", "to": "PersistenceAdapters", "allowed": false }]',
|
|
349
|
-
'}',
|
|
350
|
-
'',
|
|
351
|
-
'Config warnings are advisory by default and are included in JSON output.',
|
|
352
|
-
'Use --strict-config to make config warnings fail the check.',
|
|
353
|
-
'Use --strict-merge for the fail-closed CI profile: --strict-config + --require-gates',
|
|
354
|
-
'plus the security diagnostics surfaced by doctor. --strict is a compatibility alias.',
|
|
355
|
-
'This merge profile never depends on an editor/agent hook.',
|
|
356
|
-
'When a Git merge base is available, --strict-merge classifies the ark.config.json',
|
|
357
|
-
'transition. Weakening or judgment-required findings fail unless --policy-ack names',
|
|
358
|
-
'every finding and is bound to both policy hashes. Use --policy-base/--policy-base-ref',
|
|
359
|
-
'for an explicit comparison; ARK_POLICY_BASE_REF is the CI environment equivalent.',
|
|
360
|
-
'Add --require-write-hook claude|grok|antigravity|cursor to validate a hard local write',
|
|
361
|
-
'boundary for that specific host. Codex and OpenCode expose advisory MCP (plus best-effort',
|
|
362
|
-
'hooks where applicable) and the shared CI check; merge blocking requires repository policy',
|
|
363
|
-
'to make that status required.',
|
|
364
|
-
'',
|
|
365
|
-
'--require-gates implies --strict-config and fails when the Ark contract in AGENTS.md,',
|
|
366
|
-
'the project-rooted Ark server in .mcp.json, or fail-closed CI is missing/invalid.',
|
|
367
|
-
'Included but unclassified source files therefore stay red instead of false-green.',
|
|
368
|
-
'',
|
|
369
|
-
'--install-agent-gates writes AGENTS.md, .mcp.json, and the CI workflow for every',
|
|
370
|
-
'project, plus tool-specific templates. Known tools: claude, cursor, codex, grok',
|
|
371
|
-
'(Claude/Grok/Antigravity/Cursor hard-write hooks when covered; Codex advisory MCP;',
|
|
372
|
-
'shared CI check for all) and',
|
|
373
|
-
'windsurf, cline, copilot, kiro, roo, continue, gemini',
|
|
374
|
-
'(instruction-tier rule files derived from the same contract).',
|
|
375
|
-
'It also installs the /ark-* skills shipped in templates/skills/ into each',
|
|
376
|
-
'detected tool\'s command location (.claude/skills/, .cursor/commands/,',
|
|
377
|
-
'.agents/skills/ (Codex REPO catalog), .grok/skills/, .windsurf/workflows/,',
|
|
378
|
-
'.clinerules/workflows/, .github/prompts/).',
|
|
379
|
-
'Kiro, Roo, Continue, and Gemini have no command mechanism and receive only their',
|
|
380
|
-
'rule file. Existing files are never overwritten without --force, so re-running',
|
|
381
|
-
'after an update only adds what is missing. --skills-only restricts the write to',
|
|
382
|
-
'just the /ark-* skills (safe to --force-refresh — it leaves a customized AGENTS.md,',
|
|
383
|
-
'settings, and CI workflow untouched).',
|
|
384
|
-
'Pass --tools to pick which tool configs to write; otherwise they are auto-detected',
|
|
385
|
-
'from their config directories (.claude/, .cursor/, .codex/, .grok/, .windsurf/,',
|
|
386
|
-
'.clinerules/, .kiro/, .roo/, .continue/, .gemini/; copilot is explicit-only).',
|
|
387
|
-
'claude+cursor+codex+grok are written when nothing is detected.',
|
|
388
|
-
'',
|
|
389
|
-
'Generate a starter 11-layer config:',
|
|
390
|
-
' ark-check --print-config eleven-layer > ark.config.json',
|
|
391
|
-
'',
|
|
392
|
-
'Install agent + CI enforcement templates:',
|
|
393
|
-
' ark-check --install-agent-gates',
|
|
394
|
-
].join('\n');
|
|
395
|
-
}
|
|
396
289
|
|
|
397
290
|
function readConfig(root, configPath) {
|
|
398
291
|
const fullPath = path.isAbsolute(configPath)
|
|
@@ -1148,7 +1041,7 @@ async function main() {
|
|
|
1148
1041
|
process.exit(0);
|
|
1149
1042
|
}
|
|
1150
1043
|
if (args.help) {
|
|
1151
|
-
console.log(
|
|
1044
|
+
console.log(args.all ? checkUsageAll() : checkUsage());
|
|
1152
1045
|
return;
|
|
1153
1046
|
}
|
|
1154
1047
|
if (args.init) {
|
|
@@ -1706,20 +1599,23 @@ async function main() {
|
|
|
1706
1599
|
}),
|
|
1707
1600
|
}
|
|
1708
1601
|
: null;
|
|
1709
|
-
const currentSnapshot =
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1602
|
+
const currentSnapshot = {
|
|
1603
|
+
...buildReportSnapshot({
|
|
1604
|
+
root,
|
|
1605
|
+
config,
|
|
1606
|
+
coverage,
|
|
1607
|
+
violations: activeViolations,
|
|
1608
|
+
ok,
|
|
1609
|
+
suppressed: suppressed.length,
|
|
1610
|
+
version: arkPackageVersion(),
|
|
1611
|
+
fileCountByLayer,
|
|
1612
|
+
enforcement: enforcementForReport,
|
|
1613
|
+
score: fitness.score,
|
|
1614
|
+
mode: fitness.mode,
|
|
1615
|
+
improvementCompass: reportCompass,
|
|
1616
|
+
}),
|
|
1617
|
+
leftoverDesignWork: designDepth?.designFitness?.designWeak === true,
|
|
1618
|
+
};
|
|
1723
1619
|
const reportPayload = {
|
|
1724
1620
|
root,
|
|
1725
1621
|
config,
|
package/bin/ark-mcp-runtime.mjs
CHANGED
|
@@ -643,6 +643,25 @@ function emitHostAllow(output, { antigravityStyle, cursorStyle }) {
|
|
|
643
643
|
emitCursorAllow(output, cursorStyle);
|
|
644
644
|
}
|
|
645
645
|
|
|
646
|
+
/**
|
|
647
|
+
* Socket-style write-gate deny: two lines first. Pass/fail, no score.
|
|
648
|
+
* Rule id stays on a following line, not the first sentence.
|
|
649
|
+
*/
|
|
650
|
+
function formatWriteGateDeny({ file, reason, ruleId, nextAction, extraLines = [] }) {
|
|
651
|
+
const target = file || 'this write';
|
|
652
|
+
const why = String(reason || 'this change breaks the architecture layers').replace(/\s+/g, ' ').trim();
|
|
653
|
+
const next =
|
|
654
|
+
nextAction && /place|move|import|port/i.test(nextAction)
|
|
655
|
+
? nextAction
|
|
656
|
+
: 'Move the import or run /ark-place. Do not weaken ark.config.json.';
|
|
657
|
+
const lines = [`blocked ${target} — ${why}`, `Next: ${next}`];
|
|
658
|
+
if (ruleId) lines.push(`[${ruleId}]`);
|
|
659
|
+
for (const extra of extraLines) {
|
|
660
|
+
if (extra) lines.push(extra);
|
|
661
|
+
}
|
|
662
|
+
return lines.join('\n');
|
|
663
|
+
}
|
|
664
|
+
|
|
646
665
|
function runHookPayload(payload, gate, config, args, ts, attemptContext, output = processHookOutput()) {
|
|
647
666
|
const { toolName, toolInput, grokStyle, antigravityStyle, cursorStyle, operation } =
|
|
648
667
|
normalizeHookPayload(
|
|
@@ -796,17 +815,20 @@ function runHookPayload(payload, gate, config, args, ts, attemptContext, output
|
|
|
796
815
|
emitHostAllow(output, { antigravityStyle, cursorStyle });
|
|
797
816
|
return;
|
|
798
817
|
}
|
|
799
|
-
const
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
818
|
+
const first = result.diagnostics[0];
|
|
819
|
+
const message = formatWriteGateDeny({
|
|
820
|
+
file: `${changes.length} file(s)`,
|
|
821
|
+
reason: first?.message || `this ${toolName} breaks the architecture layers`,
|
|
822
|
+
ruleId: first?.ruleId,
|
|
823
|
+
nextAction: first?.nextAction,
|
|
824
|
+
extraLines: [
|
|
825
|
+
...result.diagnostics.slice(1).map((d) => `[${d.ruleId}] ${d.message}`),
|
|
826
|
+
...(designDelta && !designDelta.valid
|
|
827
|
+
? formatDesignDeltaBlock(designDelta).split('\n').slice(1)
|
|
828
|
+
: []),
|
|
829
|
+
'No project file was written. Fix the complete patch and retry.',
|
|
830
|
+
],
|
|
831
|
+
});
|
|
810
832
|
output.stderr(`${message}\n`);
|
|
811
833
|
if (args.hookRepair) {
|
|
812
834
|
output.stderr(
|
|
@@ -926,13 +948,7 @@ function runHookPayload(payload, gate, config, args, ts, attemptContext, output
|
|
|
926
948
|
})),
|
|
927
949
|
});
|
|
928
950
|
|
|
929
|
-
const
|
|
930
|
-
(diagnostic) =>
|
|
931
|
-
`- [${diagnostic.ruleId}] ${diagnostic.message}${diagnostic.location.line ? ` (line ${diagnostic.location.line})` : ''}\n Next action: ${diagnostic.nextAction}`
|
|
932
|
-
);
|
|
933
|
-
// Surface the per-violation fix hints (the gate carries them in `suggestion`,
|
|
934
|
-
// but the hook was dropping them). Dedupe so two infra violations sharing one
|
|
935
|
-
// hint — e.g. the mayImportInfrastructure escape hatch — print it once.
|
|
951
|
+
const firstDiagnostic = adapterResult.diagnostics[0];
|
|
936
952
|
const suggestions = [
|
|
937
953
|
...new Set(combinedViolations.map((violation) => violation.suggestion).filter(Boolean)),
|
|
938
954
|
];
|
|
@@ -940,27 +956,34 @@ function runHookPayload(payload, gate, config, args, ts, attemptContext, output
|
|
|
940
956
|
// W4: structured repair payload is opt-in (--hook-repair / ARK_HOOK_REPAIR).
|
|
941
957
|
// Default remains hard block with prose only — hosts that cannot re-inject stay clean.
|
|
942
958
|
const repair = Boolean(args.hookRepair);
|
|
943
|
-
const message =
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
959
|
+
const message = formatWriteGateDeny({
|
|
960
|
+
file: rel,
|
|
961
|
+
reason: firstDiagnostic?.message || (layer ? `${layer} write breaks the layers` : 'this write breaks the layers'),
|
|
962
|
+
ruleId: firstDiagnostic?.ruleId,
|
|
963
|
+
nextAction: firstDiagnostic?.nextAction,
|
|
964
|
+
extraLines: [
|
|
965
|
+
...adapterResult.diagnostics.slice(1).map(
|
|
966
|
+
(diagnostic) =>
|
|
967
|
+
`[${diagnostic.ruleId}] ${diagnostic.message}${diagnostic.location.line ? ` (line ${diagnostic.location.line})` : ''}`
|
|
968
|
+
),
|
|
969
|
+
...(suggestions.length > 0 ? suggestions.map((s) => `Fix: ${s}`) : []),
|
|
970
|
+
...(autoPatch && repair
|
|
971
|
+
? [
|
|
972
|
+
`autoPatch available (${autoPatch.remediationKind}, confidence ${autoPatch.confidence}): ` +
|
|
973
|
+
'apply the patched source from ARK_AUTOPATCH_JSON / ARK_REPAIR_JSON on stderr' +
|
|
974
|
+
(grokStyle ? ' (or autoPatch in the deny JSON on stdout)' : '') +
|
|
975
|
+
' instead of re-drafting. Gate still denies this write (never silent apply).',
|
|
976
|
+
]
|
|
977
|
+
: []),
|
|
978
|
+
...(autoPatch && !repair
|
|
979
|
+
? [
|
|
980
|
+
`Mechanical-safe autoPatch is available (${autoPatch.remediationKind}). ` +
|
|
981
|
+
'Enable repair payload with ARK_HOOK_REPAIR=1 or --hook-repair to receive ' +
|
|
982
|
+
'machine-readable source (still hard-blocks; host re-injects).',
|
|
983
|
+
]
|
|
984
|
+
: []),
|
|
985
|
+
],
|
|
986
|
+
});
|
|
964
987
|
output.stderr(message + '\n');
|
|
965
988
|
|
|
966
989
|
if (repair) {
|
|
@@ -1403,7 +1426,7 @@ function printSessionContext(config, profile, forbiddenGlobals, args, configPath
|
|
|
1403
1426
|
const governedPercent = coverage?.coverage?.governed?.percent ?? coverage?.governed?.percent;
|
|
1404
1427
|
if (shouldShowNewHereNudge(args.root, configPath, governedPercent, false)) {
|
|
1405
1428
|
lines.push('');
|
|
1406
|
-
lines.push('New to Ark?
|
|
1429
|
+
lines.push('New to Ark? /ark-adopt or: arkgate-check --doctor');
|
|
1407
1430
|
}
|
|
1408
1431
|
|
|
1409
1432
|
process.stdout.write(`${lines.join('\n')}\n`);
|
|
@@ -2018,9 +2041,9 @@ export async function runArkMcp({ hookInput } = {}) {
|
|
|
2018
2041
|
{
|
|
2019
2042
|
name: 'ark_identity',
|
|
2020
2043
|
description:
|
|
2021
|
-
'
|
|
2022
|
-
'
|
|
2023
|
-
'
|
|
2044
|
+
'First call. Prove this MCP process is the right project: pass project.expectedRoot ' +
|
|
2045
|
+
'(exact absolute root) and reuse the returned projectId. Do this before any other Ark tool. ' +
|
|
2046
|
+
'A missing, unmatched, or different root means restart the host and use the local CLI.',
|
|
2024
2047
|
inputSchema: { type: 'object', properties: {} },
|
|
2025
2048
|
},
|
|
2026
2049
|
{
|
|
@@ -2063,11 +2086,10 @@ export async function runArkMcp({ hookInput } = {}) {
|
|
|
2063
2086
|
{
|
|
2064
2087
|
name: 'ark_check',
|
|
2065
2088
|
description:
|
|
2066
|
-
'
|
|
2067
|
-
'
|
|
2068
|
-
'
|
|
2069
|
-
'
|
|
2070
|
-
'overall verdicts. Applies the baseline automatically when one exists. isError when not ok.',
|
|
2089
|
+
'Scan the project for architecture findings (layer-import violations, forbidden globals, ' +
|
|
2090
|
+
'cycles, config warnings). Returns pass/fail/incomplete plus evidence — not a yes/no ' +
|
|
2091
|
+
'architecture score. Same engine as arkgate-check. Applies the baseline when one exists. ' +
|
|
2092
|
+
'isError when the scan fails. Prefer after ark_identity.',
|
|
2071
2093
|
inputSchema: {
|
|
2072
2094
|
type: 'object',
|
|
2073
2095
|
properties: {
|
package/bin/ark.mjs
CHANGED
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
} from './lib/prepare-change.mjs';
|
|
36
36
|
import { runStatusCommand } from './lib/status-command.mjs';
|
|
37
37
|
import { runAgentProjectionCommand } from './lib/agent-projection-command.mjs';
|
|
38
|
+
import { setupUsage, setupUsageAll, upgradeUsage } from './lib/first-run-help.mjs';
|
|
38
39
|
|
|
39
40
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
40
41
|
const arkCheck = path.join(here, 'ark-check.mjs');
|
|
@@ -90,13 +91,14 @@ function parseArgs(argv) {
|
|
|
90
91
|
check: false,
|
|
91
92
|
stdout: false,
|
|
92
93
|
help: false,
|
|
94
|
+
all: false,
|
|
93
95
|
version: false,
|
|
94
96
|
};
|
|
95
97
|
|
|
96
98
|
const requireValue = (flag, index) => {
|
|
97
99
|
const value = argv[index + 1];
|
|
98
100
|
if (value === undefined || value.startsWith('-')) {
|
|
99
|
-
throw new Error(`Missing value for ${flag}. Run
|
|
101
|
+
throw new Error(`Missing value for ${flag}. Run arkgate --help for usage.`);
|
|
100
102
|
}
|
|
101
103
|
return value;
|
|
102
104
|
};
|
|
@@ -143,79 +145,15 @@ function parseArgs(argv) {
|
|
|
143
145
|
else if (arg === '--check') args.check = true;
|
|
144
146
|
else if (arg === '--stdout') args.stdout = true;
|
|
145
147
|
else if (arg === '--help' || arg === '-h' || arg === 'help') args.help = true;
|
|
148
|
+
else if (arg === '--all') args.all = true;
|
|
146
149
|
else if (arg === '--version' || arg === '-V') args.version = true;
|
|
147
150
|
else if (!arg.startsWith('-') && args.command === undefined) args.command = arg;
|
|
148
|
-
else throw new Error(`Unknown argument: ${arg}. Run
|
|
151
|
+
else throw new Error(`Unknown argument: ${arg}. Run arkgate --help for usage.`);
|
|
149
152
|
}
|
|
150
153
|
|
|
151
154
|
return args;
|
|
152
155
|
}
|
|
153
156
|
|
|
154
|
-
function usage() {
|
|
155
|
-
return `Usage:
|
|
156
|
-
ark start [--root <project>] [--tools <host>] [--require-write-hook <host>] [--install] [--apply] [--json]
|
|
157
|
-
ark init [--root <project>] [--preset hexagonal|layered|feature-sliced|monorepo|ui-surface|vertical-slice|ddd-bounded-contexts|clean-architecture|onion-architecture]
|
|
158
|
-
[--archetype <playbook-id>] [--tools <list>] [--require-write-hook <host>] [--yes] [--force] [--no-strict]
|
|
159
|
-
ark upgrade [--root <project>] [--tools <list>] [--apply] [--plan-digest <sha256>] [--accept-conflicts] [--refresh-skills] [--json] [--no-install] [--no-strict]
|
|
160
|
-
ark preflight --changes <change-set.json> [--change-map <map.json>] [--root <project>] [--config ark.config.json] [--manifest <manifest.json>] [--tsconfig <tsconfig.json>] [--json]
|
|
161
|
-
ark status [--root <project>] [--config ark.config.json] [--json] [--vs <git-ref>]
|
|
162
|
-
[--expected-root <abs>] [--expected-project-id sha256:…] [--tools <host>]
|
|
163
|
-
ark agents-md [--root <project>] [--config ark.config.json] [--write] [--check] [--stdout] [--json]
|
|
164
|
-
[--tools <host>]
|
|
165
|
-
|
|
166
|
-
Commands:
|
|
167
|
-
start New here? Analyze and preview the complete setup. Read-only unless --apply.
|
|
168
|
-
init Configure Ark project enforcement with explicit prompts.
|
|
169
|
-
upgrade Preview identity-proven Ark-managed asset updates. With package install,
|
|
170
|
-
--apply bumps toward registry latest when behind (not only when CLI ≠ pin)
|
|
171
|
-
and recomputes the preview; a second explicit --apply --no-install applies
|
|
172
|
-
those exact bytes and verifies them. --refresh-skills opts in to rewrite
|
|
173
|
-
customized managed skills to package templates (never silent default).
|
|
174
|
-
(alias: ark update)
|
|
175
|
-
preflight Validate one atomic create/update/delete set without writing project files.
|
|
176
|
-
status Unified session/project manifest (identity, activation, last check, rules).
|
|
177
|
-
Never prompts. Prefer --json for agents; CI=1 forces JSON.
|
|
178
|
-
agents-md Version-matched agent contract projection (ACS04). Stamps package version +
|
|
179
|
-
contract summary into a managed AGENTS.md block. Non-authoritative — not a
|
|
180
|
-
gate input. Preview by default; --write merges without clobbering outside
|
|
181
|
-
regions; --check fails on version drift; --stdout prints the block only.
|
|
182
|
-
(aliases: agents-md, agent-projection)
|
|
183
|
-
|
|
184
|
-
Options:
|
|
185
|
-
--yes Non-interactive defaults: create config if needed, install gate templates, run strict check.
|
|
186
|
-
(Also the implicit default when stdin/stdout are not a TTY — agents never hang on prompts.)
|
|
187
|
-
--force Allow generated files to overwrite existing files.
|
|
188
|
-
--no-strict Skip the final strict ark-check run.
|
|
189
|
-
--install Pin and install arkgate as a project devDependency (default for start).
|
|
190
|
-
--no-install Skip adding/installing arkgate as a project devDependency (start/upgrade).
|
|
191
|
-
--apply Apply a start plan; for upgrade, update/repreview or apply managed bytes.
|
|
192
|
-
--accept-conflicts
|
|
193
|
-
Allow upgrade to recreate deleted managed assets or replace recorded conflicts.
|
|
194
|
-
--plan-digest Digest emitted by an upgrade preview; required to apply managed bytes.
|
|
195
|
-
--json Emit the start/upgrade/status/agents-md preview as deterministic machine-readable JSON.
|
|
196
|
-
--write For agents-md: merge the version-matched projection into AGENTS.md.
|
|
197
|
-
--check For agents-md: exit 1 when projection stamp drifts from package version.
|
|
198
|
-
--stdout For agents-md: print the projection block only (no file write).
|
|
199
|
-
--expected-root / --expected-project-id
|
|
200
|
-
Optional project expectation for status (MCP-compatible binding check).
|
|
201
|
-
--preset Start from a named architecture preset instead of detection.
|
|
202
|
-
--archetype Application shape from templates/architecture-playbook.json (maps to the matching preset).
|
|
203
|
-
Valid ids: crud-product, api-backend, frontend-surface, library-sdk, cli-utility,
|
|
204
|
-
worker-pipeline, event-coordinator, integration-bridge, multi-app-workspace, prototype-spike,
|
|
205
|
-
vertical-slice-product, ddd-bounded-contexts.
|
|
206
|
-
--tools One active agent host for start (claude,cursor,codex,grok,windsurf,cline,copilot,kiro,roo,continue,gemini).
|
|
207
|
-
Omit to use the active host; an unknown host creates only the shared compact router.
|
|
208
|
-
--remove-host <host>
|
|
209
|
-
Preview or apply removal of that compact host integration; re-add it with --tools <host>.
|
|
210
|
-
--require-write-hook <host>
|
|
211
|
-
Require and verify a hard local write hook for Claude, Grok, Antigravity, or Cursor.
|
|
212
|
-
Codex/OpenCode are advisory-write plus hard CI merge only; impossible requests fail before any write.
|
|
213
|
-
|
|
214
|
-
Interactive mode (TTY, no --yes): asks what application shape you are building and maps it to a preset.
|
|
215
|
-
Non-interactive (no TTY): uses the same defaults as --yes — never calls readline on a null interface.
|
|
216
|
-
`;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
157
|
function cliVersion() {
|
|
220
158
|
try {
|
|
221
159
|
const pkg = JSON.parse(fs.readFileSync(path.join(here, '..', 'package.json'), 'utf8'));
|
|
@@ -796,23 +734,22 @@ async function start(args) {
|
|
|
796
734
|
}
|
|
797
735
|
console.log('');
|
|
798
736
|
console.log('Next (the only flow you need):');
|
|
737
|
+
console.log(` 1. Status: ${arkCommand(root, 'arkgate-check', '--doctor')} — do primary next action #1`);
|
|
799
738
|
if (falseGreenGap) {
|
|
800
|
-
console.log('
|
|
801
|
-
console.log(' → reclassify I/O dirs out of Application; then /ark-autopilot
|
|
739
|
+
console.log(' 2. Session 0 in your agent: /ark-adopt — fix the architecture config first');
|
|
740
|
+
console.log(' → reclassify I/O dirs out of Application; leftover design later via /ark-explore then /ark-autopilot.');
|
|
802
741
|
} else {
|
|
803
|
-
console.log('
|
|
804
|
-
console.log(' →
|
|
742
|
+
console.log(' 2. Session 0 in your agent: /ark-adopt');
|
|
743
|
+
console.log(' → mark the path (greenfield or brownfield). Day-to-day new files: /ark-place.');
|
|
805
744
|
}
|
|
806
|
-
console.log(`
|
|
807
|
-
console.log(` 3. After edits: ${arkCommand(root, 'ark-check', '--root . --config ark.config.json --strict-merge')}`);
|
|
745
|
+
console.log(` 3. After edits: ${arkCommand(root, 'arkgate-check', '--root . --config ark.config.json --strict-merge')}`);
|
|
808
746
|
if (mode === 'adapt' && planOk && !falseGreenGap) {
|
|
809
747
|
console.log(
|
|
810
|
-
` 4. When green but cores still optional: ${arkCommand(root, '
|
|
748
|
+
` 4. When green but cores still optional: ${arkCommand(root, 'arkgate-check', '--ratchet-cores')} → honest ENFORCE`
|
|
811
749
|
);
|
|
812
750
|
}
|
|
813
751
|
console.log('');
|
|
814
|
-
console.log('Optional later: ark-
|
|
815
|
-
console.log('Optional later: --plan · --coverage · /ark-explore · /ark-autopilot · /ark-place · ark upgrade');
|
|
752
|
+
console.log('Optional later: leftover design → /ark-explore then /ark-autopilot; bump → arkgate upgrade.');
|
|
816
753
|
return 0;
|
|
817
754
|
} finally {
|
|
818
755
|
rl?.close();
|
|
@@ -831,8 +768,12 @@ async function main() {
|
|
|
831
768
|
console.log(cliVersion());
|
|
832
769
|
return 0;
|
|
833
770
|
}
|
|
771
|
+
if (args.help && (args.command === 'upgrade' || args.command === 'update')) {
|
|
772
|
+
console.log(upgradeUsage());
|
|
773
|
+
return 0;
|
|
774
|
+
}
|
|
834
775
|
if (args.help || !args.command) {
|
|
835
|
-
console.log(
|
|
776
|
+
console.log(args.all ? setupUsageAll() : setupUsage());
|
|
836
777
|
return 0;
|
|
837
778
|
}
|
|
838
779
|
|
|
@@ -961,7 +902,7 @@ async function main() {
|
|
|
961
902
|
}
|
|
962
903
|
|
|
963
904
|
console.error(`Unknown command: ${args.command}`);
|
|
964
|
-
console.error(
|
|
905
|
+
console.error(setupUsage());
|
|
965
906
|
return 2;
|
|
966
907
|
}
|
|
967
908
|
|