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.
Files changed (72) hide show
  1. package/.parallel-codex/config.example.toml +90 -3
  2. package/README.md +240 -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-recovery.js +70 -0
  7. package/dist/cli-workspace-picker.js +330 -0
  8. package/dist/cli-workspace-transition.js +33 -0
  9. package/dist/cli-workspace.js +7 -71
  10. package/dist/cli.js +221 -23
  11. package/dist/core/collaboration-timeline.js +261 -0
  12. package/dist/core/config-errors.js +14 -0
  13. package/dist/core/config.js +154 -24
  14. package/dist/core/file-store.js +119 -6
  15. package/dist/core/lease-finalization.js +22 -0
  16. package/dist/core/paths.js +7 -0
  17. package/dist/core/process-identity.js +48 -0
  18. package/dist/core/process-mutation-turn.js +128 -0
  19. package/dist/core/process-ownership.js +276 -0
  20. package/dist/core/process-tree.js +90 -0
  21. package/dist/core/router-audit.js +155 -0
  22. package/dist/core/router-redaction.js +31 -0
  23. package/dist/core/router.js +462 -35
  24. package/dist/core/session-index.js +188 -37
  25. package/dist/core/session-manager.js +1086 -40
  26. package/dist/core/task-state-machine.js +17 -0
  27. package/dist/core/workspace-commit-recovery.js +118 -0
  28. package/dist/core/workspace.js +19 -11
  29. package/dist/doctor.js +343 -23
  30. package/dist/domain/schemas.js +127 -6
  31. package/dist/orchestrator/collaboration-channel.js +255 -4
  32. package/dist/orchestrator/feature-plan.js +70 -0
  33. package/dist/orchestrator/judge-artifacts.js +236 -0
  34. package/dist/orchestrator/orchestrator.js +1749 -202
  35. package/dist/orchestrator/prompts.js +126 -2
  36. package/dist/orchestrator/supervisor-summary.js +56 -2
  37. package/dist/orchestrator/workspace-sandbox.js +911 -0
  38. package/dist/tui/App.js +2830 -153
  39. package/dist/tui/AppShell.js +188 -23
  40. package/dist/tui/CollaborationTimelineView.js +327 -0
  41. package/dist/tui/FeatureBoardView.js +227 -0
  42. package/dist/tui/InputBar.js +514 -57
  43. package/dist/tui/RouterDiagnosticsView.js +469 -0
  44. package/dist/tui/StatusBar.js +610 -57
  45. package/dist/tui/TaskSessionsView.js +207 -0
  46. package/dist/tui/TerminalOutput.js +53 -9
  47. package/dist/tui/WorkerOutputView.js +1403 -161
  48. package/dist/tui/WorkerOverviewView.js +250 -0
  49. package/dist/tui/chat-history.js +25 -0
  50. package/dist/tui/chat-input.js +67 -19
  51. package/dist/tui/chat-paste.js +76 -0
  52. package/dist/tui/display-width.js +41 -3
  53. package/dist/tui/incremental-text-file.js +101 -0
  54. package/dist/tui/keyboard.js +46 -0
  55. package/dist/tui/markdown-text.js +14 -0
  56. package/dist/tui/raw-input-decoder.js +3 -0
  57. package/dist/tui/scrolling.js +2 -1
  58. package/dist/tui/status-line.js +318 -11
  59. package/dist/tui/task-memory.js +13 -1
  60. package/dist/tui/task-result.js +105 -0
  61. package/dist/tui/terminal-screen.js +13 -1
  62. package/dist/tui/theme-contrast.js +144 -0
  63. package/dist/tui/theme-preview.js +109 -0
  64. package/dist/tui/theme.js +158 -0
  65. package/dist/version.js +1 -1
  66. package/dist/workers/capabilities.js +212 -0
  67. package/dist/workers/live-probe.js +176 -0
  68. package/dist/workers/mock-adapter.js +39 -6
  69. package/dist/workers/native-attach.js +78 -3
  70. package/dist/workers/process-adapter.js +570 -77
  71. package/dist/workers/registry.js +4 -2
  72. 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
+ }