arkgate 4.1.1 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +81 -3
- package/README.md +15 -4
- package/bin/ark-check-runtime.mjs +16 -5
- package/bin/ark-mcp-runtime.mjs +766 -64
- package/bin/lib/agent-gates.mjs +1 -0
- package/bin/lib/ci-and-commands.mjs +16 -7
- package/bin/lib/codex-home.mjs +90 -8
- package/bin/lib/design-smells.mjs +71 -9
- package/bin/lib/doctor-plan.mjs +36 -36
- package/bin/lib/effective-contract-load.mjs +73 -9
- package/bin/lib/enforcement-state.mjs +1 -1
- package/bin/lib/gate-files.mjs +441 -9
- package/bin/lib/github-enforcement.mjs +16 -3
- package/bin/lib/hook-templates.mjs +12 -11
- package/bin/lib/html-report-evolution.mjs +114 -0
- package/bin/lib/html-report.mjs +11 -89
- package/bin/lib/import-resolve.mjs +33 -11
- package/bin/lib/install-activation.mjs +87 -0
- package/bin/lib/install-migrate.mjs +66 -50
- package/bin/lib/managed-upgrade.mjs +10 -41
- package/bin/lib/mcp-adoption.mjs +15 -5
- package/bin/lib/pilot-loop.mjs +25 -8
- package/bin/lib/project-identity.mjs +103 -0
- package/bin/lib/report-snapshot-context.mjs +28 -0
- package/bin/lib/resident-hook.mjs +33 -9
- package/bin/lib/rules-inventory.mjs +100 -8
- package/bin/lib/skill-install.mjs +272 -22
- package/bin/lib/skill-write.mjs +899 -0
- package/bin/lib/start-preview.mjs +84 -1
- package/bin/lib/upgrade-command.mjs +2 -5
- package/dist/index.cjs +13 -13
- package/dist/index.d.ts +194 -2
- package/dist/index.js +13 -13
- package/docs/README.md +5 -3
- package/docs/agent-guide.md +110 -14
- package/docs/ai-gates.md +103 -18
- package/docs/assets/ark-write-gate.svg +2 -2
- package/docs/enthusiast/how-to-agent-gates.md +6 -0
- package/docs/package-surface.md +14 -9
- package/docs/product-voice.md +13 -1
- package/package.json +3 -1
- package/schemas/ark.project-identity.schema.json +116 -0
- package/server.json +2 -2
- package/templates/skills/ark-adopt.md +9 -0
- package/templates/skills/ark-architect.md +12 -2
- package/templates/skills/ark-autopilot.md +9 -0
- package/templates/skills/ark-contract.md +11 -1
- package/templates/skills/ark-coverage.md +9 -0
- package/templates/skills/ark-explain.md +13 -1
- package/templates/skills/ark-explore.md +9 -0
- package/templates/skills/ark-fix.md +10 -1
- package/templates/skills/ark-loop.md +11 -2
- package/templates/skills/ark-place.md +17 -6
- package/templates/skills/ark-runtime.md +8 -0
- package/templates/skills/ark-think.md +14 -2
- package/templates/skills/ark-upgrade.md +9 -0
|
@@ -9,10 +9,13 @@ import {
|
|
|
9
9
|
antigravityHooks,
|
|
10
10
|
claudeSettings,
|
|
11
11
|
codexHooks,
|
|
12
|
+
codexProjectConfig,
|
|
12
13
|
grokHooks,
|
|
13
14
|
grokProjectConfig,
|
|
14
15
|
opencodeProjectConfig,
|
|
15
16
|
} from './hook-templates.mjs';
|
|
17
|
+
import { codexPrimaryTable, codexProjectMcpIsValid } from './codex-home.mjs';
|
|
18
|
+
import { codexRuntimeActivation } from './enforcement-state.mjs';
|
|
16
19
|
|
|
17
20
|
const COMPACT_HOST_TEMPLATES = {
|
|
18
21
|
claude: (root) => [
|
|
@@ -28,7 +31,10 @@ const COMPACT_HOST_TEMPLATES = {
|
|
|
28
31
|
['.mcp.json', mcpJson(root)],
|
|
29
32
|
],
|
|
30
33
|
cursor: (root) => [['.cursor/mcp.json', mcpJson(root)]],
|
|
31
|
-
codex: (root) => [
|
|
34
|
+
codex: (root) => [
|
|
35
|
+
['.codex/hooks.json', codexHooks(root)],
|
|
36
|
+
['.codex/config.toml', codexProjectConfig(root)],
|
|
37
|
+
],
|
|
32
38
|
opencode: (root) => [['opencode.json', opencodeProjectConfig(root)]],
|
|
33
39
|
windsurf: (root) => [['.windsurf/rules/ark.md', instructionRule(root)]],
|
|
34
40
|
cline: (root) => [['.clinerules/ark.md', instructionRule(root)]],
|
|
@@ -159,6 +165,12 @@ export function renderStartPreview(preview, options = {}) {
|
|
|
159
165
|
}
|
|
160
166
|
console.log('Host guarantees:');
|
|
161
167
|
for (const guarantee of preview.hostGuarantees) console.log(` ${guarantee}`);
|
|
168
|
+
if (preview.runtimeActivation) {
|
|
169
|
+
console.log('Codex MCP CONFIGURED — RUNTIME NOT VERIFIED.');
|
|
170
|
+
console.log(` Runtime activation: ${JSON.stringify(preview.runtimeActivation)}`);
|
|
171
|
+
console.log(` Restart Codex, then call ark_identity with expectedRoot "${preview.root}".`);
|
|
172
|
+
console.log(' Do not trust MCP verdicts before the project identity matches.');
|
|
173
|
+
}
|
|
162
174
|
if (preview.unresolvedDecisions.length > 0) {
|
|
163
175
|
console.log('Unresolved decisions:');
|
|
164
176
|
for (const decision of preview.unresolvedDecisions) console.log(` ${decision}`);
|
|
@@ -198,6 +210,31 @@ function planHostRemoval(args, helpers) {
|
|
|
198
210
|
const target = path.join(args.root, relativePath);
|
|
199
211
|
if (!fs.existsSync(target)) continue;
|
|
200
212
|
const before = fs.readFileSync(target);
|
|
213
|
+
if (host === 'codex' && relativePath === '.codex/config.toml') {
|
|
214
|
+
const currentText = before.toString('utf8');
|
|
215
|
+
const currentTable = codexPrimaryTable(currentText);
|
|
216
|
+
const expectedTable = codexPrimaryTable(expected);
|
|
217
|
+
if (!currentTable) continue;
|
|
218
|
+
if (
|
|
219
|
+
!expectedTable ||
|
|
220
|
+
currentTable.block.trimEnd() !== expectedTable.block.trimEnd()
|
|
221
|
+
) {
|
|
222
|
+
unresolvedDecisions.push(
|
|
223
|
+
'.codex/config.toml Ark MCP binding was customized and was left untouched.'
|
|
224
|
+
);
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
const expectedPrelude = expected.slice(0, expectedTable.start);
|
|
228
|
+
let prefix = currentText.slice(0, currentTable.start);
|
|
229
|
+
if (expectedPrelude && prefix.endsWith(expectedPrelude)) {
|
|
230
|
+
prefix = prefix.slice(0, -expectedPrelude.length);
|
|
231
|
+
}
|
|
232
|
+
let next = `${prefix}${currentText.slice(currentTable.end)}`;
|
|
233
|
+
if (prefix.length === 0) next = next.replace(/^\n+/, '');
|
|
234
|
+
const after = next.trim().length > 0 ? Buffer.from(next) : null;
|
|
235
|
+
changes.push(change(relativePath, before, after));
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
201
238
|
if (before.toString('utf8') !== expected) {
|
|
202
239
|
unresolvedDecisions.push(`${relativePath} was customized and was left untouched.`);
|
|
203
240
|
continue;
|
|
@@ -220,6 +257,32 @@ function planHostRemoval(args, helpers) {
|
|
|
220
257
|
changes.push(change('.mcp.json', null, Buffer.from(mcpJson(args.root))));
|
|
221
258
|
}
|
|
222
259
|
|
|
260
|
+
const runtimeActivation =
|
|
261
|
+
host === 'codex'
|
|
262
|
+
? codexRuntimeActivation({
|
|
263
|
+
configuredOnDisk: (() => {
|
|
264
|
+
const configPath = path.join(args.root, '.codex', 'config.toml');
|
|
265
|
+
const plannedConfig = changes.find(
|
|
266
|
+
(item) => item.path === '.codex/config.toml'
|
|
267
|
+
);
|
|
268
|
+
if (plannedConfig?.action === 'delete') return false;
|
|
269
|
+
if (plannedConfig?.afterBase64) {
|
|
270
|
+
return codexProjectMcpIsValid(
|
|
271
|
+
Buffer.from(plannedConfig.afterBase64, 'base64').toString('utf8'),
|
|
272
|
+
args.root
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
if (!fs.existsSync(configPath)) return false;
|
|
276
|
+
try {
|
|
277
|
+
return codexProjectMcpIsValid(fs.readFileSync(configPath, 'utf8'), args.root);
|
|
278
|
+
} catch {
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
})(),
|
|
282
|
+
restartRequired: true,
|
|
283
|
+
})
|
|
284
|
+
: null;
|
|
285
|
+
|
|
223
286
|
return {
|
|
224
287
|
version: 1,
|
|
225
288
|
root: args.root,
|
|
@@ -233,6 +296,7 @@ function planHostRemoval(args, helpers) {
|
|
|
233
296
|
commands: commands(args.root, args, helpers),
|
|
234
297
|
hostGuarantees: ['host removal is limited to Ark-owned compact artifacts', 'restore with the displayed --tools command'],
|
|
235
298
|
unresolvedDecisions,
|
|
299
|
+
...(runtimeActivation ? { runtimeActivation } : {}),
|
|
236
300
|
};
|
|
237
301
|
}
|
|
238
302
|
|
|
@@ -312,6 +376,18 @@ export async function planStart(args, helpers) {
|
|
|
312
376
|
changes.push(change(file, oldContent, newContent));
|
|
313
377
|
}
|
|
314
378
|
const percent = coverage.governed?.percent ?? null;
|
|
379
|
+
const codexSelected = args.tools === 'codex';
|
|
380
|
+
const runtimeActivation = codexSelected
|
|
381
|
+
? codexRuntimeActivation({
|
|
382
|
+
configuredOnDisk: (() => {
|
|
383
|
+
const config = after.get('.codex/config.toml');
|
|
384
|
+
return Boolean(
|
|
385
|
+
config && codexProjectMcpIsValid(config.toString('utf8'), root)
|
|
386
|
+
);
|
|
387
|
+
})(),
|
|
388
|
+
restartRequired: true,
|
|
389
|
+
})
|
|
390
|
+
: null;
|
|
315
391
|
return {
|
|
316
392
|
version: 1,
|
|
317
393
|
root,
|
|
@@ -329,8 +405,15 @@ export async function planStart(args, helpers) {
|
|
|
329
405
|
args.requireWriteHook ? `Hard-write hook verified for ${args.requireWriteHook}` : 'shared CI merge gate will be installed',
|
|
330
406
|
'preview phase performs no writes in the target project',
|
|
331
407
|
'apply writes the exact bytes identified by each afterHash',
|
|
408
|
+
...(codexSelected
|
|
409
|
+
? [
|
|
410
|
+
'Codex MCP is configured on disk, not runtime-verified',
|
|
411
|
+
'restart Codex and match ark_identity expectedRoot before trusting MCP verdicts',
|
|
412
|
+
]
|
|
413
|
+
: []),
|
|
332
414
|
],
|
|
333
415
|
unresolvedDecisions: percent !== null && percent < 90 ? [`Projected governed coverage is ${percent}%; review unclassified files before treating the contract as complete.`] : [],
|
|
416
|
+
...(runtimeActivation ? { runtimeActivation } : {}),
|
|
334
417
|
};
|
|
335
418
|
} finally {
|
|
336
419
|
fs.rmSync(shadowRoot, { recursive: true, force: true });
|
|
@@ -427,7 +427,7 @@ export function runUpgradeCommand(args, dependencies) {
|
|
|
427
427
|
const needsApply = wouldWrite > 0 || blocked > 0;
|
|
428
428
|
const command = buildUpgradeNextCommand(args, plan.planDigest);
|
|
429
429
|
if (args.json) {
|
|
430
|
-
// Always expose nextCommand for digest-bound
|
|
430
|
+
// Always expose nextCommand for digest-bound content/manifest apply;
|
|
431
431
|
// nothingToApply flags when content writes are zero so UIs do not urge apply.
|
|
432
432
|
console.log(
|
|
433
433
|
managedUpgradeJson(plan, {
|
|
@@ -443,15 +443,12 @@ export function runUpgradeCommand(args, dependencies) {
|
|
|
443
443
|
})
|
|
444
444
|
);
|
|
445
445
|
} else {
|
|
446
|
-
const metadataRefresh = plan.summary?.metadataRefresh ?? 0;
|
|
447
446
|
renderManagedUpgrade(plan, {
|
|
448
447
|
next: needsApply
|
|
449
448
|
? args.install
|
|
450
449
|
? `Update the package and recompute this preview with: ${command}`
|
|
451
450
|
: `Apply the exact preview with: ${command}`
|
|
452
451
|
: undefined,
|
|
453
|
-
// Human path: optional digest-bound stamp refresh without urging content apply.
|
|
454
|
-
...( !needsApply && metadataRefresh > 0 ? { optionalStampApply: command } : {}),
|
|
455
452
|
});
|
|
456
453
|
if (args.install === false) {
|
|
457
454
|
console.log(
|
|
@@ -481,7 +478,7 @@ export function runUpgradeCommand(args, dependencies) {
|
|
|
481
478
|
if (args.json) console.log(JSON.stringify(applied, null, 2));
|
|
482
479
|
else {
|
|
483
480
|
renderManagedUpgrade(applied);
|
|
484
|
-
console.log('No managed content writes pending
|
|
481
|
+
console.log('No managed content writes pending.');
|
|
485
482
|
}
|
|
486
483
|
return 0;
|
|
487
484
|
}
|