primcli 1.43.0 → 1.44.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/README.md +12 -0
- package/dist/oclif/index.js +196 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -923,6 +923,7 @@ session on this machine register itself and answer Primitive mail, and
|
|
|
923
923
|
primitive machine doctor --json
|
|
924
924
|
primitive machine doctor --fix --json
|
|
925
925
|
primitive machine doctor --fix --check claude.hook.stop --json
|
|
926
|
+
primitive machine doctor --fix --check claude.hook.stop --profile my-agent
|
|
926
927
|
```
|
|
927
928
|
|
|
928
929
|
Each check reports `{id, title, status, detail, fixable, fixed?, action?, path?}`
|
|
@@ -942,6 +943,17 @@ content) and `~/.omp/agent/AGENTS.md`, the bundled primitive-connect skill, and
|
|
|
942
943
|
saved agent profiles that were disconnected in Primitive (moved aside locally;
|
|
943
944
|
nothing changes server side).
|
|
944
945
|
|
|
946
|
+
`claude.hook.stop` names every per-session receive hook a repair would change
|
|
947
|
+
in `items: [{profile, session, hook, state}]`, where `hook` is the Claude event
|
|
948
|
+
(`Stop`, `SessionStart` or `PostToolUse`) and `state` is `missing`, `stale`,
|
|
949
|
+
`duplicate`, `outdated` or `old_address`; `profile` and `session` are null for
|
|
950
|
+
hooks written by old CLI versions that did not record them. After `--fix` it
|
|
951
|
+
also reports `changes`, the same fields plus `action` (`added`, `removed` or
|
|
952
|
+
`updated`), for each hook it changed. `--profile <name>` (repeatable, with
|
|
953
|
+
`--fix`) limits those per-session hook changes to the named profiles; combine it
|
|
954
|
+
with `--check claude.hook.stop` so nothing else is repaired. With `--profile`,
|
|
955
|
+
each item also carries `selected: false` when this run leaves it unchanged.
|
|
956
|
+
|
|
945
957
|
Repairs only touch Primitive-owned hooks, blocks and skill copies. Any existing
|
|
946
958
|
file is backed up beside itself as `<file>.primitive-bak-<timestamp>` before it
|
|
947
959
|
changes, writes are atomic, and a second `--fix` with nothing drifted changes
|
package/dist/oclif/index.js
CHANGED
|
@@ -35617,6 +35617,36 @@ function writeClaudeSettings(params) {
|
|
|
35617
35617
|
release();
|
|
35618
35618
|
}
|
|
35619
35619
|
}
|
|
35620
|
+
const STATE_PHRASES = {
|
|
35621
|
+
missing: "missing",
|
|
35622
|
+
stale: "session disconnected or removed",
|
|
35623
|
+
duplicate: "duplicate",
|
|
35624
|
+
outdated: "old CLI path",
|
|
35625
|
+
old_address: "old agent address"
|
|
35626
|
+
};
|
|
35627
|
+
/** "profile my-agent, session 1234...": who a session hook serves. */
|
|
35628
|
+
function describeSessionHookOwner(item) {
|
|
35629
|
+
const profile = item.profile ? `profile ${item.profile}` : "a profile an old CLI did not record";
|
|
35630
|
+
return item.session ? `${profile}, session ${item.session}` : profile;
|
|
35631
|
+
}
|
|
35632
|
+
/** One short line naming a hook, its owner and its state. */
|
|
35633
|
+
function describeSessionHookItem(item) {
|
|
35634
|
+
return `${item.hook} hook for ${describeSessionHookOwner(item)}: ${STATE_PHRASES[item.state]}`;
|
|
35635
|
+
}
|
|
35636
|
+
/** One short line naming what a repair did to a hook. */
|
|
35637
|
+
function describeSessionHookChange(change) {
|
|
35638
|
+
return `${change.action === "added" ? "Added" : change.action === "removed" ? "Removed" : "Rewrote"} ${change.hook} hook for ${describeSessionHookOwner(change)} (${STATE_PHRASES[change.state]})`;
|
|
35639
|
+
}
|
|
35640
|
+
/** Group items by owner, at most `limit` owners, for a one-line detail. */
|
|
35641
|
+
function summarizeOwners(items, limit = 3) {
|
|
35642
|
+
const owners = [];
|
|
35643
|
+
for (const item of items) {
|
|
35644
|
+
const owner = describeSessionHookOwner(item);
|
|
35645
|
+
if (!owners.includes(owner)) owners.push(owner);
|
|
35646
|
+
}
|
|
35647
|
+
const shown = owners.slice(0, limit).join("; ");
|
|
35648
|
+
return owners.length > limit ? `${shown}; and ${owners.length - limit} more` : shown;
|
|
35649
|
+
}
|
|
35620
35650
|
const GLOBAL_HOOKS = {
|
|
35621
35651
|
"claude.hook.session_start": {
|
|
35622
35652
|
event: "SessionStart",
|
|
@@ -35804,6 +35834,57 @@ function boundSessionProfiles(configDir) {
|
|
|
35804
35834
|
} catch {}
|
|
35805
35835
|
return bound;
|
|
35806
35836
|
}
|
|
35837
|
+
function boundKey(item) {
|
|
35838
|
+
return [
|
|
35839
|
+
resolve(item.configDir),
|
|
35840
|
+
item.profile,
|
|
35841
|
+
item.session
|
|
35842
|
+
].join("\0");
|
|
35843
|
+
}
|
|
35844
|
+
/**
|
|
35845
|
+
* Bound profiles whose receive hooks are entirely absent while the same
|
|
35846
|
+
* session receives through another connected profile, or while several profiles are
|
|
35847
|
+
* bound to it and none receives. Such a profile is not restored without
|
|
35848
|
+
* being named: its hooks were removed on purpose, or it would give the
|
|
35849
|
+
* session a second address. A profile that still has any receive hook for
|
|
35850
|
+
* the session, or the only profile bound to a session, is restored as usual.
|
|
35851
|
+
*/
|
|
35852
|
+
function heldSessionProfiles(hooks, bound, restore) {
|
|
35853
|
+
const receiving = /* @__PURE__ */ new Map();
|
|
35854
|
+
for (const event of SESSION_EVENTS) {
|
|
35855
|
+
const entries = hooks[event];
|
|
35856
|
+
if (!Array.isArray(entries)) continue;
|
|
35857
|
+
for (const entry of entries) {
|
|
35858
|
+
if (!record$4(entry) || !Array.isArray(entry.hooks)) continue;
|
|
35859
|
+
for (const hook of entry.hooks) {
|
|
35860
|
+
const parsed = parseSessionHook(hook);
|
|
35861
|
+
if (!parsed?.profile || !parsed.session) continue;
|
|
35862
|
+
if (!existsSync(parsed.configDir) || !profileStillConnected(parsed.configDir, parsed.profile)) continue;
|
|
35863
|
+
const session = parsed.session.toLowerCase();
|
|
35864
|
+
const profiles = receiving.get(session) ?? /* @__PURE__ */ new Set();
|
|
35865
|
+
profiles.add([
|
|
35866
|
+
resolve(parsed.configDir),
|
|
35867
|
+
parsed.profile,
|
|
35868
|
+
session
|
|
35869
|
+
].join("\0"));
|
|
35870
|
+
receiving.set(session, profiles);
|
|
35871
|
+
}
|
|
35872
|
+
}
|
|
35873
|
+
}
|
|
35874
|
+
const held = /* @__PURE__ */ new Map();
|
|
35875
|
+
for (const item of bound) {
|
|
35876
|
+
const key = boundKey(item);
|
|
35877
|
+
const present = receiving.get(item.session) ?? /* @__PURE__ */ new Set();
|
|
35878
|
+
if (present.has(key) || restore?.has(item.profile)) continue;
|
|
35879
|
+
const others = [...present].map((other) => other.split("\0")[1] ?? "");
|
|
35880
|
+
const siblings = bound.filter((candidate) => candidate.session === item.session);
|
|
35881
|
+
if (others.length > 0 || siblings.length > 1) held.set(key, {
|
|
35882
|
+
item,
|
|
35883
|
+
receivingAs: others.sort()
|
|
35884
|
+
});
|
|
35885
|
+
}
|
|
35886
|
+
return held;
|
|
35887
|
+
}
|
|
35807
35888
|
/** Receive hooks each bound profile should have and does not. */
|
|
35808
35889
|
function missingSessionHooks(hooks, bound, node) {
|
|
35809
35890
|
return bound.flatMap((item) => SESSION_EVENTS.filter((event) => !hasSessionReceiveHook(hooks, event, item, node)).map((event) => ({
|
|
@@ -35811,6 +35892,34 @@ function missingSessionHooks(hooks, bound, node) {
|
|
|
35811
35892
|
bound: item
|
|
35812
35893
|
})));
|
|
35813
35894
|
}
|
|
35895
|
+
/** Every hook judged for repair, in settings order, then every missing one. */
|
|
35896
|
+
function sessionHookItems(hooks, verdicts, missing) {
|
|
35897
|
+
const items = missing.map(({ event, bound }) => ({
|
|
35898
|
+
profile: bound.profile,
|
|
35899
|
+
session: bound.session,
|
|
35900
|
+
hook: event,
|
|
35901
|
+
state: "missing"
|
|
35902
|
+
}));
|
|
35903
|
+
for (const event of SESSION_EVENTS) {
|
|
35904
|
+
const entries = hooks[event];
|
|
35905
|
+
if (!Array.isArray(entries)) continue;
|
|
35906
|
+
for (const entry of entries) {
|
|
35907
|
+
if (!record$4(entry) || !Array.isArray(entry.hooks)) continue;
|
|
35908
|
+
for (const hook of entry.hooks) {
|
|
35909
|
+
const verdict = verdicts.get(hook);
|
|
35910
|
+
const parsed = parseSessionHook(hook);
|
|
35911
|
+
if (!verdict || verdict === "keep" || !parsed) continue;
|
|
35912
|
+
items.push({
|
|
35913
|
+
profile: parsed.profile,
|
|
35914
|
+
session: parsed.session?.toLowerCase() ?? null,
|
|
35915
|
+
hook: event,
|
|
35916
|
+
state: verdict
|
|
35917
|
+
});
|
|
35918
|
+
}
|
|
35919
|
+
}
|
|
35920
|
+
}
|
|
35921
|
+
return items;
|
|
35922
|
+
}
|
|
35814
35923
|
/** The exact entry `agent connect` and `session-register` install. */
|
|
35815
35924
|
function sessionHookEntry(event, item, cli) {
|
|
35816
35925
|
const pending = SESSION_HOOK_KIND[event] === "pending";
|
|
@@ -35884,7 +35993,9 @@ function inspectClaudeHooks(settings, context) {
|
|
|
35884
35993
|
};
|
|
35885
35994
|
return findings;
|
|
35886
35995
|
}
|
|
35887
|
-
const
|
|
35996
|
+
const held = heldSessionProfiles(hooks, context.bound ?? [], context.restoreProfiles);
|
|
35997
|
+
const bound = (context.bound ?? []).filter((item) => !held.has(boundKey(item)));
|
|
35998
|
+
const heldNote = held.size ? ` Left alone: ${[...held.values()].map(({ item, receivingAs }) => `profile ${item.profile} is bound to session ${item.session}, which ${receivingAs.length ? `receives as ${receivingAs.join(", ")}` : "has several bound profiles and receives as none"}`).join("; ")}. Run \`primitive machine doctor --fix --profile <profile>\` to restore one deliberately.` : "";
|
|
35888
35999
|
const counts = {
|
|
35889
36000
|
keep: 0,
|
|
35890
36001
|
stale: 0,
|
|
@@ -35892,8 +36003,11 @@ function inspectClaudeHooks(settings, context) {
|
|
|
35892
36003
|
outdated: 0,
|
|
35893
36004
|
old_address: 0
|
|
35894
36005
|
};
|
|
35895
|
-
|
|
35896
|
-
|
|
36006
|
+
const verdicts = judgeSessionHooks(hooks, context.cli, bound);
|
|
36007
|
+
for (const verdict of verdicts.values()) counts[verdict]++;
|
|
36008
|
+
const missingHooks = missingSessionHooks(hooks, bound, context.cli?.node ?? process.execPath);
|
|
36009
|
+
const missing = missingHooks.length;
|
|
36010
|
+
const items = sessionHookItems(hooks, verdicts, missingHooks);
|
|
35897
36011
|
const problems = [
|
|
35898
36012
|
missing ? `${missing} missing for connected sessions` : "",
|
|
35899
36013
|
counts.old_address ? `${counts.old_address} naming an old agent address` : "",
|
|
@@ -35903,11 +36017,12 @@ function inspectClaudeHooks(settings, context) {
|
|
|
35903
36017
|
].filter(Boolean);
|
|
35904
36018
|
findings["claude.hook.stop"] = problems.length ? {
|
|
35905
36019
|
status: "fail",
|
|
35906
|
-
detail: `Per-session receive hooks need cleanup: ${problems.join(", ")}.${blockedNote}`,
|
|
35907
|
-
fixable: canFix || counts.outdated === 0 && missing === 0 && context.blocked === null
|
|
36020
|
+
detail: `Per-session receive hooks need cleanup: ${problems.join(", ")}. Affected: ${summarizeOwners(items)}.${blockedNote}${heldNote}`,
|
|
36021
|
+
fixable: canFix || counts.outdated === 0 && missing === 0 && context.blocked === null,
|
|
36022
|
+
items
|
|
35908
36023
|
} : {
|
|
35909
36024
|
status: "ok",
|
|
35910
|
-
detail: counts.keep ? `${counts.keep} per-session receive hooks, each present once and current.` : "No per-session receive hooks; each registered session adds its own exact-session hooks."
|
|
36025
|
+
detail: `${counts.keep ? `${counts.keep} per-session receive hooks, each present once and current.` : "No per-session receive hooks; each registered session adds its own exact-session hooks."}${heldNote}`,
|
|
35911
36026
|
fixable: false
|
|
35912
36027
|
};
|
|
35913
36028
|
return findings;
|
|
@@ -35939,8 +36054,10 @@ function rewriteEntries(entries, transform) {
|
|
|
35939
36054
|
* Primitive-owned hooks are added, rewritten or removed; every other key,
|
|
35940
36055
|
* entry and sibling hook is carried over unchanged.
|
|
35941
36056
|
*/
|
|
35942
|
-
function repairClaudeHooks(settings, context, checks) {
|
|
36057
|
+
function repairClaudeHooks(settings, context, checks, options = {}) {
|
|
35943
36058
|
const changed = /* @__PURE__ */ new Set();
|
|
36059
|
+
const changes = [];
|
|
36060
|
+
const selected = (profile) => !options.profiles || profile !== null && options.profiles.has(profile);
|
|
35944
36061
|
const hooks = record$4(settings.hooks) ? { ...settings.hooks } : {};
|
|
35945
36062
|
const cli = context.blocked === null ? context.cli : null;
|
|
35946
36063
|
for (const [id, spec] of Object.entries(GLOBAL_HOOKS)) {
|
|
@@ -35973,19 +36090,38 @@ function repairClaudeHooks(settings, context, checks) {
|
|
|
35973
36090
|
}
|
|
35974
36091
|
if (checks.has("claude.hook.stop") && context.blocked === null) {
|
|
35975
36092
|
if (!SESSION_EVENTS.some((event) => eventEntries(hooks, event) === "invalid")) {
|
|
35976
|
-
const
|
|
36093
|
+
const held = heldSessionProfiles(hooks, context.bound ?? [], context.restoreProfiles);
|
|
36094
|
+
const bound = (context.bound ?? []).filter((item) => !held.has(boundKey(item)));
|
|
35977
36095
|
const verdicts = judgeSessionHooks(hooks, context.cli, bound);
|
|
35978
36096
|
for (const event of SESSION_EVENTS) {
|
|
35979
36097
|
const entries = eventEntries(hooks, event);
|
|
35980
36098
|
if (entries === "invalid" || entries.length === 0) continue;
|
|
35981
36099
|
const result = rewriteEntries(entries, (hook) => {
|
|
35982
36100
|
const verdict = verdicts.get(hook);
|
|
35983
|
-
if (verdict === "stale" || verdict === "duplicate" || verdict === "old_address") return void 0;
|
|
35984
|
-
if (verdict !== "outdated" || !cli || !record$4(hook)) return hook;
|
|
35985
36101
|
const parsed = parseSessionHook(hook);
|
|
36102
|
+
if (!verdict || verdict === "keep" || !parsed) return hook;
|
|
36103
|
+
if (!selected(parsed.profile)) return hook;
|
|
36104
|
+
const item = {
|
|
36105
|
+
profile: parsed.profile,
|
|
36106
|
+
session: parsed.session?.toLowerCase() ?? null,
|
|
36107
|
+
hook: event,
|
|
36108
|
+
state: verdict
|
|
36109
|
+
};
|
|
36110
|
+
if (verdict === "stale" || verdict === "duplicate" || verdict === "old_address") {
|
|
36111
|
+
changes.push({
|
|
36112
|
+
...item,
|
|
36113
|
+
action: "removed"
|
|
36114
|
+
});
|
|
36115
|
+
return;
|
|
36116
|
+
}
|
|
36117
|
+
if (!cli || !record$4(hook)) return hook;
|
|
35986
36118
|
const args = [...hook.args];
|
|
35987
|
-
args[0] = parsed
|
|
36119
|
+
args[0] = parsed.kind === "pending" ? cli.pending : cli.wake;
|
|
35988
36120
|
args[1] = cli.entry;
|
|
36121
|
+
changes.push({
|
|
36122
|
+
...item,
|
|
36123
|
+
action: "updated"
|
|
36124
|
+
});
|
|
35989
36125
|
return {
|
|
35990
36126
|
...hook,
|
|
35991
36127
|
command: cli.node,
|
|
@@ -35998,8 +36134,16 @@ function repairClaudeHooks(settings, context, checks) {
|
|
|
35998
36134
|
}
|
|
35999
36135
|
}
|
|
36000
36136
|
if (cli) for (const missing of missingSessionHooks(hooks, bound, cli.node)) {
|
|
36137
|
+
if (!selected(missing.bound.profile)) continue;
|
|
36001
36138
|
const entries = eventEntries(hooks, missing.event);
|
|
36002
36139
|
if (entries === "invalid") continue;
|
|
36140
|
+
changes.push({
|
|
36141
|
+
profile: missing.bound.profile,
|
|
36142
|
+
session: missing.bound.session,
|
|
36143
|
+
hook: missing.event,
|
|
36144
|
+
state: "missing",
|
|
36145
|
+
action: "added"
|
|
36146
|
+
});
|
|
36003
36147
|
hooks[missing.event] = [...entries, sessionHookEntry(missing.event, missing.bound, cli)];
|
|
36004
36148
|
changed.add("claude.hook.stop");
|
|
36005
36149
|
}
|
|
@@ -36007,14 +36151,16 @@ function repairClaudeHooks(settings, context, checks) {
|
|
|
36007
36151
|
}
|
|
36008
36152
|
if (!changed.size) return {
|
|
36009
36153
|
settings,
|
|
36010
|
-
changed
|
|
36154
|
+
changed,
|
|
36155
|
+
changes
|
|
36011
36156
|
};
|
|
36012
36157
|
return {
|
|
36013
36158
|
settings: {
|
|
36014
36159
|
...settings,
|
|
36015
36160
|
hooks
|
|
36016
36161
|
},
|
|
36017
|
-
changed
|
|
36162
|
+
changed,
|
|
36163
|
+
changes
|
|
36018
36164
|
};
|
|
36019
36165
|
}
|
|
36020
36166
|
//#endregion
|
|
@@ -47192,7 +47338,7 @@ const PRIMITIVE_TEAM_AUTHOR = {
|
|
|
47192
47338
|
url: "https://primitive.dev"
|
|
47193
47339
|
};
|
|
47194
47340
|
const SDK_VERSION_RANGE = "^1.34.0";
|
|
47195
|
-
const CLI_VERSION_RANGE = "^1.
|
|
47341
|
+
const CLI_VERSION_RANGE = "^1.44.0";
|
|
47196
47342
|
const ESBUILD_VERSION_RANGE = "^0.27.0";
|
|
47197
47343
|
function renderHandler() {
|
|
47198
47344
|
return `// env.PRIMITIVE_API_KEY, env.PRIMITIVE_WEBHOOK_SECRET, and
|
|
@@ -54258,13 +54404,15 @@ async function runMachineDoctor(options) {
|
|
|
54258
54404
|
"claude.hook.stop"
|
|
54259
54405
|
];
|
|
54260
54406
|
const repairedAhead = /* @__PURE__ */ new Set();
|
|
54407
|
+
const sessionHookChanges = [];
|
|
54261
54408
|
let settingsRead = readClaudeSettings(paths.claudeDir);
|
|
54262
54409
|
const hookContext = {
|
|
54263
54410
|
cli,
|
|
54264
54411
|
blocked: hookBlocked,
|
|
54265
54412
|
get bound() {
|
|
54266
54413
|
return boundSessionProfiles(options.configDir);
|
|
54267
|
-
}
|
|
54414
|
+
},
|
|
54415
|
+
restoreProfiles: options.profiles
|
|
54268
54416
|
};
|
|
54269
54417
|
const hookCheck = (id) => ({
|
|
54270
54418
|
inspect: () => {
|
|
@@ -54275,22 +54423,28 @@ async function runMachineDoctor(options) {
|
|
|
54275
54423
|
return check(id, finding.status, finding.detail, {
|
|
54276
54424
|
fixable: finding.fixable,
|
|
54277
54425
|
path: join(paths.claudeDir, "settings.json"),
|
|
54278
|
-
...finding.status === "fail" && !finding.fixable && hookBlocked ? { action: "install_cli" } : {}
|
|
54426
|
+
...finding.status === "fail" && !finding.fixable && hookBlocked ? { action: "install_cli" } : {},
|
|
54427
|
+
...finding.items?.length ? { items: finding.items.map((item) => options.fix && options.profiles ? {
|
|
54428
|
+
...item,
|
|
54429
|
+
selected: item.profile !== null && options.profiles.has(item.profile)
|
|
54430
|
+
} : item) } : {},
|
|
54431
|
+
...id === "claude.hook.stop" && sessionHookChanges.length ? { changes: [...sessionHookChanges] } : {}
|
|
54279
54432
|
});
|
|
54280
54433
|
},
|
|
54281
54434
|
repair: () => {
|
|
54282
54435
|
if (!settingsRead.ok) return false;
|
|
54283
54436
|
const findings = inspectClaudeHooks(settingsRead.settings, hookContext);
|
|
54284
54437
|
const ids = HOOK_CHECK_IDS.filter((hook) => (hook === id || !options.only || options.only.has(hook)) && findings[hook].status === "fail" && findings[hook].fixable);
|
|
54285
|
-
const repaired = repairClaudeHooks(settingsRead.settings, hookContext, new Set(ids));
|
|
54438
|
+
const repaired = repairClaudeHooks(settingsRead.settings, hookContext, new Set(ids), { profiles: options.profiles });
|
|
54286
54439
|
if (!repaired.changed.size) return false;
|
|
54287
|
-
for (const hook of repaired.changed) repairedAhead.add(hook);
|
|
54288
54440
|
writeClaudeSettings({
|
|
54289
54441
|
claudeDir: paths.claudeDir,
|
|
54290
54442
|
read: settingsRead.read,
|
|
54291
54443
|
settings: repaired.settings,
|
|
54292
54444
|
now
|
|
54293
54445
|
});
|
|
54446
|
+
sessionHookChanges.push(...repaired.changes);
|
|
54447
|
+
for (const hook of repaired.changed) repairedAhead.add(hook);
|
|
54294
54448
|
settingsRead = readClaudeSettings(paths.claudeDir);
|
|
54295
54449
|
return repaired.changed.has(id);
|
|
54296
54450
|
}
|
|
@@ -54537,7 +54691,7 @@ async function runMachineDoctor(options) {
|
|
|
54537
54691
|
...after,
|
|
54538
54692
|
fixed: changed && after.status !== "fail"
|
|
54539
54693
|
};
|
|
54540
|
-
if (changed && after.status === "fail") result.detail = `${after.detail} (A repair was applied but did not resolve this.)`;
|
|
54694
|
+
if (changed && after.status === "fail") result.detail = id === "claude.hook.stop" && options.profiles ? `${after.detail} (Only the selected profiles were repaired.)` : `${after.detail} (A repair was applied but did not resolve this.)`;
|
|
54541
54695
|
} catch (error) {
|
|
54542
54696
|
result = {
|
|
54543
54697
|
...result,
|
|
@@ -54569,10 +54723,25 @@ async function runMachineDoctor(options) {
|
|
|
54569
54723
|
}
|
|
54570
54724
|
//#endregion
|
|
54571
54725
|
//#region src/oclif/commands/machine-doctor.ts
|
|
54726
|
+
const ITEM_LINES = 10;
|
|
54727
|
+
/** At most `limit` indented lines, then "and N more". */
|
|
54728
|
+
function capped(lines, limit = ITEM_LINES) {
|
|
54729
|
+
const shown = lines.slice(0, limit).map((line) => ` ${line}`);
|
|
54730
|
+
if (lines.length > limit) shown.push(` and ${lines.length - limit} more`);
|
|
54731
|
+
return shown;
|
|
54732
|
+
}
|
|
54572
54733
|
function renderCheck(check) {
|
|
54573
54734
|
const tag = check.status === "ok" ? "[OK] " : check.status === "warn" ? "[WARN]" : check.status === "skip" ? "[SKIP]" : "[FAIL]";
|
|
54574
54735
|
const fixed = check.fixed ? " (fixed)" : "";
|
|
54575
|
-
|
|
54736
|
+
const lines = [`${tag} ${check.id}${fixed}: ${check.detail}`];
|
|
54737
|
+
if (check.changes?.length) lines.push(" Changed:", ...capped(check.changes.map(describeSessionHookChange)));
|
|
54738
|
+
if (check.status !== "ok" && check.items?.length) {
|
|
54739
|
+
const selected = check.items.filter((item) => item.selected !== false);
|
|
54740
|
+
const excluded = check.items.filter((item) => item.selected === false);
|
|
54741
|
+
if (selected.length) lines.push(check.changes?.length ? " Still needing repair:" : " Would change:", ...capped(selected.map(describeSessionHookItem)));
|
|
54742
|
+
if (excluded.length) lines.push(" Not selected (left unchanged by --profile):", ...capped(excluded.map(describeSessionHookItem)));
|
|
54743
|
+
}
|
|
54744
|
+
return lines.join("\n");
|
|
54576
54745
|
}
|
|
54577
54746
|
var MachineDoctorCommand = class MachineDoctorCommand extends Command {
|
|
54578
54747
|
static summary = "Check, and optionally repair, this machine's Primitive setup for coding agents";
|
|
@@ -54582,6 +54751,7 @@ var MachineDoctorCommand = class MachineDoctorCommand extends Command {
|
|
|
54582
54751
|
"<%= config.bin %> machine doctor --json",
|
|
54583
54752
|
"<%= config.bin %> machine doctor --fix --json",
|
|
54584
54753
|
"<%= config.bin %> machine doctor --fix --check claude.hook.stop --json",
|
|
54754
|
+
"<%= config.bin %> machine doctor --fix --check claude.hook.stop --profile my-agent",
|
|
54585
54755
|
"<%= config.bin %> machine doctor --runtime claude,codex --min-cli-version 1.38.0 --json"
|
|
54586
54756
|
];
|
|
54587
54757
|
static flags = {
|
|
@@ -54591,6 +54761,10 @@ var MachineDoctorCommand = class MachineDoctorCommand extends Command {
|
|
|
54591
54761
|
multiple: true,
|
|
54592
54762
|
options: [...DOCTOR_CHECK_IDS]
|
|
54593
54763
|
}),
|
|
54764
|
+
profile: Flags.string({
|
|
54765
|
+
description: "With --fix, change per-session receive hooks only for this saved agent profile; repeatable. Naming a profile is also the only way --fix restores hooks for a profile bound to a session that already receives as another profile. Other checks are unaffected, so combine with --check claude.hook.stop to repair nothing else",
|
|
54766
|
+
multiple: true
|
|
54767
|
+
}),
|
|
54594
54768
|
runtime: Flags.string({ description: "Comma-separated runtimes to check: claude, codex, omp. Defaults to every runtime found on this machine" }),
|
|
54595
54769
|
"min-cli-version": Flags.string({ description: "Report cli.version as failing when this CLI is older than the given version" }),
|
|
54596
54770
|
json: Flags.boolean({ description: "Print the report as JSON" })
|
|
@@ -54605,9 +54779,11 @@ var MachineDoctorCommand = class MachineDoctorCommand extends Command {
|
|
|
54605
54779
|
runtimes = new Set(names);
|
|
54606
54780
|
}
|
|
54607
54781
|
if (flags.check?.length && !flags.fix) this.error("--check selects which checks --fix repairs; add --fix.", { exit: 2 });
|
|
54782
|
+
if (flags.profile?.length && !flags.fix) this.error("--profile narrows what --fix repairs; add --fix.", { exit: 2 });
|
|
54608
54783
|
const report = await runMachineDoctor({
|
|
54609
54784
|
fix: flags.fix,
|
|
54610
54785
|
only: flags.check?.length ? new Set(flags.check) : void 0,
|
|
54786
|
+
profiles: flags.profile?.length ? new Set(flags.profile) : void 0,
|
|
54611
54787
|
runtimes,
|
|
54612
54788
|
configDir: this.config.configDir,
|
|
54613
54789
|
home: homedir(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "primcli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.44.0",
|
|
4
4
|
"description": "Official Primitive CLI: deploy Primitive Functions, send and inspect mail, manage endpoints, all from the terminal. Wraps the @primitivedotdev/sdk runtime client with one-shot commands.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|