sisyphi 1.2.1 → 1.2.11
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 +20 -20
- package/dist/cli.js +12461 -11237
- package/dist/cli.js.map +1 -1
- package/dist/daemon.js +1112 -564
- package/dist/daemon.js.map +1 -1
- package/dist/templates/agent-plugin/agents/CLAUDE.md +2 -2
- package/dist/templates/agent-plugin/agents/implementor.md +3 -2
- package/dist/templates/agent-plugin/agents/operator.md +3 -4
- package/dist/templates/agent-plugin/agents/plan.md +1 -1
- package/dist/templates/agent-plugin/agents/problem.md +20 -20
- package/dist/templates/agent-plugin/agents/research-lead.md +1 -1
- package/dist/templates/agent-plugin/agents/spec/engineer.md +9 -7
- package/dist/templates/agent-plugin/agents/spec/requirements-writer.md +1 -1
- package/dist/templates/agent-plugin/agents/spec.md +31 -25
- package/dist/templates/agent-plugin/hooks/CLAUDE.md +0 -1
- package/dist/templates/agent-plugin/hooks/ask-background-guard.sh +11 -11
- package/dist/templates/agent-plugin/hooks/intercept-send-message.sh +1 -1
- package/dist/templates/agent-plugin/hooks/operator-user-prompt.sh +2 -2
- package/dist/templates/agent-plugin/hooks/plan-validate.sh +3 -3
- package/dist/templates/agent-plugin/hooks/require-submit.sh +1 -1
- package/dist/templates/agent-plugin/skills/operator/SKILL.md +1 -1
- package/dist/templates/agent-suffix.md +4 -18
- package/dist/templates/companion-plugin/hooks/user-prompt-context.sh +1 -1
- package/dist/templates/dashboard-claude.md +15 -13
- package/dist/templates/orchestrator-base.md +44 -78
- package/dist/templates/orchestrator-completion.md +9 -11
- package/dist/templates/orchestrator-discovery.md +8 -8
- package/dist/templates/orchestrator-impl.md +6 -7
- package/dist/templates/orchestrator-planning.md +2 -2
- package/dist/templates/orchestrator-plugin/commands/sisyphus/scratch.md +1 -1
- package/dist/templates/orchestrator-plugin/commands/sisyphus/strategize.md +2 -2
- package/dist/templates/orchestrator-validation.md +1 -3
- package/dist/templates/termrender-haiku-system.md +5 -3
- package/dist/tui.js +1817 -1400
- package/dist/tui.js.map +1 -1
- package/native/build-notify.sh +2 -2
- package/package.json +3 -3
- package/templates/agent-plugin/agents/CLAUDE.md +2 -2
- package/templates/agent-plugin/agents/implementor.md +3 -2
- package/templates/agent-plugin/agents/operator.md +3 -4
- package/templates/agent-plugin/agents/plan.md +1 -1
- package/templates/agent-plugin/agents/problem.md +20 -20
- package/templates/agent-plugin/agents/research-lead.md +1 -1
- package/templates/agent-plugin/agents/spec/engineer.md +9 -7
- package/templates/agent-plugin/agents/spec/requirements-writer.md +1 -1
- package/templates/agent-plugin/agents/spec.md +31 -25
- package/templates/agent-plugin/hooks/CLAUDE.md +0 -1
- package/templates/agent-plugin/hooks/ask-background-guard.sh +11 -11
- package/templates/agent-plugin/hooks/intercept-send-message.sh +1 -1
- package/templates/agent-plugin/hooks/operator-user-prompt.sh +2 -2
- package/templates/agent-plugin/hooks/plan-validate.sh +3 -3
- package/templates/agent-plugin/hooks/require-submit.sh +1 -1
- package/templates/agent-plugin/skills/operator/SKILL.md +1 -1
- package/templates/agent-suffix.md +4 -18
- package/templates/companion-plugin/hooks/user-prompt-context.sh +1 -1
- package/templates/dashboard-claude.md +15 -13
- package/templates/orchestrator-base.md +44 -78
- package/templates/orchestrator-completion.md +9 -11
- package/templates/orchestrator-discovery.md +8 -8
- package/templates/orchestrator-impl.md +6 -7
- package/templates/orchestrator-planning.md +2 -2
- package/templates/orchestrator-plugin/commands/sisyphus/scratch.md +1 -1
- package/templates/orchestrator-plugin/commands/sisyphus/strategize.md +2 -2
- package/templates/orchestrator-validation.md +1 -3
- package/templates/termrender-haiku-system.md +5 -3
- package/dist/templates/agent-plugin/skills/humanloop/SKILL.md +0 -148
- package/dist/templates/agent-plugin/skills/operator-memory/SKILL.md +0 -64
- package/dist/templates/agent-plugin/skills/perspective-fanout/SKILL.md +0 -115
- package/dist/templates/agent-plugin/skills/problem-document/SKILL.md +0 -105
- package/dist/templates/agent-plugin/skills/problem-plateau-breakers/SKILL.md +0 -83
- package/dist/templates/orchestrator-plugin/skills/humanloop/SKILL.md +0 -150
- package/dist/templates/orchestrator-plugin/skills/orchestration/CLAUDE.md +0 -1
- package/dist/templates/orchestrator-plugin/skills/orchestration/SKILL.md +0 -29
- package/dist/templates/orchestrator-plugin/skills/orchestration/strategy.md +0 -160
- package/dist/templates/orchestrator-plugin/skills/orchestration/task-patterns.md +0 -266
- package/dist/templates/orchestrator-plugin/skills/orchestration/workflow-examples.md +0 -428
- package/templates/agent-plugin/skills/humanloop/SKILL.md +0 -148
- package/templates/agent-plugin/skills/operator-memory/SKILL.md +0 -64
- package/templates/agent-plugin/skills/perspective-fanout/SKILL.md +0 -115
- package/templates/agent-plugin/skills/problem-document/SKILL.md +0 -105
- package/templates/agent-plugin/skills/problem-plateau-breakers/SKILL.md +0 -83
- package/templates/orchestrator-plugin/skills/humanloop/SKILL.md +0 -150
- package/templates/orchestrator-plugin/skills/orchestration/CLAUDE.md +0 -1
- package/templates/orchestrator-plugin/skills/orchestration/SKILL.md +0 -29
- package/templates/orchestrator-plugin/skills/orchestration/strategy.md +0 -160
- package/templates/orchestrator-plugin/skills/orchestration/task-patterns.md +0 -266
- package/templates/orchestrator-plugin/skills/orchestration/workflow-examples.md +0 -428
package/dist/tui.js
CHANGED
|
@@ -220,7 +220,7 @@ var init_terminal = __esm({
|
|
|
220
220
|
|
|
221
221
|
// src/shared/paths.ts
|
|
222
222
|
import { homedir } from "os";
|
|
223
|
-
import { basename, join } from "path";
|
|
223
|
+
import { basename as basename2, join } from "path";
|
|
224
224
|
function globalDir() {
|
|
225
225
|
return join(homedir(), ".sisyphus");
|
|
226
226
|
}
|
|
@@ -276,16 +276,22 @@ function askMetaPath(cwd2, sessionId2, askId2) {
|
|
|
276
276
|
return join(askEntryDir(cwd2, sessionId2, askId2), "meta.json");
|
|
277
277
|
}
|
|
278
278
|
function askDecisionsPath(cwd2, sessionId2, askId2) {
|
|
279
|
-
return join(askEntryDir(cwd2, sessionId2, askId2), "
|
|
279
|
+
return join(askEntryDir(cwd2, sessionId2, askId2), "deck.json");
|
|
280
280
|
}
|
|
281
281
|
function askOutputPath(cwd2, sessionId2, askId2) {
|
|
282
|
-
return join(askEntryDir(cwd2, sessionId2, askId2), "
|
|
282
|
+
return join(askEntryDir(cwd2, sessionId2, askId2), "response.json");
|
|
283
283
|
}
|
|
284
284
|
function askProgressPath(cwd2, sessionId2, askId2) {
|
|
285
285
|
return join(askEntryDir(cwd2, sessionId2, askId2), "progress.json");
|
|
286
286
|
}
|
|
287
|
+
function askReviewPath(cwd2, sessionId2, askId2) {
|
|
288
|
+
return join(askEntryDir(cwd2, sessionId2, askId2), "review.json");
|
|
289
|
+
}
|
|
290
|
+
function askReviewDraftPath(cwd2, sessionId2, askId2) {
|
|
291
|
+
return join(askEntryDir(cwd2, sessionId2, askId2), "draft.json");
|
|
292
|
+
}
|
|
287
293
|
function tmuxSessionName(cwd2, sessionLabel) {
|
|
288
|
-
return `ssyph_${
|
|
294
|
+
return `ssyph_${basename2(cwd2)}_${sessionLabel}`;
|
|
289
295
|
}
|
|
290
296
|
function companionPath() {
|
|
291
297
|
return join(globalDir(), "companion.json");
|
|
@@ -676,11 +682,21 @@ var init_render = __esm({
|
|
|
676
682
|
}
|
|
677
683
|
});
|
|
678
684
|
|
|
685
|
+
// src/shared/shell.ts
|
|
686
|
+
function shellQuote(s) {
|
|
687
|
+
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
688
|
+
}
|
|
689
|
+
var init_shell = __esm({
|
|
690
|
+
"src/shared/shell.ts"() {
|
|
691
|
+
"use strict";
|
|
692
|
+
}
|
|
693
|
+
});
|
|
694
|
+
|
|
679
695
|
// src/shared/config.ts
|
|
680
|
-
import { readFileSync as
|
|
696
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
681
697
|
function readJsonFile(filePath) {
|
|
682
698
|
try {
|
|
683
|
-
const content =
|
|
699
|
+
const content = readFileSync9(filePath, "utf-8");
|
|
684
700
|
return JSON.parse(content);
|
|
685
701
|
} catch {
|
|
686
702
|
return {};
|
|
@@ -723,6 +739,7 @@ var init_config = __esm({
|
|
|
723
739
|
DEFAULT_CONFIG = {
|
|
724
740
|
model: "claude-opus-4-7[1m]",
|
|
725
741
|
pollIntervalMs: 5e3,
|
|
742
|
+
statusBarRenderTicks: 4,
|
|
726
743
|
orchestratorEffort: "xhigh",
|
|
727
744
|
agentEffort: "medium",
|
|
728
745
|
notifications: {
|
|
@@ -732,15 +749,22 @@ var init_config = __esm({
|
|
|
732
749
|
companionPopup: true,
|
|
733
750
|
requiredPlugins: [
|
|
734
751
|
{ name: "devcore", marketplace: "crouton-kit", owner: "crouton-labs" }
|
|
752
|
+
],
|
|
753
|
+
requiredCrtrPlugins: [
|
|
754
|
+
{
|
|
755
|
+
marketplace: "sisyphus",
|
|
756
|
+
plugin: "sisyphus",
|
|
757
|
+
gitUrl: "https://github.com/crouton-labs/sisyphus"
|
|
758
|
+
}
|
|
735
759
|
]
|
|
736
760
|
};
|
|
737
761
|
}
|
|
738
762
|
});
|
|
739
763
|
|
|
740
764
|
// src/daemon/history.ts
|
|
741
|
-
import { appendFileSync, mkdirSync as
|
|
765
|
+
import { appendFileSync, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5, renameSync as renameSync3, readdirSync as readdirSync4, readFileSync as readFileSync10, rmSync as rmSync3, statSync as statSync2 } from "fs";
|
|
742
766
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
743
|
-
import { dirname as
|
|
767
|
+
import { dirname as dirname4, join as join8 } from "path";
|
|
744
768
|
var init_history = __esm({
|
|
745
769
|
"src/daemon/history.ts"() {
|
|
746
770
|
"use strict";
|
|
@@ -750,12 +774,12 @@ var init_history = __esm({
|
|
|
750
774
|
|
|
751
775
|
// src/daemon/lib/atomic.ts
|
|
752
776
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
753
|
-
import { dirname as
|
|
754
|
-
import { renameSync as renameSync4, writeFileSync as
|
|
777
|
+
import { dirname as dirname5, join as join9 } from "path";
|
|
778
|
+
import { renameSync as renameSync4, writeFileSync as writeFileSync6 } from "fs";
|
|
755
779
|
function atomicWrite(filePath, data) {
|
|
756
|
-
const dir =
|
|
757
|
-
const tmpPath =
|
|
758
|
-
|
|
780
|
+
const dir = dirname5(filePath);
|
|
781
|
+
const tmpPath = join9(dir, `.atomic.${randomUUID4()}.tmp`);
|
|
782
|
+
writeFileSync6(tmpPath, data, "utf-8");
|
|
759
783
|
renameSync4(tmpPath, filePath);
|
|
760
784
|
}
|
|
761
785
|
async function withLock(key, fn) {
|
|
@@ -784,8 +808,8 @@ var init_atomic = __esm({
|
|
|
784
808
|
});
|
|
785
809
|
|
|
786
810
|
// src/shared/gitignore.ts
|
|
787
|
-
import { existsSync as
|
|
788
|
-
import { join as
|
|
811
|
+
import { existsSync as existsSync6, readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "fs";
|
|
812
|
+
import { join as join10 } from "path";
|
|
789
813
|
var init_gitignore = __esm({
|
|
790
814
|
"src/shared/gitignore.ts"() {
|
|
791
815
|
"use strict";
|
|
@@ -800,8 +824,8 @@ var init_types = __esm({
|
|
|
800
824
|
});
|
|
801
825
|
|
|
802
826
|
// src/daemon/state.ts
|
|
803
|
-
import { copyFileSync, cpSync, existsSync as
|
|
804
|
-
import { join as
|
|
827
|
+
import { copyFileSync, cpSync as cpSync2, existsSync as existsSync7, mkdirSync as mkdirSync6, readFileSync as readFileSync12, readdirSync as readdirSync5, rmSync as rmSync4, statSync as statSync3, watch as fsWatch, writeFileSync as writeFileSync8 } from "fs";
|
|
828
|
+
import { join as join11 } from "path";
|
|
805
829
|
var init_state = __esm({
|
|
806
830
|
"src/daemon/state.ts"() {
|
|
807
831
|
"use strict";
|
|
@@ -812,20 +836,10 @@ var init_state = __esm({
|
|
|
812
836
|
}
|
|
813
837
|
});
|
|
814
838
|
|
|
815
|
-
// src/shared/shell.ts
|
|
816
|
-
function shellQuote(s) {
|
|
817
|
-
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
818
|
-
}
|
|
819
|
-
var init_shell = __esm({
|
|
820
|
-
"src/shared/shell.ts"() {
|
|
821
|
-
"use strict";
|
|
822
|
-
}
|
|
823
|
-
});
|
|
824
|
-
|
|
825
839
|
// src/daemon/notify.ts
|
|
826
840
|
import { spawn, execFile as execFile2 } from "child_process";
|
|
827
|
-
import { writeFileSync as
|
|
828
|
-
import { join as
|
|
841
|
+
import { writeFileSync as writeFileSync9, mkdirSync as mkdirSync7, existsSync as existsSync8 } from "fs";
|
|
842
|
+
import { join as join12 } from "path";
|
|
829
843
|
import { homedir as homedir3 } from "os";
|
|
830
844
|
var TMUX_SOCKET, SWITCH_SCRIPT;
|
|
831
845
|
var init_notify = __esm({
|
|
@@ -878,11 +892,11 @@ var init_notify = __esm({
|
|
|
878
892
|
});
|
|
879
893
|
|
|
880
894
|
// src/daemon/ask-store.ts
|
|
881
|
-
import { existsSync as
|
|
895
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync8, readFileSync as readFileSync13, readdirSync as readdirSync6 } from "fs";
|
|
882
896
|
function readDecisions(cwd2, sessionId2, askId2) {
|
|
883
897
|
const p = askDecisionsPath(cwd2, sessionId2, askId2);
|
|
884
898
|
try {
|
|
885
|
-
return JSON.parse(
|
|
899
|
+
return JSON.parse(readFileSync13(p, { encoding: "utf-8" }));
|
|
886
900
|
} catch (_e) {
|
|
887
901
|
return null;
|
|
888
902
|
}
|
|
@@ -890,7 +904,7 @@ function readDecisions(cwd2, sessionId2, askId2) {
|
|
|
890
904
|
function readProgress(cwd2, sessionId2, askId2) {
|
|
891
905
|
const p = askProgressPath(cwd2, sessionId2, askId2);
|
|
892
906
|
try {
|
|
893
|
-
const data = JSON.parse(
|
|
907
|
+
const data = JSON.parse(readFileSync13(p, { encoding: "utf-8" }));
|
|
894
908
|
if (!Array.isArray(data["responses"])) return null;
|
|
895
909
|
return { responses: data["responses"], savedAt: data["savedAt"] };
|
|
896
910
|
} catch (_e) {
|
|
@@ -905,20 +919,45 @@ function writeOutput(cwd2, sessionId2, askId2, responses, completedAt) {
|
|
|
905
919
|
}
|
|
906
920
|
function readMeta(cwd2, sessionId2, askId2) {
|
|
907
921
|
const p = askMetaPath(cwd2, sessionId2, askId2);
|
|
908
|
-
if (!
|
|
922
|
+
if (!existsSync9(p)) {
|
|
909
923
|
return null;
|
|
910
924
|
}
|
|
911
|
-
return JSON.parse(
|
|
925
|
+
return JSON.parse(readFileSync13(p, "utf-8"));
|
|
912
926
|
}
|
|
913
927
|
async function updateMeta(cwd2, sessionId2, askId2, patch) {
|
|
914
|
-
|
|
928
|
+
const next = await withLock(askId2, () => {
|
|
915
929
|
const cur = readMeta(cwd2, sessionId2, askId2);
|
|
916
930
|
if (!cur) {
|
|
917
931
|
throw new Error(`updateMeta: askId ${askId2} not found`);
|
|
918
932
|
}
|
|
919
|
-
const
|
|
920
|
-
atomicWrite(askMetaPath(cwd2, sessionId2, askId2), JSON.stringify(
|
|
921
|
-
return
|
|
933
|
+
const updated = { ...cur, ...patch };
|
|
934
|
+
atomicWrite(askMetaPath(cwd2, sessionId2, askId2), JSON.stringify(updated, null, 2));
|
|
935
|
+
return updated;
|
|
936
|
+
});
|
|
937
|
+
if (patch.status === "answered" && next.heartbeatAskId) {
|
|
938
|
+
const hbAskId = next.heartbeatAskId;
|
|
939
|
+
cascadeResolveHeartbeatAsk(cwd2, sessionId2, hbAskId).catch((err) => {
|
|
940
|
+
console.warn(
|
|
941
|
+
`[sisyphus] heartbeat cascade-resolve failed for ${hbAskId}:`,
|
|
942
|
+
err instanceof Error ? err.message : err
|
|
943
|
+
);
|
|
944
|
+
});
|
|
945
|
+
}
|
|
946
|
+
return next;
|
|
947
|
+
}
|
|
948
|
+
async function cascadeResolveHeartbeatAsk(cwd2, sessionId2, heartbeatAskId) {
|
|
949
|
+
const hbMeta = readMeta(cwd2, sessionId2, heartbeatAskId);
|
|
950
|
+
if (!hbMeta) return;
|
|
951
|
+
if (hbMeta.status === "answered") return;
|
|
952
|
+
if (existsSync9(askOutputPath(cwd2, sessionId2, heartbeatAskId))) return;
|
|
953
|
+
writeOutput(cwd2, sessionId2, heartbeatAskId, [{
|
|
954
|
+
id: "heartbeat",
|
|
955
|
+
selectedOptionId: "ack",
|
|
956
|
+
freetext: "auto-resolved: original ask was answered"
|
|
957
|
+
}]);
|
|
958
|
+
await updateMeta(cwd2, sessionId2, heartbeatAskId, {
|
|
959
|
+
status: "answered",
|
|
960
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
922
961
|
});
|
|
923
962
|
}
|
|
924
963
|
var init_ask_store = __esm({
|
|
@@ -939,7 +978,7 @@ var single_ask_exports = {};
|
|
|
939
978
|
__export(single_ask_exports, {
|
|
940
979
|
runSingleAsk: () => runSingleAsk
|
|
941
980
|
});
|
|
942
|
-
import { existsSync as existsSync12, watchFile, unwatchFile } from "fs";
|
|
981
|
+
import { existsSync as existsSync12, watchFile as watchFile2, unwatchFile as unwatchFile2 } from "fs";
|
|
943
982
|
import { mountPanel as mountPanel2 } from "@crouton-kit/humanloop";
|
|
944
983
|
async function runSingleAsk(opts) {
|
|
945
984
|
const { cwd: cwd2, sessionId: sessionId2, askId: askId2 } = opts;
|
|
@@ -956,6 +995,8 @@ async function runSingleAsk(opts) {
|
|
|
956
995
|
let stopKeypress = null;
|
|
957
996
|
let stopResize = null;
|
|
958
997
|
const outputPath = askOutputPath(cwd2, sessionId2, askId2);
|
|
998
|
+
const decisionsPath = askDecisionsPath(cwd2, sessionId2, askId2);
|
|
999
|
+
let lastDeckJson = JSON.stringify(deck);
|
|
959
1000
|
const exit = (code) => {
|
|
960
1001
|
if (exiting) return;
|
|
961
1002
|
exiting = true;
|
|
@@ -972,7 +1013,11 @@ async function runSingleAsk(opts) {
|
|
|
972
1013
|
} catch {
|
|
973
1014
|
}
|
|
974
1015
|
try {
|
|
975
|
-
|
|
1016
|
+
unwatchFile2(outputPath, onExternalChange);
|
|
1017
|
+
} catch {
|
|
1018
|
+
}
|
|
1019
|
+
try {
|
|
1020
|
+
unwatchFile2(decisionsPath, onDeckChange);
|
|
976
1021
|
} catch {
|
|
977
1022
|
}
|
|
978
1023
|
cleanupTerminal();
|
|
@@ -988,6 +1033,16 @@ async function runSingleAsk(opts) {
|
|
|
988
1033
|
if (!existsSync12(outputPath)) return;
|
|
989
1034
|
exit(0);
|
|
990
1035
|
};
|
|
1036
|
+
const onDeckChange = () => {
|
|
1037
|
+
if (exiting || !panel) return;
|
|
1038
|
+
const next = readDecisions(cwd2, sessionId2, askId2);
|
|
1039
|
+
if (!next) return;
|
|
1040
|
+
const nextJson = JSON.stringify(next);
|
|
1041
|
+
if (nextJson === lastDeckJson) return;
|
|
1042
|
+
lastDeckJson = nextJson;
|
|
1043
|
+
panel.loadDeck(next, { progressPath: askProgressPath(cwd2, sessionId2, askId2) });
|
|
1044
|
+
flushHost(panel.render());
|
|
1045
|
+
};
|
|
991
1046
|
let lastResponses = [];
|
|
992
1047
|
const submit = (responses) => {
|
|
993
1048
|
if (exiting) return;
|
|
@@ -1038,7 +1093,8 @@ async function runSingleAsk(opts) {
|
|
|
1038
1093
|
prevFrame2 = [];
|
|
1039
1094
|
flushHost(panel.render());
|
|
1040
1095
|
});
|
|
1041
|
-
|
|
1096
|
+
watchFile2(outputPath, { interval: 250 }, onExternalChange);
|
|
1097
|
+
watchFile2(decisionsPath, { interval: 500 }, onDeckChange);
|
|
1042
1098
|
if (existsSync12(outputPath)) {
|
|
1043
1099
|
exit(0);
|
|
1044
1100
|
return;
|
|
@@ -1201,7 +1257,9 @@ function createAppState(cwd2) {
|
|
|
1201
1257
|
flowExpanded: false,
|
|
1202
1258
|
resolutionActive: false,
|
|
1203
1259
|
resolutionHandle: null,
|
|
1260
|
+
inlineDeck: null,
|
|
1204
1261
|
visuals: /* @__PURE__ */ new Map(),
|
|
1262
|
+
reviewPanel: null,
|
|
1205
1263
|
cwd: cwd2
|
|
1206
1264
|
};
|
|
1207
1265
|
}
|
|
@@ -1265,12 +1323,28 @@ function autoExpandCycle(state2) {
|
|
|
1265
1323
|
}
|
|
1266
1324
|
|
|
1267
1325
|
// src/tui/app.ts
|
|
1268
|
-
import { readFileSync as
|
|
1326
|
+
import { readFileSync as readFileSync17, existsSync as existsSync11, readdirSync as readdirSync7, statSync as statSync4 } from "fs";
|
|
1269
1327
|
import { join as join15 } from "path";
|
|
1270
1328
|
|
|
1329
|
+
// src/shared/inbox-types.ts
|
|
1330
|
+
import { basename, dirname } from "path";
|
|
1331
|
+
function coerceKind(k) {
|
|
1332
|
+
if (k !== void 0) return k;
|
|
1333
|
+
return "validation";
|
|
1334
|
+
}
|
|
1335
|
+
function sessionIdFromDir(dir) {
|
|
1336
|
+
return basename(dirname(dirname(dirname(dir))));
|
|
1337
|
+
}
|
|
1338
|
+
function cwdFromDir(dir) {
|
|
1339
|
+
return dirname(dirname(dirname(dirname(dirname(dirname(dir))))));
|
|
1340
|
+
}
|
|
1341
|
+
function askIdFromDir(dir) {
|
|
1342
|
+
return basename(dir);
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1271
1345
|
// src/tui/input.ts
|
|
1272
|
-
import { readFileSync as
|
|
1273
|
-
import { join as
|
|
1346
|
+
import { readFileSync as readFileSync5, readdirSync as readdirSync2, statSync } from "fs";
|
|
1347
|
+
import { join as join6 } from "path";
|
|
1274
1348
|
|
|
1275
1349
|
// src/shared/session-export.ts
|
|
1276
1350
|
init_paths();
|
|
@@ -1530,9 +1604,14 @@ function statusColor(status) {
|
|
|
1530
1604
|
return "white";
|
|
1531
1605
|
}
|
|
1532
1606
|
}
|
|
1533
|
-
|
|
1607
|
+
function colorEnabled() {
|
|
1608
|
+
if (process.env["FORCE_COLOR"] === "1") return true;
|
|
1609
|
+
if (process.env["NO_COLOR"] !== void 0) return false;
|
|
1610
|
+
if (process.env["TERM"] === "dumb") return false;
|
|
1611
|
+
return process.stdout.isTTY === true;
|
|
1612
|
+
}
|
|
1534
1613
|
function wrap(open, close = "\x1B[0m") {
|
|
1535
|
-
return (s) =>
|
|
1614
|
+
return (s) => colorEnabled() ? `${open}${s}${close}` : s;
|
|
1536
1615
|
}
|
|
1537
1616
|
var bold = wrap("\x1B[1m");
|
|
1538
1617
|
var dim = wrap("\x1B[2m");
|
|
@@ -1647,6 +1726,28 @@ function agentTypeColor(agentType) {
|
|
|
1647
1726
|
if (t.includes("plan")) return "yellow";
|
|
1648
1727
|
return void 0;
|
|
1649
1728
|
}
|
|
1729
|
+
var KIND_ICON = {
|
|
1730
|
+
notify: "\u2709",
|
|
1731
|
+
validation: "\u2713",
|
|
1732
|
+
decision: "\u25C6",
|
|
1733
|
+
context: "\u270E",
|
|
1734
|
+
error: "\u26A0",
|
|
1735
|
+
review: "\u25C8"
|
|
1736
|
+
};
|
|
1737
|
+
var KIND_COLOR = {
|
|
1738
|
+
notify: "gray",
|
|
1739
|
+
validation: "cyan",
|
|
1740
|
+
decision: "cyan",
|
|
1741
|
+
context: "cyan",
|
|
1742
|
+
error: "red",
|
|
1743
|
+
review: "magenta"
|
|
1744
|
+
};
|
|
1745
|
+
function kindIcon(kind) {
|
|
1746
|
+
return kind && kind in KIND_ICON ? KIND_ICON[kind] : "\xB7";
|
|
1747
|
+
}
|
|
1748
|
+
function kindColor(kind) {
|
|
1749
|
+
return kind && kind in KIND_COLOR ? KIND_COLOR[kind] : "cyan";
|
|
1750
|
+
}
|
|
1650
1751
|
function divider(width, char = "\u2500") {
|
|
1651
1752
|
return char.repeat(Math.max(0, width));
|
|
1652
1753
|
}
|
|
@@ -1765,9 +1866,10 @@ function buildTree(sessions, selectedSession, expanded, cwd2, polledContextFiles
|
|
|
1765
1866
|
const nodes = [];
|
|
1766
1867
|
const inboxBySession = /* @__PURE__ */ new Map();
|
|
1767
1868
|
for (const item of aggregateInbox) {
|
|
1768
|
-
const
|
|
1869
|
+
const sessionId2 = sessionIdFromDir(item.dir);
|
|
1870
|
+
const arr = inboxBySession.get(sessionId2) ?? [];
|
|
1769
1871
|
arr.push(item);
|
|
1770
|
-
inboxBySession.set(
|
|
1872
|
+
inboxBySession.set(sessionId2, arr);
|
|
1771
1873
|
}
|
|
1772
1874
|
const needsYou = [];
|
|
1773
1875
|
const running = [];
|
|
@@ -2300,7 +2402,7 @@ function applyColor(result, fields, facePart, mood, opts) {
|
|
|
2300
2402
|
init_paths();
|
|
2301
2403
|
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync4, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
2302
2404
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2303
|
-
import { dirname as
|
|
2405
|
+
import { dirname as dirname3, join as join5 } from "path";
|
|
2304
2406
|
|
|
2305
2407
|
// src/shared/companion-normalize.ts
|
|
2306
2408
|
function emptyStats() {
|
|
@@ -2425,7 +2527,7 @@ var ACHIEVEMENTS = [
|
|
|
2425
2527
|
// src/daemon/companion-memory.ts
|
|
2426
2528
|
init_paths();
|
|
2427
2529
|
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, renameSync, writeFileSync as writeFileSync2 } from "fs";
|
|
2428
|
-
import { dirname, join as join4 } from "path";
|
|
2530
|
+
import { dirname as dirname2, join as join4 } from "path";
|
|
2429
2531
|
import { randomUUID } from "crypto";
|
|
2430
2532
|
import { z } from "zod";
|
|
2431
2533
|
|
|
@@ -2469,10 +2571,12 @@ function augmentedPath() {
|
|
|
2469
2571
|
return prepend.length > 0 ? `${prepend.join(":")}:${basePath}` : basePath;
|
|
2470
2572
|
}
|
|
2471
2573
|
function execEnv() {
|
|
2472
|
-
|
|
2574
|
+
const env = {
|
|
2473
2575
|
...process.env,
|
|
2474
2576
|
PATH: augmentedPath()
|
|
2475
2577
|
};
|
|
2578
|
+
if (!env["LC_ALL"] && !env["LANG"]) env["LANG"] = "en_US.UTF-8";
|
|
2579
|
+
return env;
|
|
2476
2580
|
}
|
|
2477
2581
|
|
|
2478
2582
|
// src/daemon/haiku.ts
|
|
@@ -3740,433 +3844,151 @@ function renderCompanionDebugOverlay(buf, rows, cols, companion) {
|
|
|
3740
3844
|
}
|
|
3741
3845
|
}
|
|
3742
3846
|
|
|
3743
|
-
// src/
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3847
|
+
// src/shared/keymap.ts
|
|
3848
|
+
var MENU_FOR_MODE = {
|
|
3849
|
+
"leader": "topLevel",
|
|
3850
|
+
"copy-menu": "copy",
|
|
3851
|
+
"open-menu": "open",
|
|
3852
|
+
"agent-menu": "agent",
|
|
3853
|
+
"session-menu": "session",
|
|
3854
|
+
"go-menu": "go",
|
|
3855
|
+
"companion-menu": "companion"
|
|
3856
|
+
};
|
|
3857
|
+
var KEYMAP = {
|
|
3858
|
+
version: 1,
|
|
3859
|
+
topLevel: {
|
|
3860
|
+
title: " Sisyphus ",
|
|
3861
|
+
items: [
|
|
3862
|
+
{ key: "s", label: " Cycle session", action: { type: "script", name: "sisyphus-cycle" } },
|
|
3863
|
+
{ key: "h", label: " Home / dashboard", action: { type: "script", name: "sisyphus-home" } },
|
|
3864
|
+
{ key: "n", label: " New session", action: { type: "popup", name: "sisyphus-new", popup: { w: "80%", h: "60%", cwd: "current" } } },
|
|
3865
|
+
{ key: "m", label: " Message orchestrator", action: { type: "popup", name: "sisyphus-msg", popup: { w: "80%", h: "60%", cwd: "current" } } },
|
|
3866
|
+
{ key: "t", label: " Status (where am I?)", action: { type: "popup", name: "sisyphus-status-popup", popup: { w: "90%", h: "90%", cwd: "current" } } },
|
|
3867
|
+
{ key: "l", label: " Session picker", action: { type: "popup", name: "sisyphus-pick-session", popup: { w: "60%", h: "60%", cwd: "current" } } },
|
|
3868
|
+
{ key: "z", label: " Zoom pane", action: { type: "tmux", cmd: "resize-pane -Z" } },
|
|
3869
|
+
{ key: "x", label: " Kill pane (smart)", action: { type: "script", name: "sisyphus-kill-pane" } },
|
|
3870
|
+
{ key: "?", label: " Full help reference", action: { type: "popup", name: "sisyphus-help", popup: { w: "80", h: "32", title: " Keybindings " } } },
|
|
3871
|
+
{ key: "/", label: " Search / filter", action: { type: "script", name: "sisyphus-search-reports" }, tuiAction: "search" },
|
|
3872
|
+
{ key: " ", label: " Open popup explicitly", action: { type: "tui", action: "show-leader" } },
|
|
3873
|
+
{ key: "y", label: " Yank \u203A", action: { type: "submenu", ref: "copy" } },
|
|
3874
|
+
{ key: "c", label: " Side claude pane", action: { type: "script", name: "sisyphus-companion-pane" }, tuiAction: "companion-pane" },
|
|
3875
|
+
{ key: "C", label: " Companion (gamification) \u203A", action: { type: "submenu", ref: "companion" } },
|
|
3876
|
+
{ key: "o", label: " Open \u203A", action: { type: "submenu", ref: "open" } },
|
|
3877
|
+
{ key: "a", label: " Agent \u203A", action: { type: "submenu", ref: "agent" } },
|
|
3878
|
+
{ key: "S", label: " Session \u203A", action: { type: "submenu", ref: "session" } },
|
|
3879
|
+
{ key: "g", label: " Go \u203A", action: { type: "submenu", ref: "go" } }
|
|
3880
|
+
]
|
|
3881
|
+
},
|
|
3882
|
+
submenus: {
|
|
3883
|
+
companion: {
|
|
3884
|
+
title: " Companion ",
|
|
3885
|
+
items: [
|
|
3886
|
+
{ key: "p", label: " profile (overlay)", action: { type: "tui", action: "companion-overlay" } },
|
|
3887
|
+
{ key: "d", label: " debug (mood signals)", action: { type: "tui", action: "companion-debug" } }
|
|
3888
|
+
]
|
|
3889
|
+
},
|
|
3890
|
+
copy: {
|
|
3891
|
+
title: " Copy ",
|
|
3892
|
+
items: [
|
|
3893
|
+
{ key: "p", label: " session dir path", action: { type: "script", name: "sisyphus-copy-path" } },
|
|
3894
|
+
{ key: "i", label: " session UUID", action: { type: "script", name: "sisyphus-copy-id" } },
|
|
3895
|
+
{ key: "c", label: " full session context XML", action: { type: "script", name: "sisyphus-copy-context" } },
|
|
3896
|
+
{ key: "l", label: " logs (last 200 lines)", action: { type: "script", name: "sisyphus-copy-logs" } },
|
|
3897
|
+
{ key: "r", label: " latest report content", action: { type: "script", name: "sisyphus-copy-latest-report" } },
|
|
3898
|
+
{ key: "a", label: " agent ID (picker)", action: { type: "script", name: "sisyphus-copy-agent-id" } }
|
|
3899
|
+
]
|
|
3900
|
+
},
|
|
3901
|
+
open: {
|
|
3902
|
+
title: " Open ",
|
|
3903
|
+
items: [
|
|
3904
|
+
{ key: "g", label: " goal.md", action: { type: "popup", name: "sisyphus-open-goal", popup: { w: "95%", h: "95%", cwd: "current" } } },
|
|
3905
|
+
{ key: "r", label: " roadmap.md", action: { type: "popup", name: "sisyphus-open-roadmap", popup: { w: "95%", h: "95%", cwd: "current" } } },
|
|
3906
|
+
{ key: "s", label: " strategy.md", action: { type: "popup", name: "sisyphus-open-strategy", popup: { w: "95%", h: "95%", cwd: "current" } } },
|
|
3907
|
+
{ key: "l", label: " logs popup (tail)", action: { type: "popup", name: "sisyphus-open-logs", popup: { w: "90%", h: "90%", cwd: "current" } } },
|
|
3908
|
+
{ key: "d", label: " session dir in file mgr", action: { type: "script", name: "sisyphus-open-dir" } },
|
|
3909
|
+
{ key: "R", label: " latest report file", action: { type: "popup", name: "sisyphus-open-latest-report", popup: { w: "95%", h: "95%", cwd: "current" } } },
|
|
3910
|
+
{ key: "c", label: " scratch", action: { type: "popup", name: "sisyphus-open-scratch", popup: { w: "95%", h: "95%", cwd: "current" } } },
|
|
3911
|
+
{ key: "e", label: " edit context file", action: { type: "popup", name: "sisyphus-edit-context-file", popup: { w: "95%", h: "95%", cwd: "current" } }, tuiAction: "edit-context-file" }
|
|
3912
|
+
]
|
|
3913
|
+
},
|
|
3914
|
+
agent: {
|
|
3915
|
+
title: " Agent ",
|
|
3916
|
+
items: [
|
|
3917
|
+
{ key: "s", label: " spawn agent", action: { type: "popup", name: "sisyphus-spawn-agent", popup: { w: "80%", h: "70%", cwd: "current" } } },
|
|
3918
|
+
{ key: "m", label: " message agent (picker)", action: { type: "popup", name: "sisyphus-msg-agent", popup: { w: "80%", h: "60%", cwd: "current" } } },
|
|
3919
|
+
{ key: "r", label: " restart agent (picker)", action: { type: "popup", name: "sisyphus-restart-agent-popup", popup: { w: "70%", h: "50%", cwd: "current" } } },
|
|
3920
|
+
{ key: "R", label: " re-run agent (picker)", action: { type: "popup", name: "sisyphus-rerun-agent", popup: { w: "70%", h: "50%", cwd: "current" } } },
|
|
3921
|
+
{ key: "j", label: " jump to agent's pane", action: { type: "popup", name: "sisyphus-jump-to-pane", popup: { w: "60%", h: "60%" } } },
|
|
3922
|
+
{ key: "o", label: " open claude --resume", action: { type: "popup", name: "sisyphus-open-claude-agent", popup: { w: "60%", h: "60%", cwd: "current" } } },
|
|
3923
|
+
{ key: "t", label: " tail agent logs (picker)", action: { type: "popup", name: "sisyphus-tail-agent-logs", popup: { w: "90%", h: "90%", cwd: "current" } } },
|
|
3924
|
+
{ key: "k", label: " kill agent (picker)", action: { type: "popup", name: "sisyphus-kill-agent", popup: { w: "60%", h: "40%", cwd: "current" } } },
|
|
3925
|
+
{ key: "e", label: " quick-spawn Explore", action: { type: "script", name: "sisyphus-quick-spawn-explore" } },
|
|
3926
|
+
{ key: "d", label: " quick-spawn Debug", action: { type: "script", name: "sisyphus-quick-spawn-debug" } }
|
|
3927
|
+
]
|
|
3928
|
+
},
|
|
3929
|
+
session: {
|
|
3930
|
+
title: " Session ",
|
|
3931
|
+
items: [
|
|
3932
|
+
{ key: "n", label: " new session", action: { type: "popup", name: "sisyphus-new", popup: { w: "80%", h: "60%", cwd: "current" } } },
|
|
3933
|
+
{ key: "r", label: " resume", action: { type: "popup", name: "sisyphus-resume-session", popup: { w: "80%", h: "60%", cwd: "current" } } },
|
|
3934
|
+
{ key: "c", label: " continue", action: { type: "popup", name: "sisyphus-continue-session", popup: { w: "50", h: "5", borderStyle: "fg=yellow", title: " Continue Session ", cwd: "current" } } },
|
|
3935
|
+
{ key: "b", label: " rollback (prompts cycle)", action: { type: "popup", name: "sisyphus-rollback-session", popup: { w: "50", h: "5", title: " Rollback ", cwd: "current" } } },
|
|
3936
|
+
{ key: "k", label: " kill", action: { type: "popup", name: "sisyphus-kill-session", popup: { w: "40", h: "5", borderStyle: "fg=red", title: " Kill Session ", cwd: "current" } } },
|
|
3937
|
+
{ key: "d", label: " delete (confirms)", action: { type: "popup", name: "sisyphus-delete-session", popup: { w: "40", h: "5", borderStyle: "fg=red", title: " Delete Session ", cwd: "current" } } },
|
|
3938
|
+
{ key: "e", label: " export to ~/Downloads", action: { type: "popup", name: "sisyphus-export-session", popup: { w: "60", h: "8", title: " Export Session ", cwd: "current" } } },
|
|
3939
|
+
{ key: "w", label: " go to session window", action: { type: "popup", name: "sisyphus-go-to-window", popup: { w: "70%", h: "60%", cwd: "current" } } },
|
|
3940
|
+
{ key: "C", label: " clone (sisyphus session clone)", action: { type: "popup", name: "sisyphus-clone-session", popup: { w: "60%", h: "60%", cwd: "current" } } },
|
|
3941
|
+
{ key: "i", label: " history", action: { type: "popup", name: "sisyphus-history", popup: { w: "95%", h: "95%", cwd: "current" } } }
|
|
3942
|
+
]
|
|
3943
|
+
},
|
|
3944
|
+
go: {
|
|
3945
|
+
title: " Go ",
|
|
3946
|
+
items: [
|
|
3947
|
+
{ key: "w", label: " go to session window", action: { type: "popup", name: "sisyphus-go-to-window", popup: { w: "70%", h: "60%", cwd: "current" } } },
|
|
3948
|
+
{ key: "p", label: " jump to pane (picker)", action: { type: "popup", name: "sisyphus-jump-to-pane", popup: { w: "60%", h: "60%" } } },
|
|
3949
|
+
{ key: "s", label: " session picker", action: { type: "popup", name: "sisyphus-pick-session", popup: { w: "60%", h: "60%", cwd: "current" } } },
|
|
3950
|
+
{ key: "n", label: " next session", action: { type: "script", name: "sisyphus-cycle" } },
|
|
3951
|
+
{ key: "r", label: " reconnect", action: { type: "popup", name: "sisyphus-reconnect", popup: { w: "80%", h: "40%", cwd: "current" } } }
|
|
3952
|
+
]
|
|
3953
|
+
}
|
|
3954
|
+
}
|
|
3955
|
+
};
|
|
3748
3956
|
|
|
3749
|
-
// src/
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
socket.destroy();
|
|
3957
|
+
// src/tui/input.ts
|
|
3958
|
+
function dispatchComposeAction(action, content, state2, actions) {
|
|
3959
|
+
switch (action.kind) {
|
|
3960
|
+
case "new-session":
|
|
3961
|
+
actions.sendAndNotify(
|
|
3962
|
+
{ type: "start", task: content, cwd: state2.cwd },
|
|
3963
|
+
"Session created"
|
|
3964
|
+
);
|
|
3965
|
+
break;
|
|
3966
|
+
case "message-orchestrator":
|
|
3967
|
+
actions.sendAndNotify(
|
|
3968
|
+
{ type: "message", sessionId: action.sessionId, content },
|
|
3969
|
+
"Message queued"
|
|
3970
|
+
);
|
|
3971
|
+
break;
|
|
3972
|
+
case "resume":
|
|
3973
|
+
actions.sendAndNotify(
|
|
3974
|
+
{ type: "resume", sessionId: action.sessionId, cwd: state2.cwd, message: content || void 0 },
|
|
3975
|
+
"Session resumed"
|
|
3976
|
+
);
|
|
3977
|
+
break;
|
|
3978
|
+
case "continue":
|
|
3979
|
+
void (async () => {
|
|
3773
3980
|
try {
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
}
|
|
3786
|
-
|
|
3787
|
-
// src/tui/panels/mounted-humanloop.ts
|
|
3788
|
-
async function dispatchOrphanResolution(orphanTarget, selectedOptionId, deps) {
|
|
3789
|
-
if (selectedOptionId === "takeover" && orphanTarget.kind === "agent") {
|
|
3790
|
-
await deps.onOrphanTakeover?.({
|
|
3791
|
-
sessionId: deps.sessionId,
|
|
3792
|
-
agentId: orphanTarget.agentId,
|
|
3793
|
-
paneId: orphanTarget.paneId
|
|
3794
|
-
});
|
|
3795
|
-
} else if (selectedOptionId === "restart" && orphanTarget.kind === "agent") {
|
|
3796
|
-
await deps.daemonSend({ type: "restart-agent", sessionId: deps.sessionId, agentId: orphanTarget.agentId });
|
|
3797
|
-
} else if (selectedOptionId === "resume" && orphanTarget.kind === "orchestrator") {
|
|
3798
|
-
await deps.daemonSend({ type: "resume", sessionId: deps.sessionId, cwd: deps.cwd });
|
|
3799
|
-
} else if (selectedOptionId === "dismiss" && orphanTarget.kind === "orchestrator") {
|
|
3800
|
-
await deps.daemonSend({ type: "clear-orphan", sessionId: deps.sessionId, cwd: deps.cwd });
|
|
3801
|
-
}
|
|
3802
|
-
}
|
|
3803
|
-
function mountResolutionPanel(opts, state2) {
|
|
3804
|
-
let queue = [...opts.aggregateInbox];
|
|
3805
|
-
let currentIndex = opts.startIndex;
|
|
3806
|
-
let bodyCols = opts.cols;
|
|
3807
|
-
const item = () => queue[currentIndex];
|
|
3808
|
-
function buildDeck(idx) {
|
|
3809
|
-
const it = queue[idx];
|
|
3810
|
-
if (!it) return null;
|
|
3811
|
-
const deck = readDecisions(it.cwd, it.sessionId, it.askId);
|
|
3812
|
-
if (!deck) return null;
|
|
3813
|
-
deck.source = {
|
|
3814
|
-
sessionName: it.sessionName,
|
|
3815
|
-
askedBy: it.askedBy,
|
|
3816
|
-
blockedSince: it.blockedSince
|
|
3817
|
-
};
|
|
3818
|
-
return deck;
|
|
3819
|
-
}
|
|
3820
|
-
const initialDeck = buildDeck(currentIndex);
|
|
3821
|
-
if (!initialDeck) return null;
|
|
3822
|
-
let currentDeck = initialDeck;
|
|
3823
|
-
const initialProgress = readProgress(item().cwd, item().sessionId, item().askId);
|
|
3824
|
-
let answeredCount = initialProgress?.responses.length ?? 0;
|
|
3825
|
-
function getCurrentQid() {
|
|
3826
|
-
return currentDeck.interactions[answeredCount]?.id ?? currentDeck.interactions[0]?.id;
|
|
3827
|
-
}
|
|
3828
|
-
function fireVisualGen(force) {
|
|
3829
|
-
const qid = getCurrentQid();
|
|
3830
|
-
if (!qid) return;
|
|
3831
|
-
const it = item();
|
|
3832
|
-
state2.visuals.set(qid, { status: "loading", content: "", visible: true });
|
|
3833
|
-
requestRender();
|
|
3834
|
-
void (async () => {
|
|
3835
|
-
const res = await rawSend({
|
|
3836
|
-
type: "ask-generate-visual",
|
|
3837
|
-
sessionId: it.sessionId,
|
|
3838
|
-
askId: it.askId,
|
|
3839
|
-
qid,
|
|
3840
|
-
cols: bodyCols,
|
|
3841
|
-
force
|
|
3842
|
-
}, 6e4);
|
|
3843
|
-
if (res.ok) {
|
|
3844
|
-
const ansiPath = res.data.ansiPath;
|
|
3845
|
-
const ansi = readFileSync10(ansiPath, "utf-8");
|
|
3846
|
-
state2.visuals.set(qid, { status: "ready", content: ansi, visible: true });
|
|
3847
|
-
} else {
|
|
3848
|
-
state2.visuals.set(qid, { status: "error", content: "", visible: true, error: res.error });
|
|
3849
|
-
}
|
|
3850
|
-
requestRender();
|
|
3851
|
-
})();
|
|
3852
|
-
}
|
|
3853
|
-
let lastResponses = [];
|
|
3854
|
-
const submitResponses = (responses) => {
|
|
3855
|
-
void (async () => {
|
|
3856
|
-
const it = item();
|
|
3857
|
-
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3858
|
-
const meta = readMeta(it.cwd, it.sessionId, it.askId);
|
|
3859
|
-
if (meta?.orphanTarget && responses.length > 0) {
|
|
3860
|
-
const sel = responses[0].selectedOptionId;
|
|
3861
|
-
if (sel) {
|
|
3862
|
-
await dispatchOrphanResolution(meta.orphanTarget, sel, {
|
|
3863
|
-
daemonSend: opts.daemonSend,
|
|
3864
|
-
onOrphanTakeover: opts.onOrphanTakeover,
|
|
3865
|
-
sessionId: it.sessionId,
|
|
3866
|
-
cwd: it.cwd
|
|
3867
|
-
});
|
|
3868
|
-
}
|
|
3869
|
-
}
|
|
3870
|
-
writeOutput(it.cwd, it.sessionId, it.askId, responses, completedAt);
|
|
3871
|
-
await updateMeta(it.cwd, it.sessionId, it.askId, { status: "answered", completedAt });
|
|
3872
|
-
const refreshRes = await opts.daemonSend({ type: "inbox-list" });
|
|
3873
|
-
const newQueue = refreshRes.ok ? refreshRes.data?.items ?? [] : [];
|
|
3874
|
-
if (newQueue.length === 0) {
|
|
3875
|
-
opts.onUnmount();
|
|
3876
|
-
return;
|
|
3877
|
-
}
|
|
3878
|
-
queue = newQueue;
|
|
3879
|
-
currentIndex = 0;
|
|
3880
|
-
const nextItem = queue[0];
|
|
3881
|
-
const nextDeck = buildDeck(0);
|
|
3882
|
-
if (!nextDeck) {
|
|
3883
|
-
opts.onUnmount();
|
|
3884
|
-
return;
|
|
3885
|
-
}
|
|
3886
|
-
currentDeck = nextDeck;
|
|
3887
|
-
const nextProgress = readProgress(nextItem.cwd, nextItem.sessionId, nextItem.askId);
|
|
3888
|
-
answeredCount = nextProgress?.responses.length ?? 0;
|
|
3889
|
-
lastResponses = [];
|
|
3890
|
-
state2.visuals.clear();
|
|
3891
|
-
panel.loadDeck(nextDeck, {
|
|
3892
|
-
progressPath: askProgressPath(nextItem.cwd, nextItem.sessionId, nextItem.askId)
|
|
3893
|
-
});
|
|
3894
|
-
requestRender();
|
|
3895
|
-
})();
|
|
3896
|
-
};
|
|
3897
|
-
const panel = mountPanel({
|
|
3898
|
-
deck: initialDeck,
|
|
3899
|
-
cols: opts.cols,
|
|
3900
|
-
rows: opts.rows,
|
|
3901
|
-
progressPath: askProgressPath(item().cwd, item().sessionId, item().askId),
|
|
3902
|
-
onProgress: (responses) => {
|
|
3903
|
-
answeredCount = responses.length;
|
|
3904
|
-
lastResponses = responses;
|
|
3905
|
-
requestRender();
|
|
3906
|
-
const it = item();
|
|
3907
|
-
const cur = readMeta(it.cwd, it.sessionId, it.askId);
|
|
3908
|
-
if (cur?.status === "pending") {
|
|
3909
|
-
void updateMeta(it.cwd, it.sessionId, it.askId, { status: "in-progress", startedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
3910
|
-
}
|
|
3911
|
-
},
|
|
3912
|
-
onComplete: (responses) => {
|
|
3913
|
-
submitResponses(responses);
|
|
3914
|
-
},
|
|
3915
|
-
// Final-phase Enter on an incomplete deck routes here (humanloop only fires
|
|
3916
|
-
// onComplete when responses === interactions). The 'final' UI prompts
|
|
3917
|
-
// "enter submit", so honor that by submitting whatever was answered.
|
|
3918
|
-
onExit: () => {
|
|
3919
|
-
submitResponses(lastResponses);
|
|
3920
|
-
}
|
|
3921
|
-
});
|
|
3922
|
-
return {
|
|
3923
|
-
handleKey(input, key) {
|
|
3924
|
-
panel.handleKey(input, key);
|
|
3925
|
-
},
|
|
3926
|
-
render() {
|
|
3927
|
-
return panel.render();
|
|
3928
|
-
},
|
|
3929
|
-
handleResize(cols, rows) {
|
|
3930
|
-
bodyCols = cols;
|
|
3931
|
-
panel.handleResize(cols, rows);
|
|
3932
|
-
},
|
|
3933
|
-
unmount() {
|
|
3934
|
-
panel.unmount();
|
|
3935
|
-
opts.onUnmount();
|
|
3936
|
-
},
|
|
3937
|
-
canAcceptHostKeys() {
|
|
3938
|
-
return panel.canAcceptHostKeys();
|
|
3939
|
-
},
|
|
3940
|
-
advanceQueue(delta) {
|
|
3941
|
-
const newIndex = Math.max(0, Math.min(queue.length - 1, currentIndex + delta));
|
|
3942
|
-
if (newIndex === currentIndex) return;
|
|
3943
|
-
currentIndex = newIndex;
|
|
3944
|
-
const nextDeck = buildDeck(currentIndex);
|
|
3945
|
-
if (!nextDeck) return;
|
|
3946
|
-
currentDeck = nextDeck;
|
|
3947
|
-
const it = item();
|
|
3948
|
-
const progress = readProgress(it.cwd, it.sessionId, it.askId);
|
|
3949
|
-
answeredCount = progress?.responses.length ?? 0;
|
|
3950
|
-
panel.loadDeck(nextDeck, {
|
|
3951
|
-
progressPath: askProgressPath(it.cwd, it.sessionId, it.askId)
|
|
3952
|
-
});
|
|
3953
|
-
requestRender();
|
|
3954
|
-
},
|
|
3955
|
-
spaceVisualToggle() {
|
|
3956
|
-
const qid = getCurrentQid();
|
|
3957
|
-
if (!qid) return;
|
|
3958
|
-
const entry = state2.visuals.get(qid);
|
|
3959
|
-
if (!entry) {
|
|
3960
|
-
fireVisualGen(false);
|
|
3961
|
-
} else if (entry.status === "ready") {
|
|
3962
|
-
state2.visuals.set(qid, { ...entry, visible: !entry.visible });
|
|
3963
|
-
requestRender();
|
|
3964
|
-
} else if (entry.status === "loading") {
|
|
3965
|
-
} else {
|
|
3966
|
-
fireVisualGen(false);
|
|
3967
|
-
}
|
|
3968
|
-
},
|
|
3969
|
-
regenerateVisual() {
|
|
3970
|
-
const qid = getCurrentQid();
|
|
3971
|
-
if (!qid) return;
|
|
3972
|
-
state2.visuals.set(qid, { status: "loading", content: "", visible: true });
|
|
3973
|
-
requestRender();
|
|
3974
|
-
fireVisualGen(true);
|
|
3975
|
-
},
|
|
3976
|
-
getHeaderInfo() {
|
|
3977
|
-
const it = item();
|
|
3978
|
-
const askTitle = currentDeck.title ?? currentDeck.interactions[0]?.title;
|
|
3979
|
-
return {
|
|
3980
|
-
currentIndex,
|
|
3981
|
-
queueLength: queue.length,
|
|
3982
|
-
sessionName: it.sessionName,
|
|
3983
|
-
askTitle: askTitle ? askTitle.slice(0, 32) : void 0,
|
|
3984
|
-
blockedSince: it.blockedSince,
|
|
3985
|
-
kind: it.kind
|
|
3986
|
-
};
|
|
3987
|
-
},
|
|
3988
|
-
getCurrentQid
|
|
3989
|
-
};
|
|
3990
|
-
}
|
|
3991
|
-
function enterResolutionMode(state2, askId2, daemonSend, onOrphanTakeover) {
|
|
3992
|
-
const queue = state2.aggregateInbox;
|
|
3993
|
-
const startIdx = queue.findIndex((item) => item.askId === askId2);
|
|
3994
|
-
if (startIdx < 0) {
|
|
3995
|
-
if (queue.length === 0) return;
|
|
3996
|
-
enterResolutionMode(state2, queue[0].askId, daemonSend, onOrphanTakeover);
|
|
3997
|
-
return;
|
|
3998
|
-
}
|
|
3999
|
-
const handle = mountResolutionPanel(
|
|
4000
|
-
{
|
|
4001
|
-
aggregateInbox: queue,
|
|
4002
|
-
startIndex: startIdx,
|
|
4003
|
-
cols: state2.cols,
|
|
4004
|
-
rows: state2.rows - 1,
|
|
4005
|
-
daemonSend,
|
|
4006
|
-
onUnmount: () => {
|
|
4007
|
-
state2.resolutionActive = false;
|
|
4008
|
-
state2.resolutionHandle = null;
|
|
4009
|
-
state2.visuals.clear();
|
|
4010
|
-
requestRender();
|
|
4011
|
-
},
|
|
4012
|
-
onOrphanTakeover
|
|
4013
|
-
},
|
|
4014
|
-
state2
|
|
4015
|
-
);
|
|
4016
|
-
if (!handle) {
|
|
4017
|
-
requestRender();
|
|
4018
|
-
return;
|
|
4019
|
-
}
|
|
4020
|
-
state2.resolutionHandle = handle;
|
|
4021
|
-
state2.resolutionActive = true;
|
|
4022
|
-
requestRender();
|
|
4023
|
-
}
|
|
4024
|
-
|
|
4025
|
-
// src/shared/keymap.ts
|
|
4026
|
-
var MENU_FOR_MODE = {
|
|
4027
|
-
"leader": "topLevel",
|
|
4028
|
-
"copy-menu": "copy",
|
|
4029
|
-
"open-menu": "open",
|
|
4030
|
-
"agent-menu": "agent",
|
|
4031
|
-
"session-menu": "session",
|
|
4032
|
-
"go-menu": "go",
|
|
4033
|
-
"companion-menu": "companion"
|
|
4034
|
-
};
|
|
4035
|
-
var KEYMAP = {
|
|
4036
|
-
version: 1,
|
|
4037
|
-
topLevel: {
|
|
4038
|
-
title: " Sisyphus ",
|
|
4039
|
-
items: [
|
|
4040
|
-
{ key: "s", label: " Cycle session", action: { type: "script", name: "sisyphus-cycle" } },
|
|
4041
|
-
{ key: "h", label: " Home / dashboard", action: { type: "script", name: "sisyphus-home" } },
|
|
4042
|
-
{ key: "n", label: " New session", action: { type: "popup", name: "sisyphus-new", popup: { w: "80%", h: "60%", cwd: "current" } } },
|
|
4043
|
-
{ key: "m", label: " Message orchestrator", action: { type: "popup", name: "sisyphus-msg", popup: { w: "80%", h: "60%", cwd: "current" } } },
|
|
4044
|
-
{ key: "t", label: " Status (where am I?)", action: { type: "popup", name: "sisyphus-status-popup", popup: { w: "90%", h: "90%", cwd: "current" } } },
|
|
4045
|
-
{ key: "l", label: " Session picker", action: { type: "popup", name: "sisyphus-pick-session", popup: { w: "60%", h: "60%", cwd: "current" } } },
|
|
4046
|
-
{ key: "z", label: " Zoom pane", action: { type: "tmux", cmd: "resize-pane -Z" } },
|
|
4047
|
-
{ key: "x", label: " Kill pane (smart)", action: { type: "script", name: "sisyphus-kill-pane" } },
|
|
4048
|
-
{ key: "?", label: " Full help reference", action: { type: "popup", name: "sisyphus-help", popup: { w: "80", h: "32", title: " Keybindings " } } },
|
|
4049
|
-
{ key: "/", label: " Search / filter", action: { type: "script", name: "sisyphus-search-reports" }, tuiAction: "search" },
|
|
4050
|
-
{ key: " ", label: " Open popup explicitly", action: { type: "tui", action: "show-leader" } },
|
|
4051
|
-
{ key: "y", label: " Yank \u203A", action: { type: "submenu", ref: "copy" } },
|
|
4052
|
-
{ key: "c", label: " Side claude pane", action: { type: "script", name: "sisyphus-companion-pane" }, tuiAction: "companion-pane" },
|
|
4053
|
-
{ key: "C", label: " Companion (gamification) \u203A", action: { type: "submenu", ref: "companion" } },
|
|
4054
|
-
{ key: "o", label: " Open \u203A", action: { type: "submenu", ref: "open" } },
|
|
4055
|
-
{ key: "a", label: " Agent \u203A", action: { type: "submenu", ref: "agent" } },
|
|
4056
|
-
{ key: "S", label: " Session \u203A", action: { type: "submenu", ref: "session" } },
|
|
4057
|
-
{ key: "g", label: " Go \u203A", action: { type: "submenu", ref: "go" } }
|
|
4058
|
-
]
|
|
4059
|
-
},
|
|
4060
|
-
submenus: {
|
|
4061
|
-
companion: {
|
|
4062
|
-
title: " Companion ",
|
|
4063
|
-
items: [
|
|
4064
|
-
{ key: "p", label: " profile (overlay)", action: { type: "tui", action: "companion-overlay" } },
|
|
4065
|
-
{ key: "d", label: " debug (mood signals)", action: { type: "tui", action: "companion-debug" } }
|
|
4066
|
-
]
|
|
4067
|
-
},
|
|
4068
|
-
copy: {
|
|
4069
|
-
title: " Copy ",
|
|
4070
|
-
items: [
|
|
4071
|
-
{ key: "p", label: " session dir path", action: { type: "script", name: "sisyphus-copy-path" } },
|
|
4072
|
-
{ key: "i", label: " session UUID", action: { type: "script", name: "sisyphus-copy-id" } },
|
|
4073
|
-
{ key: "c", label: " full session context XML", action: { type: "script", name: "sisyphus-copy-context" } },
|
|
4074
|
-
{ key: "l", label: " logs (last 200 lines)", action: { type: "script", name: "sisyphus-copy-logs" } },
|
|
4075
|
-
{ key: "r", label: " latest report content", action: { type: "script", name: "sisyphus-copy-latest-report" } },
|
|
4076
|
-
{ key: "a", label: " agent ID (picker)", action: { type: "script", name: "sisyphus-copy-agent-id" } }
|
|
4077
|
-
]
|
|
4078
|
-
},
|
|
4079
|
-
open: {
|
|
4080
|
-
title: " Open ",
|
|
4081
|
-
items: [
|
|
4082
|
-
{ key: "g", label: " goal.md", action: { type: "popup", name: "sisyphus-open-goal", popup: { w: "95%", h: "95%", cwd: "current" } } },
|
|
4083
|
-
{ key: "r", label: " roadmap.md", action: { type: "popup", name: "sisyphus-open-roadmap", popup: { w: "95%", h: "95%", cwd: "current" } } },
|
|
4084
|
-
{ key: "s", label: " strategy.md", action: { type: "popup", name: "sisyphus-open-strategy", popup: { w: "95%", h: "95%", cwd: "current" } } },
|
|
4085
|
-
{ key: "l", label: " logs popup (tail)", action: { type: "popup", name: "sisyphus-open-logs", popup: { w: "90%", h: "90%", cwd: "current" } } },
|
|
4086
|
-
{ key: "d", label: " session dir in file mgr", action: { type: "script", name: "sisyphus-open-dir" } },
|
|
4087
|
-
{ key: "R", label: " latest report file", action: { type: "popup", name: "sisyphus-open-latest-report", popup: { w: "95%", h: "95%", cwd: "current" } } },
|
|
4088
|
-
{ key: "c", label: " scratch", action: { type: "popup", name: "sisyphus-open-scratch", popup: { w: "95%", h: "95%", cwd: "current" } } },
|
|
4089
|
-
{ key: "e", label: " edit context file", action: { type: "popup", name: "sisyphus-edit-context-file", popup: { w: "95%", h: "95%", cwd: "current" } }, tuiAction: "edit-context-file" }
|
|
4090
|
-
]
|
|
4091
|
-
},
|
|
4092
|
-
agent: {
|
|
4093
|
-
title: " Agent ",
|
|
4094
|
-
items: [
|
|
4095
|
-
{ key: "s", label: " spawn agent", action: { type: "popup", name: "sisyphus-spawn-agent", popup: { w: "80%", h: "70%", cwd: "current" } } },
|
|
4096
|
-
{ key: "m", label: " message agent (picker)", action: { type: "popup", name: "sisyphus-msg-agent", popup: { w: "80%", h: "60%", cwd: "current" } } },
|
|
4097
|
-
{ key: "r", label: " restart agent (picker)", action: { type: "popup", name: "sisyphus-restart-agent-popup", popup: { w: "70%", h: "50%", cwd: "current" } } },
|
|
4098
|
-
{ key: "R", label: " re-run agent (picker)", action: { type: "popup", name: "sisyphus-rerun-agent", popup: { w: "70%", h: "50%", cwd: "current" } } },
|
|
4099
|
-
{ key: "j", label: " jump to agent's pane", action: { type: "popup", name: "sisyphus-jump-to-pane", popup: { w: "60%", h: "60%" } } },
|
|
4100
|
-
{ key: "o", label: " open claude --resume", action: { type: "popup", name: "sisyphus-open-claude-agent", popup: { w: "60%", h: "60%", cwd: "current" } } },
|
|
4101
|
-
{ key: "t", label: " tail agent logs (picker)", action: { type: "popup", name: "sisyphus-tail-agent-logs", popup: { w: "90%", h: "90%", cwd: "current" } } },
|
|
4102
|
-
{ key: "k", label: " kill agent (picker)", action: { type: "popup", name: "sisyphus-kill-agent", popup: { w: "60%", h: "40%", cwd: "current" } } },
|
|
4103
|
-
{ key: "e", label: " quick-spawn Explore", action: { type: "script", name: "sisyphus-quick-spawn-explore" } },
|
|
4104
|
-
{ key: "d", label: " quick-spawn Debug", action: { type: "script", name: "sisyphus-quick-spawn-debug" } }
|
|
4105
|
-
]
|
|
4106
|
-
},
|
|
4107
|
-
session: {
|
|
4108
|
-
title: " Session ",
|
|
4109
|
-
items: [
|
|
4110
|
-
{ key: "n", label: " new session", action: { type: "popup", name: "sisyphus-new", popup: { w: "80%", h: "60%", cwd: "current" } } },
|
|
4111
|
-
{ key: "r", label: " resume", action: { type: "popup", name: "sisyphus-resume-session", popup: { w: "80%", h: "60%", cwd: "current" } } },
|
|
4112
|
-
{ key: "c", label: " continue", action: { type: "popup", name: "sisyphus-continue-session", popup: { w: "50", h: "5", borderStyle: "fg=yellow", title: " Continue Session ", cwd: "current" } } },
|
|
4113
|
-
{ key: "b", label: " rollback (prompts cycle)", action: { type: "popup", name: "sisyphus-rollback-session", popup: { w: "50", h: "5", title: " Rollback ", cwd: "current" } } },
|
|
4114
|
-
{ key: "k", label: " kill", action: { type: "popup", name: "sisyphus-kill-session", popup: { w: "40", h: "5", borderStyle: "fg=red", title: " Kill Session ", cwd: "current" } } },
|
|
4115
|
-
{ key: "d", label: " delete (confirms)", action: { type: "popup", name: "sisyphus-delete-session", popup: { w: "40", h: "5", borderStyle: "fg=red", title: " Delete Session ", cwd: "current" } } },
|
|
4116
|
-
{ key: "e", label: " export to ~/Downloads", action: { type: "popup", name: "sisyphus-export-session", popup: { w: "60", h: "8", title: " Export Session ", cwd: "current" } } },
|
|
4117
|
-
{ key: "w", label: " go to session window", action: { type: "popup", name: "sisyphus-go-to-window", popup: { w: "70%", h: "60%", cwd: "current" } } },
|
|
4118
|
-
{ key: "C", label: " clone (sisyphus session clone)", action: { type: "popup", name: "sisyphus-clone-session", popup: { w: "60%", h: "60%", cwd: "current" } } },
|
|
4119
|
-
{ key: "i", label: " history", action: { type: "popup", name: "sisyphus-history", popup: { w: "95%", h: "95%", cwd: "current" } } }
|
|
4120
|
-
]
|
|
4121
|
-
},
|
|
4122
|
-
go: {
|
|
4123
|
-
title: " Go ",
|
|
4124
|
-
items: [
|
|
4125
|
-
{ key: "w", label: " go to session window", action: { type: "popup", name: "sisyphus-go-to-window", popup: { w: "70%", h: "60%", cwd: "current" } } },
|
|
4126
|
-
{ key: "p", label: " jump to pane (picker)", action: { type: "popup", name: "sisyphus-jump-to-pane", popup: { w: "60%", h: "60%" } } },
|
|
4127
|
-
{ key: "s", label: " session picker", action: { type: "popup", name: "sisyphus-pick-session", popup: { w: "60%", h: "60%", cwd: "current" } } },
|
|
4128
|
-
{ key: "n", label: " next session", action: { type: "script", name: "sisyphus-cycle" } },
|
|
4129
|
-
{ key: "r", label: " reconnect", action: { type: "popup", name: "sisyphus-reconnect", popup: { w: "80%", h: "40%", cwd: "current" } } }
|
|
4130
|
-
]
|
|
4131
|
-
}
|
|
4132
|
-
}
|
|
4133
|
-
};
|
|
4134
|
-
|
|
4135
|
-
// src/tui/input.ts
|
|
4136
|
-
function dispatchComposeAction(action, content, state2, actions) {
|
|
4137
|
-
switch (action.kind) {
|
|
4138
|
-
case "new-session":
|
|
4139
|
-
actions.sendAndNotify(
|
|
4140
|
-
{ type: "start", task: content, cwd: state2.cwd },
|
|
4141
|
-
"Session created"
|
|
4142
|
-
);
|
|
4143
|
-
break;
|
|
4144
|
-
case "message-orchestrator":
|
|
4145
|
-
actions.sendAndNotify(
|
|
4146
|
-
{ type: "message", sessionId: action.sessionId, content },
|
|
4147
|
-
"Message queued"
|
|
4148
|
-
);
|
|
4149
|
-
break;
|
|
4150
|
-
case "resume":
|
|
4151
|
-
actions.sendAndNotify(
|
|
4152
|
-
{ type: "resume", sessionId: action.sessionId, cwd: state2.cwd, message: content || void 0 },
|
|
4153
|
-
"Session resumed"
|
|
4154
|
-
);
|
|
4155
|
-
break;
|
|
4156
|
-
case "continue":
|
|
4157
|
-
void (async () => {
|
|
4158
|
-
try {
|
|
4159
|
-
const contRes = await actions.send({ type: "continue", sessionId: action.sessionId });
|
|
4160
|
-
if (!contRes.ok) {
|
|
4161
|
-
notify(state2, `Error: ${contRes.error}`);
|
|
4162
|
-
return;
|
|
4163
|
-
}
|
|
4164
|
-
actions.sendAndNotify(
|
|
4165
|
-
{ type: "resume", sessionId: action.sessionId, cwd: state2.cwd, message: content || void 0 },
|
|
4166
|
-
"Session continued"
|
|
4167
|
-
);
|
|
4168
|
-
} catch (err) {
|
|
4169
|
-
notify(state2, `Error: ${err.message}`);
|
|
3981
|
+
const contRes = await actions.send({ type: "continue", sessionId: action.sessionId });
|
|
3982
|
+
if (!contRes.ok) {
|
|
3983
|
+
notify(state2, `Error: ${contRes.error}`);
|
|
3984
|
+
return;
|
|
3985
|
+
}
|
|
3986
|
+
actions.sendAndNotify(
|
|
3987
|
+
{ type: "resume", sessionId: action.sessionId, cwd: state2.cwd, message: content || void 0 },
|
|
3988
|
+
"Session continued"
|
|
3989
|
+
);
|
|
3990
|
+
} catch (err) {
|
|
3991
|
+
notify(state2, `Error: ${err.message}`);
|
|
4170
3992
|
}
|
|
4171
3993
|
})();
|
|
4172
3994
|
break;
|
|
@@ -4252,18 +4074,18 @@ var TUI_HANDLERS = {
|
|
|
4252
4074
|
function findLatestReport(cwd2, sessionId2) {
|
|
4253
4075
|
const dir = reportsDir(cwd2, sessionId2);
|
|
4254
4076
|
try {
|
|
4255
|
-
const files =
|
|
4077
|
+
const files = readdirSync2(dir);
|
|
4256
4078
|
if (files.length === 0) return null;
|
|
4257
4079
|
let latestFile = files[0];
|
|
4258
|
-
let latestMtime =
|
|
4080
|
+
let latestMtime = statSync(join6(dir, latestFile)).mtimeMs;
|
|
4259
4081
|
for (let i = 1; i < files.length; i++) {
|
|
4260
|
-
const m =
|
|
4082
|
+
const m = statSync(join6(dir, files[i])).mtimeMs;
|
|
4261
4083
|
if (m > latestMtime) {
|
|
4262
4084
|
latestMtime = m;
|
|
4263
4085
|
latestFile = files[i];
|
|
4264
4086
|
}
|
|
4265
4087
|
}
|
|
4266
|
-
return
|
|
4088
|
+
return join6(dir, latestFile);
|
|
4267
4089
|
} catch {
|
|
4268
4090
|
return null;
|
|
4269
4091
|
}
|
|
@@ -4629,7 +4451,7 @@ function handleLeaderAction(action, state2, actions) {
|
|
|
4629
4451
|
break;
|
|
4630
4452
|
}
|
|
4631
4453
|
try {
|
|
4632
|
-
const content =
|
|
4454
|
+
const content = readFileSync5(latest, "utf-8");
|
|
4633
4455
|
actions.copyToClipboard(content);
|
|
4634
4456
|
notify(state2, `Copied latest report (${content.length} chars)`);
|
|
4635
4457
|
} catch {
|
|
@@ -4689,7 +4511,7 @@ function handleLeaderAction(action, state2, actions) {
|
|
|
4689
4511
|
}
|
|
4690
4512
|
const editor = actions.resolveEditor();
|
|
4691
4513
|
try {
|
|
4692
|
-
actions.openEditorPopup(state2.cwd, editor,
|
|
4514
|
+
actions.openEditorPopup(state2.cwd, editor, join6(sessionDir(state2.cwd, selectedSessionId), "scratch.md"));
|
|
4693
4515
|
} catch {
|
|
4694
4516
|
notify(state2, "Failed to open scratch");
|
|
4695
4517
|
}
|
|
@@ -4896,7 +4718,7 @@ function handleLeaderAction(action, state2, actions) {
|
|
|
4896
4718
|
break;
|
|
4897
4719
|
}
|
|
4898
4720
|
try {
|
|
4899
|
-
actions.openShellPopup(state2.cwd, `sis session clone ${selectedSessionId}`);
|
|
4721
|
+
actions.openShellPopup(state2.cwd, `sis session recover clone ${selectedSessionId}`);
|
|
4900
4722
|
} catch {
|
|
4901
4723
|
notify(state2, "Failed to open shell");
|
|
4902
4724
|
}
|
|
@@ -4904,7 +4726,7 @@ function handleLeaderAction(action, state2, actions) {
|
|
|
4904
4726
|
}
|
|
4905
4727
|
case "history": {
|
|
4906
4728
|
try {
|
|
4907
|
-
actions.openShellPopup(state2.cwd, "sis
|
|
4729
|
+
actions.openShellPopup(state2.cwd, "sis session inspect history");
|
|
4908
4730
|
} catch {
|
|
4909
4731
|
notify(state2, "Failed to open shell");
|
|
4910
4732
|
}
|
|
@@ -4928,7 +4750,7 @@ function handleLeaderAction(action, state2, actions) {
|
|
|
4928
4750
|
}
|
|
4929
4751
|
case "reconnect": {
|
|
4930
4752
|
try {
|
|
4931
|
-
actions.openShellPopup(state2.cwd, "sis session reconnect");
|
|
4753
|
+
actions.openShellPopup(state2.cwd, "sis session recover reconnect");
|
|
4932
4754
|
} catch {
|
|
4933
4755
|
notify(state2, "Failed to open shell");
|
|
4934
4756
|
}
|
|
@@ -4944,7 +4766,7 @@ function handleLeaderAction(action, state2, actions) {
|
|
|
4944
4766
|
}
|
|
4945
4767
|
case "show-status": {
|
|
4946
4768
|
try {
|
|
4947
|
-
actions.openShellPopup(state2.cwd, `sis status${selectedSessionId ? ` ${selectedSessionId}` : ""}`);
|
|
4769
|
+
actions.openShellPopup(state2.cwd, `sis session inspect status${selectedSessionId ? ` ${selectedSessionId}` : ""}`);
|
|
4948
4770
|
} catch {
|
|
4949
4771
|
notify(state2, "Failed to open status");
|
|
4950
4772
|
}
|
|
@@ -5047,6 +4869,11 @@ function handleResolutionKey(input, key, state2, actions) {
|
|
|
5047
4869
|
const handle = state2.resolutionHandle;
|
|
5048
4870
|
if (!handle) return;
|
|
5049
4871
|
if (key.escape) {
|
|
4872
|
+
if (!handle.atDeckTop()) {
|
|
4873
|
+
handle.handleKey(input, key);
|
|
4874
|
+
requestRender();
|
|
4875
|
+
return;
|
|
4876
|
+
}
|
|
5050
4877
|
handle.unmount();
|
|
5051
4878
|
return;
|
|
5052
4879
|
}
|
|
@@ -5069,6 +4896,48 @@ function handleResolutionKey(input, key, state2, actions) {
|
|
|
5069
4896
|
handle.handleKey(input, key);
|
|
5070
4897
|
requestRender();
|
|
5071
4898
|
}
|
|
4899
|
+
function handleInlineDeckKey(input, key, state2, actions) {
|
|
4900
|
+
const handle = state2.inlineDeck;
|
|
4901
|
+
if (!handle) return;
|
|
4902
|
+
if (key.escape) {
|
|
4903
|
+
if (!handle.atDeckTop()) {
|
|
4904
|
+
handle.handleKey(input, key);
|
|
4905
|
+
requestRender();
|
|
4906
|
+
return;
|
|
4907
|
+
}
|
|
4908
|
+
handle.unmount();
|
|
4909
|
+
const nodes = actions.getNodes();
|
|
4910
|
+
const i = nodes.findIndex((n) => n.id === "needs-you-virtual");
|
|
4911
|
+
const prev = i > 0 ? nodes[i - 1] : nodes.find((n) => n.type === "session") ?? nodes[0];
|
|
4912
|
+
state2.cursorNodeId = prev?.id ?? null;
|
|
4913
|
+
state2.focusPane = "tree";
|
|
4914
|
+
requestRender();
|
|
4915
|
+
return;
|
|
4916
|
+
}
|
|
4917
|
+
if (key.tab && key.shift) {
|
|
4918
|
+
state2.focusPane = "tree";
|
|
4919
|
+
requestRender();
|
|
4920
|
+
return;
|
|
4921
|
+
}
|
|
4922
|
+
if (input === "J" && handle.canAcceptHostKeys()) {
|
|
4923
|
+
handle.advanceQueue(1);
|
|
4924
|
+
return;
|
|
4925
|
+
}
|
|
4926
|
+
if (input === "K" && handle.canAcceptHostKeys()) {
|
|
4927
|
+
handle.advanceQueue(-1);
|
|
4928
|
+
return;
|
|
4929
|
+
}
|
|
4930
|
+
if (input === " " && handle.canAcceptHostKeys()) {
|
|
4931
|
+
handle.spaceVisualToggle();
|
|
4932
|
+
return;
|
|
4933
|
+
}
|
|
4934
|
+
if (input === "R" && handle.canAcceptHostKeys()) {
|
|
4935
|
+
handle.regenerateVisual();
|
|
4936
|
+
return;
|
|
4937
|
+
}
|
|
4938
|
+
handle.handleKey(input, key);
|
|
4939
|
+
requestRender();
|
|
4940
|
+
}
|
|
5072
4941
|
function focusCycle(state2) {
|
|
5073
4942
|
const stops = [{ pane: "tree" }];
|
|
5074
4943
|
if (state2.useStackedDetail && state2.detailMode === "gsr") {
|
|
@@ -5170,6 +5039,11 @@ function handleNavigateKey(input, key, state2, actions) {
|
|
|
5170
5039
|
if (key.rightArrow || input === "l") {
|
|
5171
5040
|
const node = nodes[state2.cursorIndex];
|
|
5172
5041
|
if (!node) return;
|
|
5042
|
+
if (node.type === "needs-you-virtual" && state2.focusPane === "tree") {
|
|
5043
|
+
state2.focusPane = "detail";
|
|
5044
|
+
requestRender();
|
|
5045
|
+
return;
|
|
5046
|
+
}
|
|
5173
5047
|
if (state2.useStackedDetail && state2.focusPane === "tree" && node.type === "session" && node.expanded) {
|
|
5174
5048
|
state2.detailMode = state2.detailMode === "gsr" ? "cycle-log" : "gsr";
|
|
5175
5049
|
state2.cachedStackedLines = null;
|
|
@@ -5207,29 +5081,7 @@ function handleNavigateKey(input, key, state2, actions) {
|
|
|
5207
5081
|
if (key.return) {
|
|
5208
5082
|
const node = nodes[state2.cursorIndex];
|
|
5209
5083
|
if (!node) return;
|
|
5210
|
-
if (node.
|
|
5211
|
-
const firstItem = state2.aggregateInbox[0];
|
|
5212
|
-
if (firstItem) {
|
|
5213
|
-
enterResolutionMode(state2, firstItem.askId, actions.send, async ({ sessionId: sessionId2, agentId, paneId }) => {
|
|
5214
|
-
const res = await actions.send({ type: "status", sessionId: sessionId2 });
|
|
5215
|
-
const sess = res.ok ? res.data?.session : void 0;
|
|
5216
|
-
if (!sess) {
|
|
5217
|
-
notify(state2, "Session not found");
|
|
5218
|
-
return;
|
|
5219
|
-
}
|
|
5220
|
-
if (paneId && actions.paneExists(paneId)) {
|
|
5221
|
-
if (sess.tmuxSessionName) actions.switchToSession(sess.tmuxSessionName);
|
|
5222
|
-
if (sess.tmuxWindowId) actions.selectWindow(sess.tmuxWindowId);
|
|
5223
|
-
actions.selectPane(paneId);
|
|
5224
|
-
return;
|
|
5225
|
-
}
|
|
5226
|
-
if (sess.tmuxSessionName) actions.switchToSession(sess.tmuxSessionName);
|
|
5227
|
-
notify(state2, `Pane ${paneId ? paneId : "?"} is gone \u2014 agent ${agentId} cannot be taken over.`);
|
|
5228
|
-
});
|
|
5229
|
-
} else {
|
|
5230
|
-
notify(state2, "No pending asks");
|
|
5231
|
-
}
|
|
5232
|
-
} else if (node.expandable && !node.expanded) {
|
|
5084
|
+
if (node.expandable && !node.expanded) {
|
|
5233
5085
|
state2.expanded.add(node.id);
|
|
5234
5086
|
expandSessionLatestCycle(state2, node);
|
|
5235
5087
|
requestRender();
|
|
@@ -5510,6 +5362,28 @@ function handleKeypress(input, key, state2, actions) {
|
|
|
5510
5362
|
handleResolutionKey(input, key, state2, actions);
|
|
5511
5363
|
return;
|
|
5512
5364
|
}
|
|
5365
|
+
if (state2.reviewPanel && state2.focusPane === "detail" && actions.getCursorNode()?.type === "needs-you-virtual") {
|
|
5366
|
+
if (key.escape) {
|
|
5367
|
+
state2.reviewPanel = null;
|
|
5368
|
+
state2.focusPane = "tree";
|
|
5369
|
+
requestRender();
|
|
5370
|
+
return;
|
|
5371
|
+
}
|
|
5372
|
+
if (key.tab && key.shift) {
|
|
5373
|
+
state2.focusPane = "tree";
|
|
5374
|
+
requestRender();
|
|
5375
|
+
return;
|
|
5376
|
+
}
|
|
5377
|
+
const consumed = state2.reviewPanel.handleKey(input);
|
|
5378
|
+
if (consumed) {
|
|
5379
|
+
requestRender();
|
|
5380
|
+
return;
|
|
5381
|
+
}
|
|
5382
|
+
}
|
|
5383
|
+
if (state2.inlineDeck && state2.focusPane === "detail" && actions.getCursorNode()?.type === "needs-you-virtual") {
|
|
5384
|
+
handleInlineDeckKey(input, key, state2, actions);
|
|
5385
|
+
return;
|
|
5386
|
+
}
|
|
5513
5387
|
if (state2.mode === "search") {
|
|
5514
5388
|
handleSearchKey(input, key, state2);
|
|
5515
5389
|
} else if (state2.mode === "leader" || state2.mode === "copy-menu" || state2.mode === "open-menu" || state2.mode === "agent-menu" || state2.mode === "session-menu" || state2.mode === "go-menu" || state2.mode === "companion-menu" || state2.mode === "help" || state2.mode === "companion-overlay" || state2.mode === "companion-debug") {
|
|
@@ -5596,10 +5470,10 @@ function precomputePrefixes(nodes) {
|
|
|
5596
5470
|
}
|
|
5597
5471
|
|
|
5598
5472
|
// src/tui/lib/reports.ts
|
|
5599
|
-
import { readFileSync as
|
|
5473
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
5600
5474
|
function loadReportContent(report) {
|
|
5601
5475
|
try {
|
|
5602
|
-
return
|
|
5476
|
+
return readFileSync6(report.filePath, "utf-8");
|
|
5603
5477
|
} catch {
|
|
5604
5478
|
return report.summary;
|
|
5605
5479
|
}
|
|
@@ -5613,6 +5487,44 @@ function resolveReports(reports) {
|
|
|
5613
5487
|
}));
|
|
5614
5488
|
}
|
|
5615
5489
|
|
|
5490
|
+
// src/shared/client.ts
|
|
5491
|
+
init_paths();
|
|
5492
|
+
import { connect } from "net";
|
|
5493
|
+
function rawSend(request, timeoutMs = 1e4) {
|
|
5494
|
+
const sock = socketPath();
|
|
5495
|
+
return new Promise((resolve2, reject) => {
|
|
5496
|
+
const socket = connect(sock);
|
|
5497
|
+
let data = "";
|
|
5498
|
+
const timeout = setTimeout(() => {
|
|
5499
|
+
socket.destroy();
|
|
5500
|
+
reject(new Error(`Request timed out after ${(timeoutMs / 1e3).toFixed(0)}s. The daemon may be overloaded.
|
|
5501
|
+
Check: sis admin check doctor
|
|
5502
|
+
Logs: tail -20 ~/.sisyphus/daemon.log`));
|
|
5503
|
+
}, timeoutMs);
|
|
5504
|
+
socket.on("connect", () => {
|
|
5505
|
+
socket.write(JSON.stringify(request) + "\n");
|
|
5506
|
+
});
|
|
5507
|
+
socket.on("data", (chunk) => {
|
|
5508
|
+
data += chunk.toString();
|
|
5509
|
+
const newlineIdx = data.indexOf("\n");
|
|
5510
|
+
if (newlineIdx !== -1) {
|
|
5511
|
+
clearTimeout(timeout);
|
|
5512
|
+
const line = data.slice(0, newlineIdx);
|
|
5513
|
+
socket.destroy();
|
|
5514
|
+
try {
|
|
5515
|
+
resolve2(JSON.parse(line));
|
|
5516
|
+
} catch {
|
|
5517
|
+
reject(new Error(`Invalid JSON response from daemon: ${line}`));
|
|
5518
|
+
}
|
|
5519
|
+
}
|
|
5520
|
+
});
|
|
5521
|
+
socket.on("error", (err) => {
|
|
5522
|
+
clearTimeout(timeout);
|
|
5523
|
+
reject(err);
|
|
5524
|
+
});
|
|
5525
|
+
});
|
|
5526
|
+
}
|
|
5527
|
+
|
|
5616
5528
|
// src/tui/lib/client.ts
|
|
5617
5529
|
function send(request) {
|
|
5618
5530
|
return rawSend(request, 8e3);
|
|
@@ -5626,8 +5538,8 @@ async function inboxList() {
|
|
|
5626
5538
|
// src/tui/lib/tmux.ts
|
|
5627
5539
|
init_paths();
|
|
5628
5540
|
import { execSync as execSync3 } from "child_process";
|
|
5629
|
-
import { join as
|
|
5630
|
-
import { readFileSync as
|
|
5541
|
+
import { join as join7 } from "path";
|
|
5542
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, mkdtempSync, rmSync as rmSync2, cpSync, existsSync as existsSync5, mkdirSync as mkdirSync4 } from "fs";
|
|
5631
5543
|
import { tmpdir } from "os";
|
|
5632
5544
|
init_shell();
|
|
5633
5545
|
|
|
@@ -5693,10 +5605,10 @@ function registerDashboardWindow(cwd2) {
|
|
|
5693
5605
|
}
|
|
5694
5606
|
}
|
|
5695
5607
|
function setupCompanionPlugin() {
|
|
5696
|
-
const srcDir =
|
|
5697
|
-
const destDir =
|
|
5698
|
-
if (!
|
|
5699
|
-
|
|
5608
|
+
const srcDir = join7(import.meta.dirname, "templates", "companion-plugin");
|
|
5609
|
+
const destDir = join7(globalDir(), "companion-plugin");
|
|
5610
|
+
if (!existsSync5(destDir)) mkdirSync4(destDir, { recursive: true });
|
|
5611
|
+
cpSync(srcDir, destDir, { recursive: true });
|
|
5700
5612
|
return destDir;
|
|
5701
5613
|
}
|
|
5702
5614
|
function paneExists(paneId) {
|
|
@@ -5722,18 +5634,18 @@ function openCompanionPane(cwd2) {
|
|
|
5722
5634
|
return;
|
|
5723
5635
|
}
|
|
5724
5636
|
const pluginDir = setupCompanionPlugin();
|
|
5725
|
-
const templatePath =
|
|
5637
|
+
const templatePath = join7(import.meta.dirname, "templates", "dashboard-claude.md");
|
|
5726
5638
|
let template;
|
|
5727
5639
|
try {
|
|
5728
|
-
template =
|
|
5640
|
+
template = readFileSync7(templatePath, "utf-8");
|
|
5729
5641
|
} catch {
|
|
5730
5642
|
template = `You are a Sisyphus dashboard companion. Help the user manage multi-agent sessions.
|
|
5731
5643
|
Project: ${cwd2}
|
|
5732
|
-
Run \`sis list\` and \`sis status\` to see current state.`;
|
|
5644
|
+
Run \`sis session inspect list\` and \`sis session inspect status\` to see current state.`;
|
|
5733
5645
|
}
|
|
5734
5646
|
const rendered = template.replace(/\{\{CWD\}\}/g, cwd2);
|
|
5735
|
-
const promptPath =
|
|
5736
|
-
|
|
5647
|
+
const promptPath = join7(globalDir(), "dashboard-companion-prompt.md");
|
|
5648
|
+
writeFileSync4(promptPath, rendered, "utf-8");
|
|
5737
5649
|
const pathEnv = augmentedPath();
|
|
5738
5650
|
const claudeCmd = `SISYPHUS_COMPANION_CWD=${shellQuote(cwd2)} PATH=${shellQuote(pathEnv)} claude --dangerously-skip-permissions --plugin-dir ${shellQuote(pluginDir)} --append-system-prompt "$(cat ${shellQuote(promptPath)})"`;
|
|
5739
5651
|
const result = exec(
|
|
@@ -5749,32 +5661,32 @@ function switchToSession(sessionName) {
|
|
|
5749
5661
|
execSafe(`tmux switch-client -t ${shellQuote(sessionName)}`);
|
|
5750
5662
|
}
|
|
5751
5663
|
function editInPopup(cwd2, editor, opts) {
|
|
5752
|
-
const tmpDir = mkdtempSync(
|
|
5753
|
-
const filePath =
|
|
5664
|
+
const tmpDir = mkdtempSync(join7(tmpdir(), "sisyphus-"));
|
|
5665
|
+
const filePath = join7(tmpDir, "input.md");
|
|
5754
5666
|
try {
|
|
5755
|
-
|
|
5667
|
+
writeFileSync4(filePath, opts?.content ? opts.content : "", "utf-8");
|
|
5756
5668
|
openEditorPopup(cwd2, editor, filePath, opts?.size);
|
|
5757
|
-
const result =
|
|
5669
|
+
const result = readFileSync7(filePath, "utf-8").trim();
|
|
5758
5670
|
return result || null;
|
|
5759
5671
|
} finally {
|
|
5760
|
-
|
|
5672
|
+
rmSync2(tmpDir, { recursive: true, force: true });
|
|
5761
5673
|
}
|
|
5762
5674
|
}
|
|
5763
5675
|
function promptInPopup(prompt, opts) {
|
|
5764
5676
|
const { w = "50%", h = "3" } = opts ?? {};
|
|
5765
|
-
const tmpDir = mkdtempSync(
|
|
5766
|
-
const outFile =
|
|
5677
|
+
const tmpDir = mkdtempSync(join7(tmpdir(), "sisyphus-"));
|
|
5678
|
+
const outFile = join7(tmpDir, "result");
|
|
5767
5679
|
try {
|
|
5768
5680
|
const script = `printf ${shellQuote(prompt + " ")} && read -r line && printf '%s' "$line" > ${shellQuote(outFile)}`;
|
|
5769
5681
|
execSync3(
|
|
5770
5682
|
`tmux display-popup -E -w ${w} -h ${h} ${shellQuote(`bash -c ${shellQuote(script)}`)}`,
|
|
5771
5683
|
{ stdio: "inherit", env: EXEC_ENV }
|
|
5772
5684
|
);
|
|
5773
|
-
if (!
|
|
5774
|
-
const result =
|
|
5685
|
+
if (!existsSync5(outFile)) return null;
|
|
5686
|
+
const result = readFileSync7(outFile, "utf-8").trim();
|
|
5775
5687
|
return result || null;
|
|
5776
5688
|
} finally {
|
|
5777
|
-
|
|
5689
|
+
rmSync2(tmpDir, { recursive: true, force: true });
|
|
5778
5690
|
}
|
|
5779
5691
|
}
|
|
5780
5692
|
function openLogPopup() {
|
|
@@ -5868,10 +5780,10 @@ function openEditorPopup(cwd2, editor, filePath, size) {
|
|
|
5868
5780
|
|
|
5869
5781
|
// src/tui/lib/context.ts
|
|
5870
5782
|
init_paths();
|
|
5871
|
-
import { readFileSync as
|
|
5783
|
+
import { readFileSync as readFileSync8, readdirSync as readdirSync3 } from "fs";
|
|
5872
5784
|
function readFileSafe(filePath) {
|
|
5873
5785
|
try {
|
|
5874
|
-
return
|
|
5786
|
+
return readFileSync8(filePath, "utf-8");
|
|
5875
5787
|
} catch {
|
|
5876
5788
|
return null;
|
|
5877
5789
|
}
|
|
@@ -6765,12 +6677,6 @@ function buildCycleFlowLines(session, width, expanded) {
|
|
|
6765
6677
|
return lines;
|
|
6766
6678
|
}
|
|
6767
6679
|
|
|
6768
|
-
// src/shared/inbox-types.ts
|
|
6769
|
-
function coerceKind(k) {
|
|
6770
|
-
if (k !== void 0) return k;
|
|
6771
|
-
return "validation";
|
|
6772
|
-
}
|
|
6773
|
-
|
|
6774
6680
|
// src/tui/panels/detail.ts
|
|
6775
6681
|
function buildPlanLines(content, maxLines, width) {
|
|
6776
6682
|
const clean = stripFrontmatter(content);
|
|
@@ -7418,7 +7324,7 @@ function formatTimestampShort(iso) {
|
|
|
7418
7324
|
function renderFleetRollup(rect, state2, focused) {
|
|
7419
7325
|
const items = state2.aggregateInbox;
|
|
7420
7326
|
const sessions = state2.sessions;
|
|
7421
|
-
const cacheKey = `rollup:${items.length}:${sessions.length}:${items.map((i) => `${i.
|
|
7327
|
+
const cacheKey = `rollup:${items.length}:${sessions.length}:${items.map((i) => `${askIdFromDir(i.dir)}:${i.blockedSince}`).join(",")}:${rect.w}`;
|
|
7422
7328
|
let lines;
|
|
7423
7329
|
if (cacheKey === state2.digestCacheKey && state2.cachedDigestLines !== null) {
|
|
7424
7330
|
lines = state2.cachedDigestLines;
|
|
@@ -7432,7 +7338,7 @@ function renderFleetRollup(rect, state2, focused) {
|
|
|
7432
7338
|
for (const s of sessions) {
|
|
7433
7339
|
byStatus.set(s.status, (byStatus.get(s.status) ?? 0) + 1);
|
|
7434
7340
|
}
|
|
7435
|
-
const uniqueSessions = new Set(items.map((i) => i.
|
|
7341
|
+
const uniqueSessions = new Set(items.map((i) => sessionIdFromDir(i.dir))).size;
|
|
7436
7342
|
lines = [];
|
|
7437
7343
|
lines.push([seg(" Fleet Inbox", { color: "red", bold: true })]);
|
|
7438
7344
|
lines.push(singleLine(` ${items.length} pending across ${uniqueSessions} sessions`, { dim: true }));
|
|
@@ -7571,836 +7477,1318 @@ var HEADING_STYLES = [
|
|
|
7571
7477
|
function stripDisplayHazards(s) {
|
|
7572
7478
|
return s.replace(/✅/g, "\u2713").replace(/❌/g, "\u2717").replace(new RegExp("\\p{Emoji_Presentation}", "gu"), "");
|
|
7573
7479
|
}
|
|
7574
|
-
var INLINE_RE = /\*\*([^*\n]+)\*\*|__([^_\n]+)__|\*([^*\n]+)\*|_([^_\n]+)_|`([^`\n]+)`|~~([^~\n]+)~~|\[([^\]\n]+)\]\(([^)\n]+)\)/g;
|
|
7575
|
-
function tokenizeInline(line, baseFg, baseStyle) {
|
|
7480
|
+
var INLINE_RE = /\*\*([^*\n]+)\*\*|__([^_\n]+)__|\*([^*\n]+)\*|_([^_\n]+)_|`([^`\n]+)`|~~([^~\n]+)~~|\[([^\]\n]+)\]\(([^)\n]+)\)/g;
|
|
7481
|
+
function tokenizeInline(line, baseFg, baseStyle) {
|
|
7482
|
+
const out = [];
|
|
7483
|
+
const baseSeg = (text) => ({ text, fg: baseFg, ...baseStyle });
|
|
7484
|
+
let cursor = 0;
|
|
7485
|
+
for (const m of line.matchAll(INLINE_RE)) {
|
|
7486
|
+
const idx = m.index;
|
|
7487
|
+
if (idx > cursor) out.push(baseSeg(line.slice(cursor, idx)));
|
|
7488
|
+
if (m[1] !== void 0) {
|
|
7489
|
+
out.push({ text: m[1], fg: GLOAM.fg0, bold: true, bg: baseStyle?.bg });
|
|
7490
|
+
} else if (m[2] !== void 0) {
|
|
7491
|
+
out.push({ text: m[2], fg: GLOAM.fg0, bold: true, bg: baseStyle?.bg });
|
|
7492
|
+
} else if (m[3] !== void 0) {
|
|
7493
|
+
out.push({ text: m[3], fg: GLOAM.fg0, italic: true, bg: baseStyle?.bg });
|
|
7494
|
+
} else if (m[4] !== void 0) {
|
|
7495
|
+
out.push({ text: m[4], fg: GLOAM.fg0, italic: true, bg: baseStyle?.bg });
|
|
7496
|
+
} else if (m[5] !== void 0) {
|
|
7497
|
+
out.push({
|
|
7498
|
+
text: m[5],
|
|
7499
|
+
fg: GLOAM.aqua,
|
|
7500
|
+
bg: baseStyle?.bg ?? GLOAM.bg_bg1
|
|
7501
|
+
});
|
|
7502
|
+
} else if (m[6] !== void 0) {
|
|
7503
|
+
out.push({ text: m[6], fg: GLOAM.fg3, strikethrough: true, bg: baseStyle?.bg });
|
|
7504
|
+
} else if (m[7] !== void 0 && m[8] !== void 0) {
|
|
7505
|
+
out.push({ text: m[7], fg: GLOAM.blue, bold: true, bg: baseStyle?.bg });
|
|
7506
|
+
}
|
|
7507
|
+
cursor = idx + m[0].length;
|
|
7508
|
+
}
|
|
7509
|
+
if (cursor < line.length) out.push(baseSeg(line.slice(cursor)));
|
|
7510
|
+
if (out.length === 0) out.push(baseSeg(""));
|
|
7511
|
+
return out;
|
|
7512
|
+
}
|
|
7513
|
+
function segsDisplayWidth(segs) {
|
|
7514
|
+
let w = 0;
|
|
7515
|
+
for (const s of segs) w += stringWidth6(s.text);
|
|
7516
|
+
return w;
|
|
7517
|
+
}
|
|
7518
|
+
function segsToAtoms(segs) {
|
|
7519
|
+
const atoms = [];
|
|
7520
|
+
for (const s of segs) {
|
|
7521
|
+
if (!s.text) continue;
|
|
7522
|
+
const { text, ...style } = s;
|
|
7523
|
+
const re = /(\s+|\S+)/g;
|
|
7524
|
+
for (const m of text.matchAll(re)) {
|
|
7525
|
+
const piece = m[0];
|
|
7526
|
+
atoms.push({
|
|
7527
|
+
text: piece,
|
|
7528
|
+
width: stringWidth6(piece),
|
|
7529
|
+
style,
|
|
7530
|
+
space: /^\s+$/.test(piece)
|
|
7531
|
+
});
|
|
7532
|
+
}
|
|
7533
|
+
}
|
|
7534
|
+
return atoms;
|
|
7535
|
+
}
|
|
7536
|
+
function wrapSegs(segs, width, contIndent) {
|
|
7537
|
+
if (width <= 0) return [segs];
|
|
7538
|
+
const atoms = segsToAtoms(segs);
|
|
7539
|
+
if (atoms.length === 0) return [[{ text: "" }]];
|
|
7540
|
+
const lines = [];
|
|
7541
|
+
let current = [];
|
|
7542
|
+
let currentWidth = 0;
|
|
7543
|
+
const pushAtom = (a) => {
|
|
7544
|
+
current.push({ ...a.style, text: a.text });
|
|
7545
|
+
currentWidth += a.width;
|
|
7546
|
+
};
|
|
7547
|
+
const flushLine = () => {
|
|
7548
|
+
while (current.length > 0) {
|
|
7549
|
+
const last = current[current.length - 1];
|
|
7550
|
+
if (/^\s+$/.test(last.text) && !last.bg) {
|
|
7551
|
+
currentWidth -= stringWidth6(last.text);
|
|
7552
|
+
current.pop();
|
|
7553
|
+
} else break;
|
|
7554
|
+
}
|
|
7555
|
+
lines.push(current.length > 0 ? current : [{ text: "" }]);
|
|
7556
|
+
current = [];
|
|
7557
|
+
currentWidth = 0;
|
|
7558
|
+
};
|
|
7559
|
+
for (let i = 0; i < atoms.length; i++) {
|
|
7560
|
+
const atom = atoms[i];
|
|
7561
|
+
if (atom.space) {
|
|
7562
|
+
if (currentWidth + atom.width <= width) pushAtom(atom);
|
|
7563
|
+
continue;
|
|
7564
|
+
}
|
|
7565
|
+
if (atom.width > width) {
|
|
7566
|
+
let remaining = atom.text;
|
|
7567
|
+
while (remaining.length > 0) {
|
|
7568
|
+
const spaceLeft = width - currentWidth;
|
|
7569
|
+
if (spaceLeft <= 0) {
|
|
7570
|
+
flushLine();
|
|
7571
|
+
if (contIndent) {
|
|
7572
|
+
current.push({ text: contIndent });
|
|
7573
|
+
currentWidth = stringWidth6(contIndent);
|
|
7574
|
+
}
|
|
7575
|
+
continue;
|
|
7576
|
+
}
|
|
7577
|
+
let cut = 0;
|
|
7578
|
+
let cutW = 0;
|
|
7579
|
+
for (let k = 0; k < remaining.length; k++) {
|
|
7580
|
+
const cw = stringWidth6(remaining[k]);
|
|
7581
|
+
if (cutW + cw > spaceLeft) break;
|
|
7582
|
+
cutW += cw;
|
|
7583
|
+
cut = k + 1;
|
|
7584
|
+
}
|
|
7585
|
+
if (cut === 0) {
|
|
7586
|
+
flushLine();
|
|
7587
|
+
if (contIndent) {
|
|
7588
|
+
current.push({ text: contIndent });
|
|
7589
|
+
currentWidth = stringWidth6(contIndent);
|
|
7590
|
+
}
|
|
7591
|
+
continue;
|
|
7592
|
+
}
|
|
7593
|
+
current.push({ ...atom.style, text: remaining.slice(0, cut) });
|
|
7594
|
+
currentWidth += cutW;
|
|
7595
|
+
remaining = remaining.slice(cut);
|
|
7596
|
+
if (remaining.length > 0) {
|
|
7597
|
+
flushLine();
|
|
7598
|
+
if (contIndent) {
|
|
7599
|
+
current.push({ text: contIndent });
|
|
7600
|
+
currentWidth = stringWidth6(contIndent);
|
|
7601
|
+
}
|
|
7602
|
+
}
|
|
7603
|
+
}
|
|
7604
|
+
continue;
|
|
7605
|
+
}
|
|
7606
|
+
if (currentWidth + atom.width > width) {
|
|
7607
|
+
flushLine();
|
|
7608
|
+
if (contIndent) {
|
|
7609
|
+
current.push({ text: contIndent });
|
|
7610
|
+
currentWidth = stringWidth6(contIndent);
|
|
7611
|
+
}
|
|
7612
|
+
}
|
|
7613
|
+
pushAtom(atom);
|
|
7614
|
+
}
|
|
7615
|
+
flushLine();
|
|
7616
|
+
return lines.length > 0 ? lines : [[{ text: "" }]];
|
|
7617
|
+
}
|
|
7618
|
+
function buildHeadingLine(level, rawText, innerW) {
|
|
7619
|
+
const style = HEADING_STYLES[Math.min(level - 1, HEADING_STYLES.length - 1)];
|
|
7620
|
+
const cleanedText = stripDisplayHazards(rawText).trim();
|
|
7621
|
+
const marker = "#".repeat(level);
|
|
7622
|
+
const prefix = " ";
|
|
7623
|
+
const sep = " ";
|
|
7624
|
+
const headerSegs = [
|
|
7625
|
+
{ text: prefix, bg: style.bg },
|
|
7626
|
+
{ text: marker, fg: style.markerFg, bg: style.bg, bold: true },
|
|
7627
|
+
{ text: sep, bg: style.bg },
|
|
7628
|
+
{ text: cleanedText, fg: style.textFg, bg: style.bg, bold: true }
|
|
7629
|
+
];
|
|
7630
|
+
const used = segsDisplayWidth(headerSegs);
|
|
7631
|
+
const padW = Math.max(0, innerW - used);
|
|
7632
|
+
if (padW > 0) {
|
|
7633
|
+
headerSegs.push({ text: " ".repeat(padW), bg: style.bg });
|
|
7634
|
+
}
|
|
7635
|
+
return headerSegs;
|
|
7636
|
+
}
|
|
7637
|
+
function buildListLine(marker, body, innerW, prefixIndent, markerFg) {
|
|
7638
|
+
const indent = `${prefixIndent} `;
|
|
7639
|
+
const head = [
|
|
7640
|
+
{ text: prefixIndent, fg: GLOAM.fg2 },
|
|
7641
|
+
{ text: marker, fg: markerFg, bold: true },
|
|
7642
|
+
{ text: " ", fg: GLOAM.fg2 }
|
|
7643
|
+
];
|
|
7644
|
+
const headW = segsDisplayWidth(head);
|
|
7645
|
+
const bodySegs = tokenizeInline(stripDisplayHazards(body), GLOAM.fg1);
|
|
7646
|
+
const wrapped = wrapSegs([...head, ...bodySegs], innerW, indent);
|
|
7647
|
+
return wrapped;
|
|
7648
|
+
}
|
|
7649
|
+
function buildCheckboxLine(checked, body, innerW) {
|
|
7650
|
+
const icon = checked ? "\u2611" : "\u2610";
|
|
7651
|
+
const iconFg = checked ? GLOAM.green : GLOAM.fg4;
|
|
7652
|
+
const head = [
|
|
7653
|
+
{ text: " ", fg: GLOAM.fg2 },
|
|
7654
|
+
{ text: icon, fg: iconFg, bold: true },
|
|
7655
|
+
{ text: " ", fg: GLOAM.fg2 }
|
|
7656
|
+
];
|
|
7657
|
+
const bodyFg = checked ? GLOAM.fg3 : GLOAM.fg1;
|
|
7658
|
+
const bodyStyle = checked ? { strikethrough: true } : {};
|
|
7659
|
+
const bodySegs = tokenizeInline(stripDisplayHazards(body), bodyFg, bodyStyle);
|
|
7660
|
+
return wrapSegs([...head, ...bodySegs], innerW, " ");
|
|
7661
|
+
}
|
|
7662
|
+
function buildQuoteLine(body, innerW) {
|
|
7663
|
+
const head = [
|
|
7664
|
+
{ text: " ", fg: GLOAM.fg2 },
|
|
7665
|
+
{ text: "\u258E ", fg: GLOAM.fg3 }
|
|
7666
|
+
];
|
|
7667
|
+
const bodySegs = tokenizeInline(stripDisplayHazards(body), GLOAM.fg2, { italic: true });
|
|
7668
|
+
return wrapSegs([...head, ...bodySegs], innerW, " ");
|
|
7669
|
+
}
|
|
7670
|
+
function buildHrLine(innerW) {
|
|
7671
|
+
const w = Math.max(2, innerW - 4);
|
|
7672
|
+
return [
|
|
7673
|
+
{ text: " ", fg: GLOAM.fg4 },
|
|
7674
|
+
{ text: "\u2500".repeat(w), fg: GLOAM.fg4 }
|
|
7675
|
+
];
|
|
7676
|
+
}
|
|
7677
|
+
function buildCodeFenceLine(fence, innerW) {
|
|
7678
|
+
return [
|
|
7679
|
+
{ text: " ", fg: GLOAM.fg4 },
|
|
7680
|
+
{
|
|
7681
|
+
text: fence + " ".repeat(Math.max(0, innerW - 2 - stringWidth6(fence))),
|
|
7682
|
+
fg: GLOAM.fg4,
|
|
7683
|
+
bg: GLOAM.bg_bg1
|
|
7684
|
+
}
|
|
7685
|
+
];
|
|
7686
|
+
}
|
|
7687
|
+
function buildCodeLine(content, innerW) {
|
|
7688
|
+
const cleaned = stripDisplayHazards(content);
|
|
7689
|
+
const cw = stringWidth6(cleaned);
|
|
7690
|
+
const padW = Math.max(0, innerW - 2 - cw);
|
|
7691
|
+
return [
|
|
7692
|
+
{ text: " ", bg: GLOAM.bg_bg1 },
|
|
7693
|
+
{ text: cleaned, fg: GLOAM.aqua, bg: GLOAM.bg_bg1 },
|
|
7694
|
+
...padW > 0 ? [{ text: " ".repeat(padW), bg: GLOAM.bg_bg1 }] : []
|
|
7695
|
+
];
|
|
7696
|
+
}
|
|
7697
|
+
function buildParagraphLines(body, innerW) {
|
|
7698
|
+
const head = [{ text: " ", fg: GLOAM.fg1 }];
|
|
7699
|
+
const bodySegs = tokenizeInline(stripDisplayHazards(body), GLOAM.fg1);
|
|
7700
|
+
return wrapSegs([...head, ...bodySegs], innerW, " ");
|
|
7701
|
+
}
|
|
7702
|
+
function parseTableCells(line) {
|
|
7703
|
+
let s = line.trim();
|
|
7704
|
+
if (s.startsWith("|")) s = s.slice(1);
|
|
7705
|
+
if (s.endsWith("|") && !s.endsWith("\\|")) s = s.slice(0, -1);
|
|
7706
|
+
const cells = [];
|
|
7707
|
+
let cur = "";
|
|
7708
|
+
for (let i = 0; i < s.length; i++) {
|
|
7709
|
+
const ch = s[i];
|
|
7710
|
+
if (ch === "\\" && s[i + 1] === "|") {
|
|
7711
|
+
cur += "|";
|
|
7712
|
+
i++;
|
|
7713
|
+
} else if (ch === "|") {
|
|
7714
|
+
cells.push(cur.trim());
|
|
7715
|
+
cur = "";
|
|
7716
|
+
} else {
|
|
7717
|
+
cur += ch;
|
|
7718
|
+
}
|
|
7719
|
+
}
|
|
7720
|
+
cells.push(cur.trim());
|
|
7721
|
+
return cells;
|
|
7722
|
+
}
|
|
7723
|
+
function parseTableSeparator(line) {
|
|
7724
|
+
if (!line.includes("|") && !/^[\s:|+-]+$/.test(line)) return null;
|
|
7725
|
+
const cells = parseTableCells(line);
|
|
7726
|
+
if (cells.length === 0) return null;
|
|
7727
|
+
const aligns = [];
|
|
7728
|
+
for (const c of cells) {
|
|
7729
|
+
const m = c.match(/^(:?)\s*-{2,}\s*(:?)$/);
|
|
7730
|
+
if (!m) return null;
|
|
7731
|
+
if (m[1] === ":" && m[2] === ":") aligns.push("center");
|
|
7732
|
+
else if (m[2] === ":") aligns.push("right");
|
|
7733
|
+
else aligns.push("left");
|
|
7734
|
+
}
|
|
7735
|
+
return aligns;
|
|
7736
|
+
}
|
|
7737
|
+
function padCell(text, width, align) {
|
|
7738
|
+
const w = stringWidth6(text);
|
|
7739
|
+
const pad = Math.max(0, width - w);
|
|
7740
|
+
let left = 0;
|
|
7741
|
+
let right = 0;
|
|
7742
|
+
if (align === "right") left = pad;
|
|
7743
|
+
else if (align === "center") {
|
|
7744
|
+
left = Math.floor(pad / 2);
|
|
7745
|
+
right = pad - left;
|
|
7746
|
+
} else right = pad;
|
|
7747
|
+
return " ".repeat(left) + text + " ".repeat(right);
|
|
7748
|
+
}
|
|
7749
|
+
function wrapCell(text, width, align) {
|
|
7750
|
+
if (width <= 0) return [""];
|
|
7751
|
+
const cleaned = cleanMarkdown(text);
|
|
7752
|
+
if (cleaned === "") return [padCell("", width, align)];
|
|
7576
7753
|
const out = [];
|
|
7577
|
-
|
|
7578
|
-
let
|
|
7579
|
-
|
|
7580
|
-
|
|
7581
|
-
|
|
7582
|
-
|
|
7583
|
-
|
|
7584
|
-
|
|
7585
|
-
|
|
7586
|
-
|
|
7587
|
-
|
|
7588
|
-
|
|
7589
|
-
|
|
7590
|
-
|
|
7591
|
-
|
|
7592
|
-
|
|
7593
|
-
|
|
7594
|
-
|
|
7595
|
-
});
|
|
7596
|
-
} else if (m[6] !== void 0) {
|
|
7597
|
-
out.push({ text: m[6], fg: GLOAM.fg3, strikethrough: true, bg: baseStyle?.bg });
|
|
7598
|
-
} else if (m[7] !== void 0 && m[8] !== void 0) {
|
|
7599
|
-
out.push({ text: m[7], fg: GLOAM.blue, bold: true, bg: baseStyle?.bg });
|
|
7754
|
+
let cur = "";
|
|
7755
|
+
let curW = 0;
|
|
7756
|
+
const flush = () => {
|
|
7757
|
+
out.push(padCell(cur.replace(/\s+$/, ""), width, align));
|
|
7758
|
+
cur = "";
|
|
7759
|
+
curW = 0;
|
|
7760
|
+
};
|
|
7761
|
+
for (const piece of cleaned.match(/\s+|\S+/g) ?? []) {
|
|
7762
|
+
const isSpace = /^\s+$/.test(piece);
|
|
7763
|
+
const pw = stringWidth6(piece);
|
|
7764
|
+
if (isSpace) {
|
|
7765
|
+
if (curW === 0) continue;
|
|
7766
|
+
if (curW + pw > width) flush();
|
|
7767
|
+
else {
|
|
7768
|
+
cur += piece;
|
|
7769
|
+
curW += pw;
|
|
7770
|
+
}
|
|
7771
|
+
continue;
|
|
7600
7772
|
}
|
|
7601
|
-
|
|
7773
|
+
if (curW + pw <= width) {
|
|
7774
|
+
cur += piece;
|
|
7775
|
+
curW += pw;
|
|
7776
|
+
continue;
|
|
7777
|
+
}
|
|
7778
|
+
if (curW > 0) flush();
|
|
7779
|
+
if (pw > width) {
|
|
7780
|
+
let rem = piece;
|
|
7781
|
+
while (rem.length > 0) {
|
|
7782
|
+
let cut = 0;
|
|
7783
|
+
let cutW = 0;
|
|
7784
|
+
for (let k = 0; k < rem.length; k++) {
|
|
7785
|
+
const cw = stringWidth6(rem[k]);
|
|
7786
|
+
if (cutW + cw > width) break;
|
|
7787
|
+
cutW += cw;
|
|
7788
|
+
cut = k + 1;
|
|
7789
|
+
}
|
|
7790
|
+
if (cut === 0) cut = 1;
|
|
7791
|
+
const slice = rem.slice(0, cut);
|
|
7792
|
+
if (cut === rem.length) {
|
|
7793
|
+
cur = slice;
|
|
7794
|
+
curW = stringWidth6(slice);
|
|
7795
|
+
} else {
|
|
7796
|
+
out.push(padCell(slice, width, align));
|
|
7797
|
+
}
|
|
7798
|
+
rem = rem.slice(cut);
|
|
7799
|
+
}
|
|
7800
|
+
continue;
|
|
7801
|
+
}
|
|
7802
|
+
cur = piece;
|
|
7803
|
+
curW = pw;
|
|
7602
7804
|
}
|
|
7603
|
-
if (
|
|
7604
|
-
if (out.length === 0) out.push(baseSeg(""));
|
|
7805
|
+
if (curW > 0 || out.length === 0) flush();
|
|
7605
7806
|
return out;
|
|
7606
7807
|
}
|
|
7607
|
-
function
|
|
7608
|
-
|
|
7609
|
-
|
|
7610
|
-
|
|
7611
|
-
|
|
7612
|
-
|
|
7613
|
-
|
|
7614
|
-
|
|
7615
|
-
|
|
7616
|
-
const
|
|
7617
|
-
|
|
7618
|
-
|
|
7619
|
-
|
|
7620
|
-
|
|
7621
|
-
|
|
7622
|
-
|
|
7623
|
-
|
|
7624
|
-
|
|
7625
|
-
|
|
7808
|
+
function buildTableLines(headers, alignsIn, rowsIn, innerW) {
|
|
7809
|
+
const ncols = headers.length;
|
|
7810
|
+
if (ncols === 0) return [];
|
|
7811
|
+
const aligns = [];
|
|
7812
|
+
for (let i = 0; i < ncols; i++) {
|
|
7813
|
+
const a = alignsIn[i];
|
|
7814
|
+
aligns.push(a === void 0 ? "left" : a);
|
|
7815
|
+
}
|
|
7816
|
+
const rows = rowsIn.map((r) => {
|
|
7817
|
+
const out2 = [];
|
|
7818
|
+
for (let i = 0; i < ncols; i++) {
|
|
7819
|
+
const c = r[i];
|
|
7820
|
+
out2.push(c === void 0 ? "" : c);
|
|
7821
|
+
}
|
|
7822
|
+
return out2;
|
|
7823
|
+
});
|
|
7824
|
+
const naturalW = new Array(ncols).fill(0);
|
|
7825
|
+
const measure = (cells) => {
|
|
7826
|
+
for (let i = 0; i < ncols; i++) {
|
|
7827
|
+
const w = stringWidth6(cleanMarkdown(cells[i]));
|
|
7828
|
+
if (w > naturalW[i]) naturalW[i] = w;
|
|
7829
|
+
}
|
|
7830
|
+
};
|
|
7831
|
+
measure(headers);
|
|
7832
|
+
for (const r of rows) measure(r);
|
|
7833
|
+
const margin = 2;
|
|
7834
|
+
const overhead = 1 + ncols * 3;
|
|
7835
|
+
const available = innerW - margin - overhead;
|
|
7836
|
+
const minColW = 3;
|
|
7837
|
+
if (available < ncols * minColW) {
|
|
7838
|
+
return [[{ text: " (table too narrow to render)", fg: GLOAM.fg4, italic: true }]];
|
|
7839
|
+
}
|
|
7840
|
+
const colW = [...naturalW];
|
|
7841
|
+
for (let i = 0; i < ncols; i++) if (colW[i] < minColW) colW[i] = minColW;
|
|
7842
|
+
let total = colW.reduce((a, b) => a + b, 0);
|
|
7843
|
+
if (total > available) {
|
|
7844
|
+
while (total > available) {
|
|
7845
|
+
let widest = 0;
|
|
7846
|
+
for (let i = 1; i < ncols; i++) if (colW[i] > colW[widest]) widest = i;
|
|
7847
|
+
if (colW[widest] <= minColW) break;
|
|
7848
|
+
colW[widest]--;
|
|
7849
|
+
total--;
|
|
7850
|
+
}
|
|
7851
|
+
} else if (total < available) {
|
|
7852
|
+
while (total < available) {
|
|
7853
|
+
let widest = 0;
|
|
7854
|
+
for (let i = 1; i < ncols; i++) if (colW[i] > colW[widest]) widest = i;
|
|
7855
|
+
colW[widest]++;
|
|
7856
|
+
total++;
|
|
7626
7857
|
}
|
|
7627
7858
|
}
|
|
7628
|
-
|
|
7859
|
+
const borderFg = GLOAM.fg3;
|
|
7860
|
+
const headerFg = GLOAM.fg0;
|
|
7861
|
+
const headerBg = GLOAM.bg_bg1;
|
|
7862
|
+
const cellFg = GLOAM.fg1;
|
|
7863
|
+
const marginText = " ";
|
|
7864
|
+
const buildBorder = (left, mid, right) => {
|
|
7865
|
+
let s = left;
|
|
7866
|
+
for (let i = 0; i < ncols; i++) {
|
|
7867
|
+
s += "\u2500".repeat(colW[i] + 2);
|
|
7868
|
+
s += i === ncols - 1 ? right : mid;
|
|
7869
|
+
}
|
|
7870
|
+
return [{ text: marginText }, { text: s, fg: borderFg }];
|
|
7871
|
+
};
|
|
7872
|
+
const buildDataRow = (cells, header) => {
|
|
7873
|
+
const wrapped = [];
|
|
7874
|
+
for (let i = 0; i < ncols; i++) {
|
|
7875
|
+
const cell = cells[i];
|
|
7876
|
+
wrapped.push(wrapCell(cell === void 0 ? "" : cell, colW[i], aligns[i]));
|
|
7877
|
+
}
|
|
7878
|
+
let height = 1;
|
|
7879
|
+
for (const w of wrapped) if (w.length > height) height = w.length;
|
|
7880
|
+
for (let i = 0; i < ncols; i++) {
|
|
7881
|
+
const blank = " ".repeat(colW[i]);
|
|
7882
|
+
while (wrapped[i].length < height) wrapped[i].push(blank);
|
|
7883
|
+
}
|
|
7884
|
+
const out2 = [];
|
|
7885
|
+
for (let row = 0; row < height; row++) {
|
|
7886
|
+
const segs = [
|
|
7887
|
+
{ text: marginText },
|
|
7888
|
+
{ text: "\u2502", fg: borderFg }
|
|
7889
|
+
];
|
|
7890
|
+
for (let i = 0; i < ncols; i++) {
|
|
7891
|
+
const padded = " " + wrapped[i][row] + " ";
|
|
7892
|
+
segs.push(
|
|
7893
|
+
header ? { text: padded, fg: headerFg, bg: headerBg, bold: true } : { text: padded, fg: cellFg }
|
|
7894
|
+
);
|
|
7895
|
+
segs.push({ text: "\u2502", fg: borderFg });
|
|
7896
|
+
}
|
|
7897
|
+
out2.push(segs);
|
|
7898
|
+
}
|
|
7899
|
+
return out2;
|
|
7900
|
+
};
|
|
7901
|
+
const out = [];
|
|
7902
|
+
out.push(buildBorder("\u250C", "\u252C", "\u2510"));
|
|
7903
|
+
for (const dl of buildDataRow(headers, true)) out.push(dl);
|
|
7904
|
+
out.push(buildBorder("\u251C", "\u253C", "\u2524"));
|
|
7905
|
+
for (const r of rows) for (const dl of buildDataRow(r, false)) out.push(dl);
|
|
7906
|
+
out.push(buildBorder("\u2514", "\u2534", "\u2518"));
|
|
7907
|
+
return out;
|
|
7629
7908
|
}
|
|
7630
|
-
function
|
|
7631
|
-
if (width <= 0) return [segs];
|
|
7632
|
-
const atoms = segsToAtoms(segs);
|
|
7633
|
-
if (atoms.length === 0) return [[{ text: "" }]];
|
|
7909
|
+
function buildHighlightedMarkdownLines(content, innerW) {
|
|
7634
7910
|
const lines = [];
|
|
7635
|
-
|
|
7636
|
-
|
|
7637
|
-
|
|
7638
|
-
|
|
7639
|
-
|
|
7640
|
-
|
|
7641
|
-
|
|
7642
|
-
|
|
7643
|
-
|
|
7644
|
-
|
|
7645
|
-
|
|
7646
|
-
|
|
7647
|
-
|
|
7911
|
+
const clean = stripFrontmatter(content);
|
|
7912
|
+
if (!clean.trim()) {
|
|
7913
|
+
lines.push([{ text: " (empty)", fg: GLOAM.fg4, italic: true }]);
|
|
7914
|
+
return lines;
|
|
7915
|
+
}
|
|
7916
|
+
const rawLines = clean.split("\n");
|
|
7917
|
+
let inCodeBlock = false;
|
|
7918
|
+
for (let li = 0; li < rawLines.length; li++) {
|
|
7919
|
+
const raw = rawLines[li];
|
|
7920
|
+
const trimmed = raw.trim();
|
|
7921
|
+
if (/^```/.test(trimmed)) {
|
|
7922
|
+
inCodeBlock = !inCodeBlock;
|
|
7923
|
+
lines.push(buildCodeFenceLine(trimmed, innerW));
|
|
7924
|
+
continue;
|
|
7648
7925
|
}
|
|
7649
|
-
|
|
7650
|
-
|
|
7651
|
-
currentWidth = 0;
|
|
7652
|
-
};
|
|
7653
|
-
for (let i = 0; i < atoms.length; i++) {
|
|
7654
|
-
const atom = atoms[i];
|
|
7655
|
-
if (atom.space) {
|
|
7656
|
-
if (currentWidth + atom.width <= width) pushAtom(atom);
|
|
7926
|
+
if (inCodeBlock) {
|
|
7927
|
+
lines.push(buildCodeLine(raw.replace(/\t/g, " "), innerW));
|
|
7657
7928
|
continue;
|
|
7658
7929
|
}
|
|
7659
|
-
if (
|
|
7660
|
-
|
|
7661
|
-
|
|
7662
|
-
|
|
7663
|
-
|
|
7664
|
-
|
|
7665
|
-
|
|
7666
|
-
|
|
7667
|
-
|
|
7668
|
-
|
|
7669
|
-
|
|
7670
|
-
|
|
7671
|
-
|
|
7672
|
-
|
|
7673
|
-
for (let k = 0; k < remaining.length; k++) {
|
|
7674
|
-
const cw = stringWidth6(remaining[k]);
|
|
7675
|
-
if (cutW + cw > spaceLeft) break;
|
|
7676
|
-
cutW += cw;
|
|
7677
|
-
cut = k + 1;
|
|
7678
|
-
}
|
|
7679
|
-
if (cut === 0) {
|
|
7680
|
-
flushLine();
|
|
7681
|
-
if (contIndent) {
|
|
7682
|
-
current.push({ text: contIndent });
|
|
7683
|
-
currentWidth = stringWidth6(contIndent);
|
|
7684
|
-
}
|
|
7685
|
-
continue;
|
|
7686
|
-
}
|
|
7687
|
-
current.push({ ...atom.style, text: remaining.slice(0, cut) });
|
|
7688
|
-
currentWidth += cutW;
|
|
7689
|
-
remaining = remaining.slice(cut);
|
|
7690
|
-
if (remaining.length > 0) {
|
|
7691
|
-
flushLine();
|
|
7692
|
-
if (contIndent) {
|
|
7693
|
-
current.push({ text: contIndent });
|
|
7694
|
-
currentWidth = stringWidth6(contIndent);
|
|
7695
|
-
}
|
|
7930
|
+
if (trimmed.includes("|") && li + 1 < rawLines.length) {
|
|
7931
|
+
const sepLine = rawLines[li + 1];
|
|
7932
|
+
const sepAligns = parseTableSeparator(sepLine);
|
|
7933
|
+
if (sepAligns) {
|
|
7934
|
+
const headers = parseTableCells(raw);
|
|
7935
|
+
const tRows = [];
|
|
7936
|
+
let j = li + 2;
|
|
7937
|
+
while (j < rawLines.length) {
|
|
7938
|
+
const next = rawLines[j];
|
|
7939
|
+
if (next.trim() === "") break;
|
|
7940
|
+
if (!next.includes("|")) break;
|
|
7941
|
+
if (/^```/.test(next.trim())) break;
|
|
7942
|
+
tRows.push(parseTableCells(next));
|
|
7943
|
+
j++;
|
|
7696
7944
|
}
|
|
7945
|
+
for (const tl of buildTableLines(headers, sepAligns, tRows, innerW)) lines.push(tl);
|
|
7946
|
+
li = j - 1;
|
|
7947
|
+
continue;
|
|
7697
7948
|
}
|
|
7949
|
+
}
|
|
7950
|
+
if (trimmed === "") {
|
|
7951
|
+
const last = lines[lines.length - 1];
|
|
7952
|
+
if (last && last.length === 1 && last[0].text === "") continue;
|
|
7953
|
+
lines.push([{ text: "" }]);
|
|
7698
7954
|
continue;
|
|
7699
7955
|
}
|
|
7700
|
-
if (
|
|
7701
|
-
|
|
7702
|
-
|
|
7703
|
-
|
|
7704
|
-
|
|
7956
|
+
if (trimmed === "---") {
|
|
7957
|
+
lines.push(buildHrLine(innerW));
|
|
7958
|
+
continue;
|
|
7959
|
+
}
|
|
7960
|
+
const headMatch = raw.match(/^(\s*)(#{1,6})\s+(.+?)\s*#*\s*$/);
|
|
7961
|
+
if (headMatch) {
|
|
7962
|
+
const level = headMatch[2].length;
|
|
7963
|
+
lines.push(buildHeadingLine(level, headMatch[3], innerW));
|
|
7964
|
+
continue;
|
|
7965
|
+
}
|
|
7966
|
+
if (/^\s*([-*_])(\s*\1){2,}\s*$/.test(raw)) {
|
|
7967
|
+
lines.push(buildHrLine(innerW));
|
|
7968
|
+
continue;
|
|
7969
|
+
}
|
|
7970
|
+
const cbMatch = raw.match(/^\s*[-*+]\s+\[( |x|X)\]\s+(.+)$/);
|
|
7971
|
+
if (cbMatch) {
|
|
7972
|
+
const checked = cbMatch[1] !== " ";
|
|
7973
|
+
for (const wl of buildCheckboxLine(checked, cbMatch[2], innerW)) lines.push(wl);
|
|
7974
|
+
continue;
|
|
7975
|
+
}
|
|
7976
|
+
const numMatch = raw.match(/^(\s*)(\d+)([.)])\s+(.+)$/);
|
|
7977
|
+
if (numMatch) {
|
|
7978
|
+
const indent = numMatch[1].length > 0 ? " " : " ";
|
|
7979
|
+
const marker = `${numMatch[2]}${numMatch[3]}`;
|
|
7980
|
+
for (const wl of buildListLine(marker, numMatch[4], innerW, indent, GLOAM.purple)) {
|
|
7981
|
+
lines.push(wl);
|
|
7705
7982
|
}
|
|
7983
|
+
continue;
|
|
7706
7984
|
}
|
|
7707
|
-
|
|
7985
|
+
const bulMatch = raw.match(/^(\s*)([-*+])\s+(.+)$/);
|
|
7986
|
+
if (bulMatch) {
|
|
7987
|
+
const depth = Math.floor(bulMatch[1].length / 2);
|
|
7988
|
+
const indent = " " + " ".repeat(Math.min(depth, 4));
|
|
7989
|
+
const bullet = depth === 0 ? "\xB7" : "\u25E6";
|
|
7990
|
+
for (const wl of buildListLine(bullet, bulMatch[3], innerW, indent, GLOAM.orange)) {
|
|
7991
|
+
lines.push(wl);
|
|
7992
|
+
}
|
|
7993
|
+
continue;
|
|
7994
|
+
}
|
|
7995
|
+
const qMatch = raw.match(/^\s*>\s?(.*)$/);
|
|
7996
|
+
if (qMatch) {
|
|
7997
|
+
for (const wl of buildQuoteLine(qMatch[1], innerW)) lines.push(wl);
|
|
7998
|
+
continue;
|
|
7999
|
+
}
|
|
8000
|
+
for (const wl of buildParagraphLines(trimmed, innerW)) lines.push(wl);
|
|
7708
8001
|
}
|
|
7709
|
-
|
|
7710
|
-
|
|
7711
|
-
|
|
7712
|
-
|
|
7713
|
-
const style = HEADING_STYLES[Math.min(level - 1, HEADING_STYLES.length - 1)];
|
|
7714
|
-
const cleanedText = stripDisplayHazards(rawText).trim();
|
|
7715
|
-
const marker = "#".repeat(level);
|
|
7716
|
-
const prefix = " ";
|
|
7717
|
-
const sep = " ";
|
|
7718
|
-
const headerSegs = [
|
|
7719
|
-
{ text: prefix, bg: style.bg },
|
|
7720
|
-
{ text: marker, fg: style.markerFg, bg: style.bg, bold: true },
|
|
7721
|
-
{ text: sep, bg: style.bg },
|
|
7722
|
-
{ text: cleanedText, fg: style.textFg, bg: style.bg, bold: true }
|
|
7723
|
-
];
|
|
7724
|
-
const used = segsDisplayWidth(headerSegs);
|
|
7725
|
-
const padW = Math.max(0, innerW - used);
|
|
7726
|
-
if (padW > 0) {
|
|
7727
|
-
headerSegs.push({ text: " ".repeat(padW), bg: style.bg });
|
|
8002
|
+
while (lines.length > 0) {
|
|
8003
|
+
const last = lines[lines.length - 1];
|
|
8004
|
+
if (last.length === 1 && last[0].text === "") lines.pop();
|
|
8005
|
+
else break;
|
|
7728
8006
|
}
|
|
7729
|
-
return
|
|
7730
|
-
}
|
|
7731
|
-
function buildListLine(marker, body, innerW, prefixIndent, markerFg) {
|
|
7732
|
-
const indent = `${prefixIndent} `;
|
|
7733
|
-
const head = [
|
|
7734
|
-
{ text: prefixIndent, fg: GLOAM.fg2 },
|
|
7735
|
-
{ text: marker, fg: markerFg, bold: true },
|
|
7736
|
-
{ text: " ", fg: GLOAM.fg2 }
|
|
7737
|
-
];
|
|
7738
|
-
const headW = segsDisplayWidth(head);
|
|
7739
|
-
const bodySegs = tokenizeInline(stripDisplayHazards(body), GLOAM.fg1);
|
|
7740
|
-
const wrapped = wrapSegs([...head, ...bodySegs], innerW, indent);
|
|
7741
|
-
return wrapped;
|
|
8007
|
+
return lines;
|
|
7742
8008
|
}
|
|
7743
|
-
|
|
7744
|
-
|
|
7745
|
-
|
|
7746
|
-
|
|
7747
|
-
|
|
7748
|
-
|
|
7749
|
-
|
|
7750
|
-
|
|
7751
|
-
|
|
7752
|
-
|
|
7753
|
-
|
|
7754
|
-
|
|
8009
|
+
|
|
8010
|
+
// src/tui/panels/stacked-detail.ts
|
|
8011
|
+
var HEADERS_ACTIVE = {
|
|
8012
|
+
top: "GOAL",
|
|
8013
|
+
middle: "STRATEGY",
|
|
8014
|
+
bottom: "ROADMAP"
|
|
8015
|
+
};
|
|
8016
|
+
var HEADERS_DONE = {
|
|
8017
|
+
top: "GOAL",
|
|
8018
|
+
middle: "COMPLETION",
|
|
8019
|
+
bottom: "SUMMARY"
|
|
8020
|
+
};
|
|
8021
|
+
function renderStackedDetailRows(rect, state2, detailCtx) {
|
|
8022
|
+
const focused = state2.focusPane === "detail";
|
|
8023
|
+
const { w, h } = rect;
|
|
8024
|
+
const innerW = w - 4;
|
|
8025
|
+
const cursorNode = detailCtx.nodes[state2.cursorIndex];
|
|
8026
|
+
if (!cursorNode || !state2.selectedSession || cursorNode.sessionId !== state2.selectedSession.id) {
|
|
8027
|
+
return buildEmptyPanelRows(rect, focused, "gray", "\x1B[2mSelect a session\x1B[0m");
|
|
8028
|
+
}
|
|
8029
|
+
if (state2.detailMode === "cycle-log") {
|
|
8030
|
+
return renderCycleLogMode(rect, state2, focused);
|
|
8031
|
+
}
|
|
8032
|
+
const session = state2.selectedSession;
|
|
8033
|
+
const isDone = session.status === "completed";
|
|
8034
|
+
const headers = isDone ? HEADERS_DONE : HEADERS_ACTIVE;
|
|
8035
|
+
const middleContent = isDone ? buildCompletionContent(session) : state2.strategyContent;
|
|
8036
|
+
const bottomContent = isDone ? pickSummaryContent(state2) : state2.planContent;
|
|
8037
|
+
const cacheKey = [
|
|
8038
|
+
cursorNode.sessionId,
|
|
8039
|
+
rect.w,
|
|
8040
|
+
isDone ? "done" : "active",
|
|
8041
|
+
state2.goalContent.length,
|
|
8042
|
+
middleContent.length,
|
|
8043
|
+
bottomContent.length
|
|
8044
|
+
].join(":");
|
|
8045
|
+
let lines = state2.cachedStackedLines;
|
|
8046
|
+
if (cacheKey !== state2.stackedCacheKey || lines === null) {
|
|
8047
|
+
lines = {
|
|
8048
|
+
goal: buildSectionLines(state2.goalContent, innerW),
|
|
8049
|
+
strategy: buildSectionLines(middleContent, innerW),
|
|
8050
|
+
roadmap: buildSectionLines(bottomContent, innerW),
|
|
8051
|
+
cycleLog: []
|
|
8052
|
+
};
|
|
8053
|
+
state2.cachedStackedLines = lines;
|
|
8054
|
+
state2.stackedCacheKey = cacheKey;
|
|
8055
|
+
}
|
|
8056
|
+
const heights = allocateStripHeights(h, lines.goal.length, lines.strategy.length, lines.roadmap.length);
|
|
8057
|
+
const rows = new Array(h);
|
|
8058
|
+
const focusColor = focused ? "cyan" : "gray";
|
|
8059
|
+
const sgr = `\x1B[${colorToSGR(focusColor)}m`;
|
|
8060
|
+
const reset = "\x1B[0m";
|
|
8061
|
+
rows[0] = sgr + "\u256D" + "\u2500".repeat(w - 2) + "\u256E" + reset;
|
|
8062
|
+
rows[h - 1] = sgr + "\u2570" + "\u2500".repeat(w - 2) + "\u256F" + reset;
|
|
8063
|
+
let cursor = 1;
|
|
8064
|
+
cursor = paintStrip(
|
|
8065
|
+
rows,
|
|
8066
|
+
cursor,
|
|
8067
|
+
w,
|
|
8068
|
+
sgr,
|
|
8069
|
+
reset,
|
|
8070
|
+
headers.top,
|
|
8071
|
+
lines.goal,
|
|
8072
|
+
state2.goalScroll,
|
|
8073
|
+
heights.goalHeight,
|
|
8074
|
+
state2.focusedStrip === "goal" && focused
|
|
8075
|
+
);
|
|
8076
|
+
rows[cursor++] = sgr + "\u251C" + "\u2500".repeat(w - 2) + "\u2524" + reset;
|
|
8077
|
+
cursor = paintStrip(
|
|
8078
|
+
rows,
|
|
8079
|
+
cursor,
|
|
8080
|
+
w,
|
|
8081
|
+
sgr,
|
|
8082
|
+
reset,
|
|
8083
|
+
headers.middle,
|
|
8084
|
+
lines.strategy,
|
|
8085
|
+
state2.strategyScroll,
|
|
8086
|
+
heights.stratHeight,
|
|
8087
|
+
state2.focusedStrip === "strategy" && focused
|
|
8088
|
+
);
|
|
8089
|
+
rows[cursor++] = sgr + "\u251C" + "\u2500".repeat(w - 2) + "\u2524" + reset;
|
|
8090
|
+
paintStrip(
|
|
8091
|
+
rows,
|
|
8092
|
+
cursor,
|
|
8093
|
+
w,
|
|
8094
|
+
sgr,
|
|
8095
|
+
reset,
|
|
8096
|
+
headers.bottom,
|
|
8097
|
+
lines.roadmap,
|
|
8098
|
+
state2.roadmapScroll,
|
|
8099
|
+
heights.roadHeight,
|
|
8100
|
+
state2.focusedStrip === "roadmap" && focused
|
|
8101
|
+
);
|
|
8102
|
+
return rows;
|
|
7755
8103
|
}
|
|
7756
|
-
function
|
|
7757
|
-
const
|
|
7758
|
-
|
|
7759
|
-
{
|
|
7760
|
-
|
|
7761
|
-
|
|
7762
|
-
|
|
8104
|
+
function buildCompletionContent(session) {
|
|
8105
|
+
const parts = [];
|
|
8106
|
+
if (session.completedAt) {
|
|
8107
|
+
parts.push(`*completed ${formatTimestamp(session.completedAt)}*`);
|
|
8108
|
+
parts.push("");
|
|
8109
|
+
}
|
|
8110
|
+
if (session.completionReport && session.completionReport.trim()) {
|
|
8111
|
+
parts.push(session.completionReport.trim());
|
|
8112
|
+
} else {
|
|
8113
|
+
parts.push("_No completion report written._");
|
|
8114
|
+
}
|
|
8115
|
+
return parts.join("\n");
|
|
7763
8116
|
}
|
|
7764
|
-
function
|
|
7765
|
-
|
|
7766
|
-
|
|
7767
|
-
|
|
7768
|
-
|
|
7769
|
-
|
|
8117
|
+
function pickSummaryContent(state2) {
|
|
8118
|
+
if (state2.completionSummaryContent.trim()) return state2.completionSummaryContent;
|
|
8119
|
+
if (state2.logsCycles.length > 0) {
|
|
8120
|
+
const last = state2.logsCycles[state2.logsCycles.length - 1];
|
|
8121
|
+
return `# Cycle ${last.cycle} log
|
|
8122
|
+
|
|
8123
|
+
${last.content}`;
|
|
8124
|
+
}
|
|
8125
|
+
if (state2.strategyContent.trim()) return state2.strategyContent;
|
|
8126
|
+
return "_No completion artifacts. Try expanding context/ in the tree for raw files._";
|
|
7770
8127
|
}
|
|
7771
|
-
function
|
|
7772
|
-
|
|
7773
|
-
|
|
7774
|
-
|
|
7775
|
-
|
|
7776
|
-
|
|
7777
|
-
|
|
7778
|
-
|
|
7779
|
-
];
|
|
8128
|
+
function formatTimestamp(iso) {
|
|
8129
|
+
try {
|
|
8130
|
+
const d = new Date(iso);
|
|
8131
|
+
if (isNaN(d.getTime())) return iso;
|
|
8132
|
+
return d.toLocaleString();
|
|
8133
|
+
} catch {
|
|
8134
|
+
return iso;
|
|
8135
|
+
}
|
|
7780
8136
|
}
|
|
7781
|
-
function
|
|
7782
|
-
|
|
7783
|
-
const cw = stringWidth6(cleaned);
|
|
7784
|
-
const padW = Math.max(0, innerW - 2 - cw);
|
|
7785
|
-
return [
|
|
7786
|
-
{ text: " ", bg: GLOAM.bg_bg1 },
|
|
7787
|
-
{ text: cleaned, fg: GLOAM.aqua, bg: GLOAM.bg_bg1 },
|
|
7788
|
-
...padW > 0 ? [{ text: " ".repeat(padW), bg: GLOAM.bg_bg1 }] : []
|
|
7789
|
-
];
|
|
8137
|
+
function buildSectionLines(content, innerW) {
|
|
8138
|
+
return buildHighlightedMarkdownLines(content, innerW);
|
|
7790
8139
|
}
|
|
7791
|
-
function
|
|
7792
|
-
const
|
|
7793
|
-
const
|
|
7794
|
-
|
|
8140
|
+
function allocateStripHeights(rectH, gN, sN, _rN) {
|
|
8141
|
+
const innerH = rectH - 2;
|
|
8142
|
+
const stripsAvail = innerH - 2;
|
|
8143
|
+
const goalCap = Math.floor(stripsAvail * 0.2);
|
|
8144
|
+
const stratCap = Math.floor(stripsAvail * 0.4);
|
|
8145
|
+
const roadCap = stripsAvail - goalCap - stratCap;
|
|
8146
|
+
const goalNeed = Math.min(gN + 1, goalCap);
|
|
8147
|
+
const stratNeed = Math.min(sN + 1, stratCap);
|
|
8148
|
+
const slack = goalCap - goalNeed + (stratCap - stratNeed);
|
|
8149
|
+
return {
|
|
8150
|
+
goalHeight: Math.max(2, goalNeed),
|
|
8151
|
+
stratHeight: Math.max(2, stratNeed),
|
|
8152
|
+
roadHeight: Math.max(2, roadCap + slack)
|
|
8153
|
+
};
|
|
7795
8154
|
}
|
|
7796
|
-
function
|
|
7797
|
-
|
|
7798
|
-
|
|
7799
|
-
|
|
7800
|
-
const
|
|
7801
|
-
|
|
7802
|
-
|
|
7803
|
-
|
|
7804
|
-
|
|
7805
|
-
|
|
7806
|
-
|
|
7807
|
-
|
|
7808
|
-
|
|
7809
|
-
|
|
7810
|
-
|
|
7811
|
-
|
|
7812
|
-
|
|
8155
|
+
function paintStrip(rows, startRow, w, sgr, reset, label, lines, scroll, height, focused) {
|
|
8156
|
+
const innerW = w - 4;
|
|
8157
|
+
const borderL = sgr + "\u2502" + reset + " ";
|
|
8158
|
+
const borderR = " " + sgr + "\u2502" + reset;
|
|
8159
|
+
const headerSeg = `\x1B[${colorToSGR("yellow")};1m \u258E ${label}\x1B[0m`;
|
|
8160
|
+
const headerClipped = clipAnsi(headerSeg + (focused ? " \u25C0" : ""), innerW);
|
|
8161
|
+
rows[startRow] = borderL + headerClipped + borderR;
|
|
8162
|
+
const contentH = height - 1;
|
|
8163
|
+
const hasOverflow = lines.length > contentH;
|
|
8164
|
+
const viewableH = hasOverflow ? contentH - 1 : contentH;
|
|
8165
|
+
const maxScroll = Math.max(0, lines.length - viewableH);
|
|
8166
|
+
scroll.setMax(maxScroll);
|
|
8167
|
+
const effOffset = scroll.offset;
|
|
8168
|
+
for (let i = 0; i < viewableH; i++) {
|
|
8169
|
+
const li = effOffset + i;
|
|
8170
|
+
const ansi = li < lines.length ? renderLine(lines[li]) : "";
|
|
8171
|
+
rows[startRow + 1 + i] = borderL + clipAnsi(ansi, innerW) + borderR;
|
|
7813
8172
|
}
|
|
7814
|
-
|
|
7815
|
-
|
|
7816
|
-
}
|
|
7817
|
-
|
|
7818
|
-
if (!line.includes("|") && !/^[\s:|+-]+$/.test(line)) return null;
|
|
7819
|
-
const cells = parseTableCells(line);
|
|
7820
|
-
if (cells.length === 0) return null;
|
|
7821
|
-
const aligns = [];
|
|
7822
|
-
for (const c of cells) {
|
|
7823
|
-
const m = c.match(/^(:?)\s*-{2,}\s*(:?)$/);
|
|
7824
|
-
if (!m) return null;
|
|
7825
|
-
if (m[1] === ":" && m[2] === ":") aligns.push("center");
|
|
7826
|
-
else if (m[2] === ":") aligns.push("right");
|
|
7827
|
-
else aligns.push("left");
|
|
8173
|
+
if (hasOverflow) {
|
|
8174
|
+
const pct = maxScroll > 0 ? Math.round(effOffset / maxScroll * 100) : 100;
|
|
8175
|
+
const indicator = `\x1B[2m \u2195 ${pct}% \xB7 ${lines.length} lines\x1B[0m`;
|
|
8176
|
+
rows[startRow + 1 + viewableH] = borderL + clipAnsi(indicator, innerW) + borderR;
|
|
7828
8177
|
}
|
|
7829
|
-
return
|
|
8178
|
+
return startRow + height;
|
|
7830
8179
|
}
|
|
7831
|
-
function
|
|
7832
|
-
|
|
7833
|
-
|
|
7834
|
-
|
|
7835
|
-
|
|
7836
|
-
|
|
7837
|
-
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
|
|
8180
|
+
function renderCycleLogMode(rect, state2, focused) {
|
|
8181
|
+
if (state2.logsCycles.length === 0) {
|
|
8182
|
+
return buildEmptyPanelRows(rect, focused, "gray", "\x1B[2mNo cycle logs yet\x1B[0m");
|
|
8183
|
+
}
|
|
8184
|
+
const cacheKey = `cycleLog:${state2.logsCycles.length}:${rect.w}`;
|
|
8185
|
+
let lines;
|
|
8186
|
+
if (cacheKey === state2.stackedCacheKey && state2.cachedStackedLines !== null) {
|
|
8187
|
+
lines = state2.cachedStackedLines.cycleLog;
|
|
8188
|
+
} else {
|
|
8189
|
+
lines = buildLogsLines(state2.logsCycles, rect.w);
|
|
8190
|
+
const existing = state2.cachedStackedLines ?? { goal: [], strategy: [], roadmap: [], cycleLog: [] };
|
|
8191
|
+
state2.cachedStackedLines = { ...existing, cycleLog: lines };
|
|
8192
|
+
state2.stackedCacheKey = cacheKey;
|
|
8193
|
+
}
|
|
8194
|
+
return buildPanelRows(rect, lines, state2.detailScroll, focused, "cyan", state2.stackedRenderedCache);
|
|
7842
8195
|
}
|
|
7843
|
-
|
|
7844
|
-
|
|
7845
|
-
|
|
7846
|
-
|
|
7847
|
-
|
|
7848
|
-
|
|
7849
|
-
|
|
7850
|
-
|
|
7851
|
-
|
|
7852
|
-
|
|
7853
|
-
|
|
7854
|
-
|
|
7855
|
-
|
|
7856
|
-
|
|
7857
|
-
|
|
7858
|
-
|
|
7859
|
-
|
|
7860
|
-
|
|
7861
|
-
|
|
7862
|
-
|
|
7863
|
-
|
|
7864
|
-
|
|
7865
|
-
|
|
7866
|
-
|
|
7867
|
-
|
|
7868
|
-
|
|
7869
|
-
|
|
7870
|
-
|
|
7871
|
-
|
|
7872
|
-
|
|
7873
|
-
|
|
7874
|
-
|
|
7875
|
-
|
|
7876
|
-
|
|
7877
|
-
|
|
7878
|
-
|
|
7879
|
-
|
|
7880
|
-
|
|
7881
|
-
|
|
7882
|
-
|
|
7883
|
-
|
|
7884
|
-
if (cut === 0) cut = 1;
|
|
7885
|
-
const slice = rem.slice(0, cut);
|
|
7886
|
-
if (cut === rem.length) {
|
|
7887
|
-
cur = slice;
|
|
7888
|
-
curW = stringWidth6(slice);
|
|
7889
|
-
} else {
|
|
7890
|
-
out.push(padCell(slice, width, align));
|
|
7891
|
-
}
|
|
7892
|
-
rem = rem.slice(cut);
|
|
7893
|
-
}
|
|
7894
|
-
continue;
|
|
8196
|
+
|
|
8197
|
+
// src/tui/panels/bottom.ts
|
|
8198
|
+
init_render();
|
|
8199
|
+
var B = ansiBold;
|
|
8200
|
+
var D = ansiDim;
|
|
8201
|
+
var SEP = D("\u2502 ");
|
|
8202
|
+
var DANGER_BADGE = "\x1B[1;41;97m DANGEROUS \x1B[0m";
|
|
8203
|
+
function renderStatusLine(buf, y, state2, cursorNodeType) {
|
|
8204
|
+
const { mode, focusPane, notification, error } = state2;
|
|
8205
|
+
if (mode === "report-detail") return;
|
|
8206
|
+
let content;
|
|
8207
|
+
if (notification !== null) {
|
|
8208
|
+
const icon = /error|failed/i.test(notification) ? "\u2715" : /success|created|killed|sent|copied|deleted/i.test(notification) ? "\u2713" : "\u2139";
|
|
8209
|
+
content = `\x1B[1;33m${icon} ${notification}\x1B[0m`;
|
|
8210
|
+
} else if (error !== null) {
|
|
8211
|
+
content = `\x1B[31m\u26A0 ${error}\x1B[0m`;
|
|
8212
|
+
} else if (mode === "search") {
|
|
8213
|
+
const cursor = `\x1B[7m \x1B[0m`;
|
|
8214
|
+
content = `\x1B[1;34m/\x1B[0m${state2.searchText}${cursor}` + D(" enter to apply \xB7 esc to clear");
|
|
8215
|
+
} else if (mode === "leader") {
|
|
8216
|
+
content = `\x1B[1;35mLEADER\x1B[0m` + D(" [c]opy [o]pen [a]gent [S]ession [g]o or [s]cycle [h]ome [n]ew [m]sg [t]status [l]picker [x]kill [/]search [?]help [esc] cancel");
|
|
8217
|
+
} else if (mode === "copy-menu") {
|
|
8218
|
+
content = `\x1B[1;36mCOPY\x1B[0m` + D(" [p]ath [i]d [c] context [l]ogs [r]eport [a]gent ID [esc] cancel");
|
|
8219
|
+
} else if (mode === "open-menu") {
|
|
8220
|
+
content = `\x1B[1;32mOPEN\x1B[0m` + D(" [g]oal [r]oadmap [s]trategy [l]ogs [d]ir [R]eport [c]scratch [e]dit context [esc] cancel");
|
|
8221
|
+
} else if (mode === "agent-menu") {
|
|
8222
|
+
content = `\x1B[1;34mAGENT\x1B[0m` + D(" [s]pawn [m]sg [r]estart [R]erun [j]ump [o]pen-claude [t]ail [k]ill [e]xplore [d]ebug [esc] cancel");
|
|
8223
|
+
} else if (mode === "session-menu") {
|
|
8224
|
+
content = `\x1B[1;31mSESSION\x1B[0m` + D(" [n]ew [r]esume [c]ontinue [b]ollback [k]ill [d]elete [e]xport [w]indow [C]lone [i]history [esc] cancel");
|
|
8225
|
+
} else if (mode === "go-menu") {
|
|
8226
|
+
content = `\x1B[1;33mGO\x1B[0m` + D(" [w]indow [p]ane [s]ession [n]ext [r]econnect [esc] cancel");
|
|
8227
|
+
} else if (mode === "help") {
|
|
8228
|
+
content = `\x1B[1;33mHELP\x1B[0m` + D(" [esc] or [?] to dismiss");
|
|
8229
|
+
} else if (focusPane === "logs" || focusPane === "detail") {
|
|
8230
|
+
content = B("[jk/\u2191\u2193]") + D(" scroll ") + B("[h/\u2190/tab]") + D(" back ") + B("[t]") + D("oggle view ") + B("[F]") + D("low \xB1 ") + SEP + B("[m]") + D("sg ") + B("[g]") + D("oal ") + B("[n]") + D("ew ") + B("[p]") + D("lan ") + B("[w]") + D("indow ") + B("[R]") + D("esume ") + B("[q]") + D("uit");
|
|
8231
|
+
} else if (cursorNodeType === "needs-you-virtual") {
|
|
8232
|
+
content = B("[enter]") + D(" open ask ") + B("[esc]") + D(" back ") + SEP + B("[q]") + D("uit");
|
|
8233
|
+
} else {
|
|
8234
|
+
let contextFilePart = "";
|
|
8235
|
+
if (cursorNodeType === "context-file") {
|
|
8236
|
+
contextFilePart = B("[e]") + D("dit ") + B("[\u23CE]") + D(" open ");
|
|
7895
8237
|
}
|
|
7896
|
-
|
|
7897
|
-
curW = pw;
|
|
8238
|
+
content = contextFilePart + B("[enter]") + D(" select ") + B("[m]") + D("essage ") + B("[n]") + D("ew ") + B("[w]") + D(" tmux ") + SEP + B("[q]") + D("uit");
|
|
7898
8239
|
}
|
|
7899
|
-
if (
|
|
7900
|
-
|
|
8240
|
+
if (state2.selectedSession?.dangerousMode === true) {
|
|
8241
|
+
content = `${DANGER_BADGE} ${content}`;
|
|
8242
|
+
}
|
|
8243
|
+
writeClipped(buf, 1, y, content, buf.width - 2);
|
|
7901
8244
|
}
|
|
7902
|
-
|
|
7903
|
-
|
|
7904
|
-
|
|
7905
|
-
|
|
7906
|
-
|
|
7907
|
-
|
|
7908
|
-
|
|
8245
|
+
|
|
8246
|
+
// src/tui/panels/inbox-deck.ts
|
|
8247
|
+
init_render();
|
|
8248
|
+
import stringWidth7 from "string-width";
|
|
8249
|
+
import { readFileSync as readFileSync15 } from "fs";
|
|
8250
|
+
|
|
8251
|
+
// src/tui/panels/mounted-humanloop.ts
|
|
8252
|
+
init_ask_store();
|
|
8253
|
+
init_paths();
|
|
8254
|
+
import { readFileSync as readFileSync14, watchFile, unwatchFile } from "fs";
|
|
8255
|
+
import { mountPanel } from "@crouton-kit/humanloop";
|
|
8256
|
+
async function dispatchOrphanResolution(orphanTarget, selectedOptionId, deps) {
|
|
8257
|
+
if (selectedOptionId === "takeover" && orphanTarget.kind === "agent") {
|
|
8258
|
+
await deps.onOrphanTakeover?.({
|
|
8259
|
+
sessionId: deps.sessionId,
|
|
8260
|
+
agentId: orphanTarget.agentId,
|
|
8261
|
+
paneId: orphanTarget.paneId
|
|
8262
|
+
});
|
|
8263
|
+
} else if (selectedOptionId === "restart" && orphanTarget.kind === "agent") {
|
|
8264
|
+
await deps.daemonSend({ type: "restart-agent", sessionId: deps.sessionId, agentId: orphanTarget.agentId });
|
|
8265
|
+
} else if (selectedOptionId === "resume" && orphanTarget.kind === "orchestrator") {
|
|
8266
|
+
await deps.daemonSend({ type: "resume", sessionId: deps.sessionId, cwd: deps.cwd });
|
|
8267
|
+
} else if (selectedOptionId === "dismiss" && orphanTarget.kind === "orchestrator") {
|
|
8268
|
+
await deps.daemonSend({ type: "clear-orphan", sessionId: deps.sessionId, cwd: deps.cwd });
|
|
7909
8269
|
}
|
|
7910
|
-
|
|
7911
|
-
|
|
7912
|
-
|
|
7913
|
-
|
|
7914
|
-
|
|
8270
|
+
}
|
|
8271
|
+
function makeOrphanTakeover(state2, ops) {
|
|
8272
|
+
return async ({ sessionId: sessionId2, agentId, paneId }) => {
|
|
8273
|
+
const res = await ops.send({ type: "status", sessionId: sessionId2 });
|
|
8274
|
+
const sess = res.ok ? res.data?.session : void 0;
|
|
8275
|
+
if (!sess) {
|
|
8276
|
+
notify(state2, "Session not found");
|
|
8277
|
+
return;
|
|
7915
8278
|
}
|
|
7916
|
-
|
|
7917
|
-
|
|
7918
|
-
|
|
7919
|
-
|
|
7920
|
-
|
|
7921
|
-
const w = stringWidth6(cleanMarkdown(cells[i]));
|
|
7922
|
-
if (w > naturalW[i]) naturalW[i] = w;
|
|
8279
|
+
if (paneId && ops.paneExists(paneId)) {
|
|
8280
|
+
if (sess.tmuxSessionName) ops.switchToSession(sess.tmuxSessionName);
|
|
8281
|
+
if (sess.tmuxWindowId) ops.selectWindow(sess.tmuxWindowId);
|
|
8282
|
+
ops.selectPane(paneId);
|
|
8283
|
+
return;
|
|
7923
8284
|
}
|
|
8285
|
+
if (sess.tmuxSessionName) ops.switchToSession(sess.tmuxSessionName);
|
|
8286
|
+
notify(state2, `Pane ${paneId ? paneId : "?"} is gone \u2014 agent ${agentId} cannot be taken over.`);
|
|
7924
8287
|
};
|
|
7925
|
-
|
|
7926
|
-
|
|
7927
|
-
|
|
7928
|
-
|
|
7929
|
-
|
|
7930
|
-
const
|
|
7931
|
-
|
|
7932
|
-
return
|
|
8288
|
+
}
|
|
8289
|
+
function mountResolutionPanel(opts, state2) {
|
|
8290
|
+
let queue = [...opts.aggregateInbox];
|
|
8291
|
+
let currentIndex = opts.startIndex;
|
|
8292
|
+
let bodyCols = opts.cols;
|
|
8293
|
+
const item = () => queue[currentIndex];
|
|
8294
|
+
function itemCoords(it) {
|
|
8295
|
+
return {
|
|
8296
|
+
cwd: cwdFromDir(it.dir),
|
|
8297
|
+
sessionId: sessionIdFromDir(it.dir),
|
|
8298
|
+
askId: askIdFromDir(it.dir)
|
|
8299
|
+
};
|
|
7933
8300
|
}
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
|
|
7937
|
-
|
|
7938
|
-
|
|
7939
|
-
|
|
7940
|
-
|
|
7941
|
-
|
|
7942
|
-
|
|
7943
|
-
|
|
7944
|
-
}
|
|
7945
|
-
|
|
7946
|
-
while (total < available) {
|
|
7947
|
-
let widest = 0;
|
|
7948
|
-
for (let i = 1; i < ncols; i++) if (colW[i] > colW[widest]) widest = i;
|
|
7949
|
-
colW[widest]++;
|
|
7950
|
-
total++;
|
|
7951
|
-
}
|
|
8301
|
+
function buildDeck(idx) {
|
|
8302
|
+
const it = queue[idx];
|
|
8303
|
+
if (!it) return null;
|
|
8304
|
+
const { cwd: cwd2, sessionId: sessionId2, askId: askId2 } = itemCoords(it);
|
|
8305
|
+
const deck = readDecisions(cwd2, sessionId2, askId2);
|
|
8306
|
+
if (!deck) return null;
|
|
8307
|
+
deck.source = {
|
|
8308
|
+
sessionName: it.sessionName ?? it.source?.sessionName,
|
|
8309
|
+
askedBy: it.source?.askedBy,
|
|
8310
|
+
blockedSince: it.blockedSince
|
|
8311
|
+
};
|
|
8312
|
+
return deck;
|
|
7952
8313
|
}
|
|
7953
|
-
const
|
|
7954
|
-
const
|
|
7955
|
-
|
|
7956
|
-
|
|
7957
|
-
const
|
|
7958
|
-
|
|
7959
|
-
|
|
7960
|
-
|
|
7961
|
-
s += "\u2500".repeat(colW[i] + 2);
|
|
7962
|
-
s += i === ncols - 1 ? right : mid;
|
|
7963
|
-
}
|
|
7964
|
-
return [{ text: marginText }, { text: s, fg: borderFg }];
|
|
7965
|
-
};
|
|
7966
|
-
const buildDataRow = (cells, header) => {
|
|
7967
|
-
const wrapped = [];
|
|
7968
|
-
for (let i = 0; i < ncols; i++) {
|
|
7969
|
-
const cell = cells[i];
|
|
7970
|
-
wrapped.push(wrapCell(cell === void 0 ? "" : cell, colW[i], aligns[i]));
|
|
7971
|
-
}
|
|
7972
|
-
let height = 1;
|
|
7973
|
-
for (const w of wrapped) if (w.length > height) height = w.length;
|
|
7974
|
-
for (let i = 0; i < ncols; i++) {
|
|
7975
|
-
const blank = " ".repeat(colW[i]);
|
|
7976
|
-
while (wrapped[i].length < height) wrapped[i].push(blank);
|
|
7977
|
-
}
|
|
7978
|
-
const out2 = [];
|
|
7979
|
-
for (let row = 0; row < height; row++) {
|
|
7980
|
-
const segs = [
|
|
7981
|
-
{ text: marginText },
|
|
7982
|
-
{ text: "\u2502", fg: borderFg }
|
|
7983
|
-
];
|
|
7984
|
-
for (let i = 0; i < ncols; i++) {
|
|
7985
|
-
const padded = " " + wrapped[i][row] + " ";
|
|
7986
|
-
segs.push(
|
|
7987
|
-
header ? { text: padded, fg: headerFg, bg: headerBg, bold: true } : { text: padded, fg: cellFg }
|
|
7988
|
-
);
|
|
7989
|
-
segs.push({ text: "\u2502", fg: borderFg });
|
|
7990
|
-
}
|
|
7991
|
-
out2.push(segs);
|
|
7992
|
-
}
|
|
7993
|
-
return out2;
|
|
7994
|
-
};
|
|
7995
|
-
const out = [];
|
|
7996
|
-
out.push(buildBorder("\u250C", "\u252C", "\u2510"));
|
|
7997
|
-
for (const dl of buildDataRow(headers, true)) out.push(dl);
|
|
7998
|
-
out.push(buildBorder("\u251C", "\u253C", "\u2524"));
|
|
7999
|
-
for (const r of rows) for (const dl of buildDataRow(r, false)) out.push(dl);
|
|
8000
|
-
out.push(buildBorder("\u2514", "\u2534", "\u2518"));
|
|
8001
|
-
return out;
|
|
8002
|
-
}
|
|
8003
|
-
function buildHighlightedMarkdownLines(content, innerW) {
|
|
8004
|
-
const lines = [];
|
|
8005
|
-
const clean = stripFrontmatter(content);
|
|
8006
|
-
if (!clean.trim()) {
|
|
8007
|
-
lines.push([{ text: " (empty)", fg: GLOAM.fg4, italic: true }]);
|
|
8008
|
-
return lines;
|
|
8314
|
+
const { cwd: initCwd, sessionId: initSessionId, askId: initAskId } = itemCoords(item());
|
|
8315
|
+
const initialDeck = buildDeck(currentIndex);
|
|
8316
|
+
if (!initialDeck) return null;
|
|
8317
|
+
let currentDeck = initialDeck;
|
|
8318
|
+
const initialProgress = readProgress(initCwd, initSessionId, initAskId);
|
|
8319
|
+
let answeredCount = initialProgress?.responses.length ?? 0;
|
|
8320
|
+
function getCurrentQid() {
|
|
8321
|
+
return currentDeck.interactions[answeredCount]?.id ?? currentDeck.interactions[0]?.id;
|
|
8009
8322
|
}
|
|
8010
|
-
|
|
8011
|
-
|
|
8012
|
-
|
|
8013
|
-
const
|
|
8014
|
-
const
|
|
8015
|
-
|
|
8016
|
-
|
|
8017
|
-
|
|
8018
|
-
|
|
8019
|
-
|
|
8020
|
-
|
|
8021
|
-
|
|
8022
|
-
|
|
8023
|
-
|
|
8024
|
-
|
|
8025
|
-
|
|
8026
|
-
|
|
8027
|
-
|
|
8028
|
-
const
|
|
8029
|
-
|
|
8030
|
-
|
|
8031
|
-
|
|
8032
|
-
|
|
8033
|
-
if (next.trim() === "") break;
|
|
8034
|
-
if (!next.includes("|")) break;
|
|
8035
|
-
if (/^```/.test(next.trim())) break;
|
|
8036
|
-
tRows.push(parseTableCells(next));
|
|
8037
|
-
j++;
|
|
8038
|
-
}
|
|
8039
|
-
for (const tl of buildTableLines(headers, sepAligns, tRows, innerW)) lines.push(tl);
|
|
8040
|
-
li = j - 1;
|
|
8041
|
-
continue;
|
|
8323
|
+
function fireVisualGen(force) {
|
|
8324
|
+
const qid = getCurrentQid();
|
|
8325
|
+
if (!qid) return;
|
|
8326
|
+
const it = item();
|
|
8327
|
+
const { sessionId: itSessionId, askId: itAskId } = itemCoords(it);
|
|
8328
|
+
state2.visuals.set(qid, { status: "loading", content: "", visible: true });
|
|
8329
|
+
requestRender();
|
|
8330
|
+
void (async () => {
|
|
8331
|
+
const res = await rawSend({
|
|
8332
|
+
type: "ask-generate-visual",
|
|
8333
|
+
sessionId: itSessionId,
|
|
8334
|
+
askId: itAskId,
|
|
8335
|
+
qid,
|
|
8336
|
+
cols: bodyCols,
|
|
8337
|
+
force
|
|
8338
|
+
}, 6e4);
|
|
8339
|
+
if (res.ok) {
|
|
8340
|
+
const ansiPath = res.data.ansiPath;
|
|
8341
|
+
const ansi = readFileSync14(ansiPath, "utf-8");
|
|
8342
|
+
state2.visuals.set(qid, { status: "ready", content: ansi, visible: true });
|
|
8343
|
+
} else {
|
|
8344
|
+
const errMsg = typeof res.error === "string" ? res.error : res.error?.message;
|
|
8345
|
+
state2.visuals.set(qid, { status: "error", content: "", visible: true, error: errMsg });
|
|
8042
8346
|
}
|
|
8043
|
-
|
|
8044
|
-
|
|
8045
|
-
|
|
8046
|
-
|
|
8047
|
-
|
|
8048
|
-
|
|
8049
|
-
|
|
8050
|
-
|
|
8051
|
-
|
|
8052
|
-
|
|
8053
|
-
|
|
8054
|
-
|
|
8055
|
-
|
|
8056
|
-
|
|
8057
|
-
|
|
8058
|
-
|
|
8059
|
-
|
|
8060
|
-
|
|
8061
|
-
|
|
8062
|
-
|
|
8063
|
-
|
|
8064
|
-
|
|
8065
|
-
|
|
8066
|
-
|
|
8067
|
-
|
|
8068
|
-
|
|
8069
|
-
|
|
8070
|
-
|
|
8071
|
-
|
|
8072
|
-
|
|
8073
|
-
|
|
8074
|
-
|
|
8075
|
-
|
|
8347
|
+
requestRender();
|
|
8348
|
+
})();
|
|
8349
|
+
}
|
|
8350
|
+
let lastResponses = [];
|
|
8351
|
+
const submitResponses = (responses) => {
|
|
8352
|
+
void (async () => {
|
|
8353
|
+
const it = item();
|
|
8354
|
+
const { cwd: cwd2, sessionId: sessionId2, askId: askId2 } = itemCoords(it);
|
|
8355
|
+
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8356
|
+
const meta = readMeta(cwd2, sessionId2, askId2);
|
|
8357
|
+
if (meta?.orphanTarget && responses.length > 0) {
|
|
8358
|
+
const sel = responses[0].selectedOptionId;
|
|
8359
|
+
if (sel) {
|
|
8360
|
+
await dispatchOrphanResolution(meta.orphanTarget, sel, {
|
|
8361
|
+
daemonSend: opts.daemonSend,
|
|
8362
|
+
onOrphanTakeover: opts.onOrphanTakeover,
|
|
8363
|
+
sessionId: sessionId2,
|
|
8364
|
+
cwd: cwd2
|
|
8365
|
+
});
|
|
8366
|
+
}
|
|
8367
|
+
}
|
|
8368
|
+
writeOutput(cwd2, sessionId2, askId2, responses, completedAt);
|
|
8369
|
+
await updateMeta(cwd2, sessionId2, askId2, { status: "answered", completedAt });
|
|
8370
|
+
state2.aggregateInbox = state2.aggregateInbox.filter((i) => i.dir !== it.dir);
|
|
8371
|
+
const idx = queue.findIndex((i) => i.dir === it.dir);
|
|
8372
|
+
if (idx >= 0) queue.splice(idx, 1);
|
|
8373
|
+
if (queue.length === 0) {
|
|
8374
|
+
teardown();
|
|
8375
|
+
return;
|
|
8376
|
+
}
|
|
8377
|
+
currentIndex = Math.min(currentIndex, queue.length - 1);
|
|
8378
|
+
const nextItem = queue[currentIndex];
|
|
8379
|
+
const nextCoords = itemCoords(nextItem);
|
|
8380
|
+
const nextDeck = buildDeck(currentIndex);
|
|
8381
|
+
if (!nextDeck) {
|
|
8382
|
+
teardown();
|
|
8383
|
+
return;
|
|
8384
|
+
}
|
|
8385
|
+
currentDeck = nextDeck;
|
|
8386
|
+
const nextProgress = readProgress(nextCoords.cwd, nextCoords.sessionId, nextCoords.askId);
|
|
8387
|
+
answeredCount = nextProgress?.responses.length ?? 0;
|
|
8388
|
+
lastResponses = [];
|
|
8389
|
+
state2.visuals.clear();
|
|
8390
|
+
panel.loadDeck(nextDeck, {
|
|
8391
|
+
progressPath: askProgressPath(nextCoords.cwd, nextCoords.sessionId, nextCoords.askId)
|
|
8392
|
+
});
|
|
8393
|
+
setDeckWatch(nextCoords, nextDeck);
|
|
8394
|
+
requestRender();
|
|
8395
|
+
})();
|
|
8396
|
+
};
|
|
8397
|
+
let watchedDeckPath = null;
|
|
8398
|
+
let lastWatchedDeckJson = "";
|
|
8399
|
+
const onWatchedDeckChange = () => {
|
|
8400
|
+
const nextDeck = buildDeck(currentIndex);
|
|
8401
|
+
if (!nextDeck) return;
|
|
8402
|
+
const nextJson = JSON.stringify(nextDeck);
|
|
8403
|
+
if (nextJson === lastWatchedDeckJson) return;
|
|
8404
|
+
lastWatchedDeckJson = nextJson;
|
|
8405
|
+
currentDeck = nextDeck;
|
|
8406
|
+
const { cwd: rcwd, sessionId: rsid, askId: raid } = itemCoords(item());
|
|
8407
|
+
const progress = readProgress(rcwd, rsid, raid);
|
|
8408
|
+
answeredCount = progress?.responses.length ?? 0;
|
|
8409
|
+
state2.visuals.clear();
|
|
8410
|
+
panel.loadDeck(nextDeck, { progressPath: askProgressPath(rcwd, rsid, raid) });
|
|
8411
|
+
requestRender();
|
|
8412
|
+
};
|
|
8413
|
+
function setDeckWatch(coords, baselineDeck) {
|
|
8414
|
+
const p = askDecisionsPath(coords.cwd, coords.sessionId, coords.askId);
|
|
8415
|
+
lastWatchedDeckJson = JSON.stringify(baselineDeck);
|
|
8416
|
+
if (p === watchedDeckPath) return;
|
|
8417
|
+
if (watchedDeckPath !== null) {
|
|
8418
|
+
try {
|
|
8419
|
+
unwatchFile(watchedDeckPath, onWatchedDeckChange);
|
|
8420
|
+
} catch {
|
|
8076
8421
|
}
|
|
8077
|
-
continue;
|
|
8078
8422
|
}
|
|
8079
|
-
|
|
8080
|
-
|
|
8081
|
-
|
|
8082
|
-
|
|
8083
|
-
|
|
8084
|
-
|
|
8085
|
-
|
|
8423
|
+
watchedDeckPath = p;
|
|
8424
|
+
watchFile(p, { interval: 500 }, onWatchedDeckChange);
|
|
8425
|
+
}
|
|
8426
|
+
const panel = mountPanel({
|
|
8427
|
+
deck: initialDeck,
|
|
8428
|
+
cols: opts.cols,
|
|
8429
|
+
rows: opts.rows,
|
|
8430
|
+
progressPath: askProgressPath(initCwd, initSessionId, initAskId),
|
|
8431
|
+
onProgress: (responses) => {
|
|
8432
|
+
answeredCount = responses.length;
|
|
8433
|
+
lastResponses = responses;
|
|
8434
|
+
requestRender();
|
|
8435
|
+
const it = item();
|
|
8436
|
+
const { cwd: cwd2, sessionId: sessionId2, askId: askId2 } = itemCoords(it);
|
|
8437
|
+
const cur = readMeta(cwd2, sessionId2, askId2);
|
|
8438
|
+
if (cur?.status === "pending") {
|
|
8439
|
+
void updateMeta(cwd2, sessionId2, askId2, { status: "in-progress", startedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
8086
8440
|
}
|
|
8087
|
-
|
|
8441
|
+
},
|
|
8442
|
+
onComplete: (responses) => {
|
|
8443
|
+
submitResponses(responses);
|
|
8444
|
+
},
|
|
8445
|
+
// Final-phase Enter on an incomplete deck routes here (humanloop only fires
|
|
8446
|
+
// onComplete when responses === interactions). The 'final' UI prompts
|
|
8447
|
+
// "enter submit", so honor that by submitting whatever was answered.
|
|
8448
|
+
onExit: () => {
|
|
8449
|
+
submitResponses(lastResponses);
|
|
8088
8450
|
}
|
|
8089
|
-
|
|
8090
|
-
|
|
8091
|
-
|
|
8092
|
-
|
|
8451
|
+
});
|
|
8452
|
+
setDeckWatch({ cwd: initCwd, sessionId: initSessionId, askId: initAskId }, initialDeck);
|
|
8453
|
+
function teardown() {
|
|
8454
|
+
if (watchedDeckPath !== null) {
|
|
8455
|
+
try {
|
|
8456
|
+
unwatchFile(watchedDeckPath, onWatchedDeckChange);
|
|
8457
|
+
} catch {
|
|
8458
|
+
}
|
|
8459
|
+
watchedDeckPath = null;
|
|
8093
8460
|
}
|
|
8094
|
-
|
|
8095
|
-
|
|
8096
|
-
while (lines.length > 0) {
|
|
8097
|
-
const last = lines[lines.length - 1];
|
|
8098
|
-
if (last.length === 1 && last[0].text === "") lines.pop();
|
|
8099
|
-
else break;
|
|
8461
|
+
panel.unmount();
|
|
8462
|
+
opts.onUnmount();
|
|
8100
8463
|
}
|
|
8101
|
-
return
|
|
8464
|
+
return {
|
|
8465
|
+
handleKey(input, key) {
|
|
8466
|
+
panel.handleKey(input, key);
|
|
8467
|
+
},
|
|
8468
|
+
render() {
|
|
8469
|
+
return panel.render();
|
|
8470
|
+
},
|
|
8471
|
+
handleResize(cols, rows) {
|
|
8472
|
+
bodyCols = cols;
|
|
8473
|
+
panel.handleResize(cols, rows);
|
|
8474
|
+
},
|
|
8475
|
+
unmount() {
|
|
8476
|
+
teardown();
|
|
8477
|
+
},
|
|
8478
|
+
canAcceptHostKeys() {
|
|
8479
|
+
return panel.canAcceptHostKeys();
|
|
8480
|
+
},
|
|
8481
|
+
atDeckTop() {
|
|
8482
|
+
return panel.atDeckTop();
|
|
8483
|
+
},
|
|
8484
|
+
advanceQueue(delta) {
|
|
8485
|
+
const newIndex = Math.max(0, Math.min(queue.length - 1, currentIndex + delta));
|
|
8486
|
+
if (newIndex === currentIndex) return;
|
|
8487
|
+
currentIndex = newIndex;
|
|
8488
|
+
const nextDeck = buildDeck(currentIndex);
|
|
8489
|
+
if (!nextDeck) return;
|
|
8490
|
+
currentDeck = nextDeck;
|
|
8491
|
+
const it = item();
|
|
8492
|
+
const { cwd: advCwd, sessionId: advSid, askId: advAid } = itemCoords(it);
|
|
8493
|
+
const progress = readProgress(advCwd, advSid, advAid);
|
|
8494
|
+
answeredCount = progress?.responses.length ?? 0;
|
|
8495
|
+
panel.loadDeck(nextDeck, {
|
|
8496
|
+
progressPath: askProgressPath(advCwd, advSid, advAid)
|
|
8497
|
+
});
|
|
8498
|
+
setDeckWatch({ cwd: advCwd, sessionId: advSid, askId: advAid }, nextDeck);
|
|
8499
|
+
requestRender();
|
|
8500
|
+
},
|
|
8501
|
+
spaceVisualToggle() {
|
|
8502
|
+
const qid = getCurrentQid();
|
|
8503
|
+
if (!qid) return;
|
|
8504
|
+
const entry = state2.visuals.get(qid);
|
|
8505
|
+
if (!entry) {
|
|
8506
|
+
fireVisualGen(false);
|
|
8507
|
+
} else if (entry.status === "ready") {
|
|
8508
|
+
state2.visuals.set(qid, { ...entry, visible: !entry.visible });
|
|
8509
|
+
requestRender();
|
|
8510
|
+
} else if (entry.status === "loading") {
|
|
8511
|
+
} else {
|
|
8512
|
+
fireVisualGen(false);
|
|
8513
|
+
}
|
|
8514
|
+
},
|
|
8515
|
+
regenerateVisual() {
|
|
8516
|
+
const qid = getCurrentQid();
|
|
8517
|
+
if (!qid) return;
|
|
8518
|
+
state2.visuals.set(qid, { status: "loading", content: "", visible: true });
|
|
8519
|
+
requestRender();
|
|
8520
|
+
fireVisualGen(true);
|
|
8521
|
+
},
|
|
8522
|
+
getHeaderInfo() {
|
|
8523
|
+
const it = item();
|
|
8524
|
+
const askTitle = currentDeck.title ?? currentDeck.interactions[0]?.title;
|
|
8525
|
+
const itWithName = it;
|
|
8526
|
+
return {
|
|
8527
|
+
currentIndex,
|
|
8528
|
+
queueLength: queue.length,
|
|
8529
|
+
sessionName: itWithName.sessionName ?? it.source?.sessionName,
|
|
8530
|
+
askTitle: askTitle ?? void 0,
|
|
8531
|
+
askedBy: it.source?.askedBy,
|
|
8532
|
+
subtitle: it.subtitle,
|
|
8533
|
+
blockedSince: it.blockedSince,
|
|
8534
|
+
kind: it.kind
|
|
8535
|
+
};
|
|
8536
|
+
},
|
|
8537
|
+
getCurrentQid
|
|
8538
|
+
};
|
|
8102
8539
|
}
|
|
8103
8540
|
|
|
8104
|
-
// src/tui/panels/
|
|
8105
|
-
|
|
8106
|
-
|
|
8107
|
-
|
|
8108
|
-
|
|
8109
|
-
};
|
|
8110
|
-
|
|
8111
|
-
|
|
8112
|
-
|
|
8113
|
-
|
|
8114
|
-
|
|
8115
|
-
|
|
8116
|
-
|
|
8117
|
-
|
|
8118
|
-
|
|
8119
|
-
|
|
8120
|
-
|
|
8121
|
-
|
|
8122
|
-
|
|
8123
|
-
|
|
8124
|
-
|
|
8125
|
-
|
|
8126
|
-
|
|
8127
|
-
|
|
8128
|
-
|
|
8129
|
-
|
|
8130
|
-
|
|
8131
|
-
|
|
8132
|
-
|
|
8133
|
-
|
|
8134
|
-
|
|
8135
|
-
|
|
8136
|
-
|
|
8137
|
-
|
|
8138
|
-
|
|
8139
|
-
|
|
8140
|
-
|
|
8141
|
-
|
|
8142
|
-
goal: buildSectionLines(state2.goalContent, innerW),
|
|
8143
|
-
strategy: buildSectionLines(middleContent, innerW),
|
|
8144
|
-
roadmap: buildSectionLines(bottomContent, innerW),
|
|
8145
|
-
cycleLog: []
|
|
8146
|
-
};
|
|
8147
|
-
state2.cachedStackedLines = lines;
|
|
8148
|
-
state2.stackedCacheKey = cacheKey;
|
|
8541
|
+
// src/tui/panels/inbox-deck.ts
|
|
8542
|
+
init_paths();
|
|
8543
|
+
|
|
8544
|
+
// src/tui/panels/review-action.ts
|
|
8545
|
+
init_shell();
|
|
8546
|
+
import { execSync as execSync4 } from "child_process";
|
|
8547
|
+
import { basename as basename3, dirname as dirname6 } from "path";
|
|
8548
|
+
function mountReviewActionPanel(opts) {
|
|
8549
|
+
let selectedAction = "open";
|
|
8550
|
+
function render(cols, _rows) {
|
|
8551
|
+
const rows = [];
|
|
8552
|
+
const fileBase = basename3(opts.reviewFile);
|
|
8553
|
+
const fileDir = dirname6(opts.reviewFile);
|
|
8554
|
+
const maxPathLen = Math.max(8, cols - 4);
|
|
8555
|
+
const pathDisplay = truncate(`${fileDir}/${fileBase}`, maxPathLen);
|
|
8556
|
+
rows.push(` ${ansiColor("\u25C8", "magenta")} ${pathDisplay}`);
|
|
8557
|
+
const age = opts.blockedSince ? formatTimeAgo(opts.blockedSince) : "unknown";
|
|
8558
|
+
const commentText = opts.draftCommentCount === 1 ? "1 comment in draft" : `${opts.draftCommentCount} comments in draft`;
|
|
8559
|
+
rows.push(` ${ansiDim(age)} \xB7 ${ansiDim(commentText)}`);
|
|
8560
|
+
rows.push("");
|
|
8561
|
+
const openChevron = selectedAction === "open" ? ">" : " ";
|
|
8562
|
+
const openLabel = selectedAction === "open" ? `${openChevron} ${ansiColor("[Open editor]", "magenta", true)}` : `${openChevron} [Open editor]`;
|
|
8563
|
+
rows.push(` ${openLabel}`);
|
|
8564
|
+
const submitChevron = selectedAction === "submit" ? ">" : " ";
|
|
8565
|
+
const submitLabel = selectedAction === "submit" ? `${submitChevron} ${ansiColor("[Submit]", "magenta", true)}` : `${submitChevron} [Submit]`;
|
|
8566
|
+
rows.push(` ${submitLabel}`);
|
|
8567
|
+
rows.push("");
|
|
8568
|
+
rows.push(` ${ansiDim("j/k toggle \xB7 Enter invoke \xB7 Esc unmount")}`);
|
|
8569
|
+
return rows;
|
|
8570
|
+
}
|
|
8571
|
+
function invokeOpen() {
|
|
8572
|
+
const sisBin = process.argv[1];
|
|
8573
|
+
const cmd = `${shellQuote(sisBin)} ask review open ${shellQuote(opts.askId)} --session ${shellQuote(opts.sessionId)}`;
|
|
8574
|
+
execSync4(
|
|
8575
|
+
`tmux display-popup -E -w 90% -h 90% -d ${shellQuote(opts.cwd)} ${shellQuote(cmd)}`,
|
|
8576
|
+
{ stdio: "inherit", env: EXEC_ENV }
|
|
8577
|
+
);
|
|
8578
|
+
opts.onAfterAction();
|
|
8149
8579
|
}
|
|
8150
|
-
|
|
8151
|
-
|
|
8152
|
-
|
|
8153
|
-
|
|
8154
|
-
|
|
8155
|
-
|
|
8156
|
-
|
|
8157
|
-
let cursor = 1;
|
|
8158
|
-
cursor = paintStrip(
|
|
8159
|
-
rows,
|
|
8160
|
-
cursor,
|
|
8161
|
-
w,
|
|
8162
|
-
sgr,
|
|
8163
|
-
reset,
|
|
8164
|
-
headers.top,
|
|
8165
|
-
lines.goal,
|
|
8166
|
-
state2.goalScroll,
|
|
8167
|
-
heights.goalHeight,
|
|
8168
|
-
state2.focusedStrip === "goal" && focused
|
|
8169
|
-
);
|
|
8170
|
-
rows[cursor++] = sgr + "\u251C" + "\u2500".repeat(w - 2) + "\u2524" + reset;
|
|
8171
|
-
cursor = paintStrip(
|
|
8172
|
-
rows,
|
|
8173
|
-
cursor,
|
|
8174
|
-
w,
|
|
8175
|
-
sgr,
|
|
8176
|
-
reset,
|
|
8177
|
-
headers.middle,
|
|
8178
|
-
lines.strategy,
|
|
8179
|
-
state2.strategyScroll,
|
|
8180
|
-
heights.stratHeight,
|
|
8181
|
-
state2.focusedStrip === "strategy" && focused
|
|
8182
|
-
);
|
|
8183
|
-
rows[cursor++] = sgr + "\u251C" + "\u2500".repeat(w - 2) + "\u2524" + reset;
|
|
8184
|
-
paintStrip(
|
|
8185
|
-
rows,
|
|
8186
|
-
cursor,
|
|
8187
|
-
w,
|
|
8188
|
-
sgr,
|
|
8189
|
-
reset,
|
|
8190
|
-
headers.bottom,
|
|
8191
|
-
lines.roadmap,
|
|
8192
|
-
state2.roadmapScroll,
|
|
8193
|
-
heights.roadHeight,
|
|
8194
|
-
state2.focusedStrip === "roadmap" && focused
|
|
8195
|
-
);
|
|
8196
|
-
return rows;
|
|
8197
|
-
}
|
|
8198
|
-
function buildCompletionContent(session) {
|
|
8199
|
-
const parts = [];
|
|
8200
|
-
if (session.completedAt) {
|
|
8201
|
-
parts.push(`*completed ${formatTimestamp(session.completedAt)}*`);
|
|
8202
|
-
parts.push("");
|
|
8580
|
+
function invokeSubmit() {
|
|
8581
|
+
const sisBin = process.argv[1];
|
|
8582
|
+
execSync4(
|
|
8583
|
+
`${shellQuote(sisBin)} ask review complete ${shellQuote(opts.askId)} --session ${shellQuote(opts.sessionId)}`,
|
|
8584
|
+
{ stdio: "inherit", cwd: opts.cwd, env: EXEC_ENV }
|
|
8585
|
+
);
|
|
8586
|
+
opts.onAfterAction();
|
|
8203
8587
|
}
|
|
8204
|
-
|
|
8205
|
-
|
|
8206
|
-
|
|
8207
|
-
|
|
8588
|
+
function handleKey(input) {
|
|
8589
|
+
if (input === "j" || input === "\x1B[B") {
|
|
8590
|
+
selectedAction = "submit";
|
|
8591
|
+
return true;
|
|
8592
|
+
}
|
|
8593
|
+
if (input === "k" || input === "\x1B[A") {
|
|
8594
|
+
selectedAction = "open";
|
|
8595
|
+
return true;
|
|
8596
|
+
}
|
|
8597
|
+
if (input === "\r" || input === "\n") {
|
|
8598
|
+
if (selectedAction === "open") {
|
|
8599
|
+
invokeOpen();
|
|
8600
|
+
} else {
|
|
8601
|
+
invokeSubmit();
|
|
8602
|
+
}
|
|
8603
|
+
return true;
|
|
8604
|
+
}
|
|
8605
|
+
return false;
|
|
8208
8606
|
}
|
|
8209
|
-
return
|
|
8607
|
+
return {
|
|
8608
|
+
render,
|
|
8609
|
+
handleKey,
|
|
8610
|
+
get selectedAction() {
|
|
8611
|
+
return selectedAction;
|
|
8612
|
+
}
|
|
8613
|
+
};
|
|
8210
8614
|
}
|
|
8211
|
-
function pickSummaryContent(state2) {
|
|
8212
|
-
if (state2.completionSummaryContent.trim()) return state2.completionSummaryContent;
|
|
8213
|
-
if (state2.logsCycles.length > 0) {
|
|
8214
|
-
const last = state2.logsCycles[state2.logsCycles.length - 1];
|
|
8215
|
-
return `# Cycle ${last.cycle} log
|
|
8216
8615
|
|
|
8217
|
-
|
|
8218
|
-
|
|
8219
|
-
|
|
8220
|
-
return
|
|
8616
|
+
// src/tui/panels/inbox-deck.ts
|
|
8617
|
+
var lastMountDims = null;
|
|
8618
|
+
function mountInlineDeck(state2, cols, rows) {
|
|
8619
|
+
return mountResolutionPanel(
|
|
8620
|
+
{
|
|
8621
|
+
aggregateInbox: state2.aggregateInbox,
|
|
8622
|
+
startIndex: 0,
|
|
8623
|
+
cols,
|
|
8624
|
+
rows,
|
|
8625
|
+
daemonSend: send,
|
|
8626
|
+
onUnmount: () => {
|
|
8627
|
+
state2.inlineDeck = null;
|
|
8628
|
+
state2.visuals.clear();
|
|
8629
|
+
state2.focusPane = "tree";
|
|
8630
|
+
requestRender();
|
|
8631
|
+
},
|
|
8632
|
+
onOrphanTakeover: makeOrphanTakeover(state2, {
|
|
8633
|
+
send,
|
|
8634
|
+
paneExists,
|
|
8635
|
+
switchToSession,
|
|
8636
|
+
selectWindow,
|
|
8637
|
+
selectPane
|
|
8638
|
+
})
|
|
8639
|
+
},
|
|
8640
|
+
state2
|
|
8641
|
+
);
|
|
8221
8642
|
}
|
|
8222
|
-
function
|
|
8643
|
+
function readDraftCommentCount(cwd2, sessionId2, askId2) {
|
|
8223
8644
|
try {
|
|
8224
|
-
const
|
|
8225
|
-
|
|
8226
|
-
|
|
8645
|
+
const raw = readFileSync15(askReviewDraftPath(cwd2, sessionId2, askId2), "utf-8");
|
|
8646
|
+
const data = JSON.parse(raw);
|
|
8647
|
+
if (Array.isArray(data["comments"])) return data["comments"].length;
|
|
8648
|
+
return 0;
|
|
8227
8649
|
} catch {
|
|
8228
|
-
return
|
|
8650
|
+
return 0;
|
|
8229
8651
|
}
|
|
8230
8652
|
}
|
|
8231
|
-
function
|
|
8232
|
-
|
|
8233
|
-
|
|
8234
|
-
|
|
8235
|
-
|
|
8236
|
-
|
|
8237
|
-
|
|
8238
|
-
|
|
8239
|
-
const roadCap = stripsAvail - goalCap - stratCap;
|
|
8240
|
-
const goalNeed = Math.min(gN + 1, goalCap);
|
|
8241
|
-
const stratNeed = Math.min(sN + 1, stratCap);
|
|
8242
|
-
const slack = goalCap - goalNeed + (stratCap - stratNeed);
|
|
8243
|
-
return {
|
|
8244
|
-
goalHeight: Math.max(2, goalNeed),
|
|
8245
|
-
stratHeight: Math.max(2, stratNeed),
|
|
8246
|
-
roadHeight: Math.max(2, roadCap + slack)
|
|
8247
|
-
};
|
|
8248
|
-
}
|
|
8249
|
-
function paintStrip(rows, startRow, w, sgr, reset, label, lines, scroll, height, focused) {
|
|
8250
|
-
const innerW = w - 4;
|
|
8251
|
-
const borderL = sgr + "\u2502" + reset + " ";
|
|
8252
|
-
const borderR = " " + sgr + "\u2502" + reset;
|
|
8253
|
-
const headerSeg = `\x1B[${colorToSGR("yellow")};1m \u258E ${label}\x1B[0m`;
|
|
8254
|
-
const headerClipped = clipAnsi(headerSeg + (focused ? " \u25C0" : ""), innerW);
|
|
8255
|
-
rows[startRow] = borderL + headerClipped + borderR;
|
|
8256
|
-
const contentH = height - 1;
|
|
8257
|
-
const hasOverflow = lines.length > contentH;
|
|
8258
|
-
const viewableH = hasOverflow ? contentH - 1 : contentH;
|
|
8259
|
-
const maxScroll = Math.max(0, lines.length - viewableH);
|
|
8260
|
-
scroll.setMax(maxScroll);
|
|
8261
|
-
const effOffset = scroll.offset;
|
|
8262
|
-
for (let i = 0; i < viewableH; i++) {
|
|
8263
|
-
const li = effOffset + i;
|
|
8264
|
-
const ansi = li < lines.length ? renderLine(lines[li]) : "";
|
|
8265
|
-
rows[startRow + 1 + i] = borderL + clipAnsi(ansi, innerW) + borderR;
|
|
8266
|
-
}
|
|
8267
|
-
if (hasOverflow) {
|
|
8268
|
-
const pct = maxScroll > 0 ? Math.round(effOffset / maxScroll * 100) : 100;
|
|
8269
|
-
const indicator = `\x1B[2m \u2195 ${pct}% \xB7 ${lines.length} lines\x1B[0m`;
|
|
8270
|
-
rows[startRow + 1 + viewableH] = borderL + clipAnsi(indicator, innerW) + borderR;
|
|
8653
|
+
function readReviewFile(cwd2, sessionId2, askId2) {
|
|
8654
|
+
try {
|
|
8655
|
+
const raw = readFileSync15(askReviewPath(cwd2, sessionId2, askId2), "utf-8");
|
|
8656
|
+
const data = JSON.parse(raw);
|
|
8657
|
+
if (typeof data["file"] === "string") return data["file"];
|
|
8658
|
+
return null;
|
|
8659
|
+
} catch {
|
|
8660
|
+
return null;
|
|
8271
8661
|
}
|
|
8272
|
-
return startRow + height;
|
|
8273
8662
|
}
|
|
8274
|
-
function
|
|
8275
|
-
|
|
8276
|
-
|
|
8663
|
+
function renderReviewPanel(rect, state2, item) {
|
|
8664
|
+
const blank = clipAnsi("", rect.w);
|
|
8665
|
+
const cwd2 = cwdFromDir(item.dir);
|
|
8666
|
+
const sessionId2 = sessionIdFromDir(item.dir);
|
|
8667
|
+
const askId2 = askIdFromDir(item.dir);
|
|
8668
|
+
const reviewFile = readReviewFile(cwd2, sessionId2, askId2);
|
|
8669
|
+
const panelAskId = state2.reviewPanel?._askId;
|
|
8670
|
+
if (state2.reviewPanel && panelAskId !== askId2) {
|
|
8671
|
+
state2.reviewPanel = null;
|
|
8672
|
+
}
|
|
8673
|
+
if (!state2.reviewPanel) {
|
|
8674
|
+
if (!reviewFile) {
|
|
8675
|
+
const rows = [];
|
|
8676
|
+
const mid = Math.floor(rect.h / 2);
|
|
8677
|
+
for (let i = 0; i < rect.h; i++) {
|
|
8678
|
+
rows.push(i === mid ? clipAnsi(ansiColor("[review.json not found \u2014 Esc to dismiss]", "red"), rect.w) : blank);
|
|
8679
|
+
}
|
|
8680
|
+
return rows;
|
|
8681
|
+
}
|
|
8682
|
+
const draftCommentCount = readDraftCommentCount(cwd2, sessionId2, askId2);
|
|
8683
|
+
const panel = mountReviewActionPanel({
|
|
8684
|
+
cwd: cwd2,
|
|
8685
|
+
sessionId: sessionId2,
|
|
8686
|
+
askId: askId2,
|
|
8687
|
+
reviewFile,
|
|
8688
|
+
blockedSince: item.blockedSince,
|
|
8689
|
+
draftCommentCount,
|
|
8690
|
+
onAfterAction: () => {
|
|
8691
|
+
state2.reviewPanel = null;
|
|
8692
|
+
state2.focusPane = "tree";
|
|
8693
|
+
requestRender();
|
|
8694
|
+
}
|
|
8695
|
+
});
|
|
8696
|
+
panel["_askId"] = askId2;
|
|
8697
|
+
state2.reviewPanel = panel;
|
|
8277
8698
|
}
|
|
8278
|
-
const
|
|
8279
|
-
|
|
8280
|
-
|
|
8281
|
-
|
|
8282
|
-
|
|
8283
|
-
lines = buildLogsLines(state2.logsCycles, rect.w);
|
|
8284
|
-
const existing = state2.cachedStackedLines ?? { goal: [], strategy: [], roadmap: [], cycleLog: [] };
|
|
8285
|
-
state2.cachedStackedLines = { ...existing, cycleLog: lines };
|
|
8286
|
-
state2.stackedCacheKey = cacheKey;
|
|
8699
|
+
const panelRows = state2.reviewPanel.render(rect.w, rect.h);
|
|
8700
|
+
const result = [];
|
|
8701
|
+
for (let i = 0; i < rect.h; i++) {
|
|
8702
|
+
const line = panelRows[i];
|
|
8703
|
+
result.push(clipAnsi(line !== void 0 ? line : "", rect.w));
|
|
8287
8704
|
}
|
|
8288
|
-
return
|
|
8705
|
+
return result;
|
|
8289
8706
|
}
|
|
8290
|
-
|
|
8291
|
-
|
|
8292
|
-
|
|
8293
|
-
|
|
8294
|
-
|
|
8295
|
-
|
|
8296
|
-
|
|
8297
|
-
|
|
8298
|
-
|
|
8299
|
-
|
|
8300
|
-
|
|
8301
|
-
|
|
8302
|
-
|
|
8303
|
-
|
|
8304
|
-
|
|
8305
|
-
|
|
8306
|
-
|
|
8307
|
-
|
|
8308
|
-
|
|
8309
|
-
|
|
8310
|
-
|
|
8311
|
-
|
|
8312
|
-
|
|
8313
|
-
|
|
8314
|
-
|
|
8315
|
-
|
|
8316
|
-
|
|
8317
|
-
|
|
8318
|
-
|
|
8319
|
-
|
|
8320
|
-
|
|
8321
|
-
|
|
8322
|
-
|
|
8323
|
-
}
|
|
8324
|
-
|
|
8325
|
-
|
|
8326
|
-
|
|
8327
|
-
|
|
8328
|
-
|
|
8329
|
-
|
|
8330
|
-
|
|
8707
|
+
function renderInboxDeckRows(rect, state2) {
|
|
8708
|
+
if (rect.w <= 0 || rect.h <= 0) {
|
|
8709
|
+
return Array.from({ length: Math.max(0, rect.h) }, () => clipAnsi("", rect.w));
|
|
8710
|
+
}
|
|
8711
|
+
if (!state2.inlineDeck) lastMountDims = null;
|
|
8712
|
+
const blank = clipAnsi("", rect.w);
|
|
8713
|
+
const emptyState = () => {
|
|
8714
|
+
const rows2 = [];
|
|
8715
|
+
const mid = Math.floor(rect.h / 2);
|
|
8716
|
+
const msg = "No pending asks";
|
|
8717
|
+
const pad = Math.max(0, Math.floor((rect.w - msg.length) / 2));
|
|
8718
|
+
for (let i = 0; i < rect.h; i++) {
|
|
8719
|
+
rows2.push(i === mid ? clipAnsi(" ".repeat(pad) + ansiDim(msg), rect.w) : blank);
|
|
8720
|
+
}
|
|
8721
|
+
return rows2;
|
|
8722
|
+
};
|
|
8723
|
+
if (state2.aggregateInbox.length === 0) {
|
|
8724
|
+
return emptyState();
|
|
8725
|
+
}
|
|
8726
|
+
const firstItem = state2.aggregateInbox[0];
|
|
8727
|
+
if (firstItem.kind === "review") {
|
|
8728
|
+
return renderReviewPanel(rect, state2, firstItem);
|
|
8729
|
+
}
|
|
8730
|
+
const HEADER = 3;
|
|
8731
|
+
const bodyRows = Math.max(1, rect.h - HEADER);
|
|
8732
|
+
if (!state2.inlineDeck) {
|
|
8733
|
+
const handle2 = mountInlineDeck(state2, rect.w, bodyRows);
|
|
8734
|
+
state2.inlineDeck = handle2;
|
|
8735
|
+
lastMountDims = handle2 ? { cols: rect.w, rows: bodyRows } : null;
|
|
8736
|
+
if (!state2.inlineDeck) return emptyState();
|
|
8737
|
+
} else if (!lastMountDims || lastMountDims.cols !== rect.w || lastMountDims.rows !== bodyRows) {
|
|
8738
|
+
state2.inlineDeck.handleResize(rect.w, bodyRows);
|
|
8739
|
+
lastMountDims = { cols: rect.w, rows: bodyRows };
|
|
8740
|
+
}
|
|
8741
|
+
const handle = state2.inlineDeck;
|
|
8742
|
+
const info = handle.getHeaderInfo();
|
|
8743
|
+
const icon = kindIcon(info.kind);
|
|
8744
|
+
const iconColor = kindColor(info.kind);
|
|
8745
|
+
let prefixPlain = ` ${icon} Ask ${info.currentIndex + 1}/${info.queueLength} \xB7 ${info.sessionName ?? "?"}`;
|
|
8746
|
+
if (info.askedBy) prefixPlain += ` \xB7 ${info.askedBy}`;
|
|
8747
|
+
prefixPlain += ` \xB7 ${formatTimeAgo(info.blockedSince)} \u2014 `;
|
|
8748
|
+
const prefixDisplayWidth = stringWidth7(prefixPlain);
|
|
8749
|
+
const T = Math.max(8, rect.w - prefixDisplayWidth - 3);
|
|
8750
|
+
const title = truncate(info.askTitle ?? "", T);
|
|
8751
|
+
let line1 = ` ${ansiColor(icon, iconColor)} Ask ${info.currentIndex + 1}/${info.queueLength} \xB7 ${info.sessionName ?? "?"}`;
|
|
8752
|
+
if (info.askedBy) line1 += ` \xB7 ${info.askedBy}`;
|
|
8753
|
+
line1 += ` \xB7 ${formatTimeAgo(info.blockedSince)} \u2014 ${title}`;
|
|
8754
|
+
const row0 = clipAnsi(line1, rect.w);
|
|
8755
|
+
const row1 = clipAnsi(info.subtitle ? " " + ansiDim(info.subtitle) : "", rect.w);
|
|
8756
|
+
const row2 = blank;
|
|
8757
|
+
const qid = handle.getCurrentQid();
|
|
8758
|
+
const visualEntry = qid ? state2.visuals.get(qid) : void 0;
|
|
8759
|
+
const body = [];
|
|
8760
|
+
if (visualEntry?.visible && visualEntry.status === "ready") {
|
|
8761
|
+
const visualLines = visualEntry.content.split("\n");
|
|
8762
|
+
for (let i = 0; i < bodyRows; i++) {
|
|
8763
|
+
body.push(clipAnsi(i < visualLines.length ? visualLines[i] : "", rect.w));
|
|
8331
8764
|
}
|
|
8332
|
-
|
|
8333
|
-
|
|
8334
|
-
|
|
8335
|
-
|
|
8336
|
-
|
|
8337
|
-
|
|
8338
|
-
|
|
8339
|
-
|
|
8340
|
-
|
|
8341
|
-
|
|
8342
|
-
|
|
8343
|
-
|
|
8344
|
-
|
|
8345
|
-
|
|
8346
|
-
|
|
8347
|
-
|
|
8348
|
-
}
|
|
8349
|
-
|
|
8350
|
-
|
|
8351
|
-
validation: "cyan",
|
|
8352
|
-
decision: "cyan",
|
|
8353
|
-
context: "cyan",
|
|
8354
|
-
error: "red"
|
|
8355
|
-
};
|
|
8356
|
-
function buildInboxLines(items, width) {
|
|
8357
|
-
const lines = [];
|
|
8358
|
-
if (items.length === 0) {
|
|
8359
|
-
lines.push(singleLine(" No pending asks across the fleet", { dim: true, italic: true }));
|
|
8360
|
-
return lines;
|
|
8361
|
-
}
|
|
8362
|
-
lines.push(singleLine(` ${items.length} pending`, { bold: true }));
|
|
8363
|
-
lines.push(singleLine(" "));
|
|
8364
|
-
const contentWidth = width - 4;
|
|
8365
|
-
for (const item of items) {
|
|
8366
|
-
const kindKey = coerceKind(item.kind);
|
|
8367
|
-
const icon = kindKey in KIND_ICON ? KIND_ICON[kindKey] : "\xB7";
|
|
8368
|
-
const iconColor = kindKey in KIND_COLOR ? KIND_COLOR[kindKey] : "cyan";
|
|
8369
|
-
const source = item.sessionName ? item.sessionName : item.sessionId.slice(0, 8);
|
|
8370
|
-
const titleText = item.title ? item.title : `(${item.askId.slice(0, 8)})`;
|
|
8371
|
-
const blocked = formatTimeAgo(item.blockedSince);
|
|
8372
|
-
const maxTitle = Math.max(10, contentWidth - source.length - blocked.length - 8);
|
|
8373
|
-
lines.push([
|
|
8374
|
-
seg(" "),
|
|
8375
|
-
seg(icon, { color: iconColor }),
|
|
8376
|
-
seg(` ${source}`, { color: "yellow" }),
|
|
8377
|
-
seg(" \xB7 ", { dim: true }),
|
|
8378
|
-
seg(truncate(titleText, maxTitle), { bold: true }),
|
|
8379
|
-
seg(` ${blocked}`, { dim: true })
|
|
8380
|
-
]);
|
|
8381
|
-
if (item.subtitle) {
|
|
8382
|
-
lines.push(singleLine(` ${truncate(item.subtitle, contentWidth - 6)}`, { dim: true }));
|
|
8765
|
+
} else {
|
|
8766
|
+
const humanloopLines = handle.render();
|
|
8767
|
+
const midRow = Math.floor(bodyRows / 2);
|
|
8768
|
+
for (let i = 0; i < bodyRows; i++) {
|
|
8769
|
+
if (visualEntry?.visible && visualEntry.status === "loading" && i === midRow) {
|
|
8770
|
+
const placeholderText = "[generating visual\u2026 ~30s]";
|
|
8771
|
+
const padL = Math.max(0, Math.floor((rect.w - placeholderText.length) / 2));
|
|
8772
|
+
body.push(clipAnsi(" ".repeat(padL) + ansiDim(placeholderText), rect.w));
|
|
8773
|
+
} else if (visualEntry?.visible && visualEntry.status === "error" && i === midRow) {
|
|
8774
|
+
const errText = visualEntry.error ? visualEntry.error : "unknown";
|
|
8775
|
+
body.push(
|
|
8776
|
+
clipAnsi(
|
|
8777
|
+
ansiColor(`[visual error: ${errText}]`, "red") + ansiDim(" [R] retry"),
|
|
8778
|
+
rect.w
|
|
8779
|
+
)
|
|
8780
|
+
);
|
|
8781
|
+
} else {
|
|
8782
|
+
body.push(clipAnsi(i < humanloopLines.length ? humanloopLines[i] : "", rect.w));
|
|
8783
|
+
}
|
|
8383
8784
|
}
|
|
8384
8785
|
}
|
|
8385
|
-
|
|
8386
|
-
|
|
8387
|
-
|
|
8388
|
-
if (rect.w <= 0 || rect.h <= 0) {
|
|
8389
|
-
return buildEmptyPanelRows(rect, state2.focusPane === "detail", "red", "");
|
|
8390
|
-
}
|
|
8391
|
-
const focused = state2.focusPane === "detail";
|
|
8392
|
-
const items = state2.aggregateInbox;
|
|
8393
|
-
const fingerprint = items.map((i) => `${i.askId}:${i.status}`).join(",");
|
|
8394
|
-
const cacheKey = `${items.length}:${fingerprint}:${rect.w}`;
|
|
8395
|
-
let lines;
|
|
8396
|
-
if (cacheKey === state2.inboxCacheKey && state2.cachedInboxLines !== null) {
|
|
8397
|
-
lines = state2.cachedInboxLines;
|
|
8398
|
-
} else {
|
|
8399
|
-
lines = buildInboxLines(items, rect.w);
|
|
8400
|
-
state2.cachedInboxLines = lines;
|
|
8401
|
-
state2.inboxCacheKey = cacheKey;
|
|
8786
|
+
const rows = [row0, row1, row2];
|
|
8787
|
+
for (let i = 0; i < bodyRows && rows.length < rect.h; i++) {
|
|
8788
|
+
rows.push(body[i]);
|
|
8402
8789
|
}
|
|
8403
|
-
|
|
8790
|
+
while (rows.length < rect.h) rows.push(blank);
|
|
8791
|
+
return rows.slice(0, rect.h);
|
|
8404
8792
|
}
|
|
8405
8793
|
|
|
8406
8794
|
// src/tui/app.ts
|
|
@@ -8408,8 +8796,8 @@ init_paths();
|
|
|
8408
8796
|
|
|
8409
8797
|
// src/tui/lib/popup-compose.ts
|
|
8410
8798
|
init_shell();
|
|
8411
|
-
import { execSync as
|
|
8412
|
-
import { mkdtempSync as mkdtempSync2, writeFileSync as writeFileSync10, readFileSync as
|
|
8799
|
+
import { execSync as execSync5 } from "child_process";
|
|
8800
|
+
import { mkdtempSync as mkdtempSync2, writeFileSync as writeFileSync10, readFileSync as readFileSync16, rmSync as rmSync5 } from "fs";
|
|
8413
8801
|
import { join as join14 } from "path";
|
|
8414
8802
|
import { tmpdir as tmpdir2 } from "os";
|
|
8415
8803
|
|
|
@@ -8439,13 +8827,13 @@ function composeViaPopup(action, state2, actions) {
|
|
|
8439
8827
|
try {
|
|
8440
8828
|
writeFileSync10(tempFile, "", "utf-8");
|
|
8441
8829
|
const cmd = `NVIM_APPNAME=sisyphus nvim ${shellQuote(tempFile)}`;
|
|
8442
|
-
|
|
8830
|
+
execSync5(
|
|
8443
8831
|
`tmux display-popup -E -w 90% -h 90% -d ${shellQuote(state2.cwd)} ${shellQuote(cmd)}`,
|
|
8444
8832
|
{ stdio: "inherit", env: EXEC_ENV }
|
|
8445
8833
|
);
|
|
8446
8834
|
let rawContent = "";
|
|
8447
8835
|
try {
|
|
8448
|
-
rawContent =
|
|
8836
|
+
rawContent = readFileSync16(tempFile, "utf-8");
|
|
8449
8837
|
} catch {
|
|
8450
8838
|
}
|
|
8451
8839
|
const required = !OPTIONAL_COMPOSE.has(action.kind);
|
|
@@ -8470,7 +8858,7 @@ function getCompanion() {
|
|
|
8470
8858
|
const { mtimeMs } = statSync4(companionPath());
|
|
8471
8859
|
if (_cachedCompanion && mtimeMs === _companionMtime) return _cachedCompanion;
|
|
8472
8860
|
_companionMtime = mtimeMs;
|
|
8473
|
-
_cachedCompanion = normalizeCompanion(JSON.parse(
|
|
8861
|
+
_cachedCompanion = normalizeCompanion(JSON.parse(readFileSync17(companionPath(), "utf-8")));
|
|
8474
8862
|
return _cachedCompanion;
|
|
8475
8863
|
} catch {
|
|
8476
8864
|
return _cachedCompanion;
|
|
@@ -8481,6 +8869,7 @@ var cachedContextFilePath = null;
|
|
|
8481
8869
|
var cachedContextFileContent = null;
|
|
8482
8870
|
var prevFrame = [];
|
|
8483
8871
|
var prevResolutionActive = false;
|
|
8872
|
+
var prevCursorOnNeedsYou = false;
|
|
8484
8873
|
var prevTreeInputs = "";
|
|
8485
8874
|
var prevBottomInputs = "";
|
|
8486
8875
|
function computeTreeWidth(cols) {
|
|
@@ -8490,6 +8879,8 @@ var prevOverlayMode = "";
|
|
|
8490
8879
|
var cachedTreeRows = [];
|
|
8491
8880
|
var cachedLogSessionId = null;
|
|
8492
8881
|
var cachedLogFiles = /* @__PURE__ */ new Map();
|
|
8882
|
+
var detailPassToken = 0;
|
|
8883
|
+
var lastPolledDetailSessionId = void 0;
|
|
8493
8884
|
function renderResolutionFrame(buf, state2) {
|
|
8494
8885
|
const handle = state2.resolutionHandle;
|
|
8495
8886
|
const info = handle.getHeaderInfo();
|
|
@@ -8561,13 +8952,6 @@ function startApp(state2, cleanup2) {
|
|
|
8561
8952
|
async function poll() {
|
|
8562
8953
|
try {
|
|
8563
8954
|
let selectedSession = null;
|
|
8564
|
-
let planContent = "";
|
|
8565
|
-
let strategyContent = "";
|
|
8566
|
-
let goalContent = "";
|
|
8567
|
-
let completionSummaryContent = "";
|
|
8568
|
-
let logsContent = "";
|
|
8569
|
-
let logsCycles = [];
|
|
8570
|
-
let digestData = null;
|
|
8571
8955
|
let paneAlive = true;
|
|
8572
8956
|
let contextFiles = [];
|
|
8573
8957
|
const listPromise = send({ type: "list", cwd: state2.cwd });
|
|
@@ -8595,65 +8979,6 @@ function startApp(state2, cleanup2) {
|
|
|
8595
8979
|
const cached2 = sessions.find((s) => s.id === state2.selectedSessionId);
|
|
8596
8980
|
paneAlive = cached2?.windowAlive ?? false;
|
|
8597
8981
|
}
|
|
8598
|
-
try {
|
|
8599
|
-
const pp = roadmapPath(state2.cwd, state2.selectedSessionId);
|
|
8600
|
-
if (existsSync11(pp)) {
|
|
8601
|
-
planContent = readFileSync16(pp, "utf-8");
|
|
8602
|
-
}
|
|
8603
|
-
} catch {
|
|
8604
|
-
}
|
|
8605
|
-
try {
|
|
8606
|
-
const gp = goalPath(state2.cwd, state2.selectedSessionId);
|
|
8607
|
-
if (existsSync11(gp)) {
|
|
8608
|
-
goalContent = readFileSync16(gp, "utf-8");
|
|
8609
|
-
}
|
|
8610
|
-
} catch {
|
|
8611
|
-
}
|
|
8612
|
-
try {
|
|
8613
|
-
const sp = strategyPath(state2.cwd, state2.selectedSessionId);
|
|
8614
|
-
if (existsSync11(sp)) {
|
|
8615
|
-
strategyContent = readFileSync16(sp, "utf-8");
|
|
8616
|
-
}
|
|
8617
|
-
} catch {
|
|
8618
|
-
}
|
|
8619
|
-
try {
|
|
8620
|
-
const cp = join15(contextDir(state2.cwd, state2.selectedSessionId), "completion-summary.md");
|
|
8621
|
-
if (existsSync11(cp)) {
|
|
8622
|
-
completionSummaryContent = readFileSync16(cp, "utf-8");
|
|
8623
|
-
}
|
|
8624
|
-
} catch {
|
|
8625
|
-
}
|
|
8626
|
-
try {
|
|
8627
|
-
const ld = logsDir(state2.cwd, state2.selectedSessionId);
|
|
8628
|
-
if (existsSync11(ld)) {
|
|
8629
|
-
if (state2.selectedSessionId !== cachedLogSessionId) {
|
|
8630
|
-
cachedLogFiles = /* @__PURE__ */ new Map();
|
|
8631
|
-
cachedLogSessionId = state2.selectedSessionId;
|
|
8632
|
-
}
|
|
8633
|
-
const files = readdirSync7(ld).filter((f) => f.startsWith("cycle-")).sort();
|
|
8634
|
-
const fileSet = new Set(files);
|
|
8635
|
-
for (const key of cachedLogFiles.keys()) {
|
|
8636
|
-
if (!fileSet.has(key)) cachedLogFiles.delete(key);
|
|
8637
|
-
}
|
|
8638
|
-
for (const f of files) {
|
|
8639
|
-
const filePath = join15(ld, f);
|
|
8640
|
-
const mtime = statSync4(filePath).mtimeMs;
|
|
8641
|
-
const cached2 = cachedLogFiles.get(f);
|
|
8642
|
-
if (!cached2 || cached2.mtime !== mtime) {
|
|
8643
|
-
const match = f.match(/cycle-(\d+)\.md$/);
|
|
8644
|
-
const cycle = match ? parseInt(match[1], 10) : 0;
|
|
8645
|
-
const content = readFileSync16(filePath, "utf-8");
|
|
8646
|
-
cachedLogFiles.set(f, { mtime, cycle, content });
|
|
8647
|
-
}
|
|
8648
|
-
}
|
|
8649
|
-
logsCycles = files.map((f) => {
|
|
8650
|
-
const entry = cachedLogFiles.get(f);
|
|
8651
|
-
return { cycle: entry.cycle, content: entry.content };
|
|
8652
|
-
});
|
|
8653
|
-
logsContent = logsCycles.map((c) => c.content).join("\n");
|
|
8654
|
-
}
|
|
8655
|
-
} catch {
|
|
8656
|
-
}
|
|
8657
8982
|
try {
|
|
8658
8983
|
const cd = contextDir(state2.cwd, state2.selectedSessionId);
|
|
8659
8984
|
if (existsSync11(cd)) {
|
|
@@ -8674,37 +8999,29 @@ function startApp(state2, cleanup2) {
|
|
|
8674
8999
|
}
|
|
8675
9000
|
} catch {
|
|
8676
9001
|
}
|
|
8677
|
-
try {
|
|
8678
|
-
const dp = digestPath(state2.cwd, state2.selectedSessionId);
|
|
8679
|
-
if (existsSync11(dp)) {
|
|
8680
|
-
const raw = JSON.parse(readFileSync16(dp, "utf-8"));
|
|
8681
|
-
if (raw && typeof raw.recentWork === "string" && typeof raw.currentActivity === "string" && typeof raw.whatsNext === "string" && Array.isArray(raw.unusualEvents)) {
|
|
8682
|
-
digestData = raw;
|
|
8683
|
-
}
|
|
8684
|
-
}
|
|
8685
|
-
} catch {
|
|
8686
|
-
}
|
|
8687
|
-
}
|
|
8688
|
-
state2.cachedReportBlocks.clear();
|
|
8689
|
-
if (selectedSession) {
|
|
8690
|
-
for (const agent of selectedSession.agents) {
|
|
8691
|
-
state2.cachedReportBlocks.set(agent.id, resolveReports(agent.reports));
|
|
8692
|
-
}
|
|
8693
9002
|
}
|
|
8694
9003
|
}
|
|
8695
9004
|
state2.sessions = sessions;
|
|
8696
9005
|
state2.selectedSession = selectedSession;
|
|
8697
|
-
state2.planContent = planContent;
|
|
8698
|
-
state2.strategyContent = strategyContent;
|
|
8699
|
-
state2.goalContent = goalContent;
|
|
8700
|
-
state2.completionSummaryContent = completionSummaryContent;
|
|
8701
|
-
state2.logsContent = logsContent;
|
|
8702
|
-
state2.logsCycles = logsCycles;
|
|
8703
|
-
state2.digestData = digestData;
|
|
8704
9006
|
state2.paneAlive = paneAlive;
|
|
8705
9007
|
state2.contextFiles = contextFiles;
|
|
8706
9008
|
state2.error = null;
|
|
9009
|
+
const detailSelectionChanged = state2.selectedSessionId !== lastPolledDetailSessionId;
|
|
9010
|
+
lastPolledDetailSessionId = state2.selectedSessionId;
|
|
9011
|
+
if (detailSelectionChanged) {
|
|
9012
|
+
state2.planContent = "";
|
|
9013
|
+
state2.strategyContent = "";
|
|
9014
|
+
state2.goalContent = "";
|
|
9015
|
+
state2.completionSummaryContent = "";
|
|
9016
|
+
state2.logsContent = "";
|
|
9017
|
+
state2.logsCycles = [];
|
|
9018
|
+
state2.digestData = null;
|
|
9019
|
+
state2.cachedReportBlocks.clear();
|
|
9020
|
+
}
|
|
8707
9021
|
requestRender();
|
|
9022
|
+
const myToken = ++detailPassToken;
|
|
9023
|
+
const mySession = state2.selectedSessionId;
|
|
9024
|
+
setImmediate(() => scheduleDetailReads(myToken, mySession));
|
|
8708
9025
|
} catch (err) {
|
|
8709
9026
|
const wasError = state2.error !== null;
|
|
8710
9027
|
state2.error = err.message;
|
|
@@ -8712,6 +9029,103 @@ function startApp(state2, cleanup2) {
|
|
|
8712
9029
|
requestRender();
|
|
8713
9030
|
}
|
|
8714
9031
|
}
|
|
9032
|
+
function scheduleDetailReads(myToken, mySession) {
|
|
9033
|
+
if (myToken !== detailPassToken || state2.selectedSessionId !== mySession || state2.resolutionActive) return;
|
|
9034
|
+
let planContent = "";
|
|
9035
|
+
let strategyContent = "";
|
|
9036
|
+
let goalContent = "";
|
|
9037
|
+
let completionSummaryContent = "";
|
|
9038
|
+
let logsContent = "";
|
|
9039
|
+
let logsCycles = [];
|
|
9040
|
+
let digestData = null;
|
|
9041
|
+
if (mySession) {
|
|
9042
|
+
try {
|
|
9043
|
+
const pp = roadmapPath(state2.cwd, mySession);
|
|
9044
|
+
if (existsSync11(pp)) {
|
|
9045
|
+
planContent = readFileSync17(pp, "utf-8");
|
|
9046
|
+
}
|
|
9047
|
+
} catch {
|
|
9048
|
+
}
|
|
9049
|
+
try {
|
|
9050
|
+
const gp = goalPath(state2.cwd, mySession);
|
|
9051
|
+
if (existsSync11(gp)) {
|
|
9052
|
+
goalContent = readFileSync17(gp, "utf-8");
|
|
9053
|
+
}
|
|
9054
|
+
} catch {
|
|
9055
|
+
}
|
|
9056
|
+
try {
|
|
9057
|
+
const sp = strategyPath(state2.cwd, mySession);
|
|
9058
|
+
if (existsSync11(sp)) {
|
|
9059
|
+
strategyContent = readFileSync17(sp, "utf-8");
|
|
9060
|
+
}
|
|
9061
|
+
} catch {
|
|
9062
|
+
}
|
|
9063
|
+
try {
|
|
9064
|
+
const cp = join15(contextDir(state2.cwd, mySession), "completion-summary.md");
|
|
9065
|
+
if (existsSync11(cp)) {
|
|
9066
|
+
completionSummaryContent = readFileSync17(cp, "utf-8");
|
|
9067
|
+
}
|
|
9068
|
+
} catch {
|
|
9069
|
+
}
|
|
9070
|
+
try {
|
|
9071
|
+
const ld = logsDir(state2.cwd, mySession);
|
|
9072
|
+
if (existsSync11(ld)) {
|
|
9073
|
+
if (mySession !== cachedLogSessionId) {
|
|
9074
|
+
cachedLogFiles = /* @__PURE__ */ new Map();
|
|
9075
|
+
cachedLogSessionId = mySession;
|
|
9076
|
+
}
|
|
9077
|
+
const files = readdirSync7(ld).filter((f) => f.startsWith("cycle-")).sort();
|
|
9078
|
+
const fileSet = new Set(files);
|
|
9079
|
+
for (const key of cachedLogFiles.keys()) {
|
|
9080
|
+
if (!fileSet.has(key)) cachedLogFiles.delete(key);
|
|
9081
|
+
}
|
|
9082
|
+
for (const f of files) {
|
|
9083
|
+
const filePath = join15(ld, f);
|
|
9084
|
+
const mtime = statSync4(filePath).mtimeMs;
|
|
9085
|
+
const cached2 = cachedLogFiles.get(f);
|
|
9086
|
+
if (!cached2 || cached2.mtime !== mtime) {
|
|
9087
|
+
const match = f.match(/cycle-(\d+)\.md$/);
|
|
9088
|
+
const cycle = match ? parseInt(match[1], 10) : 0;
|
|
9089
|
+
const content = readFileSync17(filePath, "utf-8");
|
|
9090
|
+
cachedLogFiles.set(f, { mtime, cycle, content });
|
|
9091
|
+
}
|
|
9092
|
+
}
|
|
9093
|
+
logsCycles = files.map((f) => {
|
|
9094
|
+
const entry = cachedLogFiles.get(f);
|
|
9095
|
+
return { cycle: entry.cycle, content: entry.content };
|
|
9096
|
+
});
|
|
9097
|
+
logsContent = logsCycles.map((c) => c.content).join("\n");
|
|
9098
|
+
}
|
|
9099
|
+
} catch {
|
|
9100
|
+
}
|
|
9101
|
+
try {
|
|
9102
|
+
const dp = digestPath(state2.cwd, mySession);
|
|
9103
|
+
if (existsSync11(dp)) {
|
|
9104
|
+
const raw = JSON.parse(readFileSync17(dp, "utf-8"));
|
|
9105
|
+
if (raw && typeof raw.recentWork === "string" && typeof raw.currentActivity === "string" && typeof raw.whatsNext === "string" && Array.isArray(raw.unusualEvents)) {
|
|
9106
|
+
digestData = raw;
|
|
9107
|
+
}
|
|
9108
|
+
}
|
|
9109
|
+
} catch {
|
|
9110
|
+
}
|
|
9111
|
+
}
|
|
9112
|
+
if (myToken !== detailPassToken || state2.selectedSessionId !== mySession || state2.resolutionActive) return;
|
|
9113
|
+
state2.planContent = planContent;
|
|
9114
|
+
state2.strategyContent = strategyContent;
|
|
9115
|
+
state2.goalContent = goalContent;
|
|
9116
|
+
state2.completionSummaryContent = completionSummaryContent;
|
|
9117
|
+
state2.logsContent = logsContent;
|
|
9118
|
+
state2.logsCycles = logsCycles;
|
|
9119
|
+
state2.digestData = digestData;
|
|
9120
|
+
state2.cachedReportBlocks.clear();
|
|
9121
|
+
const sel = state2.selectedSession;
|
|
9122
|
+
if (sel) {
|
|
9123
|
+
for (const agent of sel.agents) {
|
|
9124
|
+
state2.cachedReportBlocks.set(agent.id, resolveReports(agent.reports));
|
|
9125
|
+
}
|
|
9126
|
+
}
|
|
9127
|
+
requestRender();
|
|
9128
|
+
}
|
|
8715
9129
|
function render() {
|
|
8716
9130
|
const stdoutRows = process.stdout.rows;
|
|
8717
9131
|
const stdoutCols = process.stdout.columns;
|
|
@@ -8750,7 +9164,7 @@ function startApp(state2, cleanup2) {
|
|
|
8750
9164
|
return s.task.toLowerCase().includes(q) || s.id.toLowerCase().includes(q);
|
|
8751
9165
|
}) : state2.sessions;
|
|
8752
9166
|
const statusFP = filteredSessions.map((s) => `${s.status}:${s.windowAlive}:${s.runningAgentCount}:${s.orphaned ?? false}`).join(",");
|
|
8753
|
-
const inboxFP = `${state2.aggregateInbox.length}:${state2.aggregateInbox.map((i) => i.
|
|
9167
|
+
const inboxFP = `${state2.aggregateInbox.length}:${state2.aggregateInbox.map((i) => askIdFromDir(i.dir)).join(",")}`;
|
|
8754
9168
|
const cacheKey = `${state2.expanded.size}:${filteredSessions.length}:${state2.selectedSession?.id}:${state2.contextFiles.length}:${state2.searchFilter}:${statusFP}:${inboxFP}`;
|
|
8755
9169
|
let nodes;
|
|
8756
9170
|
if (cacheKey === state2.treeCacheKey && state2.cachedTreeNodes !== null) {
|
|
@@ -8772,6 +9186,9 @@ function startApp(state2, cleanup2) {
|
|
|
8772
9186
|
latestNodes = nodes;
|
|
8773
9187
|
const cursorNode = nodes[state2.cursorIndex];
|
|
8774
9188
|
if (cursorNode) state2.cursorNodeId = cursorNode.id;
|
|
9189
|
+
const onNeedsYou = cursorNode?.type === "needs-you-virtual";
|
|
9190
|
+
if (prevCursorOnNeedsYou && !onNeedsYou && state2.inlineDeck) state2.inlineDeck.unmount();
|
|
9191
|
+
prevCursorOnNeedsYou = onNeedsYou;
|
|
8775
9192
|
const rawSessionId = cursorNode?.sessionId;
|
|
8776
9193
|
const newSessionId = rawSessionId ? rawSessionId : null;
|
|
8777
9194
|
if (newSessionId !== state2.selectedSessionId) {
|
|
@@ -8821,7 +9238,7 @@ function startApp(state2, cleanup2) {
|
|
|
8821
9238
|
cachedContextFilePath = cursorNode.filePath;
|
|
8822
9239
|
try {
|
|
8823
9240
|
if (existsSync11(cursorNode.filePath)) {
|
|
8824
|
-
cachedContextFileContent =
|
|
9241
|
+
cachedContextFileContent = readFileSync17(cursorNode.filePath, "utf-8");
|
|
8825
9242
|
} else {
|
|
8826
9243
|
cachedContextFileContent = null;
|
|
8827
9244
|
}
|
|
@@ -8885,7 +9302,7 @@ function startApp(state2, cleanup2) {
|
|
|
8885
9302
|
};
|
|
8886
9303
|
let detailRows;
|
|
8887
9304
|
if (cursorNode?.type === "needs-you-virtual") {
|
|
8888
|
-
detailRows =
|
|
9305
|
+
detailRows = renderInboxDeckRows(detailRect, state2);
|
|
8889
9306
|
} else if (state2.useStackedDetail) {
|
|
8890
9307
|
detailRows = renderStackedDetailRows(detailRect, state2, detailCtx);
|
|
8891
9308
|
} else {
|