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,236 @@
1
+ import { Lexer } from "marked";
2
+ export const JUDGE_REQUIRED_ARTIFACTS = [
3
+ "requirements.md",
4
+ "plan.md",
5
+ "acceptance.md",
6
+ "actor-brief.md",
7
+ "critic-brief.md"
8
+ ];
9
+ export const JUDGE_VALIDATION_FILE = "judge-validation.json";
10
+ const CONTRACT_ARTIFACTS = {
11
+ "requirements.md": { prefix: "R", label: "requirement" },
12
+ "plan.md": { prefix: "P", label: "plan step" },
13
+ "acceptance.md": { prefix: "A", label: "acceptance criterion" }
14
+ };
15
+ const PLACEHOLDER_PATTERN = /^(?:todo|tbd|pending|placeholder|to be determined|coming soon|n\/?a|none|待定|稍后(?:补充|处理)?|后续(?:补充|再说)?|暂无|未定|占位(?:内容)?)(?:\s*[::-].*)?$/iu;
16
+ const ARTIFACT_NAME_PATTERN = /^(?:requirements|plan|acceptance|actor-brief|critic-brief)\.md$/iu;
17
+ export function validateJudgeArtifacts(input) {
18
+ const requirements = validateContractArtifact("requirements.md", input["requirements.md"] ?? "");
19
+ const plan = validateContractArtifact("plan.md", input["plan.md"] ?? "");
20
+ const acceptance = validateContractArtifact("acceptance.md", input["acceptance.md"] ?? "");
21
+ const actorBrief = validateBriefArtifact("actor-brief.md", input["actor-brief.md"] ?? "");
22
+ const criticBrief = validateBriefArtifact("critic-brief.md", input["critic-brief.md"] ?? "");
23
+ const requirementIds = new Set(requirements.items.map((item) => item.id));
24
+ for (const item of acceptance.items) {
25
+ const unknown = item.references.filter((reference) => !requirementIds.has(reference));
26
+ if (unknown.length > 0) {
27
+ acceptance.validation.issues.push(issue("acceptance.md", "unknown_requirement_reference", `${item.id} references unknown requirement${unknown.length === 1 ? "" : "s"}: ${unknown.join(", ")}.`));
28
+ acceptance.validation.state = "invalid";
29
+ }
30
+ }
31
+ const artifacts = {
32
+ "requirements.md": requirements.validation,
33
+ "plan.md": plan.validation,
34
+ "acceptance.md": acceptance.validation,
35
+ "actor-brief.md": actorBrief.validation,
36
+ "critic-brief.md": criticBrief.validation
37
+ };
38
+ const issues = JUDGE_REQUIRED_ARTIFACTS.flatMap((file) => artifacts[file].issues);
39
+ return {
40
+ version: 1,
41
+ state: issues.length === 0 ? "valid" : "invalid",
42
+ artifacts,
43
+ contract: {
44
+ requirements: requirements.items,
45
+ plan: plan.items,
46
+ acceptance: acceptance.items
47
+ },
48
+ briefs: {
49
+ actor: actorBrief.content,
50
+ critic: criticBrief.content
51
+ },
52
+ issues
53
+ };
54
+ }
55
+ function validateContractArtifact(file, markdown) {
56
+ const parsed = parseMarkdown(markdown);
57
+ const issues = [];
58
+ const spec = CONTRACT_ARTIFACTS[file];
59
+ if (!markdown.trim()) {
60
+ issues.push(issue(file, "missing", `${file} is missing or empty.`));
61
+ }
62
+ else if (parsed.parseError) {
63
+ issues.push(issue(file, "parse_error", `${file} is not valid Markdown: ${parsed.parseError}`));
64
+ }
65
+ const normalized = parsed.listItems.map((text, index) => normalizeContractItem(text, spec.prefix, index));
66
+ const meaningful = normalized.filter((item) => isMeaningful(item.text));
67
+ const placeholders = normalized.filter((item) => !isMeaningful(item.text));
68
+ if (markdown.trim() && !parsed.parseError && parsed.listItems.length === 0) {
69
+ issues.push(issue(file, "missing_list_items", `${file} must contain at least one Markdown list ${spec.label}.`));
70
+ }
71
+ else if (parsed.listItems.length > 0 && meaningful.length === 0) {
72
+ issues.push(issue(file, "placeholder_only", `${file} contains only placeholder ${spec.label}s.`));
73
+ }
74
+ else if (placeholders.length > 0) {
75
+ issues.push(issue(file, "placeholder_items", `${file} still contains ${placeholders.length} placeholder ${spec.label}${placeholders.length === 1 ? "" : "s"}.`));
76
+ }
77
+ const duplicateIds = repeatedIds(meaningful);
78
+ if (duplicateIds.length > 0) {
79
+ issues.push(issue(file, "duplicate_item_id", `${file} repeats item id${duplicateIds.length === 1 ? "" : "s"}: ${duplicateIds.join(", ")}.`));
80
+ }
81
+ return {
82
+ validation: {
83
+ state: issues.length === 0 ? "valid" : "invalid",
84
+ item_count: parsed.listItems.length,
85
+ content_count: parsed.content.length,
86
+ issues
87
+ },
88
+ items: meaningful
89
+ };
90
+ }
91
+ function validateBriefArtifact(file, markdown) {
92
+ const parsed = parseMarkdown(markdown);
93
+ const issues = [];
94
+ const meaningful = parsed.content.filter(isMeaningful);
95
+ const placeholders = parsed.content.filter((content) => !isMeaningful(content));
96
+ if (!markdown.trim()) {
97
+ issues.push(issue(file, "missing", `${file} is missing or empty.`));
98
+ }
99
+ else if (parsed.parseError) {
100
+ issues.push(issue(file, "parse_error", `${file} is not valid Markdown: ${parsed.parseError}`));
101
+ }
102
+ else if (parsed.content.length === 0) {
103
+ issues.push(issue(file, "missing_content", `${file} must contain implementation guidance below its heading.`));
104
+ }
105
+ else if (meaningful.length === 0) {
106
+ issues.push(issue(file, "placeholder_only", `${file} contains only placeholder guidance.`));
107
+ }
108
+ else if (placeholders.length > 0) {
109
+ issues.push(issue(file, "placeholder_items", `${file} still contains placeholder guidance.`));
110
+ }
111
+ return {
112
+ validation: {
113
+ state: issues.length === 0 ? "valid" : "invalid",
114
+ item_count: parsed.listItems.length,
115
+ content_count: parsed.content.length,
116
+ issues
117
+ },
118
+ content: meaningful
119
+ };
120
+ }
121
+ function parseMarkdown(markdown) {
122
+ if (!markdown.trim()) {
123
+ return { listItems: [], content: [] };
124
+ }
125
+ try {
126
+ const tokens = Lexer.lex(markdown);
127
+ return {
128
+ listItems: collectListItems(tokens),
129
+ content: collectContent(tokens)
130
+ };
131
+ }
132
+ catch (error) {
133
+ return {
134
+ listItems: [],
135
+ content: [],
136
+ parseError: error instanceof Error ? error.message : String(error)
137
+ };
138
+ }
139
+ }
140
+ function collectListItems(tokens) {
141
+ const items = [];
142
+ for (const token of tokens) {
143
+ if (isListToken(token)) {
144
+ for (const item of token.items) {
145
+ const text = normalizeWhitespace(item.tokens
146
+ .filter((itemToken) => itemToken.type !== "list")
147
+ .map(tokenPlainText)
148
+ .join(" "));
149
+ items.push(text);
150
+ items.push(...collectListItems(item.tokens.filter(isListToken)));
151
+ }
152
+ continue;
153
+ }
154
+ const nested = tokenTokens(token);
155
+ if (nested.length > 0) {
156
+ items.push(...collectListItems(nested));
157
+ }
158
+ }
159
+ return items;
160
+ }
161
+ function collectContent(tokens) {
162
+ const content = [];
163
+ for (const token of tokens) {
164
+ if (token.type === "heading" || token.type === "space" || token.type === "hr" || token.type === "def") {
165
+ continue;
166
+ }
167
+ if (isListToken(token)) {
168
+ content.push(...collectListItems([token]));
169
+ continue;
170
+ }
171
+ if (token.type === "blockquote") {
172
+ content.push(...collectContent(tokenTokens(token)));
173
+ continue;
174
+ }
175
+ const text = normalizeWhitespace(tokenPlainText(token));
176
+ if (text) {
177
+ content.push(text);
178
+ }
179
+ }
180
+ return content;
181
+ }
182
+ function normalizeContractItem(text, prefix, index) {
183
+ const idPattern = new RegExp(`^\\[?(${prefix}-\\d{1,4})\\]?\\s*(?:[::-]\\s*)?`, "iu");
184
+ const match = text.match(idPattern);
185
+ const id = match?.[1]?.toUpperCase() ?? `${prefix}-${String(index + 1).padStart(3, "0")}`;
186
+ const normalizedText = normalizeWhitespace(match ? text.slice(match[0].length) : text);
187
+ const references = Array.from(normalizedText.matchAll(/\bR-\d{1,4}\b/giu), (reference) => reference[0].toUpperCase());
188
+ return {
189
+ id,
190
+ text: normalizedText,
191
+ references: [...new Set(references)]
192
+ };
193
+ }
194
+ function tokenPlainText(token) {
195
+ if (token.type === "checkbox" || token.type === "space" || token.type === "def") {
196
+ return "";
197
+ }
198
+ const nested = tokenTokens(token);
199
+ if (nested.length > 0) {
200
+ return nested.map(tokenPlainText).join(" ");
201
+ }
202
+ if ("text" in token && typeof token.text === "string") {
203
+ return token.text;
204
+ }
205
+ return typeof token.raw === "string" ? token.raw : "";
206
+ }
207
+ function tokenTokens(token) {
208
+ return "tokens" in token && Array.isArray(token.tokens) ? token.tokens : [];
209
+ }
210
+ function isListToken(token) {
211
+ return token.type === "list" && "items" in token && Array.isArray(token.items);
212
+ }
213
+ function normalizeWhitespace(value) {
214
+ return value.replace(/\s+/gu, " ").trim();
215
+ }
216
+ function isMeaningful(value) {
217
+ const normalized = normalizeWhitespace(value).replace(/[.。,,;;::!!??]+$/gu, "").trim();
218
+ return Boolean(normalized)
219
+ && /[\p{L}\p{N}]/u.test(normalized)
220
+ && !PLACEHOLDER_PATTERN.test(normalized)
221
+ && !ARTIFACT_NAME_PATTERN.test(normalized);
222
+ }
223
+ function repeatedIds(items) {
224
+ const seen = new Set();
225
+ const duplicates = new Set();
226
+ for (const item of items) {
227
+ if (seen.has(item.id)) {
228
+ duplicates.add(item.id);
229
+ }
230
+ seen.add(item.id);
231
+ }
232
+ return [...duplicates];
233
+ }
234
+ function issue(file, code, message) {
235
+ return { file, code, message };
236
+ }