arkgate 4.6.1 → 4.6.3
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 +50 -1
- package/README.md +17 -11
- package/bin/ark-check-runtime.mjs +23 -127
- package/bin/ark-mcp-runtime.mjs +75 -49
- package/bin/ark.mjs +19 -78
- package/bin/lib/ci-and-commands.mjs +4 -3
- package/bin/lib/doctor-next-actions.mjs +92 -0
- package/bin/lib/doctor-plan.mjs +59 -69
- package/bin/lib/first-run-help.mjs +221 -0
- package/bin/lib/host-support-matrix.mjs +13 -15
- package/bin/lib/start-preview.mjs +17 -10
- package/bin/lib/status-command.mjs +6 -1
- package/bin/lib/status-manifest.mjs +6 -0
- package/bin/lib/write-path-capabilities.mjs +9 -5
- package/bin/lib/write-path-detect.mjs +3 -3
- package/dist/index.cjs +19 -19
- package/dist/index.d.ts +6 -1
- package/dist/index.js +22 -22
- package/docs/README.md +5 -5
- package/docs/agent-guide.md +19 -14
- package/docs/ai-gates.md +29 -20
- package/docs/develop.md +4 -1
- package/docs/enthusiast/how-to-agent-gates.md +2 -2
- package/docs/package-surface.md +4 -3
- package/docs/product-voice.md +4 -4
- package/docs/threat-model.md +5 -1
- package/docs/use.md +9 -4
- package/package.json +2 -2
- package/server.json +3 -3
- package/templates/agent-skills/README.md +1 -1
- package/templates/agent-skills/ark-adopt/SKILL.md +14 -5
- package/templates/agent-skills/ark-architect/SKILL.md +2 -2
- package/templates/agent-skills/ark-autopilot/SKILL.md +12 -5
- package/templates/agent-skills/ark-contract/SKILL.md +1 -1
- package/templates/agent-skills/ark-coverage/SKILL.md +10 -8
- package/templates/agent-skills/ark-explain/SKILL.md +3 -2
- package/templates/agent-skills/ark-explore/SKILL.md +13 -4
- package/templates/agent-skills/ark-fix/SKILL.md +1 -1
- package/templates/agent-skills/ark-loop/SKILL.md +1 -1
- package/templates/agent-skills/ark-place/SKILL.md +10 -1
- package/templates/agent-skills/ark-think/SKILL.md +3 -2
- package/templates/agent-skills/ark-upgrade/SKILL.md +10 -3
- package/templates/skills/ark-adopt.md +14 -5
- package/templates/skills/ark-architect.md +2 -2
- package/templates/skills/ark-autopilot.md +12 -5
- package/templates/skills/ark-contract.md +1 -1
- package/templates/skills/ark-coverage.md +10 -8
- package/templates/skills/ark-explain.md +3 -2
- package/templates/skills/ark-explore.md +13 -4
- package/templates/skills/ark-fix.md +1 -1
- package/templates/skills/ark-loop.md +1 -1
- package/templates/skills/ark-place.md +10 -1
- package/templates/skills/ark-think.md +3 -2
- package/templates/skills/ark-upgrade.md +10 -3
package/bin/ark.mjs
CHANGED
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
} from './lib/prepare-change.mjs';
|
|
36
36
|
import { runStatusCommand } from './lib/status-command.mjs';
|
|
37
37
|
import { runAgentProjectionCommand } from './lib/agent-projection-command.mjs';
|
|
38
|
+
import { setupUsage, setupUsageAll, upgradeUsage } from './lib/first-run-help.mjs';
|
|
38
39
|
|
|
39
40
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
40
41
|
const arkCheck = path.join(here, 'ark-check.mjs');
|
|
@@ -90,13 +91,14 @@ function parseArgs(argv) {
|
|
|
90
91
|
check: false,
|
|
91
92
|
stdout: false,
|
|
92
93
|
help: false,
|
|
94
|
+
all: false,
|
|
93
95
|
version: false,
|
|
94
96
|
};
|
|
95
97
|
|
|
96
98
|
const requireValue = (flag, index) => {
|
|
97
99
|
const value = argv[index + 1];
|
|
98
100
|
if (value === undefined || value.startsWith('-')) {
|
|
99
|
-
throw new Error(`Missing value for ${flag}. Run
|
|
101
|
+
throw new Error(`Missing value for ${flag}. Run arkgate --help for usage.`);
|
|
100
102
|
}
|
|
101
103
|
return value;
|
|
102
104
|
};
|
|
@@ -143,79 +145,15 @@ function parseArgs(argv) {
|
|
|
143
145
|
else if (arg === '--check') args.check = true;
|
|
144
146
|
else if (arg === '--stdout') args.stdout = true;
|
|
145
147
|
else if (arg === '--help' || arg === '-h' || arg === 'help') args.help = true;
|
|
148
|
+
else if (arg === '--all') args.all = true;
|
|
146
149
|
else if (arg === '--version' || arg === '-V') args.version = true;
|
|
147
150
|
else if (!arg.startsWith('-') && args.command === undefined) args.command = arg;
|
|
148
|
-
else throw new Error(`Unknown argument: ${arg}. Run
|
|
151
|
+
else throw new Error(`Unknown argument: ${arg}. Run arkgate --help for usage.`);
|
|
149
152
|
}
|
|
150
153
|
|
|
151
154
|
return args;
|
|
152
155
|
}
|
|
153
156
|
|
|
154
|
-
function usage() {
|
|
155
|
-
return `Usage:
|
|
156
|
-
ark start [--root <project>] [--tools <host>] [--require-write-hook <host>] [--install] [--apply] [--json]
|
|
157
|
-
ark init [--root <project>] [--preset hexagonal|layered|feature-sliced|monorepo|ui-surface|vertical-slice|ddd-bounded-contexts|clean-architecture|onion-architecture]
|
|
158
|
-
[--archetype <playbook-id>] [--tools <list>] [--require-write-hook <host>] [--yes] [--force] [--no-strict]
|
|
159
|
-
ark upgrade [--root <project>] [--tools <list>] [--apply] [--plan-digest <sha256>] [--accept-conflicts] [--refresh-skills] [--json] [--no-install] [--no-strict]
|
|
160
|
-
ark preflight --changes <change-set.json> [--change-map <map.json>] [--root <project>] [--config ark.config.json] [--manifest <manifest.json>] [--tsconfig <tsconfig.json>] [--json]
|
|
161
|
-
ark status [--root <project>] [--config ark.config.json] [--json] [--vs <git-ref>]
|
|
162
|
-
[--expected-root <abs>] [--expected-project-id sha256:…] [--tools <host>]
|
|
163
|
-
ark agents-md [--root <project>] [--config ark.config.json] [--write] [--check] [--stdout] [--json]
|
|
164
|
-
[--tools <host>]
|
|
165
|
-
|
|
166
|
-
Commands:
|
|
167
|
-
start New here? Analyze and preview the complete setup. Read-only unless --apply.
|
|
168
|
-
init Configure Ark project enforcement with explicit prompts.
|
|
169
|
-
upgrade Preview identity-proven Ark-managed asset updates. With package install,
|
|
170
|
-
--apply bumps toward registry latest when behind (not only when CLI ≠ pin)
|
|
171
|
-
and recomputes the preview; a second explicit --apply --no-install applies
|
|
172
|
-
those exact bytes and verifies them. --refresh-skills opts in to rewrite
|
|
173
|
-
customized managed skills to package templates (never silent default).
|
|
174
|
-
(alias: ark update)
|
|
175
|
-
preflight Validate one atomic create/update/delete set without writing project files.
|
|
176
|
-
status Unified session/project manifest (identity, activation, last check, rules).
|
|
177
|
-
Never prompts. Prefer --json for agents; CI=1 forces JSON.
|
|
178
|
-
agents-md Version-matched agent contract projection (ACS04). Stamps package version +
|
|
179
|
-
contract summary into a managed AGENTS.md block. Non-authoritative — not a
|
|
180
|
-
gate input. Preview by default; --write merges without clobbering outside
|
|
181
|
-
regions; --check fails on version drift; --stdout prints the block only.
|
|
182
|
-
(aliases: agents-md, agent-projection)
|
|
183
|
-
|
|
184
|
-
Options:
|
|
185
|
-
--yes Non-interactive defaults: create config if needed, install gate templates, run strict check.
|
|
186
|
-
(Also the implicit default when stdin/stdout are not a TTY — agents never hang on prompts.)
|
|
187
|
-
--force Allow generated files to overwrite existing files.
|
|
188
|
-
--no-strict Skip the final strict ark-check run.
|
|
189
|
-
--install Pin and install arkgate as a project devDependency (default for start).
|
|
190
|
-
--no-install Skip adding/installing arkgate as a project devDependency (start/upgrade).
|
|
191
|
-
--apply Apply a start plan; for upgrade, update/repreview or apply managed bytes.
|
|
192
|
-
--accept-conflicts
|
|
193
|
-
Allow upgrade to recreate deleted managed assets or replace recorded conflicts.
|
|
194
|
-
--plan-digest Digest emitted by an upgrade preview; required to apply managed bytes.
|
|
195
|
-
--json Emit the start/upgrade/status/agents-md preview as deterministic machine-readable JSON.
|
|
196
|
-
--write For agents-md: merge the version-matched projection into AGENTS.md.
|
|
197
|
-
--check For agents-md: exit 1 when projection stamp drifts from package version.
|
|
198
|
-
--stdout For agents-md: print the projection block only (no file write).
|
|
199
|
-
--expected-root / --expected-project-id
|
|
200
|
-
Optional project expectation for status (MCP-compatible binding check).
|
|
201
|
-
--preset Start from a named architecture preset instead of detection.
|
|
202
|
-
--archetype Application shape from templates/architecture-playbook.json (maps to the matching preset).
|
|
203
|
-
Valid ids: crud-product, api-backend, frontend-surface, library-sdk, cli-utility,
|
|
204
|
-
worker-pipeline, event-coordinator, integration-bridge, multi-app-workspace, prototype-spike,
|
|
205
|
-
vertical-slice-product, ddd-bounded-contexts.
|
|
206
|
-
--tools One active agent host for start (claude,cursor,codex,grok,windsurf,cline,copilot,kiro,roo,continue,gemini).
|
|
207
|
-
Omit to use the active host; an unknown host creates only the shared compact router.
|
|
208
|
-
--remove-host <host>
|
|
209
|
-
Preview or apply removal of that compact host integration; re-add it with --tools <host>.
|
|
210
|
-
--require-write-hook <host>
|
|
211
|
-
Require and verify a hard local write hook for Claude, Grok, Antigravity, or Cursor.
|
|
212
|
-
Codex/OpenCode are advisory-write plus hard CI merge only; impossible requests fail before any write.
|
|
213
|
-
|
|
214
|
-
Interactive mode (TTY, no --yes): asks what application shape you are building and maps it to a preset.
|
|
215
|
-
Non-interactive (no TTY): uses the same defaults as --yes — never calls readline on a null interface.
|
|
216
|
-
`;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
157
|
function cliVersion() {
|
|
220
158
|
try {
|
|
221
159
|
const pkg = JSON.parse(fs.readFileSync(path.join(here, '..', 'package.json'), 'utf8'));
|
|
@@ -796,23 +734,22 @@ async function start(args) {
|
|
|
796
734
|
}
|
|
797
735
|
console.log('');
|
|
798
736
|
console.log('Next (the only flow you need):');
|
|
737
|
+
console.log(` 1. Status: ${arkCommand(root, 'arkgate-check', '--doctor')} — do primary next action #1`);
|
|
799
738
|
if (falseGreenGap) {
|
|
800
|
-
console.log('
|
|
801
|
-
console.log(' → reclassify I/O dirs out of Application; then /ark-autopilot
|
|
739
|
+
console.log(' 2. Session 0 in your agent: /ark-adopt — fix the architecture config first');
|
|
740
|
+
console.log(' → reclassify I/O dirs out of Application; leftover design later via /ark-explore then /ark-autopilot.');
|
|
802
741
|
} else {
|
|
803
|
-
console.log('
|
|
804
|
-
console.log(' →
|
|
742
|
+
console.log(' 2. Session 0 in your agent: /ark-adopt');
|
|
743
|
+
console.log(' → mark the path (greenfield or brownfield). Day-to-day new files: /ark-place.');
|
|
805
744
|
}
|
|
806
|
-
console.log(`
|
|
807
|
-
console.log(` 3. After edits: ${arkCommand(root, 'ark-check', '--root . --config ark.config.json --strict-merge')}`);
|
|
745
|
+
console.log(` 3. After edits: ${arkCommand(root, 'arkgate-check', '--root . --config ark.config.json --strict-merge')}`);
|
|
808
746
|
if (mode === 'adapt' && planOk && !falseGreenGap) {
|
|
809
747
|
console.log(
|
|
810
|
-
` 4. When green but cores still optional: ${arkCommand(root, '
|
|
748
|
+
` 4. When green but cores still optional: ${arkCommand(root, 'arkgate-check', '--ratchet-cores')} → honest ENFORCE`
|
|
811
749
|
);
|
|
812
750
|
}
|
|
813
751
|
console.log('');
|
|
814
|
-
console.log('Optional later: ark-
|
|
815
|
-
console.log('Optional later: --plan · --coverage · /ark-explore · /ark-autopilot · /ark-place · ark upgrade');
|
|
752
|
+
console.log('Optional later: leftover design → /ark-explore then /ark-autopilot; bump → arkgate upgrade.');
|
|
816
753
|
return 0;
|
|
817
754
|
} finally {
|
|
818
755
|
rl?.close();
|
|
@@ -831,8 +768,12 @@ async function main() {
|
|
|
831
768
|
console.log(cliVersion());
|
|
832
769
|
return 0;
|
|
833
770
|
}
|
|
771
|
+
if (args.help && (args.command === 'upgrade' || args.command === 'update')) {
|
|
772
|
+
console.log(upgradeUsage());
|
|
773
|
+
return 0;
|
|
774
|
+
}
|
|
834
775
|
if (args.help || !args.command) {
|
|
835
|
-
console.log(
|
|
776
|
+
console.log(args.all ? setupUsageAll() : setupUsage());
|
|
836
777
|
return 0;
|
|
837
778
|
}
|
|
838
779
|
|
|
@@ -961,7 +902,7 @@ async function main() {
|
|
|
961
902
|
}
|
|
962
903
|
|
|
963
904
|
console.error(`Unknown command: ${args.command}`);
|
|
964
|
-
console.error(
|
|
905
|
+
console.error(setupUsage());
|
|
965
906
|
return 2;
|
|
966
907
|
}
|
|
967
908
|
|
|
@@ -370,9 +370,10 @@ ${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.
|
|
374
|
-
|
|
375
|
-
|
|
373
|
+
and the CI check passes. Claude/Grok/Antigravity/Cursor provide hard local boundaries for their
|
|
374
|
+
listed operations. Codex provides a hard boundary for complete local apply_patch when
|
|
375
|
+
.codex/hooks.json is trusted and runtime-observed; hosted/specialized/direct-write paths remain
|
|
376
|
+
CI-backed. OpenCode uses advisory MCP plus CI. The experimental runtime is not required.
|
|
376
377
|
`;
|
|
377
378
|
}
|
|
378
379
|
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rank doctor next actions from already-computed facts (no I/O).
|
|
3
|
+
* Lets the human printer show light + #1 before honesty/compass sections.
|
|
4
|
+
*/
|
|
5
|
+
import { arkCommand } from '../ark-shared.mjs';
|
|
6
|
+
import { skillGapsForActiveHost } from './agent-gates.mjs';
|
|
7
|
+
import { agentHomeConcernIsActive, agentHomeRefreshCommand } from './agent-homes.mjs';
|
|
8
|
+
import { mergePostGreenTopActions } from './post-green-path.mjs';
|
|
9
|
+
|
|
10
|
+
export function collectDoctorNextActions(ctx) {
|
|
11
|
+
const actions = [];
|
|
12
|
+
if (!ctx.analysisComplete) actions.push('restore complete analysis, then rerun ark-check --doctor');
|
|
13
|
+
if (ctx.designSmells.length > 0 && ctx.postGreenPath) actions.push(ctx.postGreenPath.action);
|
|
14
|
+
if (ctx.coverageHonesty.greenIsNotEnforcement && ctx.coverageHonesty.worseThanNoGate) {
|
|
15
|
+
actions.push('raise governed coverage above a minority slice before treating green as enforcement');
|
|
16
|
+
}
|
|
17
|
+
if (ctx.cov.suggestions.length > 0) actions.push('classify the ungoverned directories (/ark-adopt)');
|
|
18
|
+
if (ctx.packageVersionTruth?.dualTruth) {
|
|
19
|
+
actions.push(
|
|
20
|
+
ctx.dualTruthNext ||
|
|
21
|
+
'bump package.json arkgate pin to match this CLI (or install without --no-install)'
|
|
22
|
+
);
|
|
23
|
+
} else if (ctx.packageVersionTruth?.code === 'PACKAGE_PIN_ABSENT') {
|
|
24
|
+
actions.push(
|
|
25
|
+
ctx.dualTruthNext ||
|
|
26
|
+
'Add arkgate to package.json and install so CI/npx resolve this CLI (PACKAGE_PIN_ABSENT)'
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
if (ctx.activeCount > 0) {
|
|
30
|
+
actions.push(
|
|
31
|
+
`resolve the non-baselined violations — see the classified plan (${arkCommand(ctx.root, 'ark-check', '--plan')}), then /ark-autopilot`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
if (ctx.writePath?.gap?.fix) actions.push(ctx.writePath.gap.fix);
|
|
35
|
+
if (ctx.gatesMissing.length > 0) {
|
|
36
|
+
actions.push(`install gates (${arkCommand(ctx.root, 'ark-check', '--install-agent-gates')})`);
|
|
37
|
+
}
|
|
38
|
+
const humanSkillGaps = skillGapsForActiveHost(ctx.skillGaps);
|
|
39
|
+
const legacyCodex = humanSkillGaps.some((g) => g.tool === 'codex' && g.legacyPromptsOnly);
|
|
40
|
+
const remainingGaps = humanSkillGaps.filter(
|
|
41
|
+
(g) => !(g.tool === 'codex' && (g.legacyPromptsOnly || g.legacyAdvisory))
|
|
42
|
+
);
|
|
43
|
+
const remMiss = remainingGaps.reduce((s, g) => s + g.missing, 0);
|
|
44
|
+
const remStale = remainingGaps.reduce((s, g) => s + g.stale, 0);
|
|
45
|
+
if (legacyCodex) {
|
|
46
|
+
actions.push('install Codex SKILL.md catalog (--install-agent-gates --skills-only --tools codex --force)');
|
|
47
|
+
}
|
|
48
|
+
if (remMiss + remStale > 0) {
|
|
49
|
+
actions.push('refresh /ark-* skills (--install-agent-gates --skills-only --force)');
|
|
50
|
+
}
|
|
51
|
+
if (ctx.codexHomeGap && ctx.codexConcernActive) {
|
|
52
|
+
actions.push(
|
|
53
|
+
ctx.codexHomeGap.catalogMetadataInvalid
|
|
54
|
+
? 'repair invalid Codex home catalog metadata after verifying the newest installed version'
|
|
55
|
+
: 'refresh Codex home skills (--install-agent-gates --skills-only --codex-home --force)'
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
for (const gap of ctx.agentHomeGaps) {
|
|
59
|
+
if (agentHomeConcernIsActive(gap.host)) {
|
|
60
|
+
actions.push(
|
|
61
|
+
gap.catalogMetadataInvalid
|
|
62
|
+
? `repair invalid ${gap.label} home catalog metadata after verifying the newest installed version`
|
|
63
|
+
: `refresh ${gap.label} shared agent skills (${agentHomeRefreshCommand(ctx.root, gap)})`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (ctx.analysisComplete && ctx.baselineHonesty?.dirtyBaselineRisk) {
|
|
68
|
+
actions.push('review dirty baseline freezes — fix the contract before trusting green-via-freeze');
|
|
69
|
+
}
|
|
70
|
+
if (ctx.analysisComplete && ctx.staleBaseline > 0) {
|
|
71
|
+
actions.push('tighten the baseline (--update-baseline)');
|
|
72
|
+
}
|
|
73
|
+
if (ctx.staleRunners.length > 0) {
|
|
74
|
+
actions.push(
|
|
75
|
+
`migrate command runners (${arkCommand(ctx.root, 'ark-check', '--install-agent-gates --migrate-commands')})`
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
for (const gap of ctx.adoption.gaps) {
|
|
79
|
+
if (!gap.deferred) actions.push(gap.fix || gap.message);
|
|
80
|
+
}
|
|
81
|
+
if (ctx.safety && ctx.safetyHasEntries) {
|
|
82
|
+
actions.push('resolve strict safety diagnostics before treating CI as enforcement');
|
|
83
|
+
}
|
|
84
|
+
if (ctx.showNewHere) {
|
|
85
|
+
actions.unshift('finish ark start (preview + --apply), then re-run --doctor');
|
|
86
|
+
}
|
|
87
|
+
const unique = mergePostGreenTopActions(actions, ctx.postGreenPath);
|
|
88
|
+
if (ctx.designFitness.designWeak && unique.length === 0 && ctx.postGreenPath) {
|
|
89
|
+
unique.push(ctx.postGreenPath.action);
|
|
90
|
+
}
|
|
91
|
+
return unique;
|
|
92
|
+
}
|
package/bin/lib/doctor-plan.mjs
CHANGED
|
@@ -15,9 +15,9 @@ import { describePackageVersionDualTruth } from './field-install.mjs';
|
|
|
15
15
|
import {
|
|
16
16
|
detectAgentHomeGaps,
|
|
17
17
|
agentHomeConcernIsActive,
|
|
18
|
-
agentHomeRefreshCommand,
|
|
19
18
|
} from './agent-homes.mjs';
|
|
20
19
|
import { operatingModeTitle } from './product-copy.mjs';
|
|
20
|
+
import { collectDoctorNextActions } from './doctor-next-actions.mjs';
|
|
21
21
|
export { summarizeRulesUnderContract };
|
|
22
22
|
|
|
23
23
|
/** Optional S3 dual-match classifier when ark-shared exports it (soft dep for S5 landing). */
|
|
@@ -50,7 +50,6 @@ import {
|
|
|
50
50
|
} from './design-smells.mjs';
|
|
51
51
|
import {
|
|
52
52
|
buildPostGreenNextAction,
|
|
53
|
-
mergePostGreenTopActions,
|
|
54
53
|
isDoctorHealthyNothingToDo,
|
|
55
54
|
DESIGN_WEAK_HONESTY_FLAGS,
|
|
56
55
|
} from './post-green-path.mjs';
|
|
@@ -825,9 +824,7 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
|
|
|
825
824
|
const ok = color.green('✓');
|
|
826
825
|
const warn = color.yellow('!');
|
|
827
826
|
const bad = color.red('✗');
|
|
828
|
-
const actions = [];
|
|
829
827
|
const line = (mark, text) => console.log(` ${mark} ${text}`);
|
|
830
|
-
if (!analysisComplete) actions.push('restore complete analysis, then rerun ark-check --doctor');
|
|
831
828
|
console.log(color.bold(`Ark doctor — ${path.basename(path.resolve(root)) || '.'}`));
|
|
832
829
|
if (!analysisComplete) line(warn, analysisIncompleteStatement(completeness));
|
|
833
830
|
|
|
@@ -866,6 +863,63 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
|
|
|
866
863
|
);
|
|
867
864
|
}
|
|
868
865
|
|
|
866
|
+
const safetyHasEntries = Boolean(
|
|
867
|
+
options.safety &&
|
|
868
|
+
[
|
|
869
|
+
options.safety.nonLiteralDynamicImports,
|
|
870
|
+
options.safety.tsSuppressions,
|
|
871
|
+
options.safety.anyCasts,
|
|
872
|
+
options.safety.inMemoryProductionStores,
|
|
873
|
+
options.safety.disabledPeerIsolationRules,
|
|
874
|
+
].some((entries) => Array.isArray(entries) && entries.length > 0)
|
|
875
|
+
);
|
|
876
|
+
const uniqueActions = collectDoctorNextActions({
|
|
877
|
+
root,
|
|
878
|
+
analysisComplete,
|
|
879
|
+
designSmells,
|
|
880
|
+
postGreenPath,
|
|
881
|
+
coverageHonesty,
|
|
882
|
+
cov,
|
|
883
|
+
packageVersionTruth,
|
|
884
|
+
dualTruthNext,
|
|
885
|
+
activeCount,
|
|
886
|
+
writePath,
|
|
887
|
+
gatesMissing,
|
|
888
|
+
skillGaps,
|
|
889
|
+
codexHomeGap: detectCodexHomeGap(root),
|
|
890
|
+
codexConcernActive: codexConcernIsActive(),
|
|
891
|
+
agentHomeGaps,
|
|
892
|
+
baselineHonesty,
|
|
893
|
+
staleBaseline,
|
|
894
|
+
staleRunners,
|
|
895
|
+
adoption,
|
|
896
|
+
safety: options.safety,
|
|
897
|
+
safetyHasEntries,
|
|
898
|
+
showNewHere,
|
|
899
|
+
designFitness,
|
|
900
|
+
});
|
|
901
|
+
console.log('');
|
|
902
|
+
if (isDoctorHealthyNothingToDo(designFitness, uniqueActions)) {
|
|
903
|
+
console.log(color.green('✔ Healthy — nothing to do.'));
|
|
904
|
+
console.log(color.dim(' Contract edges and design residual are clear. Keep write path + CI.'));
|
|
905
|
+
} else {
|
|
906
|
+
console.log(color.bold('Primary next action'));
|
|
907
|
+
console.log(` 1. ${uniqueActions[0]}`);
|
|
908
|
+
if (uniqueActions.length > 1) {
|
|
909
|
+
console.log(color.bold(`Also (${uniqueActions.length - 1}):`));
|
|
910
|
+
uniqueActions.slice(1).forEach((action, index) => console.log(` ${index + 2}. ${action}`));
|
|
911
|
+
}
|
|
912
|
+
if (postGreenPath) {
|
|
913
|
+
console.log(
|
|
914
|
+
color.dim(
|
|
915
|
+
` Shape residual is the primary door under ${modeTitle} — do not skill-shop explore vs coverage vs think.`
|
|
916
|
+
)
|
|
917
|
+
);
|
|
918
|
+
} else {
|
|
919
|
+
console.log(color.dim(' Doctor is the control plane: do #1 first, then re-run --doctor.'));
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
|
|
869
923
|
// P0-B — single honesty surface (never a score; never "all good" when residual remains).
|
|
870
924
|
if (productHonesty) {
|
|
871
925
|
console.log('');
|
|
@@ -907,11 +961,7 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
|
|
|
907
961
|
line(' ', color.dim(`evidence: ${smell.evidence.slice(0, 4).join(', ')}`));
|
|
908
962
|
}
|
|
909
963
|
}
|
|
910
|
-
|
|
911
|
-
// Rank first via mergePostGreenTopActions at the end (Q01 single door).
|
|
912
|
-
actions.push(postGreenPath.action);
|
|
913
|
-
}
|
|
914
|
-
// Q04 — surface one next pilot under design-weak.
|
|
964
|
+
// Q04 — surface one next pilot under leftover design work.
|
|
915
965
|
if (pilotLoop?.active && pilotLoop.nextPilot) {
|
|
916
966
|
const np = pilotLoop.nextPilot;
|
|
917
967
|
line(
|
|
@@ -967,13 +1017,9 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
|
|
|
967
1017
|
line(govMark, `Governed: ${cov.governed.percent}% (${cov.governed.classifiedFiles}/${cov.governed.totalFiles} files)`);
|
|
968
1018
|
if (coverageHonesty.greenIsNotEnforcement) {
|
|
969
1019
|
line(coverageHonesty.worseThanNoGate ? bad : warn, coverageHonesty.message);
|
|
970
|
-
if (coverageHonesty.worseThanNoGate) {
|
|
971
|
-
actions.push('raise governed coverage above a minority slice before treating green as enforcement');
|
|
972
|
-
}
|
|
973
1020
|
}
|
|
974
1021
|
if (cov.suggestions.length > 0) {
|
|
975
1022
|
line(warn, `${cov.suggestions.length} ungoverned director(y/ies) — proposals: ${arkCommand(root, 'ark-check', '--coverage')}`);
|
|
976
|
-
actions.push('classify the ungoverned directories (/ark-adopt)');
|
|
977
1023
|
}
|
|
978
1024
|
if (cov.emptyLayers.length > 0) line(warn, `Empty layers (pattern matches nothing): ${cov.emptyLayers.join(', ')}`);
|
|
979
1025
|
if (cov.layersWithoutRules.length > 0) line(warn, `Layers with no rule edge: ${cov.layersWithoutRules.join(', ')}`);
|
|
@@ -989,18 +1035,10 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
|
|
|
989
1035
|
console.log('');
|
|
990
1036
|
console.log(color.bold('Package pin (dual-truth)'));
|
|
991
1037
|
line(warn, packageVersionTruth.note);
|
|
992
|
-
actions.push(
|
|
993
|
-
dualTruthNext ||
|
|
994
|
-
'bump package.json arkgate pin to match this CLI (or install without --no-install)'
|
|
995
|
-
);
|
|
996
1038
|
} else if (packageVersionTruth?.code === 'PACKAGE_PIN_ABSENT') {
|
|
997
1039
|
console.log('');
|
|
998
1040
|
console.log(color.bold('Package pin'));
|
|
999
1041
|
line(warn, packageVersionTruth.note);
|
|
1000
|
-
actions.push(
|
|
1001
|
-
dualTruthNext ||
|
|
1002
|
-
'Add arkgate to package.json and install so CI/npx resolve this CLI (PACKAGE_PIN_ABSENT)'
|
|
1003
|
-
);
|
|
1004
1042
|
}
|
|
1005
1043
|
if (options.configWalkedUp && options.configRoot) {
|
|
1006
1044
|
line(
|
|
@@ -1030,7 +1068,6 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
|
|
|
1030
1068
|
line(warn, 'Low governed coverage or fresh config — finish start, then re-run doctor before adding layers of code.');
|
|
1031
1069
|
}
|
|
1032
1070
|
line(ok, `Optional sensor detail: ${arkCommand(root, 'ark-check', '--recommend')}`);
|
|
1033
|
-
actions.unshift('finish ark start (preview + --apply), then re-run --doctor');
|
|
1034
1071
|
}
|
|
1035
1072
|
|
|
1036
1073
|
console.log('');
|
|
@@ -1058,11 +1095,6 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
|
|
|
1058
1095
|
if (summary.concentrated) {
|
|
1059
1096
|
line(warn, color.dim(`${Math.round(summary.dominantShare * 100)}% on one edge (${summary.dominant}) — likely a contract fix, not debt`));
|
|
1060
1097
|
}
|
|
1061
|
-
if (activeCount > 0) {
|
|
1062
|
-
actions.push(
|
|
1063
|
-
`resolve the non-baselined violations — see the classified plan (${arkCommand(root, 'ark-check', '--plan')}), then /ark-autopilot`
|
|
1064
|
-
);
|
|
1065
|
-
}
|
|
1066
1098
|
}
|
|
1067
1099
|
|
|
1068
1100
|
console.log('');
|
|
@@ -1105,7 +1137,6 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
|
|
|
1105
1137
|
line(writePath.gap.severity === 'warn' ? warn : warn, writePath.gap.message);
|
|
1106
1138
|
if (writePath.gap.fix) {
|
|
1107
1139
|
line(' ', color.dim(`Fix: ${writePath.gap.fix}`));
|
|
1108
|
-
actions.push(writePath.gap.fix);
|
|
1109
1140
|
}
|
|
1110
1141
|
}
|
|
1111
1142
|
|
|
@@ -1114,7 +1145,6 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
|
|
|
1114
1145
|
if (gatesMissing.length === 0) line(ok, 'Shared gate artifacts found on disk (AGENTS.md, .mcp.json, CI); runtime activation is reported separately');
|
|
1115
1146
|
else {
|
|
1116
1147
|
line(bad, `Missing gates: ${gatesMissing.join(', ')}`);
|
|
1117
|
-
actions.push(`install gates (${arkCommand(root, 'ark-check', '--install-agent-gates')})`);
|
|
1118
1148
|
}
|
|
1119
1149
|
const humanSkillGaps = skillGapsForActiveHost(skillGaps);
|
|
1120
1150
|
const legacyCodex = humanSkillGaps.some((g) => g.tool === 'codex' && g.legacyPromptsOnly);
|
|
@@ -1129,7 +1159,6 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
|
|
|
1129
1159
|
if (remMiss + remStale === 0 && !legacyCodex) line(ok, '/ark-* skills current for detected tools');
|
|
1130
1160
|
if (legacyCodex) {
|
|
1131
1161
|
line(warn, 'Codex: legacy flat .codex/prompts only (not a loadable skill catalog)');
|
|
1132
|
-
actions.push('install Codex SKILL.md catalog (--install-agent-gates --skills-only --tools codex --force)');
|
|
1133
1162
|
}
|
|
1134
1163
|
if (codexLegacySafeDelete) {
|
|
1135
1164
|
line(
|
|
@@ -1144,7 +1173,6 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
|
|
|
1144
1173
|
warn,
|
|
1145
1174
|
`${remMiss} missing / ${remStale} content-behind-package /ark-* skill(s) for ${remainingGaps.map((g) => g.tool).join(', ')}`
|
|
1146
1175
|
);
|
|
1147
|
-
actions.push('refresh /ark-* skills (--install-agent-gates --skills-only --force)');
|
|
1148
1176
|
}
|
|
1149
1177
|
const codexHomeGap = detectCodexHomeGap(root);
|
|
1150
1178
|
if (codexHomeGap) {
|
|
@@ -1159,7 +1187,6 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
|
|
|
1159
1187
|
line(color.dim('·'), color.dim(`Codex home skills ${parts.join(', ')} (deferred — not on Codex session)`));
|
|
1160
1188
|
} else {
|
|
1161
1189
|
line(warn, `Codex home skills ${parts.join(', ')}`);
|
|
1162
|
-
actions.push(codexHomeGap.catalogMetadataInvalid ? 'repair invalid Codex home catalog metadata after verifying the newest installed version' : 'refresh Codex home skills (--install-agent-gates --skills-only --codex-home --force)');
|
|
1163
1190
|
}
|
|
1164
1191
|
}
|
|
1165
1192
|
for (const gap of agentHomeGaps) {
|
|
@@ -1174,11 +1201,6 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
|
|
|
1174
1201
|
line(color.dim('·'), color.dim(`${summary} (deferred — not this session)`));
|
|
1175
1202
|
} else {
|
|
1176
1203
|
line(warn, summary);
|
|
1177
|
-
actions.push(
|
|
1178
|
-
gap.catalogMetadataInvalid
|
|
1179
|
-
? `repair invalid ${gap.label} home catalog metadata after verifying the newest installed version`
|
|
1180
|
-
: `refresh ${gap.label} shared agent skills (${agentHomeRefreshCommand(root, gap)})`
|
|
1181
|
-
);
|
|
1182
1204
|
}
|
|
1183
1205
|
}
|
|
1184
1206
|
|
|
@@ -1193,11 +1215,9 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
|
|
|
1193
1215
|
line(baseMark, `${baseline.keys.size} frozen key(s)${analysisComplete ? '' : ' — stale comparison not verified'}`);
|
|
1194
1216
|
if (analysisComplete && baselineHonesty.dirtyBaselineRisk) {
|
|
1195
1217
|
line(warn, baselineHonesty.message);
|
|
1196
|
-
actions.push('review dirty baseline freezes — fix the contract before trusting green-via-freeze');
|
|
1197
1218
|
}
|
|
1198
1219
|
if (analysisComplete && staleBaseline > 0) {
|
|
1199
1220
|
line(warn, `${staleBaseline} stale entr(y/ies) no longer occur — tighten with --update-baseline`);
|
|
1200
|
-
actions.push('tighten the baseline (--update-baseline)');
|
|
1201
1221
|
}
|
|
1202
1222
|
}
|
|
1203
1223
|
|
|
@@ -1206,7 +1226,6 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
|
|
|
1206
1226
|
if (staleRunners.length === 0) line(ok, 'Emitted commands match the package manager');
|
|
1207
1227
|
else {
|
|
1208
1228
|
line(warn, `Stale runner in ${staleRunners.join(', ')}`);
|
|
1209
|
-
actions.push(`migrate command runners (${arkCommand(root, 'ark-check', '--install-agent-gates --migrate-commands')})`);
|
|
1210
1229
|
}
|
|
1211
1230
|
|
|
1212
1231
|
// Adoption completeness (hosts, MCP health, codex home, core optionality, origin, baseline policy)
|
|
@@ -1232,7 +1251,6 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
|
|
|
1232
1251
|
if (gap.fix) {
|
|
1233
1252
|
line(' ', color.dim(gap.deferred ? `When using Codex: ${gap.fix}` : `Fix: ${gap.fix}`));
|
|
1234
1253
|
}
|
|
1235
|
-
if (!gap.deferred) actions.push(gap.fix || gap.message);
|
|
1236
1254
|
}
|
|
1237
1255
|
if (adoption.layerBalance) {
|
|
1238
1256
|
line(warn, color.dim(adoption.layerBalance.educational));
|
|
@@ -1269,34 +1287,6 @@ export function runDoctor(root, config, files, rules, violations, asJson, option
|
|
|
1269
1287
|
for (const [label, entries] of rows) {
|
|
1270
1288
|
line(entries.length === 0 ? ok : warn, `${label}: ${entries.length}`);
|
|
1271
1289
|
}
|
|
1272
|
-
if (rows.some(([, entries]) => entries.length > 0)) {
|
|
1273
|
-
actions.push('resolve strict safety diagnostics before treating CI as enforcement');
|
|
1274
|
-
}
|
|
1275
1290
|
}
|
|
1276
1291
|
|
|
1277
|
-
console.log('');
|
|
1278
|
-
const uniqueActions = mergePostGreenTopActions(actions, postGreenPath);
|
|
1279
|
-
if (isDoctorHealthyNothingToDo(designFitness, uniqueActions)) {
|
|
1280
|
-
console.log(color.green('✔ Healthy — nothing to do.'));
|
|
1281
|
-
console.log(color.dim(' Contract edges and design residual are clear. Keep write path + CI.'));
|
|
1282
|
-
} else {
|
|
1283
|
-
if (designFitness.designWeak && uniqueActions.length === 0 && postGreenPath) {
|
|
1284
|
-
uniqueActions.push(postGreenPath.action);
|
|
1285
|
-
}
|
|
1286
|
-
console.log(color.bold(`Primary next action`));
|
|
1287
|
-
console.log(` 1. ${uniqueActions[0]}`);
|
|
1288
|
-
if (uniqueActions.length > 1) {
|
|
1289
|
-
console.log(color.bold(`Also (${uniqueActions.length - 1}):`));
|
|
1290
|
-
uniqueActions.slice(1).forEach((action, index) => console.log(` ${index + 2}. ${action}`));
|
|
1291
|
-
}
|
|
1292
|
-
if (postGreenPath) {
|
|
1293
|
-
console.log(
|
|
1294
|
-
color.dim(
|
|
1295
|
-
` Shape residual is the primary door under ${modeTitle} — do not skill-shop explore vs coverage vs think.`
|
|
1296
|
-
)
|
|
1297
|
-
);
|
|
1298
|
-
} else {
|
|
1299
|
-
console.log(color.dim(' Doctor is the control plane: do #1 first, then re-run --doctor.'));
|
|
1300
|
-
}
|
|
1301
|
-
}
|
|
1302
1292
|
}
|