arkgate 4.5.6 → 4.5.7
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 +24 -1
- package/README.md +7 -7
- package/bin/ark-check-runtime.mjs +6 -4
- package/bin/ark-mcp-runtime.mjs +82 -61
- package/bin/ark.mjs +2 -2
- package/bin/lib/agent-gates.mjs +2 -0
- package/bin/lib/ci-and-commands.mjs +2 -1
- package/bin/lib/gate-files.mjs +1 -1
- package/bin/lib/hook-templates.mjs +13 -11
- package/bin/lib/host-support-matrix.mjs +31 -11
- package/bin/lib/install-migrate.mjs +24 -0
- package/bin/lib/managed-upgrade.mjs +6 -1
- package/bin/lib/mcp-adoption.mjs +6 -1
- package/bin/lib/mcp-process-package.mjs +95 -0
- package/bin/lib/start-preview.mjs +5 -1
- package/bin/lib/write-path-capabilities.mjs +62 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/docs/README.md +2 -2
- package/docs/agent-guide.md +8 -6
- package/docs/ai-gates.md +24 -14
- package/docs/enthusiast/how-to-agent-gates.md +4 -3
- package/docs/package-surface.md +2 -2
- package/package.json +1 -1
- package/server.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,29 @@ in the immutable pre-2.0 archive linked below.
|
|
|
5
5
|
|
|
6
6
|
## Unreleased
|
|
7
7
|
|
|
8
|
+
## 4.5.7 — 2026-08-12
|
|
9
|
+
|
|
10
|
+
**Patch** over **4.5.6**. Cursor hard write path: project `.cursor/hooks.json` `preToolUse`
|
|
11
|
+
gate for `Write` / `StrReplace` (failClosed), host matrix + doctor honesty, payload mapping
|
|
12
|
+
(`contents` / `StrReplace`), install/merge without wiping sibling hooks. Repair envelope may
|
|
13
|
+
emit; Write `updated_input` reinjection is **not** claimed. Codex/OpenCode stay advisory at
|
|
14
|
+
write. Shell/Tab/human edits still rely on required CI. **No required config migration.**
|
|
15
|
+
|
|
16
|
+
**Status: prepared** (not published).
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- **Cursor hard write (CH01):** install writes `.cursor/hooks.json` with
|
|
21
|
+
`preToolUse` matcher `Write|StrReplace`, `failClosed: true`, `--hook --hook-repair`,
|
|
22
|
+
`--root-env CURSOR_PROJECT_DIR`. Merge preserves sibling Cursor hooks.
|
|
23
|
+
- **Hook runtime:** Cursor payload normalization (`contents` → `content`, `StrReplace` → Edit)
|
|
24
|
+
and stdout `{ permission: "deny", agent_message, user_message }` on block.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- Host support matrix: Cursor `hard-write: true` for listed ops; soft hosts are Codex/OpenCode.
|
|
29
|
+
- Docs: `docs/ai-gates.md`, agent guide, README matrix, CLI help.
|
|
30
|
+
|
|
8
31
|
## 4.5.6 — 2026-08-11
|
|
9
32
|
|
|
10
33
|
**Patch** over **4.5.5**. Field upgrade & multi-project MCP truth (Phase FX): registry-aware
|
|
@@ -12,7 +35,7 @@ package install, structured skip recovery, skill drift + opt-in refresh, post-up
|
|
|
12
35
|
MCP process package honesty, host selection notes, early whatsNew, inventory UX-copy quieting.
|
|
13
36
|
**No required config migration.** No new skill names, sensors, or scores. Freezes held.
|
|
14
37
|
|
|
15
|
-
**Status:
|
|
38
|
+
**Status: published** (on npm `latest`; see
|
|
16
39
|
`docs/releases/4.5.6.md`).
|
|
17
40
|
|
|
18
41
|
### Added
|
package/README.md
CHANGED
|
@@ -16,8 +16,8 @@ and makes sure a “green” check means something real.
|
|
|
16
16
|
|
|
17
17
|
</div>
|
|
18
18
|
|
|
19
|
-
> **ArkGate 4.5.
|
|
20
|
-
> [4.5.
|
|
19
|
+
> **ArkGate 4.5.6** is on npm `latest` — field upgrade truth, multi-project MCP honesty, skill drift.
|
|
20
|
+
> [4.5.6 notes](docs/releases/4.5.6.md) · [4.5.5](docs/releases/4.5.5.md) · [4.5.0](docs/releases/4.5.0.md) · [Docs hub](docs/README.md) · [Product voice](docs/product-voice.md)
|
|
21
21
|
|
|
22
22
|
---
|
|
23
23
|
|
|
@@ -113,16 +113,16 @@ Details: [docs/use.md](docs/use.md).
|
|
|
113
113
|
| 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) |
|
|
114
114
|
| 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) |
|
|
115
115
|
| 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) |
|
|
116
|
-
| Cursor | **
|
|
117
|
-
| OpenAI Codex | **Advisory / best-effort** at write (not equivalent to Claude/Grok hard block) | 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 (advisory host) |
|
|
116
|
+
| 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 (advisory host) |
|
|
117
|
+
| OpenAI Codex | **Advisory / best-effort** at write (not equivalent to Claude/Grok/Cursor hard block) | 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 (advisory host) |
|
|
118
118
|
| 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 |
|
|
119
119
|
|
|
120
120
|
**Read the CI column:** for every host, the repository-wide hard guarantee is a **required**
|
|
121
121
|
GitHub **status context** that runs the CLI — not “CI file present,” and not the CLI binary name alone.
|
|
122
|
-
|
|
122
|
+
Codex/OpenCode never get a fake hard write claim. Cursor hard write covers only listed
|
|
123
|
+
`preToolUse` ops when `.cursor/hooks.json` is installed and trusted — Shell/Tab/human edits still rely on CI.
|
|
123
124
|
|
|
124
125
|
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.
|
|
125
|
-
|
|
126
126
|
<!-- arkgate-host-support:end -->
|
|
127
127
|
|
|
128
128
|
#### Why the hard guarantee lives at the merge gate
|
|
@@ -208,7 +208,7 @@ for real systems. Details: [docs/production-hardening.md](docs/production-harden
|
|
|
208
208
|
| Config · package surface · TS | [configuration](docs/configuration.md) · [package-surface](docs/package-surface.md) · [typescript-support](docs/typescript-support.md) |
|
|
209
209
|
| Brownfield | [docs/brownfield-adoption.md](docs/brownfield-adoption.md) |
|
|
210
210
|
| Security | [SECURITY.md](SECURITY.md) |
|
|
211
|
-
| Current release (4.5.
|
|
211
|
+
| Current release (4.5.6 on npm `latest`) | [docs/releases/4.5.6.md](docs/releases/4.5.6.md) · [CHANGELOG](CHANGELOG.md) |
|
|
212
212
|
| Prior (4.5.0) | [docs/releases/4.5.0.md](docs/releases/4.5.0.md) |
|
|
213
213
|
| Prior (4.4.0) | [docs/releases/4.4.0.md](docs/releases/4.4.0.md) |
|
|
214
214
|
| Prior (4.3.0) | [docs/releases/4.3.0.md](docs/releases/4.3.0.md) |
|
|
@@ -322,9 +322,10 @@ function usage() {
|
|
|
322
322
|
'transition. Weakening or judgment-required findings fail unless --policy-ack names',
|
|
323
323
|
'every finding and is bound to both policy hashes. Use --policy-base/--policy-base-ref',
|
|
324
324
|
'for an explicit comparison; ARK_POLICY_BASE_REF is the CI environment equivalent.',
|
|
325
|
-
'Add --require-write-hook claude|grok to validate a hard local write
|
|
326
|
-
'specific host.
|
|
327
|
-
'merge blocking requires repository policy
|
|
325
|
+
'Add --require-write-hook claude|grok|antigravity|cursor to validate a hard local write',
|
|
326
|
+
'boundary for that specific host. Codex and OpenCode expose advisory MCP (plus best-effort',
|
|
327
|
+
'hooks where applicable) and the shared CI check; merge blocking requires repository policy',
|
|
328
|
+
'to make that status required.',
|
|
328
329
|
'',
|
|
329
330
|
'--require-gates implies --strict-config and fails when the Ark contract in AGENTS.md,',
|
|
330
331
|
'the project-rooted Ark server in .mcp.json, or fail-closed CI is missing/invalid.',
|
|
@@ -332,7 +333,8 @@ function usage() {
|
|
|
332
333
|
'',
|
|
333
334
|
'--install-agent-gates writes AGENTS.md, .mcp.json, and the CI workflow for every',
|
|
334
335
|
'project, plus tool-specific templates. Known tools: claude, cursor, codex, grok',
|
|
335
|
-
'(Claude/Grok hard-write hooks;
|
|
336
|
+
'(Claude/Grok/Antigravity/Cursor hard-write hooks when covered; Codex advisory MCP;',
|
|
337
|
+
'shared CI check for all) and',
|
|
336
338
|
'windsurf, cline, copilot, kiro, roo, continue, gemini',
|
|
337
339
|
'(instruction-tier rule files derived from the same contract).',
|
|
338
340
|
'It also installs the /ark-* skills shipped in templates/skills/ into each',
|
package/bin/ark-mcp-runtime.mjs
CHANGED
|
@@ -5,8 +5,11 @@ import path from 'node:path';
|
|
|
5
5
|
import readline from 'node:readline';
|
|
6
6
|
import { createHash, randomUUID } from 'node:crypto';
|
|
7
7
|
import { spawnSync } from 'node:child_process';
|
|
8
|
-
import { createRequire } from 'node:module';
|
|
9
8
|
import { fileURLToPath } from 'node:url';
|
|
9
|
+
import {
|
|
10
|
+
buildProcessPackageHonesty,
|
|
11
|
+
readProjectInstalledArkgateVersion,
|
|
12
|
+
} from './lib/mcp-process-package.mjs';
|
|
10
13
|
import {
|
|
11
14
|
DEFAULT_INTENT_PREFIXES,
|
|
12
15
|
DEFAULT_LAYER_DIRECTORIES,
|
|
@@ -351,6 +354,8 @@ function mapAntigravityToolCall(toolCall) {
|
|
|
351
354
|
* Grok Build: { toolName, toolInput: { file_path, content | old_string/new_string } }
|
|
352
355
|
* (aliases Write/Edit/MultiEdit → write/search_replace; matcher keeps both)
|
|
353
356
|
* Antigravity: { toolCall: { name, args: { TargetFile, CodeContent, … } } }
|
|
357
|
+
* Cursor: { tool_name, tool_input, hook_event_name?, workspace_roots? }
|
|
358
|
+
* Write uses `contents`; StrReplace maps to Edit (path/old_string/new_string).
|
|
354
359
|
*/
|
|
355
360
|
function normalizeHookPayload(payload, grokHookEvent = Boolean(process.env.GROK_HOOK_EVENT)) {
|
|
356
361
|
const antigravityStyle =
|
|
@@ -367,17 +372,25 @@ function normalizeHookPayload(payload, grokHookEvent = Boolean(process.env.GROK_
|
|
|
367
372
|
toolInput: { ...(mapped?.toolInput ?? {}), file_path: filePath },
|
|
368
373
|
grokStyle: true, // decision JSON on stdout (deny)
|
|
369
374
|
antigravityStyle: true,
|
|
375
|
+
cursorStyle: false,
|
|
370
376
|
operation: mapped?.operation ?? mapped?.toolName ?? null,
|
|
371
377
|
};
|
|
372
378
|
}
|
|
373
379
|
|
|
374
380
|
const rawName = payload?.tool_name ?? payload?.toolName ?? '';
|
|
375
|
-
const
|
|
381
|
+
const toolInputRaw = payload?.tool_input ?? payload?.toolInput ?? {};
|
|
382
|
+
const toolInput =
|
|
383
|
+
toolInputRaw && typeof toolInputRaw === 'object' ? { ...toolInputRaw } : {};
|
|
384
|
+
// Cursor Write uses `contents`; Claude/Grok use `content`.
|
|
385
|
+
if (toolInput.content == null && typeof toolInput.contents === 'string') {
|
|
386
|
+
toolInput.content = toolInput.contents;
|
|
387
|
+
}
|
|
376
388
|
const nameMap = {
|
|
377
389
|
Write: 'Write',
|
|
378
390
|
write: 'Write',
|
|
379
391
|
Edit: 'Edit',
|
|
380
392
|
search_replace: 'Edit',
|
|
393
|
+
StrReplace: 'Edit',
|
|
381
394
|
MultiEdit: 'MultiEdit',
|
|
382
395
|
ApplyPatch: 'ApplyPatch',
|
|
383
396
|
apply_patch: 'ApplyPatch',
|
|
@@ -388,6 +401,15 @@ function normalizeHookPayload(payload, grokHookEvent = Boolean(process.env.GROK_
|
|
|
388
401
|
const toolName = nameMap[rawName] ?? rawName;
|
|
389
402
|
const filePath =
|
|
390
403
|
toolInput.file_path ?? toolInput.filePath ?? toolInput.path ?? toolInput.target_file;
|
|
404
|
+
const cursorStyle =
|
|
405
|
+
Boolean(process.env.CURSOR_PROJECT_DIR) ||
|
|
406
|
+
Boolean(process.env.CURSOR_VERSION) ||
|
|
407
|
+
(payload != null &&
|
|
408
|
+
typeof payload === 'object' &&
|
|
409
|
+
(payload.hook_event_name === 'preToolUse' ||
|
|
410
|
+
Array.isArray(payload.workspace_roots) ||
|
|
411
|
+
rawName === 'StrReplace' ||
|
|
412
|
+
(rawName === 'Write' && typeof toolInputRaw?.contents === 'string')));
|
|
391
413
|
return {
|
|
392
414
|
toolName,
|
|
393
415
|
toolInput: { ...toolInput, file_path: filePath },
|
|
@@ -396,7 +418,8 @@ function normalizeHookPayload(payload, grokHookEvent = Boolean(process.env.GROK_
|
|
|
396
418
|
grokHookEvent ||
|
|
397
419
|
(payload != null && typeof payload === 'object' && 'toolName' in payload),
|
|
398
420
|
antigravityStyle: false,
|
|
399
|
-
|
|
421
|
+
cursorStyle,
|
|
422
|
+
operation: rawName === 'StrReplace' ? 'StrReplace' : null,
|
|
400
423
|
};
|
|
401
424
|
}
|
|
402
425
|
|
|
@@ -551,7 +574,7 @@ function designDeltaViolations(delta) {
|
|
|
551
574
|
* generated code are inserted literally, never interpreted as replacement patterns.
|
|
552
575
|
*/
|
|
553
576
|
function proposedSource(toolName, toolInput) {
|
|
554
|
-
if (toolName === 'Write') return toolInput.content;
|
|
577
|
+
if (toolName === 'Write') return toolInput.content ?? toolInput.contents;
|
|
555
578
|
|
|
556
579
|
let text = '';
|
|
557
580
|
try {
|
|
@@ -607,18 +630,30 @@ function emitAntigravityAllow(output, antigravityStyle) {
|
|
|
607
630
|
output.stdout(`${JSON.stringify({ decision: 'allow' })}\n`);
|
|
608
631
|
}
|
|
609
632
|
|
|
633
|
+
/** Cursor preToolUse accepts explicit allow; exit 0 alone also works. */
|
|
634
|
+
function emitCursorAllow(output, cursorStyle) {
|
|
635
|
+
if (!cursorStyle) return;
|
|
636
|
+
output.stdout(`${JSON.stringify({ permission: 'allow' })}\n`);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
function emitHostAllow(output, { antigravityStyle, cursorStyle }) {
|
|
640
|
+
emitAntigravityAllow(output, antigravityStyle);
|
|
641
|
+
emitCursorAllow(output, cursorStyle);
|
|
642
|
+
}
|
|
643
|
+
|
|
610
644
|
function runHookPayload(payload, gate, config, args, ts, attemptContext, output = processHookOutput()) {
|
|
611
|
-
const { toolName, toolInput, grokStyle, antigravityStyle, operation } =
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
645
|
+
const { toolName, toolInput, grokStyle, antigravityStyle, cursorStyle, operation } =
|
|
646
|
+
normalizeHookPayload(
|
|
647
|
+
payload,
|
|
648
|
+
attemptContext?.grokHookEvent ?? Boolean(process.env.GROK_HOOK_EVENT)
|
|
649
|
+
);
|
|
615
650
|
if (toolName === 'ApplyPatch') {
|
|
616
651
|
const patch = toolInput.patch ?? toolInput.input ?? toolInput.content;
|
|
617
652
|
const parsedPatch = codexPatchWrites(patch, args.root);
|
|
618
653
|
// Codex ApplyPatch is only preflighted when Ark can reconstruct every file operation.
|
|
619
654
|
// An incomplete reconstruction must not be mislabeled as atomic or hard enforcement.
|
|
620
655
|
if (!parsedPatch.complete) {
|
|
621
|
-
|
|
656
|
+
emitHostAllow(output, { antigravityStyle, cursorStyle });
|
|
622
657
|
return;
|
|
623
658
|
}
|
|
624
659
|
const patchWrites = parsedPatch.writes;
|
|
@@ -684,7 +719,7 @@ function runHookPayload(payload, gate, config, args, ts, attemptContext, output
|
|
|
684
719
|
);
|
|
685
720
|
}
|
|
686
721
|
if (changes.length === 0) {
|
|
687
|
-
|
|
722
|
+
emitHostAllow(output, { antigravityStyle, cursorStyle });
|
|
688
723
|
return;
|
|
689
724
|
}
|
|
690
725
|
result = prepareChangeFromRoot({
|
|
@@ -742,7 +777,7 @@ function runHookPayload(payload, gate, config, args, ts, attemptContext, output
|
|
|
742
777
|
? evaluateWriteDesignDelta({ root: args.root, config, changes, ts })
|
|
743
778
|
: null;
|
|
744
779
|
if (result.valid && (designDelta?.valid ?? true)) {
|
|
745
|
-
|
|
780
|
+
emitHostAllow(output, { antigravityStyle, cursorStyle });
|
|
746
781
|
return;
|
|
747
782
|
}
|
|
748
783
|
const message = [
|
|
@@ -776,23 +811,23 @@ function runHookPayload(payload, gate, config, args, ts, attemptContext, output
|
|
|
776
811
|
const filePath = toolInput.file_path;
|
|
777
812
|
if (!['Write', 'Edit', 'MultiEdit'].includes(toolName)) {
|
|
778
813
|
// Non-file tools: fail-open. Antigravity still needs an explicit allow decision.
|
|
779
|
-
|
|
814
|
+
emitHostAllow(output, { antigravityStyle, cursorStyle });
|
|
780
815
|
return;
|
|
781
816
|
}
|
|
782
817
|
if (typeof filePath !== 'string' || !SOURCE_FILE.test(filePath) || filePath.endsWith('.d.ts')) {
|
|
783
|
-
|
|
818
|
+
emitHostAllow(output, { antigravityStyle, cursorStyle });
|
|
784
819
|
return;
|
|
785
820
|
}
|
|
786
821
|
const rel = path.relative(args.root, path.resolve(filePath));
|
|
787
822
|
const segments = rel.split(path.sep);
|
|
788
823
|
if (segments[0] === '..' || segments.includes('node_modules')) {
|
|
789
|
-
|
|
824
|
+
emitHostAllow(output, { antigravityStyle, cursorStyle });
|
|
790
825
|
return;
|
|
791
826
|
}
|
|
792
827
|
|
|
793
828
|
const source = proposedSource(toolName, toolInput);
|
|
794
829
|
if (typeof source !== 'string') {
|
|
795
|
-
|
|
830
|
+
emitHostAllow(output, { antigravityStyle, cursorStyle });
|
|
796
831
|
return;
|
|
797
832
|
}
|
|
798
833
|
|
|
@@ -830,7 +865,7 @@ function runHookPayload(payload, gate, config, args, ts, attemptContext, output
|
|
|
830
865
|
})
|
|
831
866
|
: null;
|
|
832
867
|
if (result.valid && (designDelta?.valid ?? true)) {
|
|
833
|
-
|
|
868
|
+
emitHostAllow(output, { antigravityStyle, cursorStyle });
|
|
834
869
|
return;
|
|
835
870
|
}
|
|
836
871
|
|
|
@@ -861,7 +896,7 @@ function runHookPayload(payload, gate, config, args, ts, attemptContext, output
|
|
|
861
896
|
return false;
|
|
862
897
|
});
|
|
863
898
|
if (newViolations.length === 0 && (designDelta?.valid ?? true)) {
|
|
864
|
-
|
|
899
|
+
emitHostAllow(output, { antigravityStyle, cursorStyle });
|
|
865
900
|
return;
|
|
866
901
|
}
|
|
867
902
|
const combinedViolations = [...newViolations, ...designDeltaViolations(designDelta)];
|
|
@@ -923,7 +958,13 @@ function runHookPayload(payload, gate, config, args, ts, attemptContext, output
|
|
|
923
958
|
enforcement: hookEnforcement(
|
|
924
959
|
args.root,
|
|
925
960
|
attemptContext?.host ??
|
|
926
|
-
(antigravityStyle
|
|
961
|
+
(antigravityStyle
|
|
962
|
+
? 'antigravity'
|
|
963
|
+
: cursorStyle
|
|
964
|
+
? 'cursor'
|
|
965
|
+
: grokStyle
|
|
966
|
+
? 'grok'
|
|
967
|
+
: 'claude'),
|
|
927
968
|
attemptContext?.operation ??
|
|
928
969
|
operation ??
|
|
929
970
|
(antigravityStyle
|
|
@@ -932,11 +973,15 @@ function runHookPayload(payload, gate, config, args, ts, attemptContext, output
|
|
|
932
973
|
: toolName === 'MultiEdit'
|
|
933
974
|
? 'multi_replace_file_content'
|
|
934
975
|
: 'write_to_file'
|
|
935
|
-
:
|
|
976
|
+
: cursorStyle
|
|
936
977
|
? toolName === 'Edit'
|
|
937
|
-
? '
|
|
938
|
-
: '
|
|
939
|
-
:
|
|
978
|
+
? 'StrReplace'
|
|
979
|
+
: 'Write'
|
|
980
|
+
: grokStyle
|
|
981
|
+
? toolName === 'Edit'
|
|
982
|
+
? 'search_replace'
|
|
983
|
+
: 'write'
|
|
984
|
+
: toolName),
|
|
940
985
|
toolName === 'Write' || Boolean(attemptContext?.completePatch)
|
|
941
986
|
),
|
|
942
987
|
...(layer ? { layer } : {}),
|
|
@@ -968,6 +1013,16 @@ function runHookPayload(payload, gate, config, args, ts, attemptContext, output
|
|
|
968
1013
|
}) + '\n'
|
|
969
1014
|
);
|
|
970
1015
|
}
|
|
1016
|
+
// Cursor preToolUse: permission deny + agent_message (exit 2 also blocks).
|
|
1017
|
+
if (cursorStyle) {
|
|
1018
|
+
output.stdout(
|
|
1019
|
+
JSON.stringify({
|
|
1020
|
+
permission: 'deny',
|
|
1021
|
+
agent_message: message,
|
|
1022
|
+
user_message: `ArkGate blocked write to ${rel}`,
|
|
1023
|
+
}) + '\n'
|
|
1024
|
+
);
|
|
1025
|
+
}
|
|
971
1026
|
output.status(2);
|
|
972
1027
|
}
|
|
973
1028
|
|
|
@@ -1852,47 +1907,13 @@ export async function runArkMcp({ hookInput } = {}) {
|
|
|
1852
1907
|
* FX06 — process package vs project install honesty (multi-project field truth).
|
|
1853
1908
|
* Process arkgateVersion is startup-loaded; after consumer `npm i arkgate@newer`,
|
|
1854
1909
|
* long-lived MCP can report a stale process version until restart.
|
|
1910
|
+
* Algorithm lives in bin/lib/mcp-process-package.mjs for unit coverage.
|
|
1855
1911
|
*/
|
|
1856
|
-
function readProjectInstalledArkgateVersion() {
|
|
1857
|
-
try {
|
|
1858
|
-
const shallow = path.join(resolvedRoot, 'node_modules', 'arkgate', 'package.json');
|
|
1859
|
-
if (fs.existsSync(shallow)) {
|
|
1860
|
-
const v = JSON.parse(fs.readFileSync(shallow, 'utf8')).version;
|
|
1861
|
-
return typeof v === 'string' && v.trim() ? v.trim() : null;
|
|
1862
|
-
}
|
|
1863
|
-
} catch {
|
|
1864
|
-
/* fall through */
|
|
1865
|
-
}
|
|
1866
|
-
try {
|
|
1867
|
-
const requireFromProject = createRequire(path.join(resolvedRoot, 'package.json'));
|
|
1868
|
-
const pkgJson = requireFromProject.resolve('arkgate/package.json');
|
|
1869
|
-
const v = JSON.parse(fs.readFileSync(pkgJson, 'utf8')).version;
|
|
1870
|
-
return typeof v === 'string' && v.trim() ? v.trim() : null;
|
|
1871
|
-
} catch {
|
|
1872
|
-
return null;
|
|
1873
|
-
}
|
|
1874
|
-
}
|
|
1875
|
-
|
|
1876
1912
|
function processPackageHonesty() {
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
projectInstalledVersion != null &&
|
|
1882
|
-
processVersion !== projectInstalledVersion;
|
|
1883
|
-
return {
|
|
1884
|
-
schemaVersion: '1.0',
|
|
1885
|
-
notAScore: true,
|
|
1886
|
-
processArkgateVersion: processVersion,
|
|
1887
|
-
projectInstalledVersion,
|
|
1888
|
-
processPackageMismatch: mismatch,
|
|
1889
|
-
processStale: mismatch,
|
|
1890
|
-
nextAction: mismatch
|
|
1891
|
-
? 'Restart or retarget the Ark MCP server so process arkgateVersion matches the project install. Prefer project-local CLI (`npx arkgate` / `npx arkgate-check`) until identity is matched and versions align. Multi-checkout users: one expectedRoot per project; never reuse another checkout’s projectId.'
|
|
1892
|
-
: projectInstalledVersion == null
|
|
1893
|
-
? 'Project has no resolvable node_modules/arkgate; install the package or use CLI from a project that pins arkgate.'
|
|
1894
|
-
: 'Process package version matches project install for this MCP root.',
|
|
1895
|
-
};
|
|
1913
|
+
return buildProcessPackageHonesty({
|
|
1914
|
+
processVersion: typeof ark.version === 'string' ? ark.version : null,
|
|
1915
|
+
root: resolvedRoot,
|
|
1916
|
+
});
|
|
1896
1917
|
}
|
|
1897
1918
|
|
|
1898
1919
|
function contextFor(binding) {
|
package/bin/ark.mjs
CHANGED
|
@@ -207,8 +207,8 @@ Options:
|
|
|
207
207
|
--remove-host <host>
|
|
208
208
|
Preview or apply removal of that compact host integration; re-add it with --tools <host>.
|
|
209
209
|
--require-write-hook <host>
|
|
210
|
-
Require and verify a hard local write hook for Claude or
|
|
211
|
-
advisory-write plus hard CI merge only; impossible requests fail before any write.
|
|
210
|
+
Require and verify a hard local write hook for Claude, Grok, Antigravity, or Cursor.
|
|
211
|
+
Codex/OpenCode are advisory-write plus hard CI merge only; impossible requests fail before any write.
|
|
212
212
|
|
|
213
213
|
Interactive mode (TTY, no --yes): asks what application shape you are building and maps it to a preset.
|
|
214
214
|
Non-interactive (no TTY): uses the same defaults as --yes — never calls readline on a null interface.
|
package/bin/lib/agent-gates.mjs
CHANGED
|
@@ -26,9 +26,11 @@ export {
|
|
|
26
26
|
antigravityHooks,
|
|
27
27
|
claudeSettings,
|
|
28
28
|
codexProjectConfig,
|
|
29
|
+
cursorHooks,
|
|
29
30
|
grokHooks,
|
|
30
31
|
grokProjectConfig,
|
|
31
32
|
mergeAntigravityArkHook,
|
|
33
|
+
mergeCursorArkHook,
|
|
32
34
|
mergeOpencodeArkMcp,
|
|
33
35
|
opencodeProjectConfig,
|
|
34
36
|
} from './hook-templates.mjs';
|
|
@@ -370,7 +370,8 @@ ${steps}
|
|
|
370
370
|
${placementBody}
|
|
371
371
|
|
|
372
372
|
The project is only considered Ark-enforced when its host-appropriate write path is configured
|
|
373
|
-
and the CI check passes. Only Claude/Grok provide a hard local write boundary
|
|
373
|
+
and the CI check passes. Only Claude/Grok/Antigravity/Cursor provide a hard local write boundary
|
|
374
|
+
(Cursor: Write/StrReplace when .cursor/hooks.json is trusted); Codex/OpenCode use
|
|
374
375
|
advisory MCP plus CI. The experimental runtime is not required.
|
|
375
376
|
`;
|
|
376
377
|
}
|
package/bin/lib/gate-files.mjs
CHANGED
|
@@ -127,7 +127,7 @@ const FAIL_CLOSED_ARK_FLAG = /(?:^|\s)--(?:strict|strict-merge|require-gates)(?=
|
|
|
127
127
|
const COMPACT_HOST_FILES = {
|
|
128
128
|
claude: ['.claude/settings.json'],
|
|
129
129
|
grok: ['.grok/config.toml', '.grok/hooks/ark-write-gate.json'],
|
|
130
|
-
cursor: ['.cursor/mcp.json'],
|
|
130
|
+
cursor: ['.cursor/mcp.json', '.cursor/hooks.json'],
|
|
131
131
|
codex: ['.codex/hooks.json', '.codex/config.toml'],
|
|
132
132
|
windsurf: ['.windsurf/rules/ark.md'],
|
|
133
133
|
cline: ['.clinerules/ark.md'],
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
// Generated from hook-templates.source.mjs — run npm run generate:packaged-tooling.
|
|
2
|
-
import{execCommandParts as
|
|
3
|
-
`}function
|
|
4
|
-
`}function
|
|
2
|
+
import{execCommandParts as l,execRunner as i}from"../ark-shared.mjs";const s="arkgate-mcp";function h(e){const r=i(e);return`${JSON.stringify({version:1,hooks:{preToolUse:[{command:`${r} ${s} --hook --hook-repair --fail-on-new-smells --root . --root-env CURSOR_PROJECT_DIR --config ark.config.json`,matcher:"Write|StrReplace",failClosed:!0,timeout:30}]}},null,2)}
|
|
3
|
+
`}function y(e){const r=i(e);return`${JSON.stringify({hooks:{SessionStart:[{hooks:[{type:"command",command:`${r} ${s} --session-context --root . --root-env CLAUDE_PROJECT_DIR --config ark.config.json`}]}],PreToolUse:[{matcher:"Write|Edit|MultiEdit",hooks:[{type:"command",command:`${r} ${s} --hook --hook-repair --fail-on-new-smells --root . --root-env CLAUDE_PROJECT_DIR --config ark.config.json`}]}]}},null,2)}
|
|
4
|
+
`}function $(e){const r=i(e);return`${JSON.stringify({hooks:{SessionStart:[{hooks:[{type:"command",timeout:30,command:`${r} ${s} --session-context --root . --root-env CODEX_PROJECT_DIR --config ark.config.json`}]}],PreToolUse:[{matcher:"ApplyPatch|apply_patch|Write|Edit|MultiEdit",hooks:[{type:"command",timeout:30,command:`${r} ${s} --hook --hook-repair --fail-on-new-smells --root . --root-env CODEX_PROJECT_DIR --config ark.config.json`}]}]}},null,2)}
|
|
5
|
+
`}function O(e){const{command:r,args:o}=l(e,s,["--root",".","--config","ark.config.json"]),t=a=>a.replace(/\\/g,"\\\\").replace(/"/g,'\\"'),c=o.map(a=>`"${t(a)}"`).join(", ");return`# Generated by ark-check --install-agent-gates (Codex project scope).
|
|
5
6
|
# CONFIGURED ON DISK \u2014 RUNTIME NOT VERIFIED.
|
|
6
7
|
# Restart Codex, then call ark_identity with expectedRoot before trusting MCP verdicts.
|
|
7
8
|
[mcp_servers.ark]
|
|
8
9
|
command = "${t(r)}"
|
|
9
|
-
args = [${
|
|
10
|
-
`}function
|
|
10
|
+
args = [${c}]
|
|
11
|
+
`}function R(e){const{command:r,args:o}=l(e,s,["--root",".","--config","ark.config.json"]),t=o.map(c=>`"${c.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`).join(", ");return`# Generated by ark-check --install-agent-gates (Grok Build project scope).
|
|
11
12
|
# Restart Grok (or /mcps \u2192 refresh) after changes. Also loads repo-root .mcp.json.
|
|
12
13
|
[mcp_servers.ark]
|
|
13
14
|
command = "${r.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"
|
|
14
15
|
args = [${t}]
|
|
15
|
-
`}function
|
|
16
|
-
`}function
|
|
17
|
-
`}function
|
|
18
|
-
`}function
|
|
19
|
-
`}function
|
|
20
|
-
`}
|
|
16
|
+
`}function _(e){const r=i(e);return`${JSON.stringify({hooks:{SessionStart:[{hooks:[{type:"command",timeout:30,command:`${r} ${s} --session-context --root . --root-env GROK_WORKSPACE_ROOT,CLAUDE_PROJECT_DIR --config ark.config.json`}]}],PreToolUse:[{matcher:"Write|Edit|MultiEdit|write|search_replace",hooks:[{type:"command",timeout:30,command:`${r} ${s} --hook --hook-repair --fail-on-new-smells --root . --root-env GROK_WORKSPACE_ROOT,CLAUDE_PROJECT_DIR --config ark.config.json`}]}]}},null,2)}
|
|
17
|
+
`}function A(e){const r=i(e);return`${JSON.stringify({"ark-write-gate":{PreToolUse:[{matcher:"write_to_file|replace_file_content|multi_replace_file_content",hooks:[{type:"command",timeout:30,command:`${r} ${s} --hook --hook-repair --fail-on-new-smells --root . --config ark.config.json`}]}]}},null,2)}
|
|
18
|
+
`}function j(e){const{command:r,args:o}=l(e,s,["--root",".","--config","ark.config.json"]);return`${JSON.stringify({$schema:"https://opencode.ai/config.json",mcp:{ark:{type:"local",command:[r,...o],enabled:!0}}},null,2)}
|
|
19
|
+
`}function E(e,r){let o,t;try{o=e&&e.trim()?JSON.parse(e):{},t=JSON.parse(r)}catch{return null}if(!o||typeof o!="object"||Array.isArray(o)||!t||typeof t!="object"||Array.isArray(t))return null;const c=t.hooks,p=(Array.isArray(c?.preToolUse)?c.preToolUse:[]).find(n=>n&&typeof n=="object"&&typeof n.command=="string"&&/arkgate-mcp|ark-mcp/.test(n.command)&&/\s--hook(?:\s|$)/.test(` ${n.command} `));if(!p)return null;const m=o.hooks&&typeof o.hooks=="object"&&!Array.isArray(o.hooks)?{...o.hooks}:{},f=Array.isArray(m.preToolUse)?[...m.preToolUse]:[],k=n=>n&&typeof n=="object"&&typeof n.command=="string"&&/arkgate-mcp|ark-mcp/.test(n.command)&&/\s--hook(?:\s|$)/.test(` ${n.command} `)&&(typeof n.matcher!="string"||/Write/.test(n.matcher)||/StrReplace/.test(n.matcher)),g=f.filter(n=>!k(n));m.preToolUse=[...g,p];const u={...o,version:o.version??t.version??1,hooks:m};return`${JSON.stringify(u,null,2)}
|
|
20
|
+
`}function S(e,r){let o,t;try{o=e&&e.trim()?JSON.parse(e):{},t=JSON.parse(r)}catch{return null}if(!o||typeof o!="object"||Array.isArray(o)||!t||typeof t!="object"||Array.isArray(t))return null;const c=t["ark-write-gate"];if(!c||typeof c!="object")return null;const a={...o,"ark-write-gate":c};return`${JSON.stringify(a,null,2)}
|
|
21
|
+
`}function C(e,r){let o,t;try{o=e&&e.trim()?JSON.parse(e):{},t=JSON.parse(r)}catch{return null}if(!o||typeof o!="object"||Array.isArray(o))return null;const c={...o};!c.$schema&&t.$schema&&(c.$schema=t.$schema);const a=o.mcp&&typeof o.mcp=="object"&&!Array.isArray(o.mcp)?{...o.mcp}:{};return a.ark=t.mcp.ark,c.mcp=a,`${JSON.stringify(c,null,2)}
|
|
22
|
+
`}export{s as PREFERRED_MCP_BIN,A as antigravityHooks,y as claudeSettings,$ as codexHooks,O as codexProjectConfig,h as cursorHooks,_ as grokHooks,R as grokProjectConfig,S as mergeAntigravityArkHook,E as mergeCursorArkHook,C as mergeOpencodeArkMcp,j as opencodeProjectConfig};
|
|
@@ -70,9 +70,27 @@ export const HOST_SUPPORT_MATRIX = Object.freeze({
|
|
|
70
70
|
true,
|
|
71
71
|
true
|
|
72
72
|
),
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
// Cursor: official preToolUse deny / exit 2 is a hard block for matched tools when
|
|
74
|
+
// `.cursor/hooks.json` is installed + trusted. Claim hard only for Write|StrReplace;
|
|
75
|
+
// Shell/Tab/human edits still rely on CI. Repair envelope may emit; Write updated_input
|
|
76
|
+
// reinjection is not guaranteed on Cursor (agent_message + retry is the supported path).
|
|
77
|
+
cursor: hostProfile(
|
|
78
|
+
'Cursor',
|
|
79
|
+
'.cursor/hooks.json',
|
|
80
|
+
'preToolUse `Write` / `StrReplace`',
|
|
81
|
+
['Write', 'StrReplace'],
|
|
82
|
+
true,
|
|
83
|
+
false,
|
|
84
|
+
{
|
|
85
|
+
repairEnvelopeEmitted: true,
|
|
86
|
+
operationCoverage: {
|
|
87
|
+
Write: true,
|
|
88
|
+
StrReplace: true,
|
|
89
|
+
shell: false,
|
|
90
|
+
'pre-commit': false,
|
|
91
|
+
},
|
|
92
|
+
}
|
|
93
|
+
),
|
|
76
94
|
codex: hostProfile(
|
|
77
95
|
'OpenAI Codex',
|
|
78
96
|
'.codex/hooks.json',
|
|
@@ -134,14 +152,15 @@ export function renderHostSupportMatrixMarkdown() {
|
|
|
134
152
|
const rows = HOST_SUPPORT_HOSTS.map((host) => {
|
|
135
153
|
const profile = HOST_SUPPORT_MATRIX[host];
|
|
136
154
|
const capabilities = profile.capabilities;
|
|
137
|
-
// Fail-closed honesty:
|
|
138
|
-
//
|
|
155
|
+
// Fail-closed honesty: Codex/OpenCode never claim hard write; CI is required-status.
|
|
156
|
+
// Cursor claims hard only for listed preToolUse ops when hooks are installed + trusted.
|
|
157
|
+
// hookSurface already includes "PreToolUse/preToolUse …" — do not prefix again.
|
|
139
158
|
let local;
|
|
140
159
|
if (capabilities['hard-write']) {
|
|
141
160
|
local = `**Hard** block for listed ops (${profile.hookSurface}) when installed + trusted`;
|
|
142
161
|
} else if (host === 'codex') {
|
|
143
162
|
local =
|
|
144
|
-
'**Advisory / best-effort** at write (not equivalent to Claude/Grok hard block)';
|
|
163
|
+
'**Advisory / best-effort** at write (not equivalent to Claude/Grok/Cursor hard block)';
|
|
145
164
|
} else if (host === 'opencode') {
|
|
146
165
|
local =
|
|
147
166
|
'**Advisory / best-effort** at write (MCP + optional plugin; not a hard boundary)';
|
|
@@ -169,7 +188,8 @@ ${rows}
|
|
|
169
188
|
|
|
170
189
|
**Read the CI column:** for every host, the repository-wide hard guarantee is a **required**
|
|
171
190
|
GitHub **status context** that runs the CLI — not “CI file present,” and not the CLI binary name alone.
|
|
172
|
-
|
|
191
|
+
Codex/OpenCode never get a fake hard write claim. Cursor hard write covers only listed
|
|
192
|
+
\`preToolUse\` ops when \`.cursor/hooks.json\` is installed and trusted — Shell/Tab/human edits still rely on CI.
|
|
173
193
|
|
|
174
194
|
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.`;
|
|
175
195
|
}
|
|
@@ -203,14 +223,14 @@ export function doctorWritePathHonestyMessage(activeHost, hardWriteActive) {
|
|
|
203
223
|
// EH07: distinguish CLI command (arkgate-check / ark-check) from the GitHub required status context name.
|
|
204
224
|
const mergeBoundary =
|
|
205
225
|
'Required CI hard merge boundary = a required GitHub status context that runs arkgate-check --strict-merge (alias ark-check --strict-merge)';
|
|
206
|
-
if (host === 'cursor') {
|
|
207
|
-
return `Cursor:
|
|
226
|
+
if (host === 'cursor' && !hardWriteActive) {
|
|
227
|
+
return `Cursor: hard preToolUse is supported for Write/StrReplace when .cursor/hooks.json is installed + trusted; without runtime-observed hook evidence, hard is unverified. ${mergeBoundary}.`;
|
|
208
228
|
}
|
|
209
229
|
if (host === 'codex') {
|
|
210
|
-
return `Codex: write path is advisory / best-effort at write (not Claude/Grok hard). ${mergeBoundary}.`;
|
|
230
|
+
return `Codex: write path is advisory / best-effort at write (not Claude/Grok/Cursor hard). ${mergeBoundary}.`;
|
|
211
231
|
}
|
|
212
232
|
if (host === 'opencode') {
|
|
213
|
-
return `OpenCode: write path is advisory / best-effort (MCP + optional plugin; not Claude/Grok/Antigravity hard). ${mergeBoundary}.`;
|
|
233
|
+
return `OpenCode: write path is advisory / best-effort (MCP + optional plugin; not Claude/Grok/Antigravity/Cursor hard). ${mergeBoundary}.`;
|
|
214
234
|
}
|
|
215
235
|
if ((host === 'claude' || host === 'grok' || host === 'antigravity') && !hardWriteActive) {
|
|
216
236
|
const label =
|
|
@@ -25,9 +25,11 @@ import {
|
|
|
25
25
|
claudeSettings,
|
|
26
26
|
codexHooks,
|
|
27
27
|
codexProjectConfig,
|
|
28
|
+
cursorHooks,
|
|
28
29
|
grokHooks,
|
|
29
30
|
grokProjectConfig,
|
|
30
31
|
mergeAntigravityArkHook,
|
|
32
|
+
mergeCursorArkHook,
|
|
31
33
|
mergeOpencodeArkMcp,
|
|
32
34
|
opencodeProjectConfig,
|
|
33
35
|
} from './hook-templates.mjs';
|
|
@@ -194,6 +196,9 @@ export function buildManagedAssetCatalog({ root, tools, compact = false, skillsO
|
|
|
194
196
|
);
|
|
195
197
|
if (selectedTools.has('cursor')) {
|
|
196
198
|
add('.cursor/mcp.json', mcpJson(root));
|
|
199
|
+
// whole-file for managed upgrade/manifest (json-merge is install-time only via
|
|
200
|
+
// mergeCursorArkHook below — same pattern as Antigravity hooks).
|
|
201
|
+
add('.cursor/hooks.json', cursorHooks(root));
|
|
197
202
|
if (!compact) add('.cursor/rules/ark.mdc', cursorRule(root));
|
|
198
203
|
}
|
|
199
204
|
if (selectedTools.has('claude')) add('.claude/settings.json', claudeSettings(root));
|
|
@@ -516,6 +521,25 @@ export function runInstallAgentGates(args) {
|
|
|
516
521
|
if (merged === existing) return { relativePath, status: 'skipped' };
|
|
517
522
|
return writeTemplate(root, relativePath, merged, true);
|
|
518
523
|
}
|
|
524
|
+
if (relativePath === '.cursor/hooks.json') {
|
|
525
|
+
const fullPath = path.join(root, relativePath);
|
|
526
|
+
let existing = '';
|
|
527
|
+
try {
|
|
528
|
+
existing = fs.readFileSync(fullPath, 'utf8');
|
|
529
|
+
} catch {
|
|
530
|
+
// Missing hooks file → write generated Cursor preToolUse gate.
|
|
531
|
+
}
|
|
532
|
+
if (!existing) {
|
|
533
|
+
return writeTemplate(root, relativePath, content, true);
|
|
534
|
+
}
|
|
535
|
+
const merged = mergeCursorArkHook(existing, content);
|
|
536
|
+
if (merged == null) {
|
|
537
|
+
return { relativePath, status: 'skipped-non-ark' };
|
|
538
|
+
}
|
|
539
|
+
if (merged === existing) return { relativePath, status: 'skipped' };
|
|
540
|
+
// Upsert Ark preToolUse without requiring --force; never wipe sibling hooks.
|
|
541
|
+
return writeTemplate(root, relativePath, merged, true);
|
|
542
|
+
}
|
|
519
543
|
if (relativePath === '.agents/hooks.json') {
|
|
520
544
|
const fullPath = path.join(root, relativePath);
|
|
521
545
|
let existing = '';
|
|
@@ -126,7 +126,12 @@ function hasArkText(root, relativePath) {
|
|
|
126
126
|
|
|
127
127
|
const HOST_SIGNALS = {
|
|
128
128
|
claude: ['.claude/settings.json', '.claude/skills/ark-upgrade/SKILL.md'],
|
|
129
|
-
cursor: [
|
|
129
|
+
cursor: [
|
|
130
|
+
'.cursor/mcp.json',
|
|
131
|
+
'.cursor/hooks.json',
|
|
132
|
+
'.cursor/rules/ark.mdc',
|
|
133
|
+
'.cursor/commands/ark-upgrade.md',
|
|
134
|
+
],
|
|
130
135
|
codex: ['.codex/hooks.json', '.codex/config.toml', '.agents/skills/ark-upgrade/SKILL.md'],
|
|
131
136
|
grok: ['.grok/config.toml', '.grok/hooks/ark-write-gate.json', '.grok/skills/ark-upgrade/SKILL.md'],
|
|
132
137
|
antigravity: ['.agents/hooks.json', '.agents/skills/ark-upgrade/SKILL.md'],
|