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.
Files changed (83) hide show
  1. package/CHANGELOG.md +48 -1
  2. package/README.md +18 -12
  3. package/bin/ark-check-runtime.mjs +107 -8
  4. package/bin/ark-mcp-runtime.mjs +17 -1
  5. package/bin/ark.mjs +5 -3
  6. package/bin/lib/agent-homes.mjs +296 -0
  7. package/bin/lib/agent-projection.mjs +2 -2
  8. package/bin/lib/analysis-engine.mjs +3 -3
  9. package/bin/lib/ci-and-commands.mjs +16 -16
  10. package/bin/lib/config-contract.mjs +2 -0
  11. package/bin/lib/contract-smells.mjs +5 -5
  12. package/bin/lib/design-smells.mjs +4 -8
  13. package/bin/lib/doctor-advisories.mjs +9 -0
  14. package/bin/lib/doctor-plan.mjs +39 -15
  15. package/bin/lib/field-install.mjs +1 -1
  16. package/bin/lib/golden-pattern.mjs +1 -1
  17. package/bin/lib/host-support-matrix.mjs +4 -4
  18. package/bin/lib/html-report-advisories.mjs +20 -0
  19. package/bin/lib/html-report-depth.mjs +7 -8
  20. package/bin/lib/html-report.mjs +2 -1
  21. package/bin/lib/improvement-compass-map.mjs +20 -20
  22. package/bin/lib/install-migrate.mjs +12 -0
  23. package/bin/lib/pilot-loop.mjs +1 -1
  24. package/bin/lib/post-green-path.mjs +5 -4
  25. package/bin/lib/prepare-change.mjs +9 -0
  26. package/bin/lib/product-copy.mjs +32 -0
  27. package/bin/lib/skill-write.mjs +1 -1
  28. package/bin/lib/status-command.mjs +14 -0
  29. package/bin/lib/status-manifest.mjs +17 -0
  30. package/bin/lib/team-parliament-io.mjs +338 -0
  31. package/bin/lib/team-parliament.mjs +383 -0
  32. package/bin/lib/upgrade-whats-new.mjs +32 -0
  33. package/bin/lib/violations.mjs +8 -4
  34. package/dist/{configTypes-CC0FEXoF.d.ts → configTypes-B8uIcLaG.d.ts} +5 -0
  35. package/dist/eslint/index.cjs +2 -2
  36. package/dist/eslint/index.d.ts +1 -1
  37. package/dist/eslint/index.js +2 -2
  38. package/dist/index.cjs +8 -8
  39. package/dist/index.d.ts +61 -2
  40. package/dist/index.js +8 -8
  41. package/docs/README.md +5 -4
  42. package/docs/agent-guide.md +31 -24
  43. package/docs/ai-gates.md +19 -0
  44. package/docs/configuration.md +36 -2
  45. package/docs/develop.md +16 -4
  46. package/docs/enthusiast/README.md +1 -1
  47. package/docs/enthusiast/how-to-agent-gates.md +3 -3
  48. package/docs/enthusiast/how-to-pick-shape.md +2 -2
  49. package/docs/enthusiast/tutorial-first-project.md +4 -3
  50. package/docs/package-surface.md +5 -3
  51. package/docs/product-voice.md +126 -82
  52. package/docs/use.md +13 -6
  53. package/package.json +1 -1
  54. package/schemas/ark.config.schema.json +9 -0
  55. package/schemas/ark.status-manifest.schema.json +51 -0
  56. package/server.json +2 -2
  57. package/templates/agent-skills/README.md +1 -1
  58. package/templates/agent-skills/ark-adopt/SKILL.md +46 -19
  59. package/templates/agent-skills/ark-architect/SKILL.md +24 -145
  60. package/templates/agent-skills/ark-autopilot/SKILL.md +40 -30
  61. package/templates/agent-skills/ark-contract/SKILL.md +21 -105
  62. package/templates/agent-skills/ark-coverage/SKILL.md +3 -0
  63. package/templates/agent-skills/ark-explain/SKILL.md +7 -4
  64. package/templates/agent-skills/ark-explore/SKILL.md +29 -21
  65. package/templates/agent-skills/ark-fix/SKILL.md +34 -157
  66. package/templates/agent-skills/ark-loop/SKILL.md +31 -153
  67. package/templates/agent-skills/ark-place/SKILL.md +25 -13
  68. package/templates/agent-skills/ark-runtime/SKILL.md +3 -3
  69. package/templates/agent-skills/ark-think/SKILL.md +5 -2
  70. package/templates/agent-skills/ark-upgrade/SKILL.md +13 -8
  71. package/templates/skills/ark-adopt.md +46 -19
  72. package/templates/skills/ark-architect.md +24 -145
  73. package/templates/skills/ark-autopilot.md +40 -30
  74. package/templates/skills/ark-contract.md +21 -105
  75. package/templates/skills/ark-coverage.md +3 -0
  76. package/templates/skills/ark-explain.md +7 -4
  77. package/templates/skills/ark-explore.md +29 -21
  78. package/templates/skills/ark-fix.md +34 -157
  79. package/templates/skills/ark-loop.md +31 -153
  80. package/templates/skills/ark-place.md +25 -13
  81. package/templates/skills/ark-runtime.md +3 -3
  82. package/templates/skills/ark-think.md +5 -2
  83. 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 residual lenses in plain language; never “done” on green edges alone while residual remains.', '3. Call `ark_identity` with `project.expectedRoot` at the exact project root; reuse root + `projectId` on Ark MCP calls.', '4. Read the contract 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: edges debt → fix; design-weak / residual shape lenses → map then guided apply with user OK.', '', '### Contract layers (summary)', '', formatAgentProjectionLayers(layers), '');
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-contract`), 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.', '');
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';