harnery 0.32.0 → 0.33.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 +2 -0
- package/dist/commander.d.ts.map +1 -1
- package/dist/commander.js +3 -1
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +212 -1
- package/dist/commands/claude-desktop.d.ts +2 -2
- package/dist/commands/claude-desktop.d.ts.map +1 -1
- package/dist/commands/claude-desktop.js +74 -3
- package/dist/commands/doctor.d.ts +4 -2
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +45 -6
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +10 -0
- package/dist/commands/instructions.d.ts +4 -0
- package/dist/commands/instructions.d.ts.map +1 -0
- package/dist/commands/instructions.js +100 -0
- package/dist/commands/presence.d.ts.map +1 -1
- package/dist/commands/presence.js +4 -1
- package/dist/core/agents/canonical-emit.d.ts +1 -1
- package/dist/core/agents/canonical-emit.d.ts.map +1 -1
- package/dist/core/agents/canonical-emit.js +5 -3
- package/dist/core/agents/cli.js +5 -1
- package/dist/core/agents/coord-client.d.ts +11 -0
- package/dist/core/agents/coord-client.d.ts.map +1 -1
- package/dist/core/agents/index.d.ts +1 -0
- package/dist/core/agents/index.d.ts.map +1 -1
- package/dist/core/agents/index.js +1 -0
- package/dist/core/agents/render/prompt-context.d.ts +6 -0
- package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
- package/dist/core/agents/render/prompt-context.js +46 -5
- package/dist/core/agents/render/session-context.d.ts.map +1 -1
- package/dist/core/agents/render/session-context.js +8 -2
- package/dist/core/agents/rules/stop-hook.js +4 -4
- package/dist/core/agents/state/heartbeat-projector.d.ts +11 -0
- package/dist/core/agents/state/heartbeat-projector.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-projector.js +54 -0
- package/dist/core/agents/state/heartbeat-writer.d.ts +15 -0
- package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-writer.js +32 -0
- package/dist/core/agents/state/session-state.d.ts +44 -0
- package/dist/core/agents/state/session-state.d.ts.map +1 -0
- package/dist/core/agents/state/session-state.js +81 -0
- package/dist/core/hooks/adapter/events.d.ts.map +1 -1
- package/dist/core/hooks/adapter/events.js +1 -0
- package/dist/core/hooks/adapter/parse.d.ts +1 -1
- package/dist/core/hooks/adapter/parse.d.ts.map +1 -1
- package/dist/core/hooks/adapter/parse.js +2 -0
- package/dist/core/hooks/cli.js +90 -6
- package/dist/core/hooks/codex-authorization.d.ts +30 -0
- package/dist/core/hooks/codex-authorization.d.ts.map +1 -0
- package/dist/core/hooks/codex-authorization.js +199 -0
- package/dist/core/hooks/events/schema.d.ts +30 -1
- package/dist/core/hooks/events/schema.d.ts.map +1 -1
- package/dist/core/presence/blob.d.ts +4 -0
- package/dist/core/presence/blob.d.ts.map +1 -1
- package/dist/core/presence/blob.js +12 -0
- package/dist/core/presence/index.js +15 -1
- package/dist/lib/claude-desktop.d.ts +47 -0
- package/dist/lib/claude-desktop.d.ts.map +1 -1
- package/dist/lib/claude-desktop.js +169 -1
- package/dist/lib/instructions/bundle.d.ts +33 -0
- package/dist/lib/instructions/bundle.d.ts.map +1 -0
- package/dist/lib/instructions/bundle.js +149 -0
- package/package.json +2 -2
- package/src/commander.ts +3 -1
- package/src/commands/agents.ts +248 -1
- package/src/commands/claude-desktop.ts +106 -5
- package/src/commands/doctor.ts +59 -7
- package/src/commands/init.ts +11 -0
- package/src/commands/instructions.ts +129 -0
- package/src/commands/presence.ts +7 -1
- package/src/core/agents/canonical-emit.ts +6 -4
- package/src/core/agents/cli.ts +6 -1
- package/src/core/agents/coord-client.ts +11 -0
- package/src/core/agents/index.ts +1 -0
- package/src/core/agents/render/prompt-context.ts +71 -6
- package/src/core/agents/render/session-context.ts +14 -2
- package/src/core/agents/rules/stop-hook.ts +5 -5
- package/src/core/agents/state/heartbeat-projector.ts +63 -0
- package/src/core/agents/state/heartbeat-writer.ts +51 -0
- package/src/core/agents/state/session-state.ts +131 -0
- package/src/core/hooks/adapter/events.ts +1 -0
- package/src/core/hooks/adapter/parse.ts +3 -0
- package/src/core/hooks/cli.ts +98 -7
- package/src/core/hooks/codex-authorization.ts +253 -0
- package/src/core/hooks/events/schema.ts +44 -0
- package/src/core/presence/blob.ts +18 -0
- package/src/core/presence/index.ts +18 -1
- package/src/lib/claude-desktop.ts +232 -1
- package/src/lib/instructions/bundle.ts +199 -0
package/README.md
CHANGED
|
@@ -100,6 +100,8 @@ The boundary is enforced, not aspirational: CI verifies that no `harnery/lib/*`
|
|
|
100
100
|
Full docs at **[harnery.com](https://harnery.com)**:
|
|
101
101
|
|
|
102
102
|
- [Getting started](https://harnery.com/getting-started/install)
|
|
103
|
+
- [Features](https://harnery.com/features/session-state/)
|
|
104
|
+
- [Guides](https://harnery.com/guides/manage-claude-desktop-sessions/)
|
|
103
105
|
- [CLI reference](https://harnery.com/cli/)
|
|
104
106
|
- [Concepts](https://harnery.com/concepts/coord-layer)
|
|
105
107
|
- [Configuration schema](https://harnery.com/reference/config-schema)
|
package/dist/commander.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commander.d.ts","sourceRoot":"","sources":["../src/commander.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"commander.d.ts","sourceRoot":"","sources":["../src/commander.ts"],"names":[],"mappings":"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"}
|
package/dist/commander.js
CHANGED
|
@@ -43,6 +43,7 @@ import { registerFileHistoryCommand } from "./commands/file-history.js";
|
|
|
43
43
|
import { registerGovernorCommand } from "./commands/governor.js";
|
|
44
44
|
import { registerGrepCommand } from "./commands/grep.js";
|
|
45
45
|
import { registerInitCommand } from "./commands/init.js";
|
|
46
|
+
import { registerInstructionsCommand } from "./commands/instructions.js";
|
|
46
47
|
import { registerJournalCommand } from "./commands/journal.js";
|
|
47
48
|
import { registerOutlineCommand } from "./commands/outline.js";
|
|
48
49
|
import { registerPolicyCommand } from "./commands/policy.js";
|
|
@@ -131,10 +132,11 @@ export function createHarneryProgram(opts = {}) {
|
|
|
131
132
|
registerAgentsCommand(program, emit, opts.context);
|
|
132
133
|
registerCouncilCommands(program);
|
|
133
134
|
registerDoctorCommand(program, emit);
|
|
135
|
+
registerInstructionsCommand(program, emit);
|
|
134
136
|
registerInitCommand(program, emit, opts.binName);
|
|
135
137
|
registerDeinitCommand(program, emit, opts.binName);
|
|
136
138
|
registerBackupCommand(program, emit);
|
|
137
|
-
registerClaudeDesktopCommand(program, emit);
|
|
139
|
+
registerClaudeDesktopCommand(program, emit, opts.context);
|
|
138
140
|
registerSyncCommand(program, emit);
|
|
139
141
|
registerWorkflowCommand(program, emit);
|
|
140
142
|
registerWorkCommand(program, emit);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/commands/agents.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAeH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/commands/agents.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAeH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AA0F1E;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAA;CAAE,CAErF;AAyFD,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,WAAW,EACtB,cAAc,CAAC,EAAE,qBAAqB,GACrC,IAAI,CA+QN;AAsKD,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAyO7D"}
|
package/dist/commands/agents.js
CHANGED
|
@@ -20,7 +20,8 @@ import { coordBinPath } from "../core/agents/coord-bin.js";
|
|
|
20
20
|
import { readStreamTailBounded } from "../core/agents/events/consume.js";
|
|
21
21
|
import { checkGitFinalization, formatGitFinalizationFailure, readSessionWriteClaims, } from "../core/agents/finalization.js";
|
|
22
22
|
import { emitCanonical, monorepoRoot, normalizeAdapter, readHeartbeat, resolveOwner, resolveOwnerBySessionEnv, resolveOwnerWithSource, } from "../core/agents/index.js";
|
|
23
|
-
import { buildSuggestedName } from "../core/agents/state/heartbeat-writer.js";
|
|
23
|
+
import { buildLifecycleSuggestedName, buildSuggestedName, } from "../core/agents/state/heartbeat-writer.js";
|
|
24
|
+
import { foldSessionState, } from "../core/agents/state/session-state.js";
|
|
24
25
|
import { coordFreshnessSeconds, resolveBinName } from "../core/config.js";
|
|
25
26
|
import { readRemoteMachines } from "../core/presence/index.js";
|
|
26
27
|
import { registerContextCommand } from "./context.js";
|
|
@@ -101,6 +102,16 @@ function formatPlatformLabel(platform) {
|
|
|
101
102
|
return "Codex";
|
|
102
103
|
return "CC";
|
|
103
104
|
}
|
|
105
|
+
function activityOf(hb) {
|
|
106
|
+
return hb.activity ?? "unknown";
|
|
107
|
+
}
|
|
108
|
+
function taskStateOf(hb) {
|
|
109
|
+
return hb.task_state ?? "active";
|
|
110
|
+
}
|
|
111
|
+
function lifecycleLabel(hb) {
|
|
112
|
+
const state = taskStateOf(hb);
|
|
113
|
+
return state === "blocked" && hb.task_state_reason ? `${state}: ${hb.task_state_reason}` : state;
|
|
114
|
+
}
|
|
104
115
|
let emit;
|
|
105
116
|
export function registerAgentsCommand(program, emitParam, programContext) {
|
|
106
117
|
emit = emitParam;
|
|
@@ -184,6 +195,14 @@ export function registerAgentsCommand(program, emitParam, programContext) {
|
|
|
184
195
|
.action((text, opts) => {
|
|
185
196
|
runSetTask(text.join(" "), opts);
|
|
186
197
|
});
|
|
198
|
+
cmd
|
|
199
|
+
.command("lifecycle <state>")
|
|
200
|
+
.description("Declare this session's task lifecycle: active, blocked, or done")
|
|
201
|
+
.option("--reason <text>", "Explain the lifecycle declaration")
|
|
202
|
+
.option("--session-id <id>", "Target the heartbeat with this session_id directly, bypassing the ppid walk.")
|
|
203
|
+
.action((state, opts) => {
|
|
204
|
+
runLifecycle(state, opts);
|
|
205
|
+
});
|
|
187
206
|
cmd
|
|
188
207
|
.command("release-claim <path>")
|
|
189
208
|
.description("Drop a file claim from your heartbeat. Operator escape hatch when a " +
|
|
@@ -598,6 +617,12 @@ function runWhoami(opts) {
|
|
|
598
617
|
last_tool: hb.last_tool ?? null,
|
|
599
618
|
last_tool_target: hb.last_tool_target ?? null,
|
|
600
619
|
task: hb.task ?? null,
|
|
620
|
+
activity: activityOf(hb),
|
|
621
|
+
activity_updated_at: hb.activity_updated_at ?? null,
|
|
622
|
+
activity_source: hb.activity_source ?? null,
|
|
623
|
+
task_state: taskStateOf(hb),
|
|
624
|
+
task_state_updated_at: hb.task_state_updated_at ?? null,
|
|
625
|
+
task_state_reason: hb.task_state_reason ?? null,
|
|
601
626
|
turn_summary: hb.turn_summary ?? null,
|
|
602
627
|
turn_summary_updated_at: hb.turn_summary_updated_at ?? null,
|
|
603
628
|
platform: hb.platform ?? "claude-code",
|
|
@@ -687,6 +712,12 @@ function runList(opts) {
|
|
|
687
712
|
last_tool: h.last_tool ?? null,
|
|
688
713
|
last_tool_target: h.last_tool_target ?? null,
|
|
689
714
|
task: h.task ?? null,
|
|
715
|
+
activity: activityOf(h),
|
|
716
|
+
activity_updated_at: h.activity_updated_at ?? null,
|
|
717
|
+
activity_source: h.activity_source ?? null,
|
|
718
|
+
task_state: taskStateOf(h),
|
|
719
|
+
task_state_updated_at: h.task_state_updated_at ?? null,
|
|
720
|
+
task_state_reason: h.task_state_reason ?? null,
|
|
690
721
|
turn_summary: h.turn_summary ?? null,
|
|
691
722
|
turn_summary_updated_at: h.turn_summary_updated_at ?? null,
|
|
692
723
|
platform: h.platform ?? "claude-code",
|
|
@@ -712,6 +743,12 @@ function runList(opts) {
|
|
|
712
743
|
last_tool: h.last_tool ?? null,
|
|
713
744
|
last_tool_target: h.last_tool_target ?? null,
|
|
714
745
|
task: h.task ?? null,
|
|
746
|
+
activity: activityOf(h),
|
|
747
|
+
activity_updated_at: h.activity_updated_at ?? null,
|
|
748
|
+
activity_source: h.activity_source ?? null,
|
|
749
|
+
task_state: taskStateOf(h),
|
|
750
|
+
task_state_updated_at: h.task_state_updated_at ?? null,
|
|
751
|
+
task_state_reason: h.task_state_reason ?? null,
|
|
715
752
|
turn_summary: h.turn_summary ?? null,
|
|
716
753
|
turn_summary_updated_at: h.turn_summary_updated_at ?? null,
|
|
717
754
|
platform: h.platform ?? "claude-code",
|
|
@@ -738,6 +775,12 @@ function runList(opts) {
|
|
|
738
775
|
last_tool: a.last_tool ?? null,
|
|
739
776
|
last_tool_target: null,
|
|
740
777
|
task: a.task ?? null,
|
|
778
|
+
activity: a.activity ?? "unknown",
|
|
779
|
+
activity_updated_at: null,
|
|
780
|
+
activity_source: null,
|
|
781
|
+
task_state: a.task_state ?? "active",
|
|
782
|
+
task_state_updated_at: null,
|
|
783
|
+
task_state_reason: a.task_state_reason ?? null,
|
|
741
784
|
turn_summary: a.turn_summary ?? null,
|
|
742
785
|
turn_summary_updated_at: null,
|
|
743
786
|
platform: a.platform ?? "claude-code",
|
|
@@ -983,6 +1026,12 @@ async function runShow(name, opts) {
|
|
|
983
1026
|
age_secs: ageSecs,
|
|
984
1027
|
last_heartbeat_secs_ago: heartbeatAgeSecs,
|
|
985
1028
|
task: hb.task ?? null,
|
|
1029
|
+
activity: activityOf(hb),
|
|
1030
|
+
activity_updated_at: hb.activity_updated_at ?? null,
|
|
1031
|
+
activity_source: hb.activity_source ?? null,
|
|
1032
|
+
task_state: taskStateOf(hb),
|
|
1033
|
+
task_state_updated_at: hb.task_state_updated_at ?? null,
|
|
1034
|
+
task_state_reason: hb.task_state_reason ?? null,
|
|
986
1035
|
turn_summary: hb.turn_summary ?? null,
|
|
987
1036
|
turn_summary_updated_at: hb.turn_summary_updated_at ?? null,
|
|
988
1037
|
title: report?.title ?? null,
|
|
@@ -1010,6 +1059,8 @@ async function runShow(name, opts) {
|
|
|
1010
1059
|
lines.push(`agent-${data.name} ${subtitle}`);
|
|
1011
1060
|
lines.push(` session ${formatAge(ageSecs)} old · kind=${data.kind} · session_id=${data.session_id.slice(0, 8)}…`);
|
|
1012
1061
|
lines.push(` last heartbeat: ${formatAge(heartbeatAgeSecs)} ago`);
|
|
1062
|
+
lines.push(` activity: ${data.activity}`);
|
|
1063
|
+
lines.push(` lifecycle: ${data.task_state}${data.task_state === "blocked" && data.task_state_reason ? `: ${data.task_state_reason}` : ""}`);
|
|
1013
1064
|
if (data.last_tool) {
|
|
1014
1065
|
const target = data.last_tool_target ? ` ${truncate(data.last_tool_target, 80)}` : "";
|
|
1015
1066
|
lines.push(` last activity: ${data.last_tool}${target}`);
|
|
@@ -1183,6 +1234,10 @@ function runSetTask(task, opts) {
|
|
|
1183
1234
|
// heartbeat-writer.setTask. This call is the naming call exactly when the
|
|
1184
1235
|
// stamp appears across the mutation.
|
|
1185
1236
|
const priorHb = readHeartbeat(myOwner);
|
|
1237
|
+
const normalizedTask = task.length > 0 ? task : undefined;
|
|
1238
|
+
const lifecycleWarning = priorHb && (priorHb.task_state ?? "active") !== "active" && priorHb.task !== normalizedTask
|
|
1239
|
+
? `Task text changed while lifecycle is ${priorHb.task_state}; run \`${resolveBinName(root)} agents lifecycle active\` to reopen it explicitly.`
|
|
1240
|
+
: null;
|
|
1186
1241
|
// Heartbeat mutation goes through agent-coord (atomic temp+rename).
|
|
1187
1242
|
const helper = agentCoordOrExit(root);
|
|
1188
1243
|
const result = spawnSync(helper, ["set-task", myOwner, task], {
|
|
@@ -1224,6 +1279,7 @@ function runSetTask(task, opts) {
|
|
|
1224
1279
|
cleared: !task || task.length === 0,
|
|
1225
1280
|
first_of_session: firstOfSession,
|
|
1226
1281
|
suggested_session_name: suggestedName,
|
|
1282
|
+
...(lifecycleWarning ? { warning: lifecycleWarning } : {}),
|
|
1227
1283
|
// Right-time instruction: the UserPromptSubmit nudge fires before the name
|
|
1228
1284
|
// exists; this result is the moment the model holds the string.
|
|
1229
1285
|
...(suggestedName
|
|
@@ -1233,6 +1289,136 @@ function runSetTask(task, opts) {
|
|
|
1233
1289
|
: {}),
|
|
1234
1290
|
});
|
|
1235
1291
|
}
|
|
1292
|
+
function runLifecycle(rawState, opts) {
|
|
1293
|
+
const state = rawState.trim().toLowerCase();
|
|
1294
|
+
if (state !== "active" && state !== "blocked" && state !== "done") {
|
|
1295
|
+
emit.error({
|
|
1296
|
+
code: "invalid_lifecycle_state",
|
|
1297
|
+
message: "lifecycle state must be one of: active, blocked, done",
|
|
1298
|
+
});
|
|
1299
|
+
process.exitCode = 1;
|
|
1300
|
+
return;
|
|
1301
|
+
}
|
|
1302
|
+
const root = monorepoRoot();
|
|
1303
|
+
if (!root) {
|
|
1304
|
+
emit.error({
|
|
1305
|
+
code: "not_in_repo",
|
|
1306
|
+
message: "not in an agent session; coord_root() returned null",
|
|
1307
|
+
});
|
|
1308
|
+
process.exitCode = 1;
|
|
1309
|
+
return;
|
|
1310
|
+
}
|
|
1311
|
+
if (!opts.sessionId)
|
|
1312
|
+
ensureCursorSession(root);
|
|
1313
|
+
const myOwner = opts.sessionId ?? resolveOwner();
|
|
1314
|
+
if (!myOwner) {
|
|
1315
|
+
emit.error({
|
|
1316
|
+
code: "no_pidmap_entry",
|
|
1317
|
+
message: "not in an agent session; ppid walk found no pid-map entry (pass --session-id to bypass)",
|
|
1318
|
+
});
|
|
1319
|
+
process.exitCode = 1;
|
|
1320
|
+
return;
|
|
1321
|
+
}
|
|
1322
|
+
const hb = readHeartbeat(myOwner);
|
|
1323
|
+
if (!hb) {
|
|
1324
|
+
emit.error({ code: "no_heartbeat", message: noHeartbeatMessage(myOwner) });
|
|
1325
|
+
process.exitCode = 1;
|
|
1326
|
+
return;
|
|
1327
|
+
}
|
|
1328
|
+
if (hb.kind === "subagent" || hb.kind === "transient" || hb.workflow_run_id) {
|
|
1329
|
+
emit.error({
|
|
1330
|
+
code: "lifecycle_not_human_facing",
|
|
1331
|
+
message: "task lifecycle declarations are limited to human-facing sessions",
|
|
1332
|
+
});
|
|
1333
|
+
process.exitCode = 1;
|
|
1334
|
+
return;
|
|
1335
|
+
}
|
|
1336
|
+
const reason = opts.reason?.trim() || undefined;
|
|
1337
|
+
if (state === "blocked" && !reason) {
|
|
1338
|
+
emit.error({
|
|
1339
|
+
code: "blocked_reason_required",
|
|
1340
|
+
message: "lifecycle blocked requires --reason <text>",
|
|
1341
|
+
});
|
|
1342
|
+
process.exitCode = 1;
|
|
1343
|
+
return;
|
|
1344
|
+
}
|
|
1345
|
+
if (state === "done" && !hb.task) {
|
|
1346
|
+
emit.error({
|
|
1347
|
+
code: "task_required_for_done",
|
|
1348
|
+
message: "declare a current task with agents set-task before marking it done",
|
|
1349
|
+
});
|
|
1350
|
+
process.exitCode = 1;
|
|
1351
|
+
return;
|
|
1352
|
+
}
|
|
1353
|
+
const priorState = hb.task_state ?? "active";
|
|
1354
|
+
const priorReason = hb.task_state_reason || undefined;
|
|
1355
|
+
if (priorState === state && priorReason === reason) {
|
|
1356
|
+
emit.data({
|
|
1357
|
+
instance_id: myOwner,
|
|
1358
|
+
task_state: state,
|
|
1359
|
+
reason: reason ?? null,
|
|
1360
|
+
changed: false,
|
|
1361
|
+
name_reminted: false,
|
|
1362
|
+
git_finalization_checked: false,
|
|
1363
|
+
});
|
|
1364
|
+
return;
|
|
1365
|
+
}
|
|
1366
|
+
let finalization = null;
|
|
1367
|
+
if (state === "done") {
|
|
1368
|
+
const history = readSessionWriteClaims(root, hb.instance_id, hb.session_id ?? myOwner);
|
|
1369
|
+
const touchedPaths = [...new Set([...(hb.files_touched ?? []), ...history.paths])];
|
|
1370
|
+
finalization = checkGitFinalization(root, touchedPaths, {
|
|
1371
|
+
claimHistoryComplete: history.complete,
|
|
1372
|
+
});
|
|
1373
|
+
if (!finalization.ok) {
|
|
1374
|
+
emit.error({
|
|
1375
|
+
code: "git_not_finalized",
|
|
1376
|
+
message: formatGitFinalizationFailure(finalization, resolveBinName(root)).replace("the status box was not issued", "lifecycle was not changed"),
|
|
1377
|
+
});
|
|
1378
|
+
process.exitCode = 1;
|
|
1379
|
+
return;
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
const suggestedName = buildLifecycleSuggestedName(hb.name ?? "unknown", hb.task, state);
|
|
1383
|
+
const nameReminted = suggestedName !== null && suggestedName !== hb.suggested_session_name;
|
|
1384
|
+
const emitted = emitCanonical({
|
|
1385
|
+
type: "state.task_state",
|
|
1386
|
+
owner: myOwner,
|
|
1387
|
+
session: hb.session_id ?? myOwner,
|
|
1388
|
+
adapter: normalizeAdapter(hb.platform),
|
|
1389
|
+
data: {
|
|
1390
|
+
state,
|
|
1391
|
+
reason: reason ?? null,
|
|
1392
|
+
prior_state: priorState,
|
|
1393
|
+
name_reminted: nameReminted,
|
|
1394
|
+
git_finalization_checked: finalization !== null,
|
|
1395
|
+
...(nameReminted ? { suggested_session_name: suggestedName } : {}),
|
|
1396
|
+
},
|
|
1397
|
+
});
|
|
1398
|
+
if (!emitted) {
|
|
1399
|
+
emit.error({
|
|
1400
|
+
code: "lifecycle_event_failed",
|
|
1401
|
+
message: "the lifecycle event could not be recorded; heartbeat state was left unchanged",
|
|
1402
|
+
});
|
|
1403
|
+
process.exitCode = 1;
|
|
1404
|
+
return;
|
|
1405
|
+
}
|
|
1406
|
+
emit.data({
|
|
1407
|
+
instance_id: myOwner,
|
|
1408
|
+
task_state: state,
|
|
1409
|
+
prior_state: priorState,
|
|
1410
|
+
reason: reason ?? null,
|
|
1411
|
+
changed: true,
|
|
1412
|
+
name_reminted: nameReminted,
|
|
1413
|
+
suggested_session_name: nameReminted ? suggestedName : null,
|
|
1414
|
+
git_finalization_checked: finalization !== null,
|
|
1415
|
+
...(nameReminted
|
|
1416
|
+
? {
|
|
1417
|
+
note: "Reproduce suggested_session_name verbatim, by itself, in a fenced code block at the top of your reply — the operator copies it as the session/tab title.",
|
|
1418
|
+
}
|
|
1419
|
+
: {}),
|
|
1420
|
+
});
|
|
1421
|
+
}
|
|
1236
1422
|
function runSuggestName(descriptionParts, opts) {
|
|
1237
1423
|
const root = monorepoRoot();
|
|
1238
1424
|
if (!root) {
|
|
@@ -1422,6 +1608,12 @@ function runStatus(opts) {
|
|
|
1422
1608
|
instance_id: hb.instance_id,
|
|
1423
1609
|
kind: normalizeKind(hb.kind),
|
|
1424
1610
|
session_age_secs: ageSecs,
|
|
1611
|
+
activity: activityOf(hb),
|
|
1612
|
+
activity_updated_at: hb.activity_updated_at ?? null,
|
|
1613
|
+
activity_source: hb.activity_source ?? null,
|
|
1614
|
+
task_state: taskStateOf(hb),
|
|
1615
|
+
task_state_updated_at: hb.task_state_updated_at ?? null,
|
|
1616
|
+
task_state_reason: hb.task_state_reason ?? null,
|
|
1425
1617
|
files_held: filesHeld,
|
|
1426
1618
|
peers_live: livePeers.length,
|
|
1427
1619
|
peers_stale: peersStale,
|
|
@@ -1435,6 +1627,9 @@ function runStatus(opts) {
|
|
|
1435
1627
|
agents: m.agents.map((a) => ({
|
|
1436
1628
|
name: a.name || "unnamed",
|
|
1437
1629
|
task: a.task ?? null,
|
|
1630
|
+
activity: a.activity ?? "unknown",
|
|
1631
|
+
task_state: a.task_state ?? "active",
|
|
1632
|
+
task_state_reason: a.task_state_reason ?? null,
|
|
1438
1633
|
files: a.files_touched?.length ?? 0,
|
|
1439
1634
|
})),
|
|
1440
1635
|
})),
|
|
@@ -1452,6 +1647,8 @@ function runStatus(opts) {
|
|
|
1452
1647
|
}
|
|
1453
1648
|
const rows = [
|
|
1454
1649
|
["session", formatAge(ageSecs)],
|
|
1650
|
+
["activity", activityOf(hb)],
|
|
1651
|
+
["lifecycle", lifecycleLabel(hb)],
|
|
1455
1652
|
["context", ctxStr],
|
|
1456
1653
|
["files", filesStr],
|
|
1457
1654
|
["peers", peersStr],
|
|
@@ -2134,6 +2331,12 @@ function traceLine(ev, allTools) {
|
|
|
2134
2331
|
case "state.task_set":
|
|
2135
2332
|
detail = d.cleared ? "(cleared)" : clip(s("task"));
|
|
2136
2333
|
break;
|
|
2334
|
+
case "state.task_state":
|
|
2335
|
+
detail = `${s("prior_state") || "active"} → ${s("state") || "active"}${s("reason") ? ` · ${clip(s("reason"), 55)}` : ""}`;
|
|
2336
|
+
break;
|
|
2337
|
+
case "interaction.input_requested":
|
|
2338
|
+
detail = s("request_kind") || s("tool_name") || "operator input requested";
|
|
2339
|
+
break;
|
|
2137
2340
|
case "state.status_checked":
|
|
2138
2341
|
detail = "status box rendered";
|
|
2139
2342
|
break;
|
|
@@ -2247,6 +2450,7 @@ function runTrace(name, opts) {
|
|
|
2247
2450
|
}
|
|
2248
2451
|
}
|
|
2249
2452
|
const events = byId.get(resolvedId) ?? [];
|
|
2453
|
+
const state = foldSessionState(events, { instance_id: resolvedId });
|
|
2250
2454
|
const lines = events
|
|
2251
2455
|
.map((ev) => traceLine(ev, !!opts.allTools))
|
|
2252
2456
|
.filter((l) => l !== null)
|
|
@@ -2260,6 +2464,12 @@ function runTrace(name, opts) {
|
|
|
2260
2464
|
instance_id: resolvedId,
|
|
2261
2465
|
other_instances: candidateIds.filter((id) => id !== resolvedId),
|
|
2262
2466
|
total_events: events.length,
|
|
2467
|
+
activity: state.activity,
|
|
2468
|
+
activity_updated_at: state.activity_updated_at ?? null,
|
|
2469
|
+
activity_source: state.activity_source ?? null,
|
|
2470
|
+
task_state: state.task_state,
|
|
2471
|
+
task_state_updated_at: state.task_state_updated_at ?? null,
|
|
2472
|
+
task_state_reason: state.task_state_reason ?? null,
|
|
2263
2473
|
shown: shown.length,
|
|
2264
2474
|
entries: shown,
|
|
2265
2475
|
};
|
|
@@ -2270,6 +2480,7 @@ function runTrace(name, opts) {
|
|
|
2270
2480
|
emit.data(result);
|
|
2271
2481
|
const header = `Trace: agent-${displayName} (${resolvedId.slice(0, 8)}…) ${events.length} events${result.other_instances.length ? ` · ${result.other_instances.length} older instance(s) of this name` : ""}`;
|
|
2272
2482
|
process.stdout.write(`${header}\n`); // lint-ok-emission: human trace view
|
|
2483
|
+
process.stdout.write(` activity=${state.activity} · lifecycle=${state.task_state}${state.task_state === "blocked" && state.task_state_reason ? `: ${state.task_state_reason}` : ""}\n`); // lint-ok-emission: human trace view
|
|
2273
2484
|
if (shown.length === 0) {
|
|
2274
2485
|
process.stdout.write(" (no events)\n"); // lint-ok-emission: human trace view
|
|
2275
2486
|
return;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Command } from "commander";
|
|
2
|
-
import type { EmitContext } from "../commander.js";
|
|
2
|
+
import type { EmitContext, HarneryProgramContext } from "../commander.js";
|
|
3
3
|
/**
|
|
4
4
|
* `claude-desktop`: make Claude desktop-app sessions survive account
|
|
5
5
|
* switches.
|
|
@@ -15,5 +15,5 @@ import type { EmitContext } from "../commander.js";
|
|
|
15
15
|
* desktop app picks the copies up, and opening one resumes with full
|
|
16
16
|
* history.
|
|
17
17
|
*/
|
|
18
|
-
export declare function registerClaudeDesktopCommand(program: Command, emit: EmitContext): void;
|
|
18
|
+
export declare function registerClaudeDesktopCommand(program: Command, emit: EmitContext, context?: HarneryProgramContext): void;
|
|
19
19
|
//# sourceMappingURL=claude-desktop.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-desktop.d.ts","sourceRoot":"","sources":["../../src/commands/claude-desktop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"claude-desktop.d.ts","sourceRoot":"","sources":["../../src/commands/claude-desktop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAc1E;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,WAAW,EACjB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,IAAI,CA6PN"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { monorepoRoot } from "../core/agents/index.js";
|
|
2
|
+
import { applyMirror, applyTidy, findDesktopDataDirs, listAccounts, planMirror, planTidy, readCliAccount, readDesktopLifecycleIndex, } from "../lib/claude-desktop.js";
|
|
2
3
|
/**
|
|
3
4
|
* `claude-desktop`: make Claude desktop-app sessions survive account
|
|
4
5
|
* switches.
|
|
@@ -14,10 +15,10 @@ import { applyMirror, findDesktopDataDirs, listAccounts, planMirror, readCliAcco
|
|
|
14
15
|
* desktop app picks the copies up, and opening one resumes with full
|
|
15
16
|
* history.
|
|
16
17
|
*/
|
|
17
|
-
export function registerClaudeDesktopCommand(program, emit) {
|
|
18
|
+
export function registerClaudeDesktopCommand(program, emit, context) {
|
|
18
19
|
const cmd = program
|
|
19
20
|
.command("claude-desktop")
|
|
20
|
-
.description("Claude desktop-app session index: list
|
|
21
|
+
.description("Claude desktop-app session index: list, mirror, and archive completed sessions")
|
|
21
22
|
.action(() => {
|
|
22
23
|
const dirs = requireDataDirs(emit, undefined);
|
|
23
24
|
for (const dir of dirs) {
|
|
@@ -141,6 +142,76 @@ export function registerClaudeDesktopCommand(program, emit) {
|
|
|
141
142
|
});
|
|
142
143
|
}
|
|
143
144
|
});
|
|
145
|
+
cmd
|
|
146
|
+
.command("tidy")
|
|
147
|
+
.description("Archive desktop sidebar entries whose mapped Harnery lifecycle is done. " +
|
|
148
|
+
"Dry-run by default; --yes applies atomic entry updates.")
|
|
149
|
+
.option("--data-dir <path>", "Explicit desktop-app data directory")
|
|
150
|
+
.option("--account <uuid-prefix>", "Only entries under accounts matching this UUID prefix (repeatable)", collect, [])
|
|
151
|
+
.option("--include-legacy-prefix", "Also archive unmatched historical entries whose title starts with [DONE]")
|
|
152
|
+
.option("--yes", "Apply the archive plan (default is dry-run)")
|
|
153
|
+
.action(async (opts) => {
|
|
154
|
+
const dirs = requireDataDirs(emit, opts.dataDir);
|
|
155
|
+
const coordRoot = context?.resolveCoordRoot?.() ?? context?.repoRoot ?? monorepoRoot() ?? process.cwd();
|
|
156
|
+
const lifecycle = await readDesktopLifecycleIndex(coordRoot);
|
|
157
|
+
for (const dir of dirs) {
|
|
158
|
+
const plan = planTidy(listAccounts(dir), lifecycle, {
|
|
159
|
+
accounts: opts.account,
|
|
160
|
+
includeLegacyPrefix: opts.includeLegacyPrefix,
|
|
161
|
+
});
|
|
162
|
+
const planned = plan.actions.map((action) => ({
|
|
163
|
+
account_uuid: action.entry.accountUuid,
|
|
164
|
+
cli_session_id: action.entry.cliSessionId,
|
|
165
|
+
title: action.entry.title,
|
|
166
|
+
source: action.source,
|
|
167
|
+
lifecycle_updated_at: action.lifecycle?.updatedAt ?? null,
|
|
168
|
+
file: action.entry.file,
|
|
169
|
+
}));
|
|
170
|
+
const skipped = plan.skipped
|
|
171
|
+
.map((skip) => ({
|
|
172
|
+
account_uuid: skip.entry.accountUuid,
|
|
173
|
+
cli_session_id: skip.entry.cliSessionId,
|
|
174
|
+
title: skip.entry.title,
|
|
175
|
+
reason: skip.reason,
|
|
176
|
+
lifecycle: skip.lifecycle?.state ?? null,
|
|
177
|
+
}))
|
|
178
|
+
.filter((skip) => skip.reason !== "already_archived");
|
|
179
|
+
const skippedByReason = Object.fromEntries([
|
|
180
|
+
"already_archived",
|
|
181
|
+
"blocked_title",
|
|
182
|
+
"lifecycle_active",
|
|
183
|
+
"lifecycle_blocked",
|
|
184
|
+
"unmatched",
|
|
185
|
+
].map((reason) => [
|
|
186
|
+
reason,
|
|
187
|
+
plan.skipped.filter((skip) => skip.reason === reason).length,
|
|
188
|
+
]));
|
|
189
|
+
if (!opts.yes) {
|
|
190
|
+
emit.data({
|
|
191
|
+
data_dir: dir,
|
|
192
|
+
dry_run: true,
|
|
193
|
+
planned,
|
|
194
|
+
skipped,
|
|
195
|
+
skipped_by_reason: skippedByReason,
|
|
196
|
+
hint: plan.actions.length > 0 ? "re-run with --yes to archive" : "nothing to archive",
|
|
197
|
+
});
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
const result = applyTidy(plan);
|
|
201
|
+
emit.data({
|
|
202
|
+
data_dir: dir,
|
|
203
|
+
dry_run: false,
|
|
204
|
+
archived: result.archived,
|
|
205
|
+
already_archived: result.alreadyArchived,
|
|
206
|
+
planned,
|
|
207
|
+
skipped,
|
|
208
|
+
skipped_by_reason: skippedByReason,
|
|
209
|
+
hint: result.archived > 0
|
|
210
|
+
? "fully quit the Claude desktop app (tray icon, not the X) and relaunch to refresh the sidebar"
|
|
211
|
+
: "nothing to archive",
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
});
|
|
144
215
|
}
|
|
145
216
|
function collect(value, previous) {
|
|
146
217
|
return previous.concat([value]);
|
|
@@ -12,14 +12,16 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import type { Command } from "commander";
|
|
14
14
|
import type { EmitContext } from "../commander.js";
|
|
15
|
+
import { type CodexHookAuthorizationResult } from "../core/hooks/codex-authorization.js";
|
|
15
16
|
type Severity = "ok" | "warn" | "fail";
|
|
16
|
-
interface Check {
|
|
17
|
+
export interface Check {
|
|
17
18
|
name: string;
|
|
18
19
|
severity: Severity;
|
|
19
20
|
detail: string;
|
|
20
21
|
hint?: string;
|
|
21
22
|
}
|
|
22
23
|
export declare function registerDoctorCommand(program: Command, emit: EmitContext): void;
|
|
23
|
-
export declare function runChecks(): Check[]
|
|
24
|
+
export declare function runChecks(): Promise<Check[]>;
|
|
25
|
+
export declare function codexAuthorizationCheck(result: CodexHookAuthorizationResult): Check;
|
|
24
26
|
export {};
|
|
25
27
|
//# sourceMappingURL=doctor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAKnD,OAAO,EACL,KAAK,4BAA4B,EAElC,MAAM,sCAAsC,CAAC;AAe9C,KAAK,QAAQ,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;AAEvC,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAOD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAmD/E;AAED,wBAAsB,SAAS,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC,CAyBlD;AA4CD,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,4BAA4B,GAAG,KAAK,CAoBnF"}
|
package/dist/commands/doctor.js
CHANGED
|
@@ -18,7 +18,8 @@ import { BUILTIN_ADAPTER_IDS } from "../core/adapters/index.js";
|
|
|
18
18
|
import { resolveBinName, ripgrepAutoInstall } from "../core/config.js";
|
|
19
19
|
import { ADAPTER_SPECS } from "../core/hooks/adapter/events.js";
|
|
20
20
|
import { loadAdapterWiring, summarizeAdapterWiring } from "../core/hooks/adapter/wiring.js";
|
|
21
|
-
import {
|
|
21
|
+
import { probeCodexHookAuthorization, } from "../core/hooks/codex-authorization.js";
|
|
22
|
+
import { inspectCodexWslBridge, } from "../core/hooks/codex-wsl-bridge.js";
|
|
22
23
|
import { ADAPTER_BINARIES, ADAPTER_INSTALL_HINTS, ADAPTER_LOGIN_HINTS, } from "../core/workflow/adapters.js";
|
|
23
24
|
import { probeBilling } from "../core/workflow/billing.js";
|
|
24
25
|
import { checkGitHooks, gitHooksInstalled } from "../lib/instructions/git-hooks.js";
|
|
@@ -39,7 +40,7 @@ export function registerDoctorCommand(program, emit) {
|
|
|
39
40
|
emit.text(`ripgrep install failed: ${err.message}\n`);
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
|
-
const checks = runChecks();
|
|
43
|
+
const checks = await runChecks();
|
|
43
44
|
const requiredFailed = checks.some((c) => c.severity === "fail");
|
|
44
45
|
if (opts.json) {
|
|
45
46
|
emit.data({
|
|
@@ -67,13 +68,15 @@ export function registerDoctorCommand(program, emit) {
|
|
|
67
68
|
emit.setExitCode(requiredFailed ? 1 : 0);
|
|
68
69
|
});
|
|
69
70
|
}
|
|
70
|
-
export function runChecks() {
|
|
71
|
-
const
|
|
71
|
+
export async function runChecks() {
|
|
72
|
+
const codexWslStatus = inspectCodexWslBridge();
|
|
73
|
+
const codexWslBridge = checkCodexWslBridge(codexWslStatus);
|
|
72
74
|
// Probe the adapter CLIs before the hook check runs, not in output order:
|
|
73
75
|
// an unwired adapter only matters if its CLI is actually installed, and
|
|
74
76
|
// probing once here keeps that join to one spawn per CLI.
|
|
75
77
|
const workflow = BUILTIN_ADAPTER_IDS.map((id) => ({ id, ...checkWorkflowAdapter(id) }));
|
|
76
78
|
const installed = workflow.filter((w) => w.installed).map((w) => w.id);
|
|
79
|
+
const codexAuthorization = await checkCodexHookAuthorization(installed, codexWslStatus);
|
|
77
80
|
return [
|
|
78
81
|
checkNode(),
|
|
79
82
|
checkGit(),
|
|
@@ -81,6 +84,7 @@ export function runChecks() {
|
|
|
81
84
|
checkRipgrep(),
|
|
82
85
|
checkHarneryDir(),
|
|
83
86
|
checkAdapterHooks(installed),
|
|
87
|
+
...(codexAuthorization ? [codexAuthorization] : []),
|
|
84
88
|
checkGitHookRegions(),
|
|
85
89
|
...(codexWslBridge ? [codexWslBridge] : []),
|
|
86
90
|
...workflow.map((w) => w.check),
|
|
@@ -119,8 +123,7 @@ function checkGitHookRegions() {
|
|
|
119
123
|
};
|
|
120
124
|
}
|
|
121
125
|
}
|
|
122
|
-
function checkCodexWslBridge() {
|
|
123
|
-
const status = inspectCodexWslBridge();
|
|
126
|
+
function checkCodexWslBridge(status) {
|
|
124
127
|
if (!status)
|
|
125
128
|
return null;
|
|
126
129
|
return {
|
|
@@ -132,6 +135,42 @@ function checkCodexWslBridge() {
|
|
|
132
135
|
: "forward CODEX_THREAD_ID through WSLENV in the Codex shell environment policy, then start a fresh task",
|
|
133
136
|
};
|
|
134
137
|
}
|
|
138
|
+
export function codexAuthorizationCheck(result) {
|
|
139
|
+
const reviewHint = "terminal UI: run `/hooks`; Codex Desktop: open Settings > Hooks; after approval, start a fresh task";
|
|
140
|
+
if (result.status === "runnable") {
|
|
141
|
+
return { name: "codex:hook authorization", severity: "ok", detail: result.detail };
|
|
142
|
+
}
|
|
143
|
+
if (result.status === "review_required" || result.status === "disabled") {
|
|
144
|
+
return {
|
|
145
|
+
name: "codex:hook authorization",
|
|
146
|
+
severity: "warn",
|
|
147
|
+
detail: result.detail,
|
|
148
|
+
hint: reviewHint,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
name: "codex:hook authorization",
|
|
153
|
+
severity: "warn",
|
|
154
|
+
detail: `authorization unverified: ${result.detail}`,
|
|
155
|
+
hint: reviewHint,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
async function checkCodexHookAuthorization(installedAdapters, codexWslStatus) {
|
|
159
|
+
const root = findCoordProjectRoot();
|
|
160
|
+
if (!root || !summarizeAdapterWiring(root).wired.includes("codex"))
|
|
161
|
+
return null;
|
|
162
|
+
if (codexWslStatus) {
|
|
163
|
+
return {
|
|
164
|
+
name: "codex:hook authorization",
|
|
165
|
+
severity: "warn",
|
|
166
|
+
detail: "unverified from WSL; the Windows-native Codex runtime owns this workspace's trust state",
|
|
167
|
+
hint: "run the host's Windows bridge doctor, or open Codex Desktop Settings > Hooks; start a fresh task after approval",
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
if (!installedAdapters.includes("codex"))
|
|
171
|
+
return null;
|
|
172
|
+
return codexAuthorizationCheck(await probeCodexHookAuthorization({ cwd: root }));
|
|
173
|
+
}
|
|
135
174
|
function checkRipgrep() {
|
|
136
175
|
const found = findRg();
|
|
137
176
|
if (found) {
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ import type { EmitContext } from "../commander.js";
|
|
|
20
20
|
import { type AdapterId, type AdapterSpec } from "../core/hooks/adapter/events.js";
|
|
21
21
|
import { type SettingsFile } from "../core/hooks/adapter/wiring.js";
|
|
22
22
|
export declare function registerInitCommand(program: Command, emit: EmitContext, binName?: string): void;
|
|
23
|
+
export declare function codexHookReviewAction(adapter: AdapterId): string | null;
|
|
23
24
|
/**
|
|
24
25
|
* Merge agent-hook entries into a adapter settings object in place, idempotently.
|
|
25
26
|
* Preserves every existing hook; rewrites events already wired to
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD,OAAO,EAAiB,KAAK,SAAS,EAAE,KAAK,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAClG,OAAO,EAKL,KAAK,YAAY,EAClB,MAAM,iCAAiC,CAAC;AAmBzC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD,OAAO,EAAiB,KAAK,SAAS,EAAE,KAAK,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAClG,OAAO,EAKL,KAAK,YAAY,EAClB,MAAM,iCAAiC,CAAC;AAmBzC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAyJ/F;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,SAAS,GAAG,MAAM,GAAG,IAAI,CAOvE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,YAAY,EACtB,IAAI,EAAE,WAAW,EACjB,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,SAAS,GACjB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAqCvE;AAyDD;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,YAAY,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAmB1F;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAmDhG;AASD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAkCxF"}
|