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.
Files changed (46) hide show
  1. package/CHANGELOG.md +30 -1
  2. package/README.md +5 -3
  3. package/bin/ark-check-runtime.mjs +23 -127
  4. package/bin/ark-mcp-runtime.mjs +70 -48
  5. package/bin/ark.mjs +19 -78
  6. package/bin/lib/doctor-next-actions.mjs +92 -0
  7. package/bin/lib/doctor-plan.mjs +59 -69
  8. package/bin/lib/first-run-help.mjs +221 -0
  9. package/bin/lib/start-preview.mjs +17 -10
  10. package/bin/lib/status-command.mjs +5 -0
  11. package/bin/lib/status-manifest.mjs +6 -0
  12. package/dist/index.cjs +19 -19
  13. package/dist/index.d.ts +6 -1
  14. package/dist/index.js +22 -22
  15. package/docs/README.md +5 -5
  16. package/docs/agent-guide.md +1 -1
  17. package/docs/enthusiast/how-to-agent-gates.md +1 -1
  18. package/docs/package-surface.md +3 -3
  19. package/docs/use.md +4 -4
  20. package/package.json +2 -2
  21. package/server.json +3 -3
  22. package/templates/agent-skills/README.md +1 -1
  23. package/templates/agent-skills/ark-adopt/SKILL.md +14 -5
  24. package/templates/agent-skills/ark-architect/SKILL.md +2 -2
  25. package/templates/agent-skills/ark-autopilot/SKILL.md +12 -5
  26. package/templates/agent-skills/ark-contract/SKILL.md +1 -1
  27. package/templates/agent-skills/ark-coverage/SKILL.md +6 -5
  28. package/templates/agent-skills/ark-explain/SKILL.md +3 -2
  29. package/templates/agent-skills/ark-explore/SKILL.md +13 -4
  30. package/templates/agent-skills/ark-fix/SKILL.md +1 -1
  31. package/templates/agent-skills/ark-loop/SKILL.md +1 -1
  32. package/templates/agent-skills/ark-place/SKILL.md +10 -1
  33. package/templates/agent-skills/ark-think/SKILL.md +3 -2
  34. package/templates/agent-skills/ark-upgrade/SKILL.md +10 -3
  35. package/templates/skills/ark-adopt.md +14 -5
  36. package/templates/skills/ark-architect.md +2 -2
  37. package/templates/skills/ark-autopilot.md +12 -5
  38. package/templates/skills/ark-contract.md +1 -1
  39. package/templates/skills/ark-coverage.md +6 -5
  40. package/templates/skills/ark-explain.md +3 -2
  41. package/templates/skills/ark-explore.md +13 -4
  42. package/templates/skills/ark-fix.md +1 -1
  43. package/templates/skills/ark-loop.md +1 -1
  44. package/templates/skills/ark-place.md +10 -1
  45. package/templates/skills/ark-think.md +3 -2
  46. 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: prepared** (not yet on npm `latest`; see `docs/releases/4.6.1.md`).
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.0** is on npm `latest` — clearer language + shared agent home skills.
20
- > [4.6.0 notes](docs/releases/4.6.0.md) · [4.5.7](docs/releases/4.5.7.md) · [4.5.6](docs/releases/4.5.6.md) · [Docs hub](docs/README.md) · [Product voice](docs/product-voice.md)
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.0 on npm `latest`) | [docs/releases/4.6.0.md](docs/releases/4.6.0.md) · [CHANGELOG](CHANGELOG.md) |
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 ark-check --help for usage.`);
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 ark-check --help for usage.`);
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(usage());
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 = buildReportSnapshot({
1710
- root,
1711
- config,
1712
- coverage,
1713
- violations: activeViolations,
1714
- ok,
1715
- suppressed: suppressed.length,
1716
- version: arkPackageVersion(),
1717
- fileCountByLayer,
1718
- enforcement: enforcementForReport,
1719
- score: fitness.score,
1720
- mode: fitness.mode,
1721
- improvementCompass: reportCompass,
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,
@@ -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 message = [
800
- `Ark architecture gate blocked this complete ${toolName} (${changes.length} governed file(s)):`,
801
- ...result.diagnostics.map(
802
- (diagnostic) =>
803
- `- [${diagnostic.ruleId}] ${diagnostic.message}\n Next action: ${diagnostic.nextAction}`
804
- ),
805
- ...(designDelta && !designDelta.valid
806
- ? formatDesignDeltaBlock(designDelta).split('\n').slice(1)
807
- : []),
808
- 'No project file was written. Fix the complete patch and retry.',
809
- ].join('\n');
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 lines = adapterResult.diagnostics.map(
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
- `Ark architecture gate blocked this write to ${rel}${layer ? ` (layer: ${layer})` : ''}:`,
945
- ...lines,
946
- ...(suggestions.length > 0 ? ['Fix:', ...suggestions.map((s) => ` ${s}`)] : []),
947
- ...(autoPatch && repair
948
- ? [
949
- `autoPatch available (${autoPatch.remediationKind}, confidence ${autoPatch.confidence}): ` +
950
- 'apply the patched source from ARK_AUTOPATCH_JSON / ARK_REPAIR_JSON on stderr' +
951
- (grokStyle ? ' (or autoPatch in the deny JSON on stdout)' : '') +
952
- ' instead of re-drafting. Gate still denies this write (never silent apply).',
953
- ]
954
- : []),
955
- ...(autoPatch && !repair
956
- ? [
957
- `Mechanical-safe autoPatch is available (${autoPatch.remediationKind}). ` +
958
- 'Enable repair payload with ARK_HOOK_REPAIR=1 or --hook-repair to receive ' +
959
- 'machine-readable source (still hard-blocks; host re-injects).',
960
- ]
961
- : []),
962
- 'Fix the violations and retry. Call the project-bound ark_manifest MCP tool for the architecture contract.',
963
- ].join('\n');
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? Run /ark-adopt (or /ark-architect) or: ark-check --recommend');
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
- 'Return the canonical ArkGate project, config, contract, and live MCP runtime identity. ' +
2022
- 'Pass project.expectedRoot and/or expectedProjectId to verify this process before ' +
2023
- 'trusting any architecture evidence.',
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
- 'Run the full Ark architecture check on the project and return structured results ' +
2067
- '(layer-import violations, forbidden globals, circular deps, config warnings). Use ' +
2068
- 'this to answer "is the architecture currently valid?" instead of shelling out to ' +
2069
- 'ark-check. Preserves legacy ok and adds identity/completeness/graph/coverage/gates/' +
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 ark --help for usage.`);
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 ark --help for usage.`);
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(' 1. In your agent: /ark-adopt — fix the architecture config first');
801
- console.log(' → reclassify I/O dirs out of Application; then /ark-autopilot for residual debt.');
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(' 1. In your agent: /ark-autopilot');
804
- console.log(' → explore first, dual plan (remediation + pattern bets), safe fixes, leave gates on.');
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(` 2. Status anytime: ${arkCommand(root, 'ark-check', '--doctor')}`);
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, 'ark-check', '--ratchet-cores')} → honest ENFORCE`
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-check --report ark-report.html (captures a day-zero/evolution report).');
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(usage());
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(usage());
905
+ console.error(setupUsage());
965
906
  return 2;
966
907
  }
967
908