arkgate 4.6.1 → 4.6.3
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 +50 -1
- package/README.md +17 -11
- package/bin/ark-check-runtime.mjs +23 -127
- package/bin/ark-mcp-runtime.mjs +75 -49
- package/bin/ark.mjs +19 -78
- package/bin/lib/ci-and-commands.mjs +4 -3
- 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/host-support-matrix.mjs +13 -15
- package/bin/lib/start-preview.mjs +17 -10
- package/bin/lib/status-command.mjs +6 -1
- package/bin/lib/status-manifest.mjs +6 -0
- package/bin/lib/write-path-capabilities.mjs +9 -5
- package/bin/lib/write-path-detect.mjs +3 -3
- 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 +19 -14
- package/docs/ai-gates.md +29 -20
- package/docs/develop.md +4 -1
- package/docs/enthusiast/how-to-agent-gates.md +2 -2
- package/docs/package-surface.md +4 -3
- package/docs/product-voice.md +4 -4
- package/docs/threat-model.md +5 -1
- package/docs/use.md +9 -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 +10 -8
- 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 +10 -8
- 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,53 @@ in the immutable pre-2.0 archive linked below.
|
|
|
5
5
|
|
|
6
6
|
## Unreleased
|
|
7
7
|
|
|
8
|
+
## 4.6.3 — 2026-08-18
|
|
9
|
+
|
|
10
|
+
**Patch** over **4.6.2**. Codex CLI and local ChatGPT Desktop/App Server now get a
|
|
11
|
+
runtime-proven pre-write block for complete `apply_patch` calls. ArkGate accepts the current
|
|
12
|
+
`tool_input.command` payload, while incomplete, hosted, specialized, shell/direct, and human
|
|
13
|
+
write paths remain CI-backed. **No required config migration.**
|
|
14
|
+
|
|
15
|
+
**Status: prepared** (not yet on npm `latest`; see `docs/releases/4.6.3.md`).
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- **Codex hook payload:** current `PreToolUse` `apply_patch` bodies are read from
|
|
20
|
+
`tool_input.command`; historical patch/input/content fields stay compatible.
|
|
21
|
+
- **Operation-scoped hard write:** a complete trusted and runtime-observed local patch can report
|
|
22
|
+
`hard:true` and exit `2` before disk mutation. Hook files alone stay unverified.
|
|
23
|
+
- **Honesty surfaces:** host matrix, doctor/status, `--require-write-hook codex`, onboarding,
|
|
24
|
+
upgrade self-service, skills, and public docs now share the same boundary.
|
|
25
|
+
- **All-path backstop:** required `arkgate-check --strict-merge` CI remains mandatory. MCP stays
|
|
26
|
+
advisory and repair reinjection is not claimed.
|
|
27
|
+
|
|
28
|
+
## 4.6.2 — 2026-08-16
|
|
29
|
+
|
|
30
|
+
**Patch** over **4.6.1**. First-contact copy: a newcomer (human or coding agent) sees what
|
|
31
|
+
to do in a few lines — `arkgate` / `arkgate-check --help`, start wrap-up, doctor light +
|
|
32
|
+
#1, write-gate deny, SessionStart, MCP tool order, and the five doors. Same 13 skill names.
|
|
33
|
+
**No required config migration.**
|
|
34
|
+
|
|
35
|
+
**Status: published** (on npm `latest`; see `docs/releases/4.6.2.md`).
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
|
|
39
|
+
- **First-run help:** `arkgate --help` and `arkgate-check --help` are short; encyclopedia
|
|
40
|
+
text is `--help --all`. `arkgate upgrade --help` is preview vs apply.
|
|
41
|
+
- **Start wrap-up:** doctor → `/ark-adopt` session 0 (not `/ark-autopilot` as step 1).
|
|
42
|
+
- **Doctor:** operating-mode light + primary next action #1 print first.
|
|
43
|
+
- **Write-gate deny:** `blocked {file} — {reason}` then `Next:` (move the import / `/ark-place`).
|
|
44
|
+
Rule id on a following line. No “call ark_manifest”.
|
|
45
|
+
- **Agents:** SessionStart points at `/ark-adopt` or `arkgate-check --doctor`. `ark_identity`
|
|
46
|
+
is first. `ark_check` is a scan (pass/fail/incomplete), not a yes/no architecture score.
|
|
47
|
+
`server.json` first sentence is the layers definition.
|
|
48
|
+
- **Skills:** five doors open with when + steps. Shortcuts are not the first-run menu.
|
|
49
|
+
- **Status:** `nextAction` is `map-leftover-design` when leftover design work remains
|
|
50
|
+
(never `stay-enforced`).
|
|
51
|
+
- **npm `description`:** `One architecture config. One check. One coach.` (not “co-pilot”).
|
|
52
|
+
- **`docs/use.md`:** Cursor hard-blocks Write/StrReplace when hooks are trusted;
|
|
53
|
+
Codex/OpenCode stay advisory.
|
|
54
|
+
|
|
8
55
|
## 4.6.1 — 2026-08-14
|
|
9
56
|
|
|
10
57
|
**Patch** over **4.6.0**. Five-door autonomy (skills write or map in-turn; CLI is sensor +
|
|
@@ -12,7 +59,7 @@ gate) plus team parliament (law vs feature: stewards, mixed-PR deny, ratchet vs
|
|
|
12
59
|
base, cheap `--changed` check). Same 13 skill names. Steward identity is a GitHub handle or
|
|
13
60
|
email, not git `user.name`. **No required config migration.**
|
|
14
61
|
|
|
15
|
-
**Status:
|
|
62
|
+
**Status: published** (on npm `latest`; see `docs/releases/4.6.1.md`).
|
|
16
63
|
|
|
17
64
|
### Added
|
|
18
65
|
|
|
@@ -28,6 +75,8 @@ email, not git `user.name`. **No required config migration.**
|
|
|
28
75
|
|
|
29
76
|
- Doctor, compact router, and public lanes prefer the five doors. Historical changelogs stay
|
|
30
77
|
as shipped.
|
|
78
|
+
- Published 4.6.1 tarball `README.md` still banners 4.6.0 (packed at `1eadc96` before the
|
|
79
|
+
pointer flip). Tree README on `main` is current. No 4.6.2 for that banner.
|
|
31
80
|
|
|
32
81
|
## 4.6.0 — 2026-08-12
|
|
33
82
|
|
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.2** is on npm `latest` — first-contact copy (what to do in a few lines).
|
|
20
|
+
> **4.6.3 is prepared** — Codex CLI/local Desktop hard-block complete trusted `apply_patch`.
|
|
21
|
+
> [4.6.3 notes](docs/releases/4.6.3.md) · [4.6.2](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
|
|
|
@@ -116,14 +117,15 @@ Details: [docs/use.md](docs/use.md).
|
|
|
116
117
|
| Claude Code | **Hard** block for listed ops (PreToolUse `Write` / `Edit` / `MultiEdit`) when installed + trusted | Advisory; the agent must call it | **Required GitHub status context** running `arkgate-check --strict-merge` (alias `ark-check`) | Emitted on hook deny; host must re-inject (hard path when installed + trusted) |
|
|
117
118
|
| Grok Build | **Hard** block for listed ops (PreToolUse `write` / `search_replace` (plus aliases)) when installed + trusted | Advisory; the agent must call it | **Required GitHub status context** running `arkgate-check --strict-merge` (alias `ark-check`) | Emitted on hook deny; host must re-inject (hard path when installed + trusted) |
|
|
118
119
|
| Google Antigravity | **Hard** block for listed ops (PreToolUse `write_to_file` / `replace_file_content` / `multi_replace_file_content`) when installed + trusted | Advisory; the agent must call it | **Required GitHub status context** running `arkgate-check --strict-merge` (alias `ark-check`) | Emitted on hook deny; host must re-inject (hard path when installed + trusted) |
|
|
119
|
-
| Cursor | **Hard** block for listed ops (preToolUse `Write` / `StrReplace`) when installed + trusted | Advisory; the agent must call it | **Required GitHub status context** running `arkgate-check --strict-merge` (alias `ark-check`) | Envelope may emit (`--hook-repair`); reinjection **not** guaranteed
|
|
120
|
-
| OpenAI Codex | **
|
|
120
|
+
| Cursor | **Hard** block for listed ops (preToolUse `Write` / `StrReplace`) when installed + trusted | Advisory; the agent must call it | **Required GitHub status context** running `arkgate-check --strict-merge` (alias `ark-check`) | Envelope may emit (`--hook-repair`); reinjection **not** guaranteed |
|
|
121
|
+
| OpenAI Codex | **Hard** block for listed ops (PreToolUse `apply_patch` in Codex CLI and local ChatGPT Desktop/App Server) when installed + trusted | Advisory; the agent must call it | **Required GitHub status context** running `arkgate-check --strict-merge` (alias `ark-check`) | Envelope may emit (`--hook-repair`); reinjection **not** guaranteed |
|
|
121
122
|
| OpenCode | **Advisory / best-effort** at write (MCP + optional plugin; not a hard boundary) | Advisory; the agent must call it | **Required GitHub status context** running `arkgate-check --strict-merge` (alias `ark-check`) | No hard-boundary payload |
|
|
122
123
|
|
|
123
124
|
**Read the CI column:** for every host, the repository-wide hard guarantee is a **required**
|
|
124
125
|
GitHub **status context** that runs the CLI — not “CI file present,” and not the CLI binary name alone.
|
|
125
|
-
Codex
|
|
126
|
-
`preToolUse` ops
|
|
126
|
+
Codex hard write covers only a complete local `apply_patch`; Cursor covers only listed
|
|
127
|
+
`preToolUse` ops. In both cases the project hook must be installed + trusted, while shell/direct
|
|
128
|
+
filesystem writes, hosted or specialized opt-out paths, and human edits still rely on CI.
|
|
127
129
|
|
|
128
130
|
This table describes the supported profile **after its files are installed and the host loads/trusts them**. A hard local boundary covers only the listed hook operations; alternate tools, direct filesystem writes, and human edits still rely on CI. MCP validation is advisory because the agent must call it. The CI check blocks a merge only when the repository makes that status required. Repair **envelopes** may be emitted without reinjection being guaranteed; silent auto-apply never happens. Run `arkgate-check --doctor` (or `ark-check --doctor`) for the evidence actually detected in the current repository.
|
|
129
131
|
<!-- arkgate-host-support:end -->
|
|
@@ -132,10 +134,11 @@ This table describes the supported profile **after its files are installed and t
|
|
|
132
134
|
|
|
133
135
|
The split above is a deliberate trade-off, not a gap. ArkGate validates at the earliest boundary
|
|
134
136
|
each host offers and enforces at the earliest boundary a repository can make non-bypassable: the
|
|
135
|
-
required merge status. Hard hooks (Claude Code, Grok Build, Google Antigravity
|
|
136
|
-
write operations at write time; advisory
|
|
137
|
-
while it works. But any local boundary can
|
|
138
|
-
|
|
137
|
+
required merge status. Hard hooks (Claude Code, Grok Build, Google Antigravity, Cursor, and
|
|
138
|
+
Codex’s complete local `apply_patch`) deny their listed write operations at write time; advisory
|
|
139
|
+
surfaces (MCP, rules, OpenCode plugins) coach the agent while it works. But any local boundary can
|
|
140
|
+
be routed around — another tool, a hosted/specialized path, a direct filesystem write, or a human
|
|
141
|
+
edit — so the only guarantee ArkGate claims for every path is the
|
|
139
142
|
`arkgate-check --strict-merge` check, and only when the repository makes that status required.
|
|
140
143
|
Local checks optimize feedback speed; the merge gate owns correctness.
|
|
141
144
|
|
|
@@ -213,7 +216,10 @@ for real systems. Details: [docs/production-hardening.md](docs/production-harden
|
|
|
213
216
|
| Config · package surface · TS | [configuration](docs/configuration.md) · [package-surface](docs/package-surface.md) · [typescript-support](docs/typescript-support.md) |
|
|
214
217
|
| Brownfield | [docs/brownfield-adoption.md](docs/brownfield-adoption.md) |
|
|
215
218
|
| Security | [SECURITY.md](SECURITY.md) |
|
|
216
|
-
| Current release (4.6.
|
|
219
|
+
| Current release (4.6.2 on npm `latest`) | [docs/releases/4.6.2.md](docs/releases/4.6.2.md) · [CHANGELOG](CHANGELOG.md) |
|
|
220
|
+
| Prepared patch (4.6.3) | [docs/releases/4.6.3.md](docs/releases/4.6.3.md) |
|
|
221
|
+
| Prior (4.6.1) | [docs/releases/4.6.1.md](docs/releases/4.6.1.md) |
|
|
222
|
+
| Prior (4.6.0) | [docs/releases/4.6.0.md](docs/releases/4.6.0.md) |
|
|
217
223
|
| Prior (4.5.7) | [docs/releases/4.5.7.md](docs/releases/4.5.7.md) |
|
|
218
224
|
| Prior (4.5.0) | [docs/releases/4.5.0.md](docs/releases/4.5.0.md) |
|
|
219
225
|
| 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
|
@@ -358,6 +358,7 @@ function mapAntigravityToolCall(toolCall) {
|
|
|
358
358
|
* Antigravity: { toolCall: { name, args: { TargetFile, CodeContent, … } } }
|
|
359
359
|
* Cursor: { tool_name, tool_input, hook_event_name?, workspace_roots? }
|
|
360
360
|
* Write uses `contents`; StrReplace maps to Edit (path/old_string/new_string).
|
|
361
|
+
* Codex: { tool_name: "apply_patch", tool_input: { command: "*** Begin Patch..." } }
|
|
361
362
|
*/
|
|
362
363
|
function normalizeHookPayload(payload, grokHookEvent = Boolean(process.env.GROK_HOOK_EVENT)) {
|
|
363
364
|
const antigravityStyle =
|
|
@@ -643,6 +644,25 @@ function emitHostAllow(output, { antigravityStyle, cursorStyle }) {
|
|
|
643
644
|
emitCursorAllow(output, cursorStyle);
|
|
644
645
|
}
|
|
645
646
|
|
|
647
|
+
/**
|
|
648
|
+
* Socket-style write-gate deny: two lines first. Pass/fail, no score.
|
|
649
|
+
* Rule id stays on a following line, not the first sentence.
|
|
650
|
+
*/
|
|
651
|
+
function formatWriteGateDeny({ file, reason, ruleId, nextAction, extraLines = [] }) {
|
|
652
|
+
const target = file || 'this write';
|
|
653
|
+
const why = String(reason || 'this change breaks the architecture layers').replace(/\s+/g, ' ').trim();
|
|
654
|
+
const next =
|
|
655
|
+
nextAction && /place|move|import|port/i.test(nextAction)
|
|
656
|
+
? nextAction
|
|
657
|
+
: 'Move the import or run /ark-place. Do not weaken ark.config.json.';
|
|
658
|
+
const lines = [`blocked ${target} — ${why}`, `Next: ${next}`];
|
|
659
|
+
if (ruleId) lines.push(`[${ruleId}]`);
|
|
660
|
+
for (const extra of extraLines) {
|
|
661
|
+
if (extra) lines.push(extra);
|
|
662
|
+
}
|
|
663
|
+
return lines.join('\n');
|
|
664
|
+
}
|
|
665
|
+
|
|
646
666
|
function runHookPayload(payload, gate, config, args, ts, attemptContext, output = processHookOutput()) {
|
|
647
667
|
const { toolName, toolInput, grokStyle, antigravityStyle, cursorStyle, operation } =
|
|
648
668
|
normalizeHookPayload(
|
|
@@ -650,7 +670,10 @@ function runHookPayload(payload, gate, config, args, ts, attemptContext, output
|
|
|
650
670
|
attemptContext?.grokHookEvent ?? Boolean(process.env.GROK_HOOK_EVENT)
|
|
651
671
|
);
|
|
652
672
|
if (toolName === 'ApplyPatch') {
|
|
653
|
-
|
|
673
|
+
// Current Codex CLI/Desktop hook schema uses tool_input.command. Keep the
|
|
674
|
+
// historical fields for older clients and existing integration fixtures.
|
|
675
|
+
const patch =
|
|
676
|
+
toolInput.command ?? toolInput.patch ?? toolInput.input ?? toolInput.content;
|
|
654
677
|
const parsedPatch = codexPatchWrites(patch, args.root);
|
|
655
678
|
// Codex ApplyPatch is only preflighted when Ark can reconstruct every file operation.
|
|
656
679
|
// An incomplete reconstruction must not be mislabeled as atomic or hard enforcement.
|
|
@@ -796,17 +819,20 @@ function runHookPayload(payload, gate, config, args, ts, attemptContext, output
|
|
|
796
819
|
emitHostAllow(output, { antigravityStyle, cursorStyle });
|
|
797
820
|
return;
|
|
798
821
|
}
|
|
799
|
-
const
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
822
|
+
const first = result.diagnostics[0];
|
|
823
|
+
const message = formatWriteGateDeny({
|
|
824
|
+
file: `${changes.length} file(s)`,
|
|
825
|
+
reason: first?.message || `this ${toolName} breaks the architecture layers`,
|
|
826
|
+
ruleId: first?.ruleId,
|
|
827
|
+
nextAction: first?.nextAction,
|
|
828
|
+
extraLines: [
|
|
829
|
+
...result.diagnostics.slice(1).map((d) => `[${d.ruleId}] ${d.message}`),
|
|
830
|
+
...(designDelta && !designDelta.valid
|
|
831
|
+
? formatDesignDeltaBlock(designDelta).split('\n').slice(1)
|
|
832
|
+
: []),
|
|
833
|
+
'No project file was written. Fix the complete patch and retry.',
|
|
834
|
+
],
|
|
835
|
+
});
|
|
810
836
|
output.stderr(`${message}\n`);
|
|
811
837
|
if (args.hookRepair) {
|
|
812
838
|
output.stderr(
|
|
@@ -926,13 +952,7 @@ function runHookPayload(payload, gate, config, args, ts, attemptContext, output
|
|
|
926
952
|
})),
|
|
927
953
|
});
|
|
928
954
|
|
|
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.
|
|
955
|
+
const firstDiagnostic = adapterResult.diagnostics[0];
|
|
936
956
|
const suggestions = [
|
|
937
957
|
...new Set(combinedViolations.map((violation) => violation.suggestion).filter(Boolean)),
|
|
938
958
|
];
|
|
@@ -940,27 +960,34 @@ function runHookPayload(payload, gate, config, args, ts, attemptContext, output
|
|
|
940
960
|
// W4: structured repair payload is opt-in (--hook-repair / ARK_HOOK_REPAIR).
|
|
941
961
|
// Default remains hard block with prose only — hosts that cannot re-inject stay clean.
|
|
942
962
|
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
|
-
|
|
963
|
+
const message = formatWriteGateDeny({
|
|
964
|
+
file: rel,
|
|
965
|
+
reason: firstDiagnostic?.message || (layer ? `${layer} write breaks the layers` : 'this write breaks the layers'),
|
|
966
|
+
ruleId: firstDiagnostic?.ruleId,
|
|
967
|
+
nextAction: firstDiagnostic?.nextAction,
|
|
968
|
+
extraLines: [
|
|
969
|
+
...adapterResult.diagnostics.slice(1).map(
|
|
970
|
+
(diagnostic) =>
|
|
971
|
+
`[${diagnostic.ruleId}] ${diagnostic.message}${diagnostic.location.line ? ` (line ${diagnostic.location.line})` : ''}`
|
|
972
|
+
),
|
|
973
|
+
...(suggestions.length > 0 ? suggestions.map((s) => `Fix: ${s}`) : []),
|
|
974
|
+
...(autoPatch && repair
|
|
975
|
+
? [
|
|
976
|
+
`autoPatch available (${autoPatch.remediationKind}, confidence ${autoPatch.confidence}): ` +
|
|
977
|
+
'apply the patched source from ARK_AUTOPATCH_JSON / ARK_REPAIR_JSON on stderr' +
|
|
978
|
+
(grokStyle ? ' (or autoPatch in the deny JSON on stdout)' : '') +
|
|
979
|
+
' instead of re-drafting. Gate still denies this write (never silent apply).',
|
|
980
|
+
]
|
|
981
|
+
: []),
|
|
982
|
+
...(autoPatch && !repair
|
|
983
|
+
? [
|
|
984
|
+
`Mechanical-safe autoPatch is available (${autoPatch.remediationKind}). ` +
|
|
985
|
+
'Enable repair payload with ARK_HOOK_REPAIR=1 or --hook-repair to receive ' +
|
|
986
|
+
'machine-readable source (still hard-blocks; host re-injects).',
|
|
987
|
+
]
|
|
988
|
+
: []),
|
|
989
|
+
],
|
|
990
|
+
});
|
|
964
991
|
output.stderr(message + '\n');
|
|
965
992
|
|
|
966
993
|
if (repair) {
|
|
@@ -1403,7 +1430,7 @@ function printSessionContext(config, profile, forbiddenGlobals, args, configPath
|
|
|
1403
1430
|
const governedPercent = coverage?.coverage?.governed?.percent ?? coverage?.governed?.percent;
|
|
1404
1431
|
if (shouldShowNewHereNudge(args.root, configPath, governedPercent, false)) {
|
|
1405
1432
|
lines.push('');
|
|
1406
|
-
lines.push('New to Ark?
|
|
1433
|
+
lines.push('New to Ark? /ark-adopt or: arkgate-check --doctor');
|
|
1407
1434
|
}
|
|
1408
1435
|
|
|
1409
1436
|
process.stdout.write(`${lines.join('\n')}\n`);
|
|
@@ -2018,9 +2045,9 @@ export async function runArkMcp({ hookInput } = {}) {
|
|
|
2018
2045
|
{
|
|
2019
2046
|
name: 'ark_identity',
|
|
2020
2047
|
description:
|
|
2021
|
-
'
|
|
2022
|
-
'
|
|
2023
|
-
'
|
|
2048
|
+
'First call. Prove this MCP process is the right project: pass project.expectedRoot ' +
|
|
2049
|
+
'(exact absolute root) and reuse the returned projectId. Do this before any other Ark tool. ' +
|
|
2050
|
+
'A missing, unmatched, or different root means restart the host and use the local CLI.',
|
|
2024
2051
|
inputSchema: { type: 'object', properties: {} },
|
|
2025
2052
|
},
|
|
2026
2053
|
{
|
|
@@ -2063,11 +2090,10 @@ export async function runArkMcp({ hookInput } = {}) {
|
|
|
2063
2090
|
{
|
|
2064
2091
|
name: 'ark_check',
|
|
2065
2092
|
description:
|
|
2066
|
-
'
|
|
2067
|
-
'
|
|
2068
|
-
'
|
|
2069
|
-
'
|
|
2070
|
-
'overall verdicts. Applies the baseline automatically when one exists. isError when not ok.',
|
|
2093
|
+
'Scan the project for architecture findings (layer-import violations, forbidden globals, ' +
|
|
2094
|
+
'cycles, config warnings). Returns pass/fail/incomplete plus evidence — not a yes/no ' +
|
|
2095
|
+
'architecture score. Same engine as arkgate-check. Applies the baseline when one exists. ' +
|
|
2096
|
+
'isError when the scan fails. Prefer after ark_identity.',
|
|
2071
2097
|
inputSchema: {
|
|
2072
2098
|
type: 'object',
|
|
2073
2099
|
properties: {
|