parallel-codex-tui 0.1.3 → 0.1.4
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/.parallel-codex/config.example.toml +90 -3
- package/README.md +240 -21
- package/dist/bootstrap.js +37 -17
- package/dist/cli-args.js +34 -3
- package/dist/cli-help.js +20 -0
- package/dist/cli-startup-recovery.js +70 -0
- package/dist/cli-workspace-picker.js +330 -0
- package/dist/cli-workspace-transition.js +33 -0
- package/dist/cli-workspace.js +7 -71
- package/dist/cli.js +221 -23
- package/dist/core/collaboration-timeline.js +261 -0
- package/dist/core/config-errors.js +14 -0
- package/dist/core/config.js +154 -24
- package/dist/core/file-store.js +119 -6
- package/dist/core/lease-finalization.js +22 -0
- package/dist/core/paths.js +7 -0
- package/dist/core/process-identity.js +48 -0
- package/dist/core/process-mutation-turn.js +128 -0
- package/dist/core/process-ownership.js +276 -0
- package/dist/core/process-tree.js +90 -0
- package/dist/core/router-audit.js +155 -0
- package/dist/core/router-redaction.js +31 -0
- package/dist/core/router.js +462 -35
- package/dist/core/session-index.js +188 -37
- package/dist/core/session-manager.js +1086 -40
- package/dist/core/task-state-machine.js +17 -0
- package/dist/core/workspace-commit-recovery.js +118 -0
- package/dist/core/workspace.js +19 -11
- package/dist/doctor.js +343 -23
- package/dist/domain/schemas.js +127 -6
- package/dist/orchestrator/collaboration-channel.js +255 -4
- package/dist/orchestrator/feature-plan.js +70 -0
- package/dist/orchestrator/judge-artifacts.js +236 -0
- package/dist/orchestrator/orchestrator.js +1749 -202
- package/dist/orchestrator/prompts.js +126 -2
- package/dist/orchestrator/supervisor-summary.js +56 -2
- package/dist/orchestrator/workspace-sandbox.js +911 -0
- package/dist/tui/App.js +2830 -153
- package/dist/tui/AppShell.js +188 -23
- package/dist/tui/CollaborationTimelineView.js +327 -0
- package/dist/tui/FeatureBoardView.js +227 -0
- package/dist/tui/InputBar.js +514 -57
- package/dist/tui/RouterDiagnosticsView.js +469 -0
- package/dist/tui/StatusBar.js +610 -57
- package/dist/tui/TaskSessionsView.js +207 -0
- package/dist/tui/TerminalOutput.js +53 -9
- package/dist/tui/WorkerOutputView.js +1403 -161
- package/dist/tui/WorkerOverviewView.js +250 -0
- package/dist/tui/chat-history.js +25 -0
- package/dist/tui/chat-input.js +67 -19
- package/dist/tui/chat-paste.js +76 -0
- package/dist/tui/display-width.js +41 -3
- package/dist/tui/incremental-text-file.js +101 -0
- package/dist/tui/keyboard.js +46 -0
- package/dist/tui/markdown-text.js +14 -0
- package/dist/tui/raw-input-decoder.js +3 -0
- package/dist/tui/scrolling.js +2 -1
- package/dist/tui/status-line.js +318 -11
- package/dist/tui/task-memory.js +13 -1
- package/dist/tui/task-result.js +105 -0
- package/dist/tui/terminal-screen.js +13 -1
- package/dist/tui/theme-contrast.js +144 -0
- package/dist/tui/theme-preview.js +109 -0
- package/dist/tui/theme.js +158 -0
- package/dist/version.js +1 -1
- package/dist/workers/capabilities.js +212 -0
- package/dist/workers/live-probe.js +176 -0
- package/dist/workers/mock-adapter.js +39 -6
- package/dist/workers/native-attach.js +78 -3
- package/dist/workers/process-adapter.js +570 -77
- package/dist/workers/registry.js +4 -2
- package/package.json +5 -2
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import { compactEndByDisplayWidth, displayWidth } from "./display-width.js";
|
|
4
|
+
import { TUI_THEME } from "./theme.js";
|
|
5
|
+
export function FeatureBoardView({ timeline, selectedIndex, loading = false, error = null, notice = null, height = 20, terminalWidth = process.stdout.columns || 120 }) {
|
|
6
|
+
const viewportHeight = Math.max(1, Math.trunc(height));
|
|
7
|
+
const width = featureBoardContentWidth(terminalWidth);
|
|
8
|
+
const lines = featureBoardDisplayLines(timeline, selectedIndex, viewportHeight, terminalWidth, {
|
|
9
|
+
loading,
|
|
10
|
+
error,
|
|
11
|
+
notice
|
|
12
|
+
});
|
|
13
|
+
const blankRows = Math.max(0, viewportHeight - lines.length);
|
|
14
|
+
return (_jsxs(Box, { flexDirection: "column", height: viewportHeight, children: [lines.map((line, index) => (_jsx(FeatureBoardRow, { line: line, width: width }, `${line.featureIndex ?? line.tone}-${index}`))), Array.from({ length: blankRows }, (_, index) => (_jsx(Text, { backgroundColor: TUI_THEME.surface, children: " ".repeat(width) }, `feature-board-fill-${index}`)))] }));
|
|
15
|
+
}
|
|
16
|
+
export function featureBoardDisplayLines(timeline, selectedIndex, height, terminalWidth, state = {}) {
|
|
17
|
+
const viewportHeight = Math.max(1, Math.trunc(height));
|
|
18
|
+
const width = featureBoardContentWidth(terminalWidth);
|
|
19
|
+
const lines = [
|
|
20
|
+
{ text: fitFeatureBoardCandidates(["Feature board", "Features", "Feat"], width), tone: "heading" }
|
|
21
|
+
];
|
|
22
|
+
if (viewportHeight >= 3) {
|
|
23
|
+
lines.push({ text: featureBoardSummary(timeline, width), tone: "muted" });
|
|
24
|
+
}
|
|
25
|
+
let slots = Math.max(0, viewportHeight - lines.length);
|
|
26
|
+
if (slots === 0) {
|
|
27
|
+
return lines;
|
|
28
|
+
}
|
|
29
|
+
if (state.loading && !timeline) {
|
|
30
|
+
lines.push({ text: fitFeatureBoardText("loading feature evidence", width), tone: "muted" });
|
|
31
|
+
return lines;
|
|
32
|
+
}
|
|
33
|
+
if (state.error) {
|
|
34
|
+
lines.push({ text: fitFeatureBoardText(`error · ${safeFeatureBoardText(state.error)}`, width), tone: "danger" });
|
|
35
|
+
return lines;
|
|
36
|
+
}
|
|
37
|
+
if (!timeline) {
|
|
38
|
+
lines.push({ text: fitFeatureBoardText("no feature board", width), tone: "muted" });
|
|
39
|
+
return lines;
|
|
40
|
+
}
|
|
41
|
+
if (timeline.features.length === 0) {
|
|
42
|
+
lines.push({ text: fitFeatureBoardText("no planned features", width), tone: "muted" });
|
|
43
|
+
return lines;
|
|
44
|
+
}
|
|
45
|
+
if (state.notice && lines.length < viewportHeight) {
|
|
46
|
+
lines.push({
|
|
47
|
+
text: fitFeatureBoardText(safeFeatureBoardText(state.notice), width),
|
|
48
|
+
tone: "warning"
|
|
49
|
+
});
|
|
50
|
+
slots = Math.max(0, viewportHeight - lines.length);
|
|
51
|
+
}
|
|
52
|
+
if (slots === 0) {
|
|
53
|
+
return lines;
|
|
54
|
+
}
|
|
55
|
+
const selected = clampFeatureIndex(selectedIndex, timeline.features.length);
|
|
56
|
+
const rowsPerFeature = terminalWidth >= 48 && slots >= timeline.features.length * 2 ? 2 : 1;
|
|
57
|
+
const visibleCount = Math.min(timeline.features.length, Math.max(1, Math.floor(slots / rowsPerFeature)));
|
|
58
|
+
const start = featureBoardWindowStart(selected, timeline.features.length, visibleCount);
|
|
59
|
+
for (let index = start; index < start + visibleCount; index += 1) {
|
|
60
|
+
const feature = timeline.features[index];
|
|
61
|
+
if (!feature) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const blocked = featureBoardBlockedDependencies(timeline, feature);
|
|
65
|
+
lines.push({
|
|
66
|
+
text: featureBoardFeatureText(feature, index === selected, blocked, width),
|
|
67
|
+
tone: featureBoardStateTone(feature.state),
|
|
68
|
+
featureIndex: index
|
|
69
|
+
});
|
|
70
|
+
if (rowsPerFeature === 2) {
|
|
71
|
+
lines.push({
|
|
72
|
+
text: featureBoardEvidenceText(timeline, feature, width),
|
|
73
|
+
tone: "muted",
|
|
74
|
+
featureIndex: index
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return lines;
|
|
79
|
+
}
|
|
80
|
+
export function moveFeatureBoardSelection(current, delta, featureCount, wrap = false) {
|
|
81
|
+
if (featureCount <= 0) {
|
|
82
|
+
return 0;
|
|
83
|
+
}
|
|
84
|
+
const normalized = clampFeatureIndex(current, featureCount);
|
|
85
|
+
const next = normalized + Math.trunc(delta);
|
|
86
|
+
if (wrap) {
|
|
87
|
+
return ((next % featureCount) + featureCount) % featureCount;
|
|
88
|
+
}
|
|
89
|
+
return Math.min(featureCount - 1, Math.max(0, next));
|
|
90
|
+
}
|
|
91
|
+
function FeatureBoardRow({ line, width }) {
|
|
92
|
+
const fill = Math.max(0, width - displayWidth(line.text));
|
|
93
|
+
return (_jsxs(Text, { children: [_jsx(Text, { ...featureBoardLineTheme(line.tone), children: line.text }), fill > 0 ? _jsx(Text, { backgroundColor: TUI_THEME.surface, children: " ".repeat(fill) }) : null] }));
|
|
94
|
+
}
|
|
95
|
+
function featureBoardSummary(timeline, width) {
|
|
96
|
+
if (!timeline) {
|
|
97
|
+
return fitFeatureBoardCandidates(["waiting for task evidence", "waiting"], width);
|
|
98
|
+
}
|
|
99
|
+
const approved = timeline.features.filter((feature) => feature.state === "approved").length;
|
|
100
|
+
const active = timeline.features.filter((feature) => featureBoardStateIsActive(feature.state)).length;
|
|
101
|
+
const revision = timeline.features.filter((feature) => feature.state === "revision_needed").length;
|
|
102
|
+
const failed = timeline.features.filter((feature) => feature.state === "failed" || feature.state === "cancelled").length;
|
|
103
|
+
const blocked = timeline.features.filter((feature) => featureBoardBlockedDependencies(timeline, feature).length > 0).length;
|
|
104
|
+
const full = [
|
|
105
|
+
`${timeline.features.length} ${timeline.features.length === 1 ? "feature" : "features"}`,
|
|
106
|
+
...(approved > 0 ? [`${approved} approved`] : []),
|
|
107
|
+
...(active > 0 ? [`${active} active`] : []),
|
|
108
|
+
...(revision > 0 ? [`${revision} ${revision === 1 ? "revision" : "revisions"}`] : []),
|
|
109
|
+
...(blocked > 0 ? [`${blocked} blocked`] : []),
|
|
110
|
+
...(failed > 0 ? [`${failed} failed`] : [])
|
|
111
|
+
].join(" · ");
|
|
112
|
+
return fitFeatureBoardCandidates([
|
|
113
|
+
full,
|
|
114
|
+
`${timeline.features.length} features · ${approved} done · ${blocked} blocked`,
|
|
115
|
+
`${timeline.features.length} features`,
|
|
116
|
+
`${timeline.features.length}f`
|
|
117
|
+
], width);
|
|
118
|
+
}
|
|
119
|
+
function featureBoardFeatureText(feature, selected, blocked, width) {
|
|
120
|
+
const marker = selected ? "> " : " ";
|
|
121
|
+
const state = humanizeFeatureState(feature.state);
|
|
122
|
+
const debt = typeof feature.unresolvedFindings === "number"
|
|
123
|
+
? feature.unresolvedFindings
|
|
124
|
+
: Math.max(0, feature.findings - feature.replies);
|
|
125
|
+
const review = debt > 0 ? `${debt} open ${debt === 1 ? "finding" : "findings"}` : "";
|
|
126
|
+
const blocker = blocked.length > 0
|
|
127
|
+
? `blocked by ${blocked.map((item) => safeFeatureBoardText(item.title)).join(", ")}`
|
|
128
|
+
: "";
|
|
129
|
+
return fitFeatureBoardCandidates([
|
|
130
|
+
[marker + `T${feature.turnId}`, safeFeatureBoardText(feature.title), state, review, blocker].filter(Boolean).join(" · "),
|
|
131
|
+
[marker + safeFeatureBoardText(feature.title), state, review, blocker].filter(Boolean).join(" · "),
|
|
132
|
+
[marker + safeFeatureBoardText(feature.title), state].join(" · "),
|
|
133
|
+
marker + safeFeatureBoardText(feature.id),
|
|
134
|
+
marker.trimEnd()
|
|
135
|
+
], width);
|
|
136
|
+
}
|
|
137
|
+
function featureBoardEvidenceText(timeline, feature, width) {
|
|
138
|
+
const dependencies = featureBoardDependencies(timeline, feature);
|
|
139
|
+
const dependencyText = dependencies.length > 0
|
|
140
|
+
? `deps ${dependencies.map((item) => safeFeatureBoardText(item.title)).join(", ")}`
|
|
141
|
+
: "independent";
|
|
142
|
+
const evidence = feature.latestFinding
|
|
143
|
+
? `finding · ${safeFeatureBoardText(feature.latestFinding)}`
|
|
144
|
+
: feature.latestReply
|
|
145
|
+
? `reply · ${safeFeatureBoardText(feature.latestReply)}`
|
|
146
|
+
: safeFeatureBoardText(feature.description);
|
|
147
|
+
return fitFeatureBoardText(` ${[dependencyText, evidence].filter(Boolean).join(" · ")}`, width);
|
|
148
|
+
}
|
|
149
|
+
function featureBoardDependencies(timeline, feature) {
|
|
150
|
+
return feature.dependsOn.flatMap((dependency) => {
|
|
151
|
+
const resolved = timeline.features.find((candidate) => (candidate.id === dependency || candidate.id === `${feature.turnId}-${dependency}`));
|
|
152
|
+
return resolved ? [resolved] : [];
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
function featureBoardBlockedDependencies(timeline, feature) {
|
|
156
|
+
if (feature.state === "approved") {
|
|
157
|
+
return [];
|
|
158
|
+
}
|
|
159
|
+
return featureBoardDependencies(timeline, feature).filter((dependency) => dependency.state !== "approved");
|
|
160
|
+
}
|
|
161
|
+
function featureBoardStateIsActive(state) {
|
|
162
|
+
return state === "actor_running" || state === "critic_running" || state === "integrating" || state === "verifying";
|
|
163
|
+
}
|
|
164
|
+
function featureBoardStateTone(state) {
|
|
165
|
+
if (state === "approved") {
|
|
166
|
+
return "success";
|
|
167
|
+
}
|
|
168
|
+
if (state === "failed" || state === "cancelled") {
|
|
169
|
+
return "danger";
|
|
170
|
+
}
|
|
171
|
+
if (state === "revision_needed") {
|
|
172
|
+
return "warning";
|
|
173
|
+
}
|
|
174
|
+
if (featureBoardStateIsActive(state)) {
|
|
175
|
+
return "active";
|
|
176
|
+
}
|
|
177
|
+
return "muted";
|
|
178
|
+
}
|
|
179
|
+
function humanizeFeatureState(state) {
|
|
180
|
+
if (state === "revision_needed") {
|
|
181
|
+
return "revision pending";
|
|
182
|
+
}
|
|
183
|
+
return state.replaceAll("_", " ");
|
|
184
|
+
}
|
|
185
|
+
function featureBoardWindowStart(selected, count, visibleCount) {
|
|
186
|
+
if (visibleCount <= 0 || count <= visibleCount) {
|
|
187
|
+
return 0;
|
|
188
|
+
}
|
|
189
|
+
return Math.min(count - visibleCount, Math.max(0, selected - Math.floor(visibleCount / 2)));
|
|
190
|
+
}
|
|
191
|
+
function clampFeatureIndex(index, count) {
|
|
192
|
+
return Math.min(Math.max(0, count - 1), Math.max(0, Math.trunc(index)));
|
|
193
|
+
}
|
|
194
|
+
function fitFeatureBoardCandidates(candidates, width) {
|
|
195
|
+
const fitted = candidates.find((candidate) => displayWidth(candidate) <= width);
|
|
196
|
+
return fitted ?? fitFeatureBoardText(candidates.at(-1) ?? "", width);
|
|
197
|
+
}
|
|
198
|
+
function fitFeatureBoardText(text, width) {
|
|
199
|
+
return compactEndByDisplayWidth(text, Math.max(1, width));
|
|
200
|
+
}
|
|
201
|
+
function safeFeatureBoardText(text) {
|
|
202
|
+
return text
|
|
203
|
+
.replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "")
|
|
204
|
+
.replace(/[\u0000-\u001f\u007f]/g, " ")
|
|
205
|
+
.replace(/\s+/g, " ")
|
|
206
|
+
.trim();
|
|
207
|
+
}
|
|
208
|
+
function featureBoardLineTheme(tone) {
|
|
209
|
+
return {
|
|
210
|
+
backgroundColor: TUI_THEME.surface,
|
|
211
|
+
color: tone === "heading" || tone === "active"
|
|
212
|
+
? TUI_THEME.accent
|
|
213
|
+
: tone === "success"
|
|
214
|
+
? TUI_THEME.success
|
|
215
|
+
: tone === "warning"
|
|
216
|
+
? TUI_THEME.warning
|
|
217
|
+
: tone === "danger"
|
|
218
|
+
? TUI_THEME.danger
|
|
219
|
+
: tone === "muted"
|
|
220
|
+
? TUI_THEME.muted
|
|
221
|
+
: TUI_THEME.text,
|
|
222
|
+
...(tone === "heading" || tone === "danger" ? { bold: true } : {})
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function featureBoardContentWidth(terminalWidth) {
|
|
226
|
+
return Math.max(1, terminalWidth - 2);
|
|
227
|
+
}
|