arkgate 4.5.7 → 4.6.1
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 +48 -1
- package/README.md +18 -12
- package/bin/ark-check-runtime.mjs +107 -8
- package/bin/ark-mcp-runtime.mjs +17 -1
- package/bin/ark.mjs +5 -3
- package/bin/lib/agent-homes.mjs +296 -0
- package/bin/lib/agent-projection.mjs +2 -2
- package/bin/lib/analysis-engine.mjs +3 -3
- package/bin/lib/ci-and-commands.mjs +16 -16
- package/bin/lib/config-contract.mjs +2 -0
- package/bin/lib/contract-smells.mjs +5 -5
- package/bin/lib/design-smells.mjs +4 -8
- package/bin/lib/doctor-advisories.mjs +9 -0
- package/bin/lib/doctor-plan.mjs +39 -15
- package/bin/lib/field-install.mjs +1 -1
- package/bin/lib/golden-pattern.mjs +1 -1
- package/bin/lib/host-support-matrix.mjs +4 -4
- package/bin/lib/html-report-advisories.mjs +20 -0
- package/bin/lib/html-report-depth.mjs +7 -8
- package/bin/lib/html-report.mjs +2 -1
- package/bin/lib/improvement-compass-map.mjs +20 -20
- package/bin/lib/install-migrate.mjs +12 -0
- package/bin/lib/pilot-loop.mjs +1 -1
- package/bin/lib/post-green-path.mjs +5 -4
- package/bin/lib/prepare-change.mjs +9 -0
- package/bin/lib/product-copy.mjs +32 -0
- package/bin/lib/skill-write.mjs +1 -1
- package/bin/lib/status-command.mjs +14 -0
- package/bin/lib/status-manifest.mjs +17 -0
- package/bin/lib/team-parliament-io.mjs +338 -0
- package/bin/lib/team-parliament.mjs +383 -0
- package/bin/lib/upgrade-whats-new.mjs +32 -0
- package/bin/lib/violations.mjs +8 -4
- package/dist/{configTypes-CC0FEXoF.d.ts → configTypes-B8uIcLaG.d.ts} +5 -0
- package/dist/eslint/index.cjs +2 -2
- package/dist/eslint/index.d.ts +1 -1
- package/dist/eslint/index.js +2 -2
- package/dist/index.cjs +8 -8
- package/dist/index.d.ts +61 -2
- package/dist/index.js +8 -8
- package/docs/README.md +5 -4
- package/docs/agent-guide.md +31 -24
- package/docs/ai-gates.md +19 -0
- package/docs/configuration.md +36 -2
- package/docs/develop.md +16 -4
- package/docs/enthusiast/README.md +1 -1
- package/docs/enthusiast/how-to-agent-gates.md +3 -3
- package/docs/enthusiast/how-to-pick-shape.md +2 -2
- package/docs/enthusiast/tutorial-first-project.md +4 -3
- package/docs/package-surface.md +5 -3
- package/docs/product-voice.md +126 -82
- package/docs/use.md +13 -6
- package/package.json +1 -1
- package/schemas/ark.config.schema.json +9 -0
- package/schemas/ark.status-manifest.schema.json +51 -0
- package/server.json +2 -2
- package/templates/agent-skills/README.md +1 -1
- package/templates/agent-skills/ark-adopt/SKILL.md +46 -19
- package/templates/agent-skills/ark-architect/SKILL.md +24 -145
- package/templates/agent-skills/ark-autopilot/SKILL.md +40 -30
- package/templates/agent-skills/ark-contract/SKILL.md +21 -105
- package/templates/agent-skills/ark-coverage/SKILL.md +3 -0
- package/templates/agent-skills/ark-explain/SKILL.md +7 -4
- package/templates/agent-skills/ark-explore/SKILL.md +29 -21
- package/templates/agent-skills/ark-fix/SKILL.md +34 -157
- package/templates/agent-skills/ark-loop/SKILL.md +31 -153
- package/templates/agent-skills/ark-place/SKILL.md +25 -13
- package/templates/agent-skills/ark-runtime/SKILL.md +3 -3
- package/templates/agent-skills/ark-think/SKILL.md +5 -2
- package/templates/agent-skills/ark-upgrade/SKILL.md +13 -8
- package/templates/skills/ark-adopt.md +46 -19
- package/templates/skills/ark-architect.md +24 -145
- package/templates/skills/ark-autopilot.md +40 -30
- package/templates/skills/ark-contract.md +21 -105
- package/templates/skills/ark-coverage.md +3 -0
- package/templates/skills/ark-explain.md +7 -4
- package/templates/skills/ark-explore.md +29 -21
- package/templates/skills/ark-fix.md +34 -157
- package/templates/skills/ark-loop.md +31 -153
- package/templates/skills/ark-place.md +25 -13
- package/templates/skills/ark-runtime.md +3 -3
- package/templates/skills/ark-think.md +5 -2
- package/templates/skills/ark-upgrade.md +13 -8
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared agent home skill catalogs (Claude / Grok), Codex-parity monotonic install.
|
|
3
|
+
* Repo catalogs stay per-project; these homes are the machine floor (never downgrade).
|
|
4
|
+
*/
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
import os from 'node:os';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { arkCommand } from '../ark-shared.mjs';
|
|
9
|
+
import { isTempOrUpgradeRoot } from './codex-home.mjs';
|
|
10
|
+
import {
|
|
11
|
+
arkPackageVersion,
|
|
12
|
+
assessSkillCatalogParity,
|
|
13
|
+
detectActiveAgentHost,
|
|
14
|
+
isValidSemver,
|
|
15
|
+
isVersionOlder,
|
|
16
|
+
skillTemplateNames,
|
|
17
|
+
skillTemplates,
|
|
18
|
+
} from './skill-install.mjs';
|
|
19
|
+
import {
|
|
20
|
+
HOME_SKILL_CATALOG,
|
|
21
|
+
HOME_SKILL_PENDING_CATALOG,
|
|
22
|
+
installSkillCatalog,
|
|
23
|
+
skillInstallLine,
|
|
24
|
+
} from './skill-write.mjs';
|
|
25
|
+
|
|
26
|
+
/** @typedef {'claude'|'grok'} AgentHomeHost */
|
|
27
|
+
|
|
28
|
+
const HOSTS = {
|
|
29
|
+
claude: {
|
|
30
|
+
id: 'claude',
|
|
31
|
+
label: 'Claude',
|
|
32
|
+
envKey: 'CLAUDE_HOME',
|
|
33
|
+
defaultDirName: '.claude',
|
|
34
|
+
flag: '--claude-home',
|
|
35
|
+
},
|
|
36
|
+
grok: {
|
|
37
|
+
id: 'grok',
|
|
38
|
+
label: 'Grok',
|
|
39
|
+
envKey: 'GROK_HOME',
|
|
40
|
+
defaultDirName: '.grok',
|
|
41
|
+
flag: '--grok-home',
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export function agentHomeHostIds() {
|
|
46
|
+
return Object.keys(HOSTS);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function claudeHomeDir(env = process.env, homeDir = os.homedir()) {
|
|
50
|
+
return resolveHomeDir(HOSTS.claude, env, homeDir);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function grokHomeDir(env = process.env, homeDir = os.homedir()) {
|
|
54
|
+
return resolveHomeDir(HOSTS.grok, env, homeDir);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function claudeSkillsDir(env = process.env, homeDir = os.homedir()) {
|
|
58
|
+
return path.join(claudeHomeDir(env, homeDir), 'skills');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function grokSkillsDir(env = process.env, homeDir = os.homedir()) {
|
|
62
|
+
return path.join(grokHomeDir(env, homeDir), 'skills');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function usesDefaultClaudeHome(env = process.env, homeDir = os.homedir()) {
|
|
66
|
+
return usesDefaultHome(HOSTS.claude, env, homeDir);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function usesDefaultGrokHome(env = process.env, homeDir = os.homedir()) {
|
|
70
|
+
return usesDefaultHome(HOSTS.grok, env, homeDir);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function resolveHomeDir(spec, env, homeDir) {
|
|
74
|
+
const configured = env?.[spec.envKey];
|
|
75
|
+
if (typeof configured === 'string' && configured.trim() !== '') {
|
|
76
|
+
return path.resolve(configured);
|
|
77
|
+
}
|
|
78
|
+
return path.resolve(homeDir, spec.defaultDirName);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function usesDefaultHome(spec, env, homeDir) {
|
|
82
|
+
const configured = env?.[spec.envKey];
|
|
83
|
+
if (typeof configured !== 'string' || configured.trim() === '') return true;
|
|
84
|
+
return path.resolve(configured) === path.resolve(homeDir, spec.defaultDirName);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function skillsDirFor(host, env = process.env) {
|
|
88
|
+
return host === 'grok' ? grokSkillsDir(env) : claudeSkillsDir(env);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function readHomeCatalogFloor(skillsDir) {
|
|
92
|
+
const readOne = (file) => {
|
|
93
|
+
try {
|
|
94
|
+
const value = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
95
|
+
const version = typeof value?.packageVersion === 'string' ? value.packageVersion : null;
|
|
96
|
+
const valid =
|
|
97
|
+
value &&
|
|
98
|
+
value.schemaVersion === '1.0' &&
|
|
99
|
+
isValidSemver(version) &&
|
|
100
|
+
Array.isArray(value.skills);
|
|
101
|
+
return { exists: true, valid, version: valid ? version : null };
|
|
102
|
+
} catch (error) {
|
|
103
|
+
if (error && error.code === 'ENOENT') return { exists: false, valid: true, version: null };
|
|
104
|
+
return { exists: true, valid: false, version: null };
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
const catalog = readOne(path.join(skillsDir, HOME_SKILL_CATALOG));
|
|
108
|
+
const pending = readOne(path.join(skillsDir, HOME_SKILL_PENDING_CATALOG));
|
|
109
|
+
let floorVersion = catalog.version;
|
|
110
|
+
if (pending.version && (!floorVersion || isVersionOlder(floorVersion, pending.version))) {
|
|
111
|
+
floorVersion = pending.version;
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
floorVersion,
|
|
115
|
+
pendingVersion: pending.version,
|
|
116
|
+
hasMetadata: catalog.exists || pending.exists,
|
|
117
|
+
metadataInvalid:
|
|
118
|
+
(catalog.exists && !catalog.valid) || (pending.exists && !pending.valid),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function homeInPlay(parity, catalogState) {
|
|
123
|
+
return parity.presentCount > 0 || catalogState.hasMetadata;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Detect Claude/Grok user-home ark-* catalogs that lag this package.
|
|
128
|
+
* Absent homes are not debt. Stamp-only body-match is not content-behind
|
|
129
|
+
* (assessSkillCatalogParity already treats identity match as current).
|
|
130
|
+
*
|
|
131
|
+
* @param {string} root
|
|
132
|
+
* @param {NodeJS.ProcessEnv} [env]
|
|
133
|
+
* @returns {Array<{
|
|
134
|
+
* host: AgentHomeHost,
|
|
135
|
+
* label: string,
|
|
136
|
+
* skillsDir: string,
|
|
137
|
+
* missing: number,
|
|
138
|
+
* stale: number,
|
|
139
|
+
* presentCount: number,
|
|
140
|
+
* expectedCount: number,
|
|
141
|
+
* packageVersion: string|null,
|
|
142
|
+
* catalogVersion: string|null,
|
|
143
|
+
* pendingRecoveryRequired: boolean,
|
|
144
|
+
* catalogMetadataInvalid: boolean,
|
|
145
|
+
* catalogStateReason: string|null,
|
|
146
|
+
* flag: string,
|
|
147
|
+
* }>}
|
|
148
|
+
*/
|
|
149
|
+
export function detectAgentHomeGaps(root, env = process.env) {
|
|
150
|
+
if (!fs.existsSync(path.join(root, 'AGENTS.md'))) return [];
|
|
151
|
+
if (fs.existsSync(path.join(root, 'templates', 'skills'))) return [];
|
|
152
|
+
const skillNames = skillTemplateNames();
|
|
153
|
+
if (skillNames.length === 0) return [];
|
|
154
|
+
const packageVersion = arkPackageVersion();
|
|
155
|
+
const gaps = [];
|
|
156
|
+
for (const host of agentHomeHostIds()) {
|
|
157
|
+
const spec = HOSTS[host];
|
|
158
|
+
const dir = skillsDirFor(host, env);
|
|
159
|
+
const skillFile = (name) => path.join(dir, name, 'SKILL.md');
|
|
160
|
+
const parity = assessSkillCatalogParity(skillNames, skillFile, packageVersion);
|
|
161
|
+
const catalogState = readHomeCatalogFloor(dir);
|
|
162
|
+
if (!homeInPlay(parity, catalogState)) continue;
|
|
163
|
+
const newerFloor =
|
|
164
|
+
catalogState.floorVersion &&
|
|
165
|
+
isValidSemver(packageVersion) &&
|
|
166
|
+
isVersionOlder(packageVersion, catalogState.floorVersion);
|
|
167
|
+
const pendingRecoveryRequired =
|
|
168
|
+
catalogState.pendingVersion !== null && !newerFloor;
|
|
169
|
+
const needsAttention =
|
|
170
|
+
!newerFloor &&
|
|
171
|
+
(parity.missing > 0 ||
|
|
172
|
+
parity.stale > 0 ||
|
|
173
|
+
pendingRecoveryRequired ||
|
|
174
|
+
catalogState.metadataInvalid);
|
|
175
|
+
if (!needsAttention) continue;
|
|
176
|
+
gaps.push({
|
|
177
|
+
host,
|
|
178
|
+
label: spec.label,
|
|
179
|
+
skillsDir: dir,
|
|
180
|
+
missing: parity.missing,
|
|
181
|
+
stale: parity.stale,
|
|
182
|
+
presentCount: parity.presentCount,
|
|
183
|
+
expectedCount: skillNames.length,
|
|
184
|
+
packageVersion,
|
|
185
|
+
catalogVersion: catalogState.floorVersion,
|
|
186
|
+
pendingRecoveryRequired,
|
|
187
|
+
catalogMetadataInvalid: catalogState.metadataInvalid,
|
|
188
|
+
catalogStateReason: catalogState.metadataInvalid
|
|
189
|
+
? 'invalid catalog metadata'
|
|
190
|
+
: pendingRecoveryRequired
|
|
191
|
+
? 'interrupted catalog commit'
|
|
192
|
+
: null,
|
|
193
|
+
flag: spec.flag,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
return gaps;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Claude home is loaded by Claude Code and often by Cursor. Treat both as in-session.
|
|
201
|
+
* Grok home is urgent only on a Grok session (or when ARK_ACTIVE_HOST=grok).
|
|
202
|
+
*/
|
|
203
|
+
export function agentHomeConcernIsActive(host, env = process.env) {
|
|
204
|
+
const active = detectActiveAgentHost(env);
|
|
205
|
+
if (host === 'claude') return active === 'claude' || active === 'cursor' || !active;
|
|
206
|
+
if (host === 'grok') return active === 'grok' || !active;
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function agentHomeRefreshCommand(root, gap) {
|
|
211
|
+
return arkCommand(
|
|
212
|
+
root,
|
|
213
|
+
'ark-check',
|
|
214
|
+
`--install-agent-gates --skills-only ${gap.flag} --force`
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* @param {{
|
|
220
|
+
* root: string,
|
|
221
|
+
* skills?: Array<[string, string]>,
|
|
222
|
+
* version: string|null,
|
|
223
|
+
* force?: boolean,
|
|
224
|
+
* claudeHome?: boolean,
|
|
225
|
+
* grokHome?: boolean,
|
|
226
|
+
* agentHomes?: boolean,
|
|
227
|
+
* json?: boolean,
|
|
228
|
+
* env?: NodeJS.ProcessEnv,
|
|
229
|
+
* }} args
|
|
230
|
+
* @returns {Array<{ host: string, results: object[] }>}
|
|
231
|
+
*/
|
|
232
|
+
export function installRequestedAgentHomes(args) {
|
|
233
|
+
const env = args.env ?? process.env;
|
|
234
|
+
const wantClaude = Boolean(args.claudeHome || args.agentHomes);
|
|
235
|
+
const wantGrok = Boolean(args.grokHome || args.agentHomes);
|
|
236
|
+
if (!wantClaude && !wantGrok) return [];
|
|
237
|
+
const skills = args.skills ?? skillTemplates();
|
|
238
|
+
const version = args.version ?? arkPackageVersion();
|
|
239
|
+
const installed = [];
|
|
240
|
+
const targets = [
|
|
241
|
+
wantClaude ? { host: 'claude', spec: HOSTS.claude, dir: claudeSkillsDir(env), usesDefault: usesDefaultClaudeHome(env) } : null,
|
|
242
|
+
wantGrok ? { host: 'grok', spec: HOSTS.grok, dir: grokSkillsDir(env), usesDefault: usesDefaultGrokHome(env) } : null,
|
|
243
|
+
].filter(Boolean);
|
|
244
|
+
|
|
245
|
+
for (const target of targets) {
|
|
246
|
+
if (isTempOrUpgradeRoot(args.root) && target.usesDefault) {
|
|
247
|
+
if (!args.json) {
|
|
248
|
+
console.log('');
|
|
249
|
+
console.log(
|
|
250
|
+
`${target.spec.label} home skills: skipped (temp/upgrade --root must not mutate default ~/${target.spec.defaultDirName}).`
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
installed.push({
|
|
254
|
+
host: target.host,
|
|
255
|
+
skipped: true,
|
|
256
|
+
reason: 'temp-root-default-home',
|
|
257
|
+
results: [],
|
|
258
|
+
});
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
if (!args.json) {
|
|
262
|
+
console.log('');
|
|
263
|
+
console.log(
|
|
264
|
+
`${target.spec.label} home skills (scope=home-shared; source=${version ? `arkgate@${version}` : 'arkgate@unknown'}; target=${target.dir}/<name>/SKILL.md):`
|
|
265
|
+
);
|
|
266
|
+
console.log(
|
|
267
|
+
' Compatibility: monotonic downgrade protection requires ArkGate 4.2.0+ writers; older packages cannot lower this catalog.'
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
try {
|
|
271
|
+
fs.mkdirSync(target.dir, { recursive: true });
|
|
272
|
+
} catch (error) {
|
|
273
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
274
|
+
if (!args.json) console.error(` FAILED to create ${target.dir} (${message})`);
|
|
275
|
+
installed.push({
|
|
276
|
+
host: target.host,
|
|
277
|
+
skipped: false,
|
|
278
|
+
results: [{ relativePath: target.dir, status: 'failed', message }],
|
|
279
|
+
});
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
const results = [];
|
|
283
|
+
for (const result of installSkillCatalog({
|
|
284
|
+
directory: target.dir,
|
|
285
|
+
skills,
|
|
286
|
+
packageVersion: version,
|
|
287
|
+
force: args.force,
|
|
288
|
+
scope: 'home',
|
|
289
|
+
})) {
|
|
290
|
+
if (!args.json) console.log(skillInstallLine(result));
|
|
291
|
+
results.push(result);
|
|
292
|
+
}
|
|
293
|
+
installed.push({ host: target.host, skipped: false, results });
|
|
294
|
+
}
|
|
295
|
+
return installed;
|
|
296
|
+
}
|
|
@@ -148,10 +148,10 @@ export function buildAgentProjectionBody(facts) {
|
|
|
148
148
|
'',
|
|
149
149
|
];
|
|
150
150
|
if (profile === 'compact') {
|
|
151
|
-
lines.push('### Primary path', '', '1. Run doctor (`ark-check --doctor`) — status light + primary next action.', '2. Read the improvement compass (not a score). Name
|
|
151
|
+
lines.push('### Primary path', '', '1. Run doctor (`ark-check --doctor`) — status light + primary next action.', '2. Read the improvement compass (not a score). Name leftover work in plain language; never “done” on green imports alone while leftover design work remains.', '3. Call `ark_identity` with `project.expectedRoot` at the exact project root; reuse root + `projectId` on Ark MCP calls.', '4. Read architecture config with `ark_manifest` (same expectation). `ark://manifest` is compatibility-only / unverified.', '5. Place files inside configured layers; validate; run the check command above on violations — fix architecture, do not weaken the gate.', '6. Single door: import-rule debt → fix; leftover design work / residual shape lenses → map then guided apply with user OK.', '', '### Contract layers (summary)', '', formatAgentProjectionLayers(layers), '');
|
|
152
152
|
}
|
|
153
153
|
else {
|
|
154
|
-
lines.push('### Contract layers', '', formatAgentProjectionLayers(layers), '', 'When creating a **new** kind of code that no layer covers, update `ark.config.json` first (`/ark-
|
|
154
|
+
lines.push('### Contract layers', '', formatAgentProjectionLayers(layers), '', 'When creating a **new** kind of code that no layer covers, update `ark.config.json` first (`/ark-adopt`), then place the file.', '', '### Diagnostic codes (short list)', '', formatAgentProjectionCatalogShortList(catalog, docsPath), '', '### Session truth', '', '- Machine snapshot: `ark status --json` (or MCP `ark_status`) — identity, activation honesty, last check, residual counts. **Not a score.**', '- Authoritative contract: `ark_manifest` after a matched `ark_identity` handshake.', '');
|
|
155
155
|
}
|
|
156
156
|
lines.push('### Enforcement surfaces (authoritative)', '', AGENT_PROJECTION_ENFORCEMENT_SURFACES.map((surface) => `- \`${surface}\``).join('\n'), '');
|
|
157
157
|
return lines.join('\n').replace(/\n{3,}/g, '\n\n').trimEnd() + '\n';
|