parallel-codex-tui 0.1.3 → 0.1.5

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.
Files changed (78) hide show
  1. package/.parallel-codex/config.example.toml +136 -3
  2. package/README.md +299 -21
  3. package/dist/bootstrap.js +37 -17
  4. package/dist/cli-args.js +34 -3
  5. package/dist/cli-help.js +20 -0
  6. package/dist/cli-startup-preflight.js +18 -0
  7. package/dist/cli-startup-recovery.js +82 -0
  8. package/dist/cli-workspace-picker.js +330 -0
  9. package/dist/cli-workspace-transition.js +33 -0
  10. package/dist/cli-workspace.js +7 -71
  11. package/dist/cli.js +234 -24
  12. package/dist/core/collaboration-timeline.js +268 -0
  13. package/dist/core/config-errors.js +14 -0
  14. package/dist/core/config.js +297 -109
  15. package/dist/core/file-store.js +119 -6
  16. package/dist/core/lease-finalization.js +22 -0
  17. package/dist/core/paths.js +7 -0
  18. package/dist/core/process-identity.js +48 -0
  19. package/dist/core/process-mutation-turn.js +128 -0
  20. package/dist/core/process-ownership.js +276 -0
  21. package/dist/core/process-tree.js +90 -0
  22. package/dist/core/router-audit.js +155 -0
  23. package/dist/core/router-redaction.js +31 -0
  24. package/dist/core/router.js +462 -35
  25. package/dist/core/session-index.js +412 -88
  26. package/dist/core/session-manager.js +1110 -40
  27. package/dist/core/task-session-details.js +175 -0
  28. package/dist/core/task-state-machine.js +18 -0
  29. package/dist/core/workspace-commit-recovery.js +118 -0
  30. package/dist/core/workspace.js +19 -11
  31. package/dist/doctor.js +373 -34
  32. package/dist/domain/schemas.js +142 -7
  33. package/dist/orchestrator/collaboration-channel.js +289 -6
  34. package/dist/orchestrator/feature-plan.js +70 -0
  35. package/dist/orchestrator/final-acceptance.js +86 -0
  36. package/dist/orchestrator/judge-artifacts.js +236 -0
  37. package/dist/orchestrator/orchestrator.js +2086 -203
  38. package/dist/orchestrator/prompts.js +168 -5
  39. package/dist/orchestrator/supervisor-summary.js +56 -2
  40. package/dist/orchestrator/workspace-sandbox.js +927 -0
  41. package/dist/tui/App.js +3187 -161
  42. package/dist/tui/AppShell.js +196 -25
  43. package/dist/tui/CollaborationTimelineView.js +327 -0
  44. package/dist/tui/FeatureBoardView.js +232 -0
  45. package/dist/tui/InputBar.js +581 -57
  46. package/dist/tui/RouterDiagnosticsView.js +469 -0
  47. package/dist/tui/StatusBar.js +610 -57
  48. package/dist/tui/StatusDetailView.js +164 -0
  49. package/dist/tui/TaskSessionDetailView.js +222 -0
  50. package/dist/tui/TaskSessionsView.js +210 -0
  51. package/dist/tui/TerminalOutput.js +53 -9
  52. package/dist/tui/WorkerOutputView.js +1404 -161
  53. package/dist/tui/WorkerOverviewView.js +269 -0
  54. package/dist/tui/chat-history.js +25 -0
  55. package/dist/tui/chat-input.js +67 -19
  56. package/dist/tui/chat-paste.js +76 -0
  57. package/dist/tui/display-width.js +41 -3
  58. package/dist/tui/incremental-text-file.js +101 -0
  59. package/dist/tui/keyboard.js +49 -0
  60. package/dist/tui/markdown-text.js +14 -0
  61. package/dist/tui/raw-input-decoder.js +3 -0
  62. package/dist/tui/scrolling.js +2 -1
  63. package/dist/tui/status-line.js +360 -11
  64. package/dist/tui/task-memory.js +13 -1
  65. package/dist/tui/task-result.js +105 -0
  66. package/dist/tui/terminal-screen.js +13 -1
  67. package/dist/tui/theme-contrast.js +144 -0
  68. package/dist/tui/theme-preview.js +109 -0
  69. package/dist/tui/theme.js +158 -0
  70. package/dist/version.js +1 -1
  71. package/dist/workers/capabilities.js +213 -0
  72. package/dist/workers/live-probe.js +177 -0
  73. package/dist/workers/mock-adapter.js +73 -8
  74. package/dist/workers/native-attach.js +106 -16
  75. package/dist/workers/process-adapter.js +572 -77
  76. package/dist/workers/provider.js +26 -0
  77. package/dist/workers/registry.js +12 -20
  78. package/package.json +11 -2
@@ -0,0 +1,232 @@
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 paused = timeline.features.filter((feature) => feature.state === "paused").length;
103
+ const failed = timeline.features.filter((feature) => feature.state === "failed" || feature.state === "cancelled").length;
104
+ const blocked = timeline.features.filter((feature) => featureBoardBlockedDependencies(timeline, feature).length > 0).length;
105
+ const full = [
106
+ `${timeline.features.length} ${timeline.features.length === 1 ? "feature" : "features"}`,
107
+ ...(approved > 0 ? [`${approved} approved`] : []),
108
+ ...(active > 0 ? [`${active} active`] : []),
109
+ ...(revision > 0 ? [`${revision} ${revision === 1 ? "revision" : "revisions"}`] : []),
110
+ ...(paused > 0 ? [`${paused} paused`] : []),
111
+ ...(blocked > 0 ? [`${blocked} blocked`] : []),
112
+ ...(failed > 0 ? [`${failed} failed`] : [])
113
+ ].join(" · ");
114
+ return fitFeatureBoardCandidates([
115
+ full,
116
+ `${timeline.features.length} features · ${approved} done · ${blocked} blocked`,
117
+ `${timeline.features.length} features`,
118
+ `${timeline.features.length}f`
119
+ ], width);
120
+ }
121
+ function featureBoardFeatureText(feature, selected, blocked, width) {
122
+ const marker = selected ? "> " : " ";
123
+ const state = humanizeFeatureState(feature.state);
124
+ const debt = typeof feature.unresolvedFindings === "number"
125
+ ? feature.unresolvedFindings
126
+ : Math.max(0, feature.findings - feature.replies);
127
+ const review = debt > 0 ? `${debt} open ${debt === 1 ? "finding" : "findings"}` : "";
128
+ const blocker = blocked.length > 0
129
+ ? `blocked by ${blocked.map((item) => safeFeatureBoardText(item.title)).join(", ")}`
130
+ : "";
131
+ return fitFeatureBoardCandidates([
132
+ [marker + `T${feature.turnId}`, safeFeatureBoardText(feature.title), state, review, blocker].filter(Boolean).join(" · "),
133
+ [marker + safeFeatureBoardText(feature.title), state, review, blocker].filter(Boolean).join(" · "),
134
+ [marker + safeFeatureBoardText(feature.title), state].join(" · "),
135
+ marker + safeFeatureBoardText(feature.id),
136
+ marker.trimEnd()
137
+ ], width);
138
+ }
139
+ function featureBoardEvidenceText(timeline, feature, width) {
140
+ const dependencies = featureBoardDependencies(timeline, feature);
141
+ const dependencyText = dependencies.length > 0
142
+ ? `deps ${dependencies.map((item) => safeFeatureBoardText(item.title)).join(", ")}`
143
+ : "independent";
144
+ const assignmentText = feature.actorEngine && feature.criticEngine
145
+ ? `actor ${feature.actorEngine} · critic ${feature.criticEngine}`
146
+ : "";
147
+ const evidence = feature.latestFinding
148
+ ? `finding · ${safeFeatureBoardText(feature.latestFinding)}`
149
+ : feature.latestReply
150
+ ? `reply · ${safeFeatureBoardText(feature.latestReply)}`
151
+ : safeFeatureBoardText(feature.description);
152
+ return fitFeatureBoardText(` ${[assignmentText, dependencyText, evidence].filter(Boolean).join(" · ")}`, width);
153
+ }
154
+ function featureBoardDependencies(timeline, feature) {
155
+ return feature.dependsOn.flatMap((dependency) => {
156
+ const resolved = timeline.features.find((candidate) => (candidate.id === dependency || candidate.id === `${feature.turnId}-${dependency}`));
157
+ return resolved ? [resolved] : [];
158
+ });
159
+ }
160
+ function featureBoardBlockedDependencies(timeline, feature) {
161
+ if (feature.state === "approved") {
162
+ return [];
163
+ }
164
+ return featureBoardDependencies(timeline, feature).filter((dependency) => dependency.state !== "approved");
165
+ }
166
+ function featureBoardStateIsActive(state) {
167
+ return state === "actor_running" || state === "critic_running" || state === "integrating" || state === "verifying";
168
+ }
169
+ function featureBoardStateTone(state) {
170
+ if (state === "approved") {
171
+ return "success";
172
+ }
173
+ if (state === "failed" || state === "cancelled") {
174
+ return "danger";
175
+ }
176
+ if (state === "revision_needed" || state === "paused") {
177
+ return "warning";
178
+ }
179
+ if (featureBoardStateIsActive(state)) {
180
+ return "active";
181
+ }
182
+ return "muted";
183
+ }
184
+ function humanizeFeatureState(state) {
185
+ if (state === "revision_needed") {
186
+ return "revision pending";
187
+ }
188
+ return state.replaceAll("_", " ");
189
+ }
190
+ function featureBoardWindowStart(selected, count, visibleCount) {
191
+ if (visibleCount <= 0 || count <= visibleCount) {
192
+ return 0;
193
+ }
194
+ return Math.min(count - visibleCount, Math.max(0, selected - Math.floor(visibleCount / 2)));
195
+ }
196
+ function clampFeatureIndex(index, count) {
197
+ return Math.min(Math.max(0, count - 1), Math.max(0, Math.trunc(index)));
198
+ }
199
+ function fitFeatureBoardCandidates(candidates, width) {
200
+ const fitted = candidates.find((candidate) => displayWidth(candidate) <= width);
201
+ return fitted ?? fitFeatureBoardText(candidates.at(-1) ?? "", width);
202
+ }
203
+ function fitFeatureBoardText(text, width) {
204
+ return compactEndByDisplayWidth(text, Math.max(1, width));
205
+ }
206
+ function safeFeatureBoardText(text) {
207
+ return text
208
+ .replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "")
209
+ .replace(/[\u0000-\u001f\u007f]/g, " ")
210
+ .replace(/\s+/g, " ")
211
+ .trim();
212
+ }
213
+ function featureBoardLineTheme(tone) {
214
+ return {
215
+ backgroundColor: TUI_THEME.surface,
216
+ color: tone === "heading" || tone === "active"
217
+ ? TUI_THEME.accent
218
+ : tone === "success"
219
+ ? TUI_THEME.success
220
+ : tone === "warning"
221
+ ? TUI_THEME.warning
222
+ : tone === "danger"
223
+ ? TUI_THEME.danger
224
+ : tone === "muted"
225
+ ? TUI_THEME.muted
226
+ : TUI_THEME.text,
227
+ ...(tone === "heading" || tone === "danger" ? { bold: true } : {})
228
+ };
229
+ }
230
+ function featureBoardContentWidth(terminalWidth) {
231
+ return Math.max(1, terminalWidth - 2);
232
+ }