arkgate 4.8.3 → 4.8.5
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 +279 -1
- package/README.md +17 -7
- package/bin/ark-check-runtime.mjs +340 -5
- package/bin/ark-layer-match.mjs +170 -13
- package/bin/ark-mcp-runtime.mjs +9 -2
- package/bin/lib/agent-homes.mjs +41 -6
- package/bin/lib/analysis-completeness.mjs +86 -0
- package/bin/lib/analysis-engine.mjs +8 -8
- package/bin/lib/architecture-scan.mjs +2 -0
- package/bin/lib/ark-order-invariants.mjs +37 -0
- package/bin/lib/ark-order-sensors.mjs +4 -0
- package/bin/lib/arkrules-contract.mjs +8 -1
- package/bin/lib/check-args.mjs +68 -0
- package/bin/lib/config-contract.mjs +26 -0
- package/bin/lib/design-smells.mjs +85 -0
- package/bin/lib/diagnostic-catalog.mjs +9 -1
- package/bin/lib/first-run-help.mjs +16 -4
- package/bin/lib/invariant-coverage-io.mjs +175 -19
- package/bin/lib/invariant-coverage.mjs +110 -7
- package/bin/lib/literal-path-drift-io.mjs +569 -0
- package/bin/lib/literal-path-drift.mjs +761 -0
- package/bin/lib/policy-delta-io.mjs +5 -0
- package/bin/lib/remediation.mjs +25 -1
- package/bin/lib/rules-under-contract.mjs +5 -0
- package/bin/lib/scan-files.mjs +54 -0
- package/bin/lib/sensor-promote-cli.mjs +372 -0
- package/bin/lib/sensor-promote-io.mjs +246 -0
- package/bin/lib/sensor-promotion.mjs +363 -0
- package/bin/lib/skill-catalog-apply.mjs +18 -1
- package/bin/lib/skill-install.mjs +4 -3
- package/bin/lib/upgrade-whats-new.mjs +1 -1
- package/dist/{configTypes-dNJ2C0yx.d.ts → configTypes-dy5PfTqS.d.ts} +31 -0
- package/dist/{diagnosticCatalog-C5GgeyEE.d.ts → diagnosticCatalog-DMO30svh.d.ts} +75 -7
- package/dist/eslint/index.cjs +5 -5
- package/dist/eslint/index.d.ts +34 -1
- package/dist/eslint/index.js +5 -5
- package/dist/index.cjs +30 -30
- package/dist/index.d.ts +68 -5
- package/dist/index.js +29 -29
- package/dist/nestjs/index.cjs +5 -5
- package/dist/nestjs/index.d.ts +3 -3
- package/dist/nestjs/index.js +5 -5
- package/dist/order/index.cjs +1 -1
- package/dist/order/index.d.ts +11 -2
- package/dist/order/index.js +1 -1
- package/dist/runtime/index.cjs +11 -11
- package/dist/runtime/index.d.ts +30 -7
- package/dist/runtime/index.js +11 -11
- package/dist/{types-dK24fDZa.d.ts → types-BuM8WNqe.d.ts} +1 -1
- package/dist/{types-DeK7SYGC.d.ts → types-CzE6LMaW.d.ts} +2 -2
- package/docs/README.md +5 -4
- package/docs/agent-guide.md +186 -4
- package/docs/ai-gates.md +13 -2
- package/docs/arkorder.md +210 -0
- package/docs/configuration.md +88 -5
- package/docs/develop.md +4 -2
- package/docs/diagnostics.md +97 -1
- package/docs/enthusiast/how-to-agent-gates.md +1 -1
- package/docs/package-surface.md +37 -5
- package/docs/use.md +1 -1
- package/package.json +3 -2
- package/schemas/ark.config.schema.json +63 -0
- package/server.json +3 -3
- package/templates/agent-skills/README.md +1 -1
- package/templates/agent-skills/ark-adopt/SKILL.md +5 -0
- package/templates/agent-skills/ark-coverage/SKILL.md +1 -0
- package/templates/agent-skills/ark-upgrade/SKILL.md +1 -1
- package/templates/skills/ark-adopt.md +5 -0
- package/templates/skills/ark-coverage.md +1 -0
- package/templates/skills/ark-upgrade.md +1 -1
package/bin/lib/agent-homes.mjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared agent home skill catalogs (Claude / Grok), Codex-parity
|
|
3
|
-
* Repo catalogs stay per-project; these homes are the
|
|
2
|
+
* Shared agent home skill catalogs (Claude / Grok / Antigravity), Codex-parity
|
|
3
|
+
* monotonic install. Repo catalogs stay per-project; these homes are the
|
|
4
|
+
* machine floor (never downgrade). Antigravity's official global catalog is
|
|
5
|
+
* `~/.gemini/config/skills` — not pruned when a project `.agents/skills` exists.
|
|
4
6
|
*/
|
|
5
7
|
import fs from 'node:fs';
|
|
6
8
|
import os from 'node:os';
|
|
@@ -23,7 +25,7 @@ import {
|
|
|
23
25
|
skillInstallLine,
|
|
24
26
|
} from './skill-write.mjs';
|
|
25
27
|
|
|
26
|
-
/** @typedef {'claude'|'grok'} AgentHomeHost */
|
|
28
|
+
/** @typedef {'claude'|'grok'|'antigravity'} AgentHomeHost */
|
|
27
29
|
|
|
28
30
|
const HOSTS = {
|
|
29
31
|
claude: {
|
|
@@ -40,6 +42,14 @@ const HOSTS = {
|
|
|
40
42
|
defaultDirName: '.grok',
|
|
41
43
|
flag: '--grok-home',
|
|
42
44
|
},
|
|
45
|
+
antigravity: {
|
|
46
|
+
id: 'antigravity',
|
|
47
|
+
label: 'Antigravity',
|
|
48
|
+
envKey: 'ANTIGRAVITY_HOME',
|
|
49
|
+
// Official global catalog is ~/.gemini/config/skills (docs/skills).
|
|
50
|
+
defaultDirName: path.join('.gemini', 'config'),
|
|
51
|
+
flag: '--antigravity-home',
|
|
52
|
+
},
|
|
43
53
|
};
|
|
44
54
|
|
|
45
55
|
export function agentHomeHostIds() {
|
|
@@ -62,6 +72,18 @@ export function grokSkillsDir(env = process.env, homeDir = os.homedir()) {
|
|
|
62
72
|
return path.join(grokHomeDir(env, homeDir), 'skills');
|
|
63
73
|
}
|
|
64
74
|
|
|
75
|
+
export function antigravityHomeDir(env = process.env, homeDir = os.homedir()) {
|
|
76
|
+
return resolveHomeDir(HOSTS.antigravity, env, homeDir);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function antigravitySkillsDir(env = process.env, homeDir = os.homedir()) {
|
|
80
|
+
return path.join(antigravityHomeDir(env, homeDir), 'skills');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function usesDefaultAntigravityHome(env = process.env, homeDir = os.homedir()) {
|
|
84
|
+
return usesDefaultHome(HOSTS.antigravity, env, homeDir);
|
|
85
|
+
}
|
|
86
|
+
|
|
65
87
|
export function usesDefaultClaudeHome(env = process.env, homeDir = os.homedir()) {
|
|
66
88
|
return usesDefaultHome(HOSTS.claude, env, homeDir);
|
|
67
89
|
}
|
|
@@ -85,7 +107,8 @@ function usesDefaultHome(spec, env, homeDir) {
|
|
|
85
107
|
}
|
|
86
108
|
|
|
87
109
|
function skillsDirFor(host, env = process.env) {
|
|
88
|
-
|
|
110
|
+
const spec = HOSTS[host];
|
|
111
|
+
return path.join(resolveHomeDir(spec, env), 'skills');
|
|
89
112
|
}
|
|
90
113
|
|
|
91
114
|
function readHomeCatalogFloor(skillsDir) {
|
|
@@ -124,7 +147,7 @@ function homeInPlay(parity, catalogState) {
|
|
|
124
147
|
}
|
|
125
148
|
|
|
126
149
|
/**
|
|
127
|
-
* Detect Claude/Grok user-home ark-* catalogs that lag this package.
|
|
150
|
+
* Detect Claude/Grok/Antigravity user-home ark-* catalogs that lag this package.
|
|
128
151
|
* Absent homes are not debt. Stamp-only body-match is not content-behind
|
|
129
152
|
* (assessSkillCatalogParity already treats identity match as current).
|
|
130
153
|
*
|
|
@@ -199,11 +222,13 @@ export function detectAgentHomeGaps(root, env = process.env) {
|
|
|
199
222
|
/**
|
|
200
223
|
* Claude home is loaded by Claude Code and often by Cursor. Treat both as in-session.
|
|
201
224
|
* Grok home is urgent only on a Grok session (or when ARK_ACTIVE_HOST=grok).
|
|
225
|
+
* Antigravity global catalog is urgent on an Antigravity session (agy / ANTIGRAVITY).
|
|
202
226
|
*/
|
|
203
227
|
export function agentHomeConcernIsActive(host, env = process.env) {
|
|
204
228
|
const active = detectActiveAgentHost(env);
|
|
205
229
|
if (host === 'claude') return active === 'claude' || active === 'cursor' || !active;
|
|
206
230
|
if (host === 'grok') return active === 'grok' || !active;
|
|
231
|
+
if (host === 'antigravity') return active === 'antigravity' || !active;
|
|
207
232
|
return true;
|
|
208
233
|
}
|
|
209
234
|
|
|
@@ -223,6 +248,7 @@ export function agentHomeRefreshCommand(root, gap) {
|
|
|
223
248
|
* force?: boolean,
|
|
224
249
|
* claudeHome?: boolean,
|
|
225
250
|
* grokHome?: boolean,
|
|
251
|
+
* antigravityHome?: boolean,
|
|
226
252
|
* agentHomes?: boolean,
|
|
227
253
|
* json?: boolean,
|
|
228
254
|
* env?: NodeJS.ProcessEnv,
|
|
@@ -233,13 +259,22 @@ export function installRequestedAgentHomes(args) {
|
|
|
233
259
|
const env = args.env ?? process.env;
|
|
234
260
|
const wantClaude = Boolean(args.claudeHome || args.agentHomes);
|
|
235
261
|
const wantGrok = Boolean(args.grokHome || args.agentHomes);
|
|
236
|
-
|
|
262
|
+
const wantAntigravity = Boolean(args.antigravityHome || args.agentHomes);
|
|
263
|
+
if (!wantClaude && !wantGrok && !wantAntigravity) return [];
|
|
237
264
|
const skills = args.skills ?? skillTemplates();
|
|
238
265
|
const version = args.version ?? arkPackageVersion();
|
|
239
266
|
const installed = [];
|
|
240
267
|
const targets = [
|
|
241
268
|
wantClaude ? { host: 'claude', spec: HOSTS.claude, dir: claudeSkillsDir(env), usesDefault: usesDefaultClaudeHome(env) } : null,
|
|
242
269
|
wantGrok ? { host: 'grok', spec: HOSTS.grok, dir: grokSkillsDir(env), usesDefault: usesDefaultGrokHome(env) } : null,
|
|
270
|
+
wantAntigravity
|
|
271
|
+
? {
|
|
272
|
+
host: 'antigravity',
|
|
273
|
+
spec: HOSTS.antigravity,
|
|
274
|
+
dir: antigravitySkillsDir(env),
|
|
275
|
+
usesDefault: usesDefaultAntigravityHome(env),
|
|
276
|
+
}
|
|
277
|
+
: null,
|
|
243
278
|
].filter(Boolean);
|
|
244
279
|
|
|
245
280
|
for (const target of targets) {
|
|
@@ -26,3 +26,89 @@ export function analysisIncompleteStatement(completeness) {
|
|
|
26
26
|
? 'Analysis incomplete: governed parse diagnostics prevent a complete architecture verdict.'
|
|
27
27
|
: 'Analysis unavailable: no API-compatible TypeScript host could produce architecture evidence.';
|
|
28
28
|
}
|
|
29
|
+
|
|
30
|
+
/** Public diagnostic id for a verdict asked to certify an empty file set. */
|
|
31
|
+
export const EMPTY_ANALYSIS_RULE_ID = 'ANALYSIS_COVERS_NO_FILES';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Refuse a verdict over zero governed files when the tree has source to govern.
|
|
35
|
+
*
|
|
36
|
+
* Every rule is vacuously satisfied on an empty set, so a green here certifies
|
|
37
|
+
* nothing while reading exactly like a green over a governed tree. That is a
|
|
38
|
+
* false green — the one failure mode CI trusts.
|
|
39
|
+
*
|
|
40
|
+
* The refusal separates ArkGate's own limitation from a fact about the repo, the
|
|
41
|
+
* same way the coverage budget does. Two states are a mismatch and refuse:
|
|
42
|
+
*
|
|
43
|
+
* - source exists under the analyzed root and the contract governs none of it —
|
|
44
|
+
* the contract does not describe this tree;
|
|
45
|
+
* - the analyzed root is not the root the caller asked for, because the contract
|
|
46
|
+
* was found outside it and its directory was adopted — ArkGate checked a
|
|
47
|
+
* different tree and found nothing in it.
|
|
48
|
+
*
|
|
49
|
+
* No governable source anywhere under the root, on the root the caller asked
|
|
50
|
+
* for, is a genuinely greenfield repo: `--init` is designed to land a contract
|
|
51
|
+
* before the code arrives, that is not a mismatch to refuse, and `--plan` /
|
|
52
|
+
* `--doctor` already carry the `empty-scope` adoption gap for it.
|
|
53
|
+
*
|
|
54
|
+
* Report modes (`--plan`, `--doctor`, `--coverage`) are how a user diagnoses and
|
|
55
|
+
* fixes an empty scope, so callers must not apply this refusal to them. It
|
|
56
|
+
* belongs to the verdict path. (`--adopt-contract` and `--suggest-include` never
|
|
57
|
+
* reach it: their handlers return earlier.)
|
|
58
|
+
*
|
|
59
|
+
* `ungovernedSourceCount` must come from a probe the contract cannot steer — see
|
|
60
|
+
* `countUngovernedSourceFiles` in scan-files.mjs. Feeding it a count that honours
|
|
61
|
+
* `config.exclude` reopens the false green through `exclude: ["**"]`.
|
|
62
|
+
*
|
|
63
|
+
* @param {{
|
|
64
|
+
* governedFileCount?: number,
|
|
65
|
+
* ungovernedSourceCount?: number,
|
|
66
|
+
* ungovernedSourceCap?: number,
|
|
67
|
+
* root?: string,
|
|
68
|
+
* requestedRoot?: string,
|
|
69
|
+
* configPath?: string,
|
|
70
|
+
* configWalkedUp?: boolean,
|
|
71
|
+
* }} [input]
|
|
72
|
+
* @returns {{ ruleId: string, message: string, nextAction: string } | null}
|
|
73
|
+
*/
|
|
74
|
+
export function emptyAnalysisRefusal(input = {}) {
|
|
75
|
+
const count = Number(input.governedFileCount);
|
|
76
|
+
if (!Number.isFinite(count) || count !== 0) return null;
|
|
77
|
+
|
|
78
|
+
const ungoverned = Math.max(0, Number(input.ungovernedSourceCount) || 0);
|
|
79
|
+
const cap = Math.max(0, Number(input.ungovernedSourceCap) || 0);
|
|
80
|
+
const root = String(input.root ?? '');
|
|
81
|
+
const configPath = String(input.configPath ?? '');
|
|
82
|
+
const requestedRoot = String(input.requestedRoot ?? '');
|
|
83
|
+
const movedRoot =
|
|
84
|
+
input.configWalkedUp === true && requestedRoot.length > 0 && requestedRoot !== root;
|
|
85
|
+
|
|
86
|
+
// Greenfield on the root the caller asked for: nothing to govern, nothing to mistake.
|
|
87
|
+
if (ungoverned === 0 && !movedRoot) return null;
|
|
88
|
+
|
|
89
|
+
// The probe stops at a cap, so say "at least N" rather than claim a census it never took.
|
|
90
|
+
const counted = cap > 0 && ungoverned >= cap ? `at least ${ungoverned}` : `${ungoverned}`;
|
|
91
|
+
|
|
92
|
+
const evidence = movedRoot
|
|
93
|
+
? `${counted} source file(s) exist under ${root}, which is not the ${requestedRoot} this ` +
|
|
94
|
+
`run was asked for — the contract at ${configPath} lives outside it`
|
|
95
|
+
: `${counted} source file(s) exist under ${root} and none of them matched the include and ` +
|
|
96
|
+
`layer patterns in ${configPath}`;
|
|
97
|
+
|
|
98
|
+
const message =
|
|
99
|
+
`Analysis covered 0 files: ${evidence}. Every rule is vacuously satisfied on an empty set, ` +
|
|
100
|
+
'so a pass here would certify nothing.';
|
|
101
|
+
|
|
102
|
+
const scopeHint = movedRoot
|
|
103
|
+
? `This run analyzed ${root}, not the ${requestedRoot} you asked for: the contract at ` +
|
|
104
|
+
`${configPath} lives outside ${requestedRoot}, and ark-check adopted the directory holding ` +
|
|
105
|
+
'it as the project root. Pass a contract inside the tree you want checked.'
|
|
106
|
+
: `Point --root at the tree the contract describes (this run analyzed ${root}), or fix the ` +
|
|
107
|
+
`include / exclude / layer patterns in ${configPath} so they match real files.`;
|
|
108
|
+
|
|
109
|
+
const nextAction =
|
|
110
|
+
`${scopeHint} \`npx arkgate-check --root . --plan\` and \`--coverage\` report the empty scope ` +
|
|
111
|
+
'without refusing, and `--adopt-contract --write` proposes an include that matches this tree.';
|
|
112
|
+
|
|
113
|
+
return { ruleId: EMPTY_ANALYSIS_RULE_ID, message, nextAction };
|
|
114
|
+
}
|