parallel-codex-tui 0.3.0 → 0.3.2
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 +19 -11
- package/dist/cli.js +3 -1
- package/dist/core/collaboration-timeline.js +3 -1
- package/dist/core/role-configuration.js +47 -8
- package/dist/core/session-index.js +123 -10
- package/dist/core/session-manager.js +15 -4
- package/dist/core/task-report.js +510 -0
- package/dist/core/task-search.js +142 -0
- package/dist/domain/schemas.js +9 -0
- package/dist/orchestrator/collaboration-channel.js +11 -3
- package/dist/orchestrator/orchestrator.js +117 -38
- package/dist/tui/App.js +125 -14
- package/dist/tui/FeatureBoardView.js +4 -1
- package/dist/tui/InputBar.js +22 -7
- package/dist/tui/StatusDetailView.js +27 -6
- package/dist/tui/TaskSessionsView.js +30 -9
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/tui/App.js
CHANGED
|
@@ -119,6 +119,7 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
119
119
|
const [taskSessionsError, setTaskSessionsError] = useState(null);
|
|
120
120
|
const [taskSessionsNotice, setTaskSessionsNotice] = useState(null);
|
|
121
121
|
const [taskSessionsIncludeArchived, setTaskSessionsIncludeArchived] = useState(false);
|
|
122
|
+
const [taskSessionQuery, setTaskSessionQuery] = useState("");
|
|
122
123
|
const [taskSessionAction, setTaskSessionAction] = useState(null);
|
|
123
124
|
const [taskSessionDetails, setTaskSessionDetails] = useState(null);
|
|
124
125
|
const [taskSessionDetailSelectedWorkerIndex, setTaskSessionDetailSelectedWorkerIndex] = useState(0);
|
|
@@ -174,6 +175,7 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
174
175
|
const mainConversationActionRef = useRef(mainConversationAction);
|
|
175
176
|
const taskSessionsLoadingRef = useRef(taskSessionsLoading);
|
|
176
177
|
const taskSessionsIncludeArchivedRef = useRef(taskSessionsIncludeArchived);
|
|
178
|
+
const taskSessionQueryRef = useRef("");
|
|
177
179
|
const taskSessionActionRef = useRef(taskSessionAction);
|
|
178
180
|
const taskSessionDetailsRef = useRef(null);
|
|
179
181
|
const taskSessionDetailSelectedWorkerIndexRef = useRef(0);
|
|
@@ -264,7 +266,7 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
264
266
|
&& (config.workers[selectedTaskSessionDetailWorker.engine]?.interactive.forkArgs.length ?? 0) > 0);
|
|
265
267
|
const featureCanCancel = busy && (selectedBoardFeature?.state === "actor_running"
|
|
266
268
|
|| selectedBoardFeature?.state === "critic_running");
|
|
267
|
-
const featureCanReassign = assignableProviderIds.length >
|
|
269
|
+
const featureCanReassign = assignableProviderIds.length > 0
|
|
268
270
|
&& !busy
|
|
269
271
|
&& canRetryTask
|
|
270
272
|
&& selectedBoardFeature?.state !== "approved";
|
|
@@ -453,6 +455,9 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
453
455
|
useEffect(() => {
|
|
454
456
|
taskSessionsIncludeArchivedRef.current = taskSessionsIncludeArchived;
|
|
455
457
|
}, [taskSessionsIncludeArchived]);
|
|
458
|
+
useEffect(() => {
|
|
459
|
+
taskSessionQueryRef.current = taskSessionQuery;
|
|
460
|
+
}, [taskSessionQuery]);
|
|
456
461
|
useEffect(() => {
|
|
457
462
|
taskSessionActionRef.current = taskSessionAction;
|
|
458
463
|
}, [taskSessionAction]);
|
|
@@ -1246,10 +1251,38 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
1246
1251
|
const sessionAction = taskSessionActionRef.current;
|
|
1247
1252
|
if (sessionAction) {
|
|
1248
1253
|
if (chunk === "\x1b") {
|
|
1254
|
+
if (sessionAction.type === "search") {
|
|
1255
|
+
const preferred = taskSessionsRef.current[selectedTaskSessionIndexRef.current]?.id ?? null;
|
|
1256
|
+
updateTaskSessionAction(null);
|
|
1257
|
+
setTaskSessionsError(null);
|
|
1258
|
+
void refreshTaskSessionsRef.current(preferred, sessionAction.previousQuery);
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
1249
1261
|
updateTaskSessionAction(null);
|
|
1250
1262
|
setTaskSessionsError(null);
|
|
1251
1263
|
return;
|
|
1252
1264
|
}
|
|
1265
|
+
if (sessionAction.type === "search") {
|
|
1266
|
+
const update = applyChatInputChunk(sessionAction.value, chunk, sessionAction.cursor);
|
|
1267
|
+
const preferred = taskSessionsRef.current[selectedTaskSessionIndexRef.current]?.id ?? null;
|
|
1268
|
+
if (update.submit !== null) {
|
|
1269
|
+
updateTaskSessionAction(null);
|
|
1270
|
+
setTaskSessionsError(null);
|
|
1271
|
+
void refreshTaskSessionsRef.current(preferred, update.submit);
|
|
1272
|
+
}
|
|
1273
|
+
else {
|
|
1274
|
+
updateTaskSessionAction({
|
|
1275
|
+
...sessionAction,
|
|
1276
|
+
value: update.value,
|
|
1277
|
+
cursor: update.cursor
|
|
1278
|
+
});
|
|
1279
|
+
if (update.value !== sessionAction.value) {
|
|
1280
|
+
setTaskSessionsError(null);
|
|
1281
|
+
void refreshTaskSessionsRef.current(preferred, update.value);
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
return;
|
|
1285
|
+
}
|
|
1253
1286
|
if (taskSessionsLoadingRef.current) {
|
|
1254
1287
|
return;
|
|
1255
1288
|
}
|
|
@@ -1286,6 +1319,17 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
1286
1319
|
if (taskSessionsLoadingRef.current) {
|
|
1287
1320
|
return;
|
|
1288
1321
|
}
|
|
1322
|
+
if (isWorkerSearchShortcut(chunk, {})) {
|
|
1323
|
+
updateTaskSessionAction({
|
|
1324
|
+
type: "search",
|
|
1325
|
+
value: taskSessionQueryRef.current,
|
|
1326
|
+
cursor: Array.from(taskSessionQueryRef.current).length,
|
|
1327
|
+
previousQuery: taskSessionQueryRef.current
|
|
1328
|
+
});
|
|
1329
|
+
setTaskSessionsError(null);
|
|
1330
|
+
setTaskSessionsNotice(null);
|
|
1331
|
+
return;
|
|
1332
|
+
}
|
|
1289
1333
|
if (isRouterDiagnosticsShortcut(chunk, {})) {
|
|
1290
1334
|
void openRouterDiagnosticsRef.current();
|
|
1291
1335
|
return;
|
|
@@ -1303,6 +1347,11 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
1303
1347
|
return;
|
|
1304
1348
|
}
|
|
1305
1349
|
const selectedSession = taskSessionsRef.current[selectedTaskSessionIndexRef.current];
|
|
1350
|
+
if ((chunk === "x" || chunk === "X") && taskSessionQueryRef.current.trim()) {
|
|
1351
|
+
void refreshTaskSessionsRef.current(selectedSession?.id ?? null, "");
|
|
1352
|
+
setTaskSessionsNotice("Task search cleared");
|
|
1353
|
+
return;
|
|
1354
|
+
}
|
|
1306
1355
|
if (chunk === "r" || chunk === "R") {
|
|
1307
1356
|
if (selectedSession) {
|
|
1308
1357
|
updateTaskSessionAction({
|
|
@@ -1396,6 +1445,24 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
1396
1445
|
const pendingAssignment = featureAssignmentPromptRef.current;
|
|
1397
1446
|
if (pendingAssignment) {
|
|
1398
1447
|
for (const featureChunk of featureChunks) {
|
|
1448
|
+
const modelEdit = featureAssignmentPromptRef.current?.modelEdit;
|
|
1449
|
+
if (modelEdit) {
|
|
1450
|
+
if (featureChunk === "\x1b") {
|
|
1451
|
+
updateFeatureAssignmentPrompt({ ...pendingAssignment, modelEdit: undefined });
|
|
1452
|
+
continue;
|
|
1453
|
+
}
|
|
1454
|
+
const update = applyChatInputChunk(modelEdit.value, featureChunk, modelEdit.cursor);
|
|
1455
|
+
if (update.submit !== null) {
|
|
1456
|
+
void reassignSelectedFeatureRef.current(pendingAssignment, modelEdit.role, update.submit);
|
|
1457
|
+
}
|
|
1458
|
+
else {
|
|
1459
|
+
updateFeatureAssignmentPrompt({
|
|
1460
|
+
...pendingAssignment,
|
|
1461
|
+
modelEdit: { ...modelEdit, value: update.value, cursor: update.cursor }
|
|
1462
|
+
});
|
|
1463
|
+
}
|
|
1464
|
+
continue;
|
|
1465
|
+
}
|
|
1399
1466
|
if (featureChunk === "\x1b" || featureChunk === "m" || featureChunk === "M") {
|
|
1400
1467
|
updateFeatureAssignmentPrompt(null);
|
|
1401
1468
|
setFeatureBoardNotice(null);
|
|
@@ -1409,6 +1476,16 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
1409
1476
|
void reassignSelectedFeatureRef.current(pendingAssignment, "critic");
|
|
1410
1477
|
return;
|
|
1411
1478
|
}
|
|
1479
|
+
if (featureChunk === "1" || featureChunk === "2") {
|
|
1480
|
+
const feature = collaborationTimelineRef.current?.features.find((item) => item.id === pendingAssignment.featureId);
|
|
1481
|
+
const role = featureChunk === "1" ? "actor" : "critic";
|
|
1482
|
+
const value = role === "actor" ? feature?.actorModel ?? "" : feature?.criticModel ?? "";
|
|
1483
|
+
updateFeatureAssignmentPrompt({
|
|
1484
|
+
...pendingAssignment,
|
|
1485
|
+
modelEdit: { role, value, cursor: Array.from(value).length }
|
|
1486
|
+
});
|
|
1487
|
+
return;
|
|
1488
|
+
}
|
|
1412
1489
|
}
|
|
1413
1490
|
return;
|
|
1414
1491
|
}
|
|
@@ -1468,7 +1545,7 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
1468
1545
|
featureId: feature.id,
|
|
1469
1546
|
title: feature.title
|
|
1470
1547
|
});
|
|
1471
|
-
setFeatureBoardNotice(`Reassign ${feature.title} · A Actor (${feature.actorEngine ?? config.pairing.actor}) · C Critic (${feature.criticEngine ?? config.pairing.critic})`);
|
|
1548
|
+
setFeatureBoardNotice(`Reassign ${feature.title} · A Actor (${featureTargetDisplay(config, feature.actorEngine ?? config.pairing.actor, feature.actorModel)}) · C Critic (${featureTargetDisplay(config, feature.criticEngine ?? config.pairing.critic, feature.criticModel)})`);
|
|
1472
1549
|
return;
|
|
1473
1550
|
}
|
|
1474
1551
|
if (featureChunk === "r" || featureChunk === "R") {
|
|
@@ -2628,11 +2705,16 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
2628
2705
|
setTaskSessionDetailNotice(null);
|
|
2629
2706
|
updateTaskSessionAction(null);
|
|
2630
2707
|
updateMainConversationAction(null);
|
|
2708
|
+
taskSessionQueryRef.current = "";
|
|
2709
|
+
setTaskSessionQuery("");
|
|
2631
2710
|
sessionCenterModeRef.current = "tasks";
|
|
2632
2711
|
setSessionCenterMode("tasks");
|
|
2633
2712
|
await refreshTaskSessions(activeTaskIdRef.current);
|
|
2634
2713
|
}
|
|
2635
|
-
async function refreshTaskSessions(preferredTaskId = null) {
|
|
2714
|
+
async function refreshTaskSessions(preferredTaskId = null, query = taskSessionQueryRef.current) {
|
|
2715
|
+
const normalizedQuery = query.replace(/[\u0000-\u001f\u007f]/g, " ").slice(0, 1000);
|
|
2716
|
+
taskSessionQueryRef.current = normalizedQuery;
|
|
2717
|
+
setTaskSessionQuery(normalizedQuery);
|
|
2636
2718
|
taskSessionsLoadingRef.current = true;
|
|
2637
2719
|
setTaskSessionsLoading(true);
|
|
2638
2720
|
const sequence = taskSessionsLoadSequenceRef.current + 1;
|
|
@@ -2642,7 +2724,8 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
2642
2724
|
throw new Error("Task sessions are unavailable");
|
|
2643
2725
|
}
|
|
2644
2726
|
const tasks = await loadTaskSessions({
|
|
2645
|
-
includeArchived: taskSessionsIncludeArchivedRef.current
|
|
2727
|
+
includeArchived: taskSessionsIncludeArchivedRef.current,
|
|
2728
|
+
...(normalizedQuery.trim() ? { query: normalizedQuery } : {})
|
|
2646
2729
|
});
|
|
2647
2730
|
if (taskSessionsLoadSequenceRef.current !== sequence) {
|
|
2648
2731
|
return;
|
|
@@ -3087,7 +3170,7 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
3087
3170
|
featureAssignmentPromptRef.current = next;
|
|
3088
3171
|
setFeatureAssignmentPrompt(next);
|
|
3089
3172
|
}
|
|
3090
|
-
async function reassignSelectedFeature(prompt, role) {
|
|
3173
|
+
async function reassignSelectedFeature(prompt, role, model) {
|
|
3091
3174
|
const feature = collaborationTimelineRef.current?.features.find((item) => item.id === prompt.featureId);
|
|
3092
3175
|
if (!feature) {
|
|
3093
3176
|
setFeatureBoardNotice(`Feature no longer exists: ${prompt.featureId}`);
|
|
@@ -3097,15 +3180,22 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
3097
3180
|
const current = role === "actor"
|
|
3098
3181
|
? feature.actorEngine ?? config.pairing.actor
|
|
3099
3182
|
: feature.criticEngine ?? config.pairing.critic;
|
|
3100
|
-
const engine =
|
|
3183
|
+
const engine = model === undefined
|
|
3184
|
+
? nextAssignableFeatureEngine(current, assignableProviderIds)
|
|
3185
|
+
: current;
|
|
3101
3186
|
try {
|
|
3102
|
-
await orchestrator.reassignFeature({
|
|
3187
|
+
const result = await orchestrator.reassignFeature({
|
|
3103
3188
|
taskId: prompt.taskId,
|
|
3104
3189
|
featureId: prompt.featureId,
|
|
3105
3190
|
role,
|
|
3106
|
-
engine
|
|
3191
|
+
engine,
|
|
3192
|
+
...(model === undefined ? {} : { model })
|
|
3107
3193
|
});
|
|
3108
|
-
|
|
3194
|
+
updateFeatureAssignmentPrompt({ ...prompt, modelEdit: undefined });
|
|
3195
|
+
const assignedModel = role === "actor"
|
|
3196
|
+
? result.assignment.actor_model
|
|
3197
|
+
: result.assignment.critic_model;
|
|
3198
|
+
setFeatureBoardNotice(`${role === "actor" ? "Actor" : "Critic"} reassigned to ${featureTargetDisplay(config, engine, assignedModel)} · Ctrl+R resumes the task.`);
|
|
3109
3199
|
await refreshCollaborationTimeline(false);
|
|
3110
3200
|
}
|
|
3111
3201
|
catch (error) {
|
|
@@ -3383,6 +3473,12 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
3383
3473
|
if (typeof orchestrator.updateRoleConfiguration !== "function") {
|
|
3384
3474
|
throw new Error("Role configuration is unavailable in this runtime");
|
|
3385
3475
|
}
|
|
3476
|
+
if (typeof orchestrator.validateRoleConfiguration === "function") {
|
|
3477
|
+
const validation = await orchestrator.validateRoleConfiguration(draft);
|
|
3478
|
+
if (!validation.ok) {
|
|
3479
|
+
throw new Error(formatRoleConfigurationPreflightFailure(validation.lines));
|
|
3480
|
+
}
|
|
3481
|
+
}
|
|
3386
3482
|
const snapshot = await orchestrator.updateRoleConfiguration({
|
|
3387
3483
|
scope,
|
|
3388
3484
|
roles: draft,
|
|
@@ -3478,7 +3574,7 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
3478
3574
|
if (view === "workspace") {
|
|
3479
3575
|
return (_jsx(Box, { flexDirection: "column", height: terminalSize.rows, children: _jsx(WorkspacePicker, { cwd: cwd, choices: workspaceChoices, terminalHeight: terminalSize.rows, terminalWidth: terminalWidth, onCancel: closeWorkspacePicker, onSelect: (workspace) => void selectWorkspace(workspace) }) }));
|
|
3480
3576
|
}
|
|
3481
|
-
return (_jsx(AppShell, { view: view, cwd: cwd, taskId: activeTaskId, statusText: [visibleTaskStatus, visibleRouteSummary, configChange?.compact].filter(Boolean).join(" | "), contentHeight: contentHeight, showStatusBar: config.ui.showStatusBar, input: _jsx(InputBar, { mode: view === "worker" && workerSearch.open ? "worker-search" : view, ready: inputReady, busy: busy, routeFallback: Boolean(routeFallbackPrompt), collaborationDetail: collaborationDetailOpen, collaborationUnresolved: collaborationUnresolvedOnly, collaborationBack: collaborationReturnViewRef.current, featureCanCancel: featureCanCancel, featureCanPause: featureCanCancel, featureCanReassign: featureCanReassign, featureCancelConfirm: featureCancelPrompt?.action === "cancel", featurePauseConfirm: featureCancelPrompt?.action === "pause", featureAssignment: Boolean(featureAssignmentPrompt), taskSessionAction: sessionCenterMode === "conversations"
|
|
3577
|
+
return (_jsx(AppShell, { view: view, cwd: cwd, taskId: activeTaskId, statusText: [visibleTaskStatus, visibleRouteSummary, configChange?.compact].filter(Boolean).join(" | "), contentHeight: contentHeight, showStatusBar: config.ui.showStatusBar, input: _jsx(InputBar, { mode: view === "worker" && workerSearch.open ? "worker-search" : view, ready: inputReady, busy: busy, routeFallback: Boolean(routeFallbackPrompt), collaborationDetail: collaborationDetailOpen, collaborationUnresolved: collaborationUnresolvedOnly, collaborationBack: collaborationReturnViewRef.current, featureCanCancel: featureCanCancel, featureCanPause: featureCanCancel, featureCanReassign: featureCanReassign, featureCancelConfirm: featureCancelPrompt?.action === "cancel", featurePauseConfirm: featureCancelPrompt?.action === "pause", featureAssignment: Boolean(featureAssignmentPrompt), featureEditingModel: featureAssignmentPrompt?.modelEdit ?? null, taskSessionAction: sessionCenterMode === "conversations"
|
|
3482
3578
|
? mainConversationAction?.type === "rename"
|
|
3483
3579
|
? { type: "rename", value: mainConversationAction.value, cursor: mainConversationAction.cursor }
|
|
3484
3580
|
: mainConversationAction?.type === "delete"
|
|
@@ -3488,17 +3584,19 @@ export function App({ config, orchestrator, cwd, initialTaskId = null, initialRo
|
|
|
3488
3584
|
? { type: "rename", value: taskSessionAction.value, cursor: taskSessionAction.cursor }
|
|
3489
3585
|
: taskSessionAction?.type === "delete"
|
|
3490
3586
|
? { type: "delete", title: taskSessionAction.title }
|
|
3491
|
-
:
|
|
3587
|
+
: taskSessionAction?.type === "search"
|
|
3588
|
+
? { type: "search", value: taskSessionAction.value, cursor: taskSessionAction.cursor }
|
|
3589
|
+
: null, taskSessionsIncludeArchived: sessionCenterMode === "conversations"
|
|
3492
3590
|
? mainConversationsIncludeArchived
|
|
3493
|
-
: taskSessionsIncludeArchived, mainConversationSessions: sessionCenterMode === "conversations" && !taskSessionDetails, taskSessionDetail: Boolean(taskSessionDetails), taskSessionDetailHasNative: taskSessionDetailHasNative, taskSessionDetailCanFork: taskSessionDetailCanFork, canRetry: canRetryTask, hasWorkers: workers.length > 0, hasActiveTask: Boolean(activeTaskId), hasTaskResult: hasTaskResult, taskResultExpanded: taskResultExpanded, chatScrollOffset: chatScrollOffset, chatMaxScrollOffset: chatMaxScrollOffset, nativeClosed: view === "native" && nativeAttach?.closedCode !== null, searchMatchIndex: workerSearch.matchIndex, searchMatchCount: workerNavigationTargets.searchOffsets.length, clipboardNotice: clipboardNotice, roleScope: roleConfigurationScope, roleEditingModel: roleModelEdit, roleCanApply: roleConfigurationScope !== "task" || Boolean(activeTaskId), roleSaving: roleConfigurationSaving, roleHasOverride: roleConfigurationHasOverride, value: workerSearch.open && view === "worker"
|
|
3591
|
+
: taskSessionsIncludeArchived, taskSessionQuery: taskSessionQuery, mainConversationSessions: sessionCenterMode === "conversations" && !taskSessionDetails, taskSessionDetail: Boolean(taskSessionDetails), taskSessionDetailHasNative: taskSessionDetailHasNative, taskSessionDetailCanFork: taskSessionDetailCanFork, canRetry: canRetryTask, hasWorkers: workers.length > 0, hasActiveTask: Boolean(activeTaskId), hasTaskResult: hasTaskResult, taskResultExpanded: taskResultExpanded, chatScrollOffset: chatScrollOffset, chatMaxScrollOffset: chatMaxScrollOffset, nativeClosed: view === "native" && nativeAttach?.closedCode !== null, searchMatchIndex: workerSearch.matchIndex, searchMatchCount: workerNavigationTargets.searchOffsets.length, clipboardNotice: clipboardNotice, roleScope: roleConfigurationScope, roleEditingModel: roleModelEdit, roleCanApply: roleConfigurationScope !== "task" || Boolean(activeTaskId), roleSaving: roleConfigurationSaving, roleHasOverride: roleConfigurationHasOverride, value: workerSearch.open && view === "worker"
|
|
3494
3592
|
? workerSearch.query
|
|
3495
3593
|
: view === "native" || view === "router" || view === "workers" || view === "features" || view === "sessions" || view === "collaboration" || view === "status" || view === "roles" ? "" : input, cursor: workerSearch.open && view === "worker"
|
|
3496
3594
|
? workerSearch.cursor
|
|
3497
|
-
: view === "chat" ? inputCursor : undefined, terminalWidth: terminalWidth, onChange: view === "native" ? setNativeInput : setInput, onSubmit: view === "native" ? undefined : submit }), error: attachError, children: view === "roles" ? (_jsx(RoleConfigurationView, { snapshot: roleConfigurationSnapshot, draft: roleConfigurationDraft, scope: roleConfigurationScope, selectedRoleIndex: roleConfigurationSelectedIndex, loading: roleConfigurationLoading, saving: roleConfigurationSaving, notice: roleConfigurationNotice, error: roleConfigurationError, hasActiveTask: Boolean(activeTaskId), height: contentHeight, terminalWidth: terminalWidth })) : view === "status" ? (_jsx(StatusDetailView, { cwd: cwd, taskId: activeTaskId, mode: activeMode, busy: busy, canRetry: canRetryTask, taskStatus: visibleTaskStatus, routeStatus: visibleRouteStatus, routeReason: routePending ? undefined : lastRoute?.reason, pairing: config.pairing, roleSelection: activeRoleSelection, configStatus: configChange?.detail, configRestartRequired: configChange?.kind === "restart", workers: workers, selectedWorkerIndex: selectedWorkerIndex, height: contentHeight, terminalWidth: terminalWidth })) : view === "native" ? (_jsx(NativeAttachView, { attach: nativeAttach, viewportHeight: contentHeight })) : view === "sessions" ? (taskSessionDetails ? (_jsx(TaskSessionDetailView, { details: taskSessionDetails, selectedWorkerIndex: taskSessionDetailSelectedWorkerIndex, loading: taskSessionsLoading, error: taskSessionsError, notice: taskSessionDetailNotice, height: contentHeight, terminalWidth: terminalWidth })) : sessionCenterMode === "conversations" ? (_jsx(MainConversationsView, { conversations: mainConversations, selectedIndex: selectedMainConversationIndex, includeArchived: mainConversationsIncludeArchived, notice: taskSessionsNotice, action: mainConversationAction?.type === "rename"
|
|
3595
|
+
: view === "chat" ? inputCursor : undefined, terminalWidth: terminalWidth, onChange: view === "native" ? setNativeInput : setInput, onSubmit: view === "native" ? undefined : submit }), error: attachError, children: view === "roles" ? (_jsx(RoleConfigurationView, { snapshot: roleConfigurationSnapshot, draft: roleConfigurationDraft, scope: roleConfigurationScope, selectedRoleIndex: roleConfigurationSelectedIndex, loading: roleConfigurationLoading, saving: roleConfigurationSaving, notice: roleConfigurationNotice, error: roleConfigurationError, hasActiveTask: Boolean(activeTaskId), height: contentHeight, terminalWidth: terminalWidth })) : view === "status" ? (_jsx(StatusDetailView, { cwd: cwd, taskId: activeTaskId, mode: activeMode, busy: busy, canRetry: canRetryTask, taskStatus: visibleTaskStatus, routeStatus: visibleRouteStatus, routeReason: routePending ? undefined : lastRoute?.reason, pairing: config.pairing, roleSelection: activeRoleSelection, roleConfigurationSnapshot: roleConfigurationSnapshot, configStatus: configChange?.detail, configRestartRequired: configChange?.kind === "restart", workers: workers, selectedWorkerIndex: selectedWorkerIndex, height: contentHeight, terminalWidth: terminalWidth })) : view === "native" ? (_jsx(NativeAttachView, { attach: nativeAttach, viewportHeight: contentHeight })) : view === "sessions" ? (taskSessionDetails ? (_jsx(TaskSessionDetailView, { details: taskSessionDetails, selectedWorkerIndex: taskSessionDetailSelectedWorkerIndex, loading: taskSessionsLoading, error: taskSessionsError, notice: taskSessionDetailNotice, height: contentHeight, terminalWidth: terminalWidth })) : sessionCenterMode === "conversations" ? (_jsx(MainConversationsView, { conversations: mainConversations, selectedIndex: selectedMainConversationIndex, includeArchived: mainConversationsIncludeArchived, notice: taskSessionsNotice, action: mainConversationAction?.type === "rename"
|
|
3498
3596
|
? { type: "rename", title: mainConversationAction.title }
|
|
3499
3597
|
: mainConversationAction?.type === "delete"
|
|
3500
3598
|
? { type: "delete", title: mainConversationAction.title }
|
|
3501
|
-
: null, loading: taskSessionsLoading, error: taskSessionsError, height: contentHeight, terminalWidth: terminalWidth })) : (_jsx(TaskSessionsView, { tasks: taskSessions, activeTaskId: activeTaskId, selectedIndex: selectedTaskSessionIndex, includeArchived: taskSessionsIncludeArchived, notice: taskSessionsNotice, action: taskSessionAction?.type === "rename"
|
|
3599
|
+
: null, loading: taskSessionsLoading, error: taskSessionsError, height: contentHeight, terminalWidth: terminalWidth })) : (_jsx(TaskSessionsView, { tasks: taskSessions, activeTaskId: activeTaskId, selectedIndex: selectedTaskSessionIndex, includeArchived: taskSessionsIncludeArchived, query: taskSessionQuery, notice: taskSessionsNotice, action: taskSessionAction?.type === "rename"
|
|
3502
3600
|
? { type: "rename", title: taskSessionAction.title }
|
|
3503
3601
|
: taskSessionAction?.type === "delete"
|
|
3504
3602
|
? { type: "delete", title: taskSessionAction.title }
|
|
@@ -4356,6 +4454,10 @@ function sessionDetailWorkerLabel(worker) {
|
|
|
4356
4454
|
function featureEngineDisplay(config, engine) {
|
|
4357
4455
|
return workerProviderLabel(config, engine);
|
|
4358
4456
|
}
|
|
4457
|
+
function featureTargetDisplay(config, engine, model) {
|
|
4458
|
+
const effectiveModel = model?.trim() || config.workers[engine]?.model.name || "default";
|
|
4459
|
+
return `${featureEngineDisplay(config, engine)}/${effectiveModel}`;
|
|
4460
|
+
}
|
|
4359
4461
|
function compactChatText(text, maxLength) {
|
|
4360
4462
|
if (maxLength <= 5) {
|
|
4361
4463
|
return takeChatTextStartByDisplayWidth(text, maxLength);
|
|
@@ -4745,6 +4847,15 @@ function compactNativeAttachRole(label) {
|
|
|
4745
4847
|
function compactNativeSessionForTitle(sessionId, maxLength) {
|
|
4746
4848
|
return compactEndByDisplayWidth(sessionId, Math.min(maxLength, 16));
|
|
4747
4849
|
}
|
|
4850
|
+
export function formatRoleConfigurationPreflightFailure(lines) {
|
|
4851
|
+
const clean = lines
|
|
4852
|
+
.map((line) => line.replace(/[\u0000-\u001f\u007f]/g, " ").replace(/\s+/g, " ").trim())
|
|
4853
|
+
.filter(Boolean);
|
|
4854
|
+
const actionable = clean.filter((line) => (/\b(?:denied|error|failed|invalid|missing|unavailable|unsupported)\b/i.test(line)
|
|
4855
|
+
|| /\bnot (?:found|supported|available)\b/i.test(line)));
|
|
4856
|
+
const detail = (actionable.length > 0 ? actionable : clean).slice(0, 4);
|
|
4857
|
+
return ["Provider preflight failed", ...detail].join(" · ");
|
|
4858
|
+
}
|
|
4748
4859
|
function isAbortError(error) {
|
|
4749
4860
|
return error instanceof Error && error.name === "AbortError";
|
|
4750
4861
|
}
|
|
@@ -142,7 +142,10 @@ function featureBoardEvidenceText(timeline, feature, width) {
|
|
|
142
142
|
? `deps ${dependencies.map((item) => safeFeatureBoardText(item.title)).join(", ")}`
|
|
143
143
|
: "independent";
|
|
144
144
|
const assignmentText = feature.actorEngine && feature.criticEngine
|
|
145
|
-
?
|
|
145
|
+
? [
|
|
146
|
+
`actor ${feature.actorEngine}/${feature.actorModel?.trim() || "default"}`,
|
|
147
|
+
`critic ${feature.criticEngine}/${feature.criticModel?.trim() || "default"}`
|
|
148
|
+
].join(" · ")
|
|
146
149
|
: "";
|
|
147
150
|
const evidence = feature.latestFinding
|
|
148
151
|
? `finding · ${safeFeatureBoardText(feature.latestFinding)}`
|
package/dist/tui/InputBar.js
CHANGED
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
3
|
import { compactEndByDisplayWidth, compactTailByDisplayWidth, displayWidth } from "./display-width.js";
|
|
4
4
|
import { TUI_THEME } from "./theme.js";
|
|
5
|
-
export function InputBar({ mode, ready = true, busy = false, routeFallback = false, collaborationDetail = false, collaborationUnresolved = false, collaborationBack = "workers", featureCanCancel = false, featureCanPause = false, featureCanReassign = false, featureCancelConfirm = false, featurePauseConfirm = false, featureAssignment = false, taskSessionAction = null, taskSessionsIncludeArchived = false, mainConversationSessions = false, taskSessionDetail = false, taskSessionDetailHasNative = false, taskSessionDetailCanFork = false, canRetry = false, hasWorkers = false, hasActiveTask = false, hasTaskResult = false, taskResultExpanded = false, chatScrollOffset = 0, chatMaxScrollOffset = 0, nativeClosed = false, searchMatchIndex = 0, searchMatchCount = 0, clipboardNotice = null, roleScope = "next", roleEditingModel = null, roleCanApply = true, roleSaving = false, roleHasOverride = false, value, cursor, terminalWidth: providedTerminalWidth, onChange, onSubmit }) {
|
|
5
|
+
export function InputBar({ mode, ready = true, busy = false, routeFallback = false, collaborationDetail = false, collaborationUnresolved = false, collaborationBack = "workers", featureCanCancel = false, featureCanPause = false, featureCanReassign = false, featureCancelConfirm = false, featurePauseConfirm = false, featureAssignment = false, featureEditingModel = null, taskSessionAction = null, taskSessionsIncludeArchived = false, taskSessionQuery = "", mainConversationSessions = false, taskSessionDetail = false, taskSessionDetailHasNative = false, taskSessionDetailCanFork = false, canRetry = false, hasWorkers = false, hasActiveTask = false, hasTaskResult = false, taskResultExpanded = false, chatScrollOffset = 0, chatMaxScrollOffset = 0, nativeClosed = false, searchMatchIndex = 0, searchMatchCount = 0, clipboardNotice = null, roleScope = "next", roleEditingModel = null, roleCanApply = true, roleSaving = false, roleHasOverride = false, value, cursor, terminalWidth: providedTerminalWidth, onChange, onSubmit }) {
|
|
6
6
|
const terminalWidth = providedTerminalWidth ?? process.stdout.columns ?? 120;
|
|
7
7
|
const fillRail = providedTerminalWidth !== undefined || typeof process.stdout.columns === "number";
|
|
8
8
|
if (clipboardNotice) {
|
|
@@ -30,11 +30,25 @@ export function InputBar({ mode, ready = true, busy = false, routeFallback = fal
|
|
|
30
30
|
const hints = roleConfigurationInputHints(terminalWidth, roleScope, roleCanApply, roleSaving, roleHasOverride);
|
|
31
31
|
return (_jsxs(InputRail, { terminalWidth: terminalWidth, textWidth: displayWidth(`${hints.label}${hints.detail}`), fill: fillRail, children: [_jsx(Text, { backgroundColor: TUI_THEME.rail, color: roleSaving ? TUI_THEME.warning : TUI_THEME.accent, bold: true, children: hints.label }), hints.detail ? _jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.muted, children: hints.detail }) : null] }));
|
|
32
32
|
}
|
|
33
|
+
if (mode === "features" && featureEditingModel) {
|
|
34
|
+
const prefix = `${featureEditingModel.role} model > `;
|
|
35
|
+
const valueWidth = Math.max(1, terminalWidth - displayWidth(prefix) - 3);
|
|
36
|
+
const display = chatInputDisplayParts(featureEditingModel.value, featureEditingModel.cursor, valueWidth);
|
|
37
|
+
const textWidth = displayWidth(`${prefix}${display.before}|${display.after}`);
|
|
38
|
+
return (_jsxs(InputRail, { terminalWidth: terminalWidth, textWidth: textWidth, fill: fillRail, children: [_jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.accent, bold: true, children: prefix }), _jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.text, children: display.before }), _jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.accent, bold: true, children: "|" }), _jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.text, children: display.after })] }));
|
|
39
|
+
}
|
|
33
40
|
if (mode === "status") {
|
|
34
41
|
const hints = statusDetailInputHints(terminalWidth);
|
|
35
42
|
return (_jsxs(InputRail, { terminalWidth: terminalWidth, textWidth: displayWidth(`${hints.label}${hints.detail}`), fill: fillRail, children: [_jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.accent, bold: true, children: hints.label }), hints.detail ? _jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.muted, children: hints.detail }) : null] }));
|
|
36
43
|
}
|
|
37
44
|
if (mode === "sessions") {
|
|
45
|
+
if (taskSessionAction?.type === "search") {
|
|
46
|
+
const prefix = terminalWidth < 12 ? "/ " : "find > ";
|
|
47
|
+
const valueWidth = Math.max(1, terminalWidth - displayWidth(prefix) - 3);
|
|
48
|
+
const display = chatInputDisplayParts(taskSessionAction.value, taskSessionAction.cursor, valueWidth);
|
|
49
|
+
const textWidth = displayWidth(`${prefix}${display.before}|${display.after}`);
|
|
50
|
+
return (_jsxs(InputRail, { terminalWidth: terminalWidth, textWidth: textWidth, fill: fillRail, children: [_jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.accent, bold: true, children: prefix }), _jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.text, children: display.before }), _jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.accent, bold: true, children: "|" }), _jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.text, children: display.after })] }));
|
|
51
|
+
}
|
|
38
52
|
if (taskSessionAction?.type === "rename") {
|
|
39
53
|
const prefix = terminalWidth < 12 ? "> " : "rename > ";
|
|
40
54
|
const valueWidth = Math.max(1, terminalWidth - displayWidth(prefix) - 3);
|
|
@@ -54,7 +68,7 @@ export function InputBar({ mode, ready = true, busy = false, routeFallback = fal
|
|
|
54
68
|
const hints = mainConversationSessionsInputHints(terminalWidth, taskSessionsIncludeArchived);
|
|
55
69
|
return (_jsxs(InputRail, { terminalWidth: terminalWidth, textWidth: displayWidth(`${hints.label}${hints.detail}`), fill: fillRail, children: [_jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.accent, bold: true, children: hints.label }), hints.detail ? _jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.muted, children: hints.detail }) : null] }));
|
|
56
70
|
}
|
|
57
|
-
const hints = taskSessionsInputHints(terminalWidth, taskSessionsIncludeArchived);
|
|
71
|
+
const hints = taskSessionsInputHints(terminalWidth, taskSessionsIncludeArchived, Boolean(taskSessionQuery.trim()));
|
|
58
72
|
return (_jsxs(InputRail, { terminalWidth: terminalWidth, textWidth: displayWidth(`${hints.label}${hints.detail}`), fill: fillRail, children: [_jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.accent, bold: true, children: hints.label }), hints.detail ? _jsx(Text, { backgroundColor: TUI_THEME.rail, color: TUI_THEME.muted, children: hints.detail }) : null] }));
|
|
59
73
|
}
|
|
60
74
|
if (mode === "collaboration") {
|
|
@@ -481,9 +495,9 @@ function workerOverviewInputHints(width) {
|
|
|
481
495
|
function featureBoardInputHints(width, options) {
|
|
482
496
|
if (options.assignment) {
|
|
483
497
|
return selectInputHints(width, [
|
|
484
|
-
{ label: "assign
|
|
485
|
-
{ label: "assign", detail: " · A
|
|
486
|
-
{ label: "assign", detail: " · A
|
|
498
|
+
{ label: "assign", detail: " · A/C provider · 1/2 model · M/Esc done" },
|
|
499
|
+
{ label: "assign", detail: " · A/C provider · 1/2 model · Esc done" },
|
|
500
|
+
{ label: "assign", detail: " · A/C provider · 1/2 model" },
|
|
487
501
|
{ label: "provider", detail: " · A · C · Esc" },
|
|
488
502
|
{ label: "provider", detail: "" },
|
|
489
503
|
{ label: "M", detail: "" }
|
|
@@ -589,10 +603,11 @@ function collaborationDetailInputHints(width) {
|
|
|
589
603
|
{ label: "e", detail: "" }
|
|
590
604
|
]);
|
|
591
605
|
}
|
|
592
|
-
function taskSessionsInputHints(width, includeArchived) {
|
|
606
|
+
function taskSessionsInputHints(width, includeArchived, hasQuery) {
|
|
593
607
|
const archivedAction = includeArchived ? "H hide archived" : "H archived";
|
|
608
|
+
const searchAction = hasQuery ? "^F edit · X clear" : "^F find";
|
|
594
609
|
return selectInputHints(width, [
|
|
595
|
-
{ label: "sessions", detail: ` · Up/Dn select · Enter restore · C conversations · I inspect · R rename · A archive · D delete · E export · ${archivedAction} · Esc back` },
|
|
610
|
+
{ label: "sessions", detail: ` · Up/Dn select · Enter restore · C conversations · I inspect · R rename · A archive · D delete · E export · ${archivedAction} · Esc back · ${searchAction}` },
|
|
596
611
|
{ label: "sessions", detail: " · Up/Dn select · Enter restore · C conversations · I inspect · R rename · A archive · D delete · E export · Esc back" },
|
|
597
612
|
{ label: "sessions", detail: " · Up/Dn select · Enter restore · C chats · I inspect · R rename · A archive · D delete · Esc back" },
|
|
598
613
|
{ label: "sessions", detail: " · Up/Dn select · Enter restore · C chats · I inspect · R rename · A archive · Esc back" },
|
|
@@ -24,10 +24,10 @@ export function statusDetailDisplayLines(input, width, height) {
|
|
|
24
24
|
text: fitStatusDetailText(`workspace · ${basename(input.cwd) || input.cwd} · ${input.cwd}`, safeWidth),
|
|
25
25
|
tone: "muted"
|
|
26
26
|
},
|
|
27
|
-
{
|
|
28
|
-
text: fitStatusDetailText(
|
|
29
|
-
tone:
|
|
30
|
-
}
|
|
27
|
+
...roleMatrixDetailLines(input).map((line) => ({
|
|
28
|
+
text: fitStatusDetailText(line.text, safeWidth),
|
|
29
|
+
tone: line.tone
|
|
30
|
+
}))
|
|
31
31
|
];
|
|
32
32
|
if (input.configStatus?.trim()) {
|
|
33
33
|
lines.push({
|
|
@@ -59,15 +59,36 @@ export function statusDetailDisplayLines(input, width, height) {
|
|
|
59
59
|
}
|
|
60
60
|
return lines.slice(0, maxLines);
|
|
61
61
|
}
|
|
62
|
-
function
|
|
62
|
+
function roleMatrixDetail(label, pairing, selection) {
|
|
63
63
|
return [
|
|
64
|
-
|
|
64
|
+
label,
|
|
65
65
|
rolePairingEntry("main", selection?.main.engine ?? pairing.main, selection?.main.model),
|
|
66
66
|
rolePairingEntry("judge", selection?.judge.engine ?? pairing.judge, selection?.judge.model),
|
|
67
67
|
rolePairingEntry("actor", selection?.actor.engine ?? pairing.actor, selection?.actor.model),
|
|
68
68
|
rolePairingEntry("critic", selection?.critic.engine ?? pairing.critic, selection?.critic.model)
|
|
69
69
|
].join(" · ");
|
|
70
70
|
}
|
|
71
|
+
function roleMatrixDetailLines(input) {
|
|
72
|
+
const snapshot = input.roleConfigurationSnapshot;
|
|
73
|
+
const lines = [{
|
|
74
|
+
text: roleMatrixDetail("actual roles", input.pairing, input.roleSelection),
|
|
75
|
+
tone: "text"
|
|
76
|
+
}];
|
|
77
|
+
if (!snapshot) {
|
|
78
|
+
return lines;
|
|
79
|
+
}
|
|
80
|
+
const next = snapshot.next ?? snapshot.task ?? snapshot.future;
|
|
81
|
+
const source = snapshot.next ? "one shot" : snapshot.task ? "task default" : "future default";
|
|
82
|
+
lines.push({
|
|
83
|
+
text: roleMatrixDetail(`next roles (${source})`, input.pairing, next),
|
|
84
|
+
tone: snapshot.next ? "active" : "muted"
|
|
85
|
+
});
|
|
86
|
+
lines.push({
|
|
87
|
+
text: roleMatrixDetail("future roles", input.pairing, snapshot.future),
|
|
88
|
+
tone: "muted"
|
|
89
|
+
});
|
|
90
|
+
return lines;
|
|
91
|
+
}
|
|
71
92
|
function rolePairingEntry(role, engine, model) {
|
|
72
93
|
return [role, engine, model?.trim()].filter(Boolean).join("/");
|
|
73
94
|
}
|
|
@@ -2,13 +2,14 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
3
|
import { compactEndByDisplayWidth, displayWidth } from "./display-width.js";
|
|
4
4
|
import { TUI_THEME } from "./theme.js";
|
|
5
|
-
export function TaskSessionsView({ tasks, activeTaskId, selectedIndex, includeArchived = false, notice = null, action = null, loading = false, error = null, height = 20, terminalWidth = process.stdout.columns || 120 }) {
|
|
5
|
+
export function TaskSessionsView({ tasks, activeTaskId, selectedIndex, includeArchived = false, query = "", notice = null, action = null, loading = false, error = null, height = 20, terminalWidth = process.stdout.columns || 120 }) {
|
|
6
6
|
const viewportHeight = Math.max(1, height);
|
|
7
7
|
const width = taskSessionsContentWidth(terminalWidth);
|
|
8
8
|
const lines = taskSessionsDisplayLines(tasks, activeTaskId, selectedIndex, viewportHeight, terminalWidth, {
|
|
9
9
|
loading,
|
|
10
10
|
error,
|
|
11
11
|
includeArchived,
|
|
12
|
+
query,
|
|
12
13
|
notice,
|
|
13
14
|
action
|
|
14
15
|
});
|
|
@@ -21,7 +22,10 @@ export function taskSessionsDisplayLines(tasks, activeTaskId, selectedIndex, hei
|
|
|
21
22
|
const lines = [
|
|
22
23
|
{
|
|
23
24
|
text: fitTaskSessionCandidates([
|
|
24
|
-
state.includeArchived
|
|
25
|
+
taskSessionsHeading(Boolean(state.includeArchived), state.query ?? ""),
|
|
26
|
+
...((state.query ?? "").trim()
|
|
27
|
+
? [`Find · ${safeTaskSessionText(state.query ?? "")}`, `/ ${safeTaskSessionText(state.query ?? "")}`]
|
|
28
|
+
: []),
|
|
25
29
|
state.includeArchived ? "Sessions · all" : "Sessions",
|
|
26
30
|
"Tasks",
|
|
27
31
|
"T"
|
|
@@ -30,7 +34,7 @@ export function taskSessionsDisplayLines(tasks, activeTaskId, selectedIndex, hei
|
|
|
30
34
|
}
|
|
31
35
|
];
|
|
32
36
|
if (viewportHeight >= 3) {
|
|
33
|
-
lines.push({ text: taskSessionSummary(tasks, width), tone: "muted" });
|
|
37
|
+
lines.push({ text: taskSessionSummary(tasks, width, state.query ?? ""), tone: "muted" });
|
|
34
38
|
}
|
|
35
39
|
if (viewportHeight >= 4 && state.action) {
|
|
36
40
|
lines.push({
|
|
@@ -58,7 +62,10 @@ export function taskSessionsDisplayLines(tasks, activeTaskId, selectedIndex, hei
|
|
|
58
62
|
}
|
|
59
63
|
if (tasks.length === 0) {
|
|
60
64
|
if (slots > 0) {
|
|
61
|
-
lines.push({
|
|
65
|
+
lines.push({
|
|
66
|
+
text: fitTaskSessionText(state.query?.trim() ? "No matching task sessions" : "No saved task sessions", width),
|
|
67
|
+
tone: "muted"
|
|
68
|
+
});
|
|
62
69
|
}
|
|
63
70
|
return lines;
|
|
64
71
|
}
|
|
@@ -94,7 +101,7 @@ function TaskSessionRow({ line, width }) {
|
|
|
94
101
|
const theme = taskSessionLineTheme(line.tone);
|
|
95
102
|
return (_jsxs(Text, { children: [_jsx(Text, { ...theme, children: line.text }), trailingWidth > 0 ? _jsx(Text, { backgroundColor: TUI_THEME.surface, children: " ".repeat(trailingWidth) }) : null] }));
|
|
96
103
|
}
|
|
97
|
-
function taskSessionSummary(tasks, width) {
|
|
104
|
+
function taskSessionSummary(tasks, width, query) {
|
|
98
105
|
const counts = new Map();
|
|
99
106
|
let archived = 0;
|
|
100
107
|
for (const task of tasks) {
|
|
@@ -112,11 +119,16 @@ function taskSessionSummary(tasks, width) {
|
|
|
112
119
|
if (archived > 0) {
|
|
113
120
|
parts.push(`${archived} archived`);
|
|
114
121
|
}
|
|
122
|
+
const searching = Boolean(query.trim());
|
|
123
|
+
const prefix = searching
|
|
124
|
+
? `${tasks.length} ${tasks.length === 1 ? "match" : "matches"}`
|
|
125
|
+
: `${tasks.length} ${tasks.length === 1 ? "task" : "tasks"}`;
|
|
126
|
+
const compactPrefix = searching ? `${tasks.length} matches` : `${tasks.length} tasks`;
|
|
115
127
|
return fitTaskSessionCandidates([
|
|
116
|
-
[
|
|
117
|
-
`${
|
|
118
|
-
|
|
119
|
-
`${tasks.length}t`
|
|
128
|
+
[prefix, ...parts].join(" · "),
|
|
129
|
+
`${compactPrefix} · ${counts.get("running") ?? 0} active · ${counts.get("failed") ?? 0} failed`,
|
|
130
|
+
compactPrefix,
|
|
131
|
+
`${tasks.length}${searching ? "m" : "t"}`
|
|
120
132
|
], width);
|
|
121
133
|
}
|
|
122
134
|
function taskSessionRowText(task, selected, active, width) {
|
|
@@ -130,7 +142,9 @@ function taskSessionRowText(task, selected, active, width) {
|
|
|
130
142
|
const workers = `${task.workerCount} ${task.workerCount === 1 ? "worker" : "workers"}`;
|
|
131
143
|
const native = `${task.nativeSessionCount} native`;
|
|
132
144
|
const compactId = task.id.replace(/^task-/, "#");
|
|
145
|
+
const match = safeTaskSessionText(task.searchMatch?.summary ?? "");
|
|
133
146
|
return fitTaskSessionCandidates([
|
|
147
|
+
...(match ? [[marker + title, status, match].join(" · ")] : []),
|
|
134
148
|
[marker + title, status, turns, workers, native, date].join(" · "),
|
|
135
149
|
[marker + title, status, turns, workers, native].join(" · "),
|
|
136
150
|
[marker + title, status, turns, workers].join(" · "),
|
|
@@ -139,6 +153,13 @@ function taskSessionRowText(task, selected, active, width) {
|
|
|
139
153
|
marker.trimEnd()
|
|
140
154
|
], width);
|
|
141
155
|
}
|
|
156
|
+
function taskSessionsHeading(includeArchived, query) {
|
|
157
|
+
const scope = includeArchived ? "archived shown" : "active";
|
|
158
|
+
const cleanQuery = safeTaskSessionText(query);
|
|
159
|
+
return cleanQuery
|
|
160
|
+
? `Task sessions · ${scope} · find ${cleanQuery}`
|
|
161
|
+
: includeArchived ? "Task sessions · archived shown" : "Task sessions";
|
|
162
|
+
}
|
|
142
163
|
function taskSessionWindowStart(selected, count, visibleCount) {
|
|
143
164
|
if (visibleCount <= 0 || count <= visibleCount) {
|
|
144
165
|
return 0;
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = "0.3.
|
|
1
|
+
export const version = "0.3.2";
|