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
@@ -1,3 +1,20 @@
1
+ export function buildMainPrompt(input) {
2
+ const role = roleConfig(input.role, "Main", [
3
+ "Answer the user directly for simple chat and explanation requests."
4
+ ]);
5
+ return [
6
+ `# Role: ${role.title}`,
7
+ "",
8
+ ...instructionLines(role.instructions),
9
+ ...(input.context?.trim()
10
+ ? ["", "# Active task context", "", input.context.trim()]
11
+ : []),
12
+ "",
13
+ "User request:",
14
+ input.request,
15
+ ""
16
+ ].join("\n");
17
+ }
1
18
  export function buildJudgePrompt(input) {
2
19
  const role = roleConfig(input.role, "Judge", [
3
20
  "You clarify requirements and write task files. Do not implement code."
@@ -9,6 +26,12 @@ export function buildJudgePrompt(input) {
9
26
  "",
10
27
  `Task directory: ${input.taskDir}`,
11
28
  ...(input.workerDir ? [`Worker directory: ${input.workerDir}`] : []),
29
+ ...(input.workspaceDir ? [
30
+ `Project workspace (read-only): ${input.workspaceDir}`,
31
+ "Inspect the project workspace when needed, but never modify it. Write only the Judge artifacts listed below.",
32
+ "Actors execute in isolated feature workspaces. In every artifact, use logical project root to mean the Actor's assigned feature workspace/current working directory.",
33
+ "Never put the absolute live workspace path into implementation instructions or acceptance paths."
34
+ ] : []),
12
35
  ...turnLines(input.turn),
13
36
  "",
14
37
  "Write these files in the worker directory above:",
@@ -17,6 +40,19 @@ export function buildJudgePrompt(input) {
17
40
  "- acceptance.md",
18
41
  "- actor-brief.md",
19
42
  "- critic-brief.md",
43
+ "- features.json",
44
+ "",
45
+ "Markdown artifact contract:",
46
+ "- requirements.md must use list items with stable requirement ids, for example: - [R-001] one actionable requirement",
47
+ "- plan.md must use ordered steps with stable plan ids, for example: 1. [P-001] one concrete implementation step",
48
+ "- acceptance.md must use list items with stable acceptance ids and related requirement ids, for example: - [A-001] [R-001] one observable check or command",
49
+ "- actor-brief.md and critic-brief.md must contain concrete role guidance below their headings",
50
+ "- Do not leave TODO, TBD, 待定, or placeholder-only content in any artifact",
51
+ "",
52
+ "features.json must contain version 1 and at most 8 features.",
53
+ "Use safe lowercase ids made from letters, numbers, and hyphens.",
54
+ "List dependencies in \"depends_on\"; independent features can run in parallel.",
55
+ "Example: {\"version\":1,\"features\":[{\"id\":\"ui\",\"title\":\"UI\",\"description\":\"Build the interface\",\"depends_on\":[]}]}",
20
56
  "",
21
57
  "User request:",
22
58
  input.request,
@@ -34,6 +70,13 @@ export function buildActorPrompt(input) {
34
70
  "",
35
71
  `Task directory: ${input.taskDir}`,
36
72
  `Judge directory: ${input.judgeDir}`,
73
+ ...(input.workspaceDir ? [
74
+ `Feature workspace: ${input.workspaceDir}`,
75
+ "The feature workspace above is the logical project root for this run.",
76
+ "Resolve every project-root, repository-root, or current-project path to this exact feature workspace.",
77
+ "Never write implementation files to the shared live workspace or any parent of the task directory.",
78
+ "Keep all implementation changes inside this feature workspace. Use task and feature directories only for coordination files."
79
+ ] : []),
37
80
  ...turnLines(input.turn),
38
81
  ...featureLines(input.feature),
39
82
  "",
@@ -49,7 +92,7 @@ export function buildActorPrompt(input) {
49
92
  "",
50
93
  "Feature mailbox writes:",
51
94
  "- actor-worklog.md with implementation notes for this feature.",
52
- "- actor-replies.jsonl with one JSON object per Critic finding you fixed.",
95
+ '- actor-replies.jsonl with one JSON object per Critic finding you fixed: {"finding_id":"C-001","status":"fixed","notes":"what changed"}.',
53
96
  "",
54
97
  input.revision ? `Revision request:\n${input.revision}` : "No Critic revision request is active.",
55
98
  "",
@@ -70,6 +113,12 @@ export function buildCriticPrompt(input) {
70
113
  `Task directory: ${input.taskDir}`,
71
114
  `Judge directory: ${input.judgeDir}`,
72
115
  `Actor directory: ${input.actorDir ?? ""}`,
116
+ ...(input.workspaceDir ? [
117
+ `Review workspace: ${input.workspaceDir}`,
118
+ "This is a disposable review copy of the Actor feature workspace and is the logical project root for review.",
119
+ "Do not modify implementation files. Any implementation changes made in this review copy are discarded.",
120
+ "Do not modify the Actor feature workspace or live workspace."
121
+ ] : []),
73
122
  ...turnLines(input.turn),
74
123
  ...featureLines(input.feature),
75
124
  "",
@@ -78,7 +127,71 @@ export function buildCriticPrompt(input) {
78
127
  "",
79
128
  "Write review.md in your worker directory. Include APPROVED when no blocking findings remain.",
80
129
  "If revision is required, include REVISION_REQUIRED and a concise fix list.",
81
- "Write critic-findings.jsonl in the feature mailbox with one JSON object per blocking issue.",
130
+ 'Write critic-findings.jsonl in the feature mailbox with one JSON object per blocking issue: {"id":"C-001","severity":"blocker","summary":"what must change"}.',
131
+ "",
132
+ "User request:",
133
+ input.request,
134
+ ""
135
+ ].join("\n");
136
+ }
137
+ export function buildWaveCriticPrompt(input) {
138
+ const role = waveRoleConfig(input.role, "Wave Critic", [
139
+ "Verify the combined feature result against all Judge requirements before it reaches the live workspace."
140
+ ]);
141
+ return [
142
+ `# Role: ${role.title}`,
143
+ "",
144
+ ...instructionLines(role.instructions),
145
+ "",
146
+ `Task directory: ${input.taskDir}`,
147
+ `Judge directory: ${input.judgeDir}`,
148
+ `Worker directory: ${input.workerDir}`,
149
+ `Combined verification workspace: ${input.workspaceDir}`,
150
+ `Wave: ${input.wave}/${input.waves}`,
151
+ `Features in this wave: ${input.featureIds.join(", ")}`,
152
+ ...turnLines(input.turn),
153
+ "",
154
+ "Live workspace has not been updated. Review only the combined verification workspace.",
155
+ "Treat the combined verification workspace as the logical project root for this review.",
156
+ "Read Judge requirements.md, plan.md, acceptance.md, critic-brief.md, and every feature decisions.md.",
157
+ "Run relevant tests, builds, and cross-feature checks in the combined verification workspace.",
158
+ "Do not modify implementation files.",
159
+ "",
160
+ "Write review.md in the worker directory.",
161
+ "Include APPROVED only when the combined result satisfies the full request and Judge acceptance.md.",
162
+ "Otherwise include REVISION_REQUIRED with a concise, actionable fix list.",
163
+ "Do not omit the decision marker.",
164
+ "",
165
+ "User request:",
166
+ input.request,
167
+ ""
168
+ ].join("\n");
169
+ }
170
+ export function buildWaveActorPrompt(input) {
171
+ const role = waveRoleConfig(input.role, "Wave Actor", [
172
+ "Resolve combined integration findings without reopening approved feature scope unnecessarily."
173
+ ]);
174
+ return [
175
+ `# Role: ${role.title}`,
176
+ "",
177
+ ...instructionLines(role.instructions),
178
+ "",
179
+ `Task directory: ${input.taskDir}`,
180
+ `Judge directory: ${input.judgeDir}`,
181
+ `Worker directory: ${input.workerDir}`,
182
+ `Combined integration workspace: ${input.workspaceDir}`,
183
+ `Wave: ${input.wave}/${input.waves}`,
184
+ `Features in this wave: ${input.featureIds.join(", ")}`,
185
+ ...turnLines(input.turn),
186
+ "",
187
+ "Modify only the combined integration workspace. Do not modify the live workspace or isolated feature workspaces.",
188
+ "Treat the combined integration workspace as the logical project root for this revision.",
189
+ "Read Judge requirements and acceptance, feature decisions, and the Wave Critic review below.",
190
+ "Run relevant verification after fixing the combined result.",
191
+ "Write worklog.md and patch.diff in the worker directory.",
192
+ "",
193
+ "Wave Critic review:",
194
+ input.review?.trim() || "REVISION_REQUIRED\nNo review details were provided.",
82
195
  "",
83
196
  "User request:",
84
197
  input.request,
@@ -91,6 +204,12 @@ function roleConfig(role, title, instructions) {
91
204
  instructions: role?.instructions?.length ? role.instructions : instructions
92
205
  };
93
206
  }
207
+ function waveRoleConfig(role, title, instructions) {
208
+ return {
209
+ title: role ? `${role.title} · Wave` : title,
210
+ instructions: role?.instructions?.length ? role.instructions : instructions
211
+ };
212
+ }
94
213
  function instructionLines(instructions) {
95
214
  if (instructions.length === 1) {
96
215
  return [instructions[0]];
@@ -103,6 +222,11 @@ function featureLines(feature) {
103
222
  }
104
223
  return [
105
224
  `Feature id: ${feature.featureId}`,
225
+ `Feature title: ${feature.featureTitle}`,
226
+ `Feature description: ${feature.featureDescription}`,
227
+ `Feature specification: ${feature.featureSpecPath}`,
228
+ `Feature dependencies: ${feature.featureDependencies.length > 0 ? feature.featureDependencies.join(", ") : "(none)"}`,
229
+ "Work only on this feature scope and honor completed dependency outputs.",
106
230
  `Feature directory: ${feature.featureDir}`,
107
231
  `Actor/Critic dialogue log: ${feature.dialoguePath}`,
108
232
  `Actor feature worklog: ${feature.actorWorklogPath}`,
@@ -13,6 +13,8 @@ export async function buildSupervisorSummary(dirs) {
13
13
  const findings = dirs.featureCriticFindingsPath
14
14
  ? await readTextIfExists(dirs.featureCriticFindingsPath)
15
15
  : "";
16
+ const changedFiles = changedFilesSummary(dirs.changedPaths ?? []);
17
+ const verification = dirs.verification?.trim() || verificationEvidence(review);
16
18
  return [
17
19
  "Complex task completed.",
18
20
  "",
@@ -22,17 +24,69 @@ export async function buildSupervisorSummary(dirs) {
22
24
  "Actor work:",
23
25
  excerpt(worklog),
24
26
  "",
27
+ "Changed files:",
28
+ excerpt(changedFiles),
29
+ "",
25
30
  "Critic review:",
26
31
  excerpt(review),
27
32
  "",
33
+ "Verification:",
34
+ excerpt(verification),
35
+ "",
28
36
  "Critic findings:",
29
37
  excerpt(findings)
30
38
  ].join("\n");
31
39
  }
40
+ function changedFilesSummary(paths) {
41
+ const unique = [...new Set(paths.map(sanitizeSummaryPath).filter(Boolean))].sort();
42
+ if (unique.length === 0) {
43
+ return "";
44
+ }
45
+ const visible = unique.slice(0, 50);
46
+ return [
47
+ ...visible.map((path) => `- ${path}`),
48
+ ...(unique.length > visible.length ? [`- ... and ${unique.length - visible.length} more`] : [])
49
+ ].join("\n");
50
+ }
51
+ function verificationEvidence(review) {
52
+ const lines = review.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
53
+ const decision = lines
54
+ .map(cleanReviewDecisionLine)
55
+ .find((line) => /^(?:APPROVED|REVISION_REQUIRED|REJECTED|FAILED)\b/i.test(line));
56
+ const evidence = lines.filter((line) => (!/^#{1,6}\s*(?:review|verification)\b/i.test(line)
57
+ && !/^(?:verification|tests?)\s*:$/i.test(line)
58
+ && /(?:`[^`]*(?:test|build|lint|typecheck|check)[^`]*`|\b(?:passed|verified|verification|tests?|build|lint|typecheck|smoke)\b)/i.test(line)
59
+ && cleanReviewDecisionLine(line) !== decision));
60
+ const uniqueEvidence = [...new Set(evidence)].slice(0, 12);
61
+ return [
62
+ ...(decision ? [`Critic decision: ${decision.match(/^(?:APPROVED|REVISION_REQUIRED|REJECTED|FAILED)/i)?.[0]?.toUpperCase() ?? decision}`] : []),
63
+ ...uniqueEvidence
64
+ ].join("\n");
65
+ }
66
+ function cleanReviewDecisionLine(line) {
67
+ return line
68
+ .trim()
69
+ .replace(/^#{1,6}\s+/, "")
70
+ .replace(/^[-*]\s+/, "")
71
+ .replace(/^\*\*([^*]+)\*\*$/, "$1")
72
+ .trim();
73
+ }
74
+ function sanitizeSummaryPath(path) {
75
+ return path
76
+ .replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "")
77
+ .replace(/[\u0000-\u001f\u007f]/g, "")
78
+ .trim();
79
+ }
32
80
  function excerpt(text) {
33
- const trimmed = text.trim();
81
+ const trimmed = escapeSupervisorSectionDelimiters(text).trim();
34
82
  if (!trimmed) {
35
83
  return "(empty)";
36
84
  }
37
- return trimmed.length > 800 ? `${trimmed.slice(0, 797)}...` : trimmed;
85
+ const codePoints = Array.from(trimmed);
86
+ return codePoints.length > 800 ? `${codePoints.slice(0, 797).join("")}...` : trimmed;
87
+ }
88
+ function escapeSupervisorSectionDelimiters(text) {
89
+ return text.split(/\r?\n/).map((line) => (/^(?:Requirements|Actor work|Changed files|Critic review|Verification|Critic findings):\s*$/i.test(line.trim())
90
+ ? `> ${line.trim()}`
91
+ : line)).join("\n");
38
92
  }