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.
- package/.parallel-codex/config.example.toml +136 -3
- package/README.md +299 -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-preflight.js +18 -0
- package/dist/cli-startup-recovery.js +82 -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 +234 -24
- package/dist/core/collaboration-timeline.js +268 -0
- package/dist/core/config-errors.js +14 -0
- package/dist/core/config.js +297 -109
- 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 +412 -88
- package/dist/core/session-manager.js +1110 -40
- package/dist/core/task-session-details.js +175 -0
- package/dist/core/task-state-machine.js +18 -0
- package/dist/core/workspace-commit-recovery.js +118 -0
- package/dist/core/workspace.js +19 -11
- package/dist/doctor.js +373 -34
- package/dist/domain/schemas.js +142 -7
- package/dist/orchestrator/collaboration-channel.js +289 -6
- package/dist/orchestrator/feature-plan.js +70 -0
- package/dist/orchestrator/final-acceptance.js +86 -0
- package/dist/orchestrator/judge-artifacts.js +236 -0
- package/dist/orchestrator/orchestrator.js +2086 -203
- package/dist/orchestrator/prompts.js +168 -5
- package/dist/orchestrator/supervisor-summary.js +56 -2
- package/dist/orchestrator/workspace-sandbox.js +927 -0
- package/dist/tui/App.js +3187 -161
- package/dist/tui/AppShell.js +196 -25
- package/dist/tui/CollaborationTimelineView.js +327 -0
- package/dist/tui/FeatureBoardView.js +232 -0
- package/dist/tui/InputBar.js +581 -57
- package/dist/tui/RouterDiagnosticsView.js +469 -0
- package/dist/tui/StatusBar.js +610 -57
- package/dist/tui/StatusDetailView.js +164 -0
- package/dist/tui/TaskSessionDetailView.js +222 -0
- package/dist/tui/TaskSessionsView.js +210 -0
- package/dist/tui/TerminalOutput.js +53 -9
- package/dist/tui/WorkerOutputView.js +1404 -161
- package/dist/tui/WorkerOverviewView.js +269 -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 +49 -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 +360 -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 +213 -0
- package/dist/workers/live-probe.js +177 -0
- package/dist/workers/mock-adapter.js +73 -8
- package/dist/workers/native-attach.js +106 -16
- package/dist/workers/process-adapter.js +572 -77
- package/dist/workers/provider.js +26 -0
- package/dist/workers/registry.js +12 -20
- package/package.json +11 -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,14 @@ export function buildActorPrompt(input) {
|
|
|
34
70
|
"",
|
|
35
71
|
`Task directory: ${input.taskDir}`,
|
|
36
72
|
`Judge directory: ${input.judgeDir}`,
|
|
73
|
+
...(input.workerDir ? [`Worker directory: ${input.workerDir}`] : []),
|
|
74
|
+
...(input.workspaceDir ? [
|
|
75
|
+
`Feature workspace: ${input.workspaceDir}`,
|
|
76
|
+
"The feature workspace above is the logical project root for this run.",
|
|
77
|
+
"Resolve every project-root, repository-root, or current-project path to this exact feature workspace.",
|
|
78
|
+
"Never write implementation files to the shared live workspace or any parent of the task directory.",
|
|
79
|
+
"Keep all implementation changes inside this feature workspace. Use task and feature directories only for coordination files."
|
|
80
|
+
] : []),
|
|
37
81
|
...turnLines(input.turn),
|
|
38
82
|
...featureLines(input.feature),
|
|
39
83
|
"",
|
|
@@ -43,13 +87,13 @@ export function buildActorPrompt(input) {
|
|
|
43
87
|
"- acceptance.md",
|
|
44
88
|
"- actor-brief.md",
|
|
45
89
|
"",
|
|
46
|
-
"Write in
|
|
90
|
+
"Write these coordination artifacts in the exact worker directory above, not in the feature workspace:",
|
|
47
91
|
"- worklog.md",
|
|
48
92
|
"- patch.diff when a diff is available",
|
|
49
93
|
"",
|
|
50
94
|
"Feature mailbox writes:",
|
|
51
95
|
"- actor-worklog.md with implementation notes for this feature.",
|
|
52
|
-
|
|
96
|
+
'- actor-replies.jsonl with one JSON object per Critic finding you fixed: {"finding_id":"C-001","status":"fixed","notes":"what changed"}.',
|
|
53
97
|
"",
|
|
54
98
|
input.revision ? `Revision request:\n${input.revision}` : "No Critic revision request is active.",
|
|
55
99
|
"",
|
|
@@ -69,16 +113,123 @@ export function buildCriticPrompt(input) {
|
|
|
69
113
|
"",
|
|
70
114
|
`Task directory: ${input.taskDir}`,
|
|
71
115
|
`Judge directory: ${input.judgeDir}`,
|
|
116
|
+
...(input.workerDir ? [`Worker directory: ${input.workerDir}`] : []),
|
|
72
117
|
`Actor directory: ${input.actorDir ?? ""}`,
|
|
118
|
+
...(input.workspaceDir ? [
|
|
119
|
+
`Review workspace: ${input.workspaceDir}`,
|
|
120
|
+
"This is a disposable review copy of the Actor feature workspace and is the logical project root for review.",
|
|
121
|
+
"Do not modify implementation files. Any implementation changes made in this review copy are discarded.",
|
|
122
|
+
"Do not modify the Actor feature workspace or live workspace."
|
|
123
|
+
] : []),
|
|
73
124
|
...turnLines(input.turn),
|
|
74
125
|
...featureLines(input.feature),
|
|
75
126
|
"",
|
|
76
127
|
"Read Judge files and Actor output.",
|
|
77
128
|
"Read actor-replies.jsonl when reviewing a revision.",
|
|
78
129
|
"",
|
|
79
|
-
"Write review.md in
|
|
130
|
+
"Write review.md in the exact worker directory above, not in the disposable review workspace. Include APPROVED when no blocking findings remain.",
|
|
80
131
|
"If revision is required, include REVISION_REQUIRED and a concise fix list.",
|
|
81
|
-
|
|
132
|
+
'Write critic-findings.jsonl in the feature mailbox with one JSON object per blocking issue: {"id":"C-001","severity":"blocker","summary":"what must change"}.',
|
|
133
|
+
"",
|
|
134
|
+
"User request:",
|
|
135
|
+
input.request,
|
|
136
|
+
""
|
|
137
|
+
].join("\n");
|
|
138
|
+
}
|
|
139
|
+
export function buildWaveCriticPrompt(input) {
|
|
140
|
+
const role = waveRoleConfig(input.role, "Wave Critic", [
|
|
141
|
+
"Verify the combined feature result against all Judge requirements before it reaches the live workspace."
|
|
142
|
+
]);
|
|
143
|
+
return [
|
|
144
|
+
`# Role: ${role.title}`,
|
|
145
|
+
"",
|
|
146
|
+
...instructionLines(role.instructions),
|
|
147
|
+
"",
|
|
148
|
+
`Task directory: ${input.taskDir}`,
|
|
149
|
+
`Judge directory: ${input.judgeDir}`,
|
|
150
|
+
`Worker directory: ${input.workerDir}`,
|
|
151
|
+
`Combined verification workspace: ${input.workspaceDir}`,
|
|
152
|
+
`Wave: ${input.wave}/${input.waves}`,
|
|
153
|
+
`Features in this wave: ${input.featureIds.join(", ")}`,
|
|
154
|
+
...turnLines(input.turn),
|
|
155
|
+
"",
|
|
156
|
+
"Live workspace has not been updated. Review only the combined verification workspace.",
|
|
157
|
+
"Treat the combined verification workspace as the logical project root for this review.",
|
|
158
|
+
"Read Judge requirements.md, plan.md, acceptance.md, critic-brief.md, and every feature decisions.md.",
|
|
159
|
+
"Run relevant tests, builds, and cross-feature checks in the combined verification workspace.",
|
|
160
|
+
"Do not modify implementation files.",
|
|
161
|
+
"",
|
|
162
|
+
"Write review.md in the worker directory.",
|
|
163
|
+
"Include APPROVED only when the combined result satisfies the full request and Judge acceptance.md.",
|
|
164
|
+
"Otherwise include REVISION_REQUIRED with a concise, actionable fix list.",
|
|
165
|
+
"Do not omit the decision marker.",
|
|
166
|
+
"",
|
|
167
|
+
"User request:",
|
|
168
|
+
input.request,
|
|
169
|
+
""
|
|
170
|
+
].join("\n");
|
|
171
|
+
}
|
|
172
|
+
export function buildWaveActorPrompt(input) {
|
|
173
|
+
const role = waveRoleConfig(input.role, "Wave Actor", [
|
|
174
|
+
"Resolve combined integration findings without reopening approved feature scope unnecessarily."
|
|
175
|
+
]);
|
|
176
|
+
return [
|
|
177
|
+
`# Role: ${role.title}`,
|
|
178
|
+
"",
|
|
179
|
+
...instructionLines(role.instructions),
|
|
180
|
+
"",
|
|
181
|
+
`Task directory: ${input.taskDir}`,
|
|
182
|
+
`Judge directory: ${input.judgeDir}`,
|
|
183
|
+
`Worker directory: ${input.workerDir}`,
|
|
184
|
+
`Combined integration workspace: ${input.workspaceDir}`,
|
|
185
|
+
`Wave: ${input.wave}/${input.waves}`,
|
|
186
|
+
`Features in this wave: ${input.featureIds.join(", ")}`,
|
|
187
|
+
...turnLines(input.turn),
|
|
188
|
+
"",
|
|
189
|
+
"Modify only the combined integration workspace. Do not modify the live workspace or isolated feature workspaces.",
|
|
190
|
+
"Treat the combined integration workspace as the logical project root for this revision.",
|
|
191
|
+
"Read Judge requirements and acceptance, feature decisions, and the Wave Critic review below.",
|
|
192
|
+
"Run relevant verification after fixing the combined result.",
|
|
193
|
+
"Write worklog.md and patch.diff in the worker directory.",
|
|
194
|
+
"",
|
|
195
|
+
"Wave Critic review:",
|
|
196
|
+
input.review?.trim() || "REVISION_REQUIRED\nNo review details were provided.",
|
|
197
|
+
"",
|
|
198
|
+
"User request:",
|
|
199
|
+
input.request,
|
|
200
|
+
""
|
|
201
|
+
].join("\n");
|
|
202
|
+
}
|
|
203
|
+
export function buildFinalJudgePrompt(input) {
|
|
204
|
+
const role = roleConfig(input.role, "Final Judge", [
|
|
205
|
+
"Perform the final integration acceptance against the requirements you established before the task can be completed."
|
|
206
|
+
]);
|
|
207
|
+
return [
|
|
208
|
+
`# Role: ${role.title} · Final acceptance`,
|
|
209
|
+
"",
|
|
210
|
+
...instructionLines(role.instructions),
|
|
211
|
+
"",
|
|
212
|
+
`Task directory: ${input.taskDir}`,
|
|
213
|
+
`Judge artifact directory: ${input.judgeDir}`,
|
|
214
|
+
`Final Judge worker directory: ${input.workerDir}`,
|
|
215
|
+
`Final verification workspace: ${input.workspaceDir}`,
|
|
216
|
+
`Supervisor summary: ${input.supervisorSummaryPath}`,
|
|
217
|
+
...turnLines(input.turn),
|
|
218
|
+
"",
|
|
219
|
+
"The verification workspace is a disposable snapshot of the integrated project.",
|
|
220
|
+
"Inspect and test it as the logical project root. Do not modify the live workspace.",
|
|
221
|
+
"Read requirements.md, acceptance.md, the supervisor summary, feature decisions, and Critic reviews.",
|
|
222
|
+
"Run the commands needed to validate the complete integrated result, including cross-feature behavior.",
|
|
223
|
+
"",
|
|
224
|
+
`Required acceptance criterion ids: ${JSON.stringify(input.expectedCriterionIds)}`,
|
|
225
|
+
`Authoritative changed paths: ${JSON.stringify(input.changedPaths)}`,
|
|
226
|
+
"",
|
|
227
|
+
"Write final-acceptance.json in the Final Judge worker directory.",
|
|
228
|
+
"It must be strict JSON with this shape:",
|
|
229
|
+
'{"version":1,"decision":"approved|rejected","summary":"concise verdict","acceptance":[{"criterion_id":"A-001","status":"passed|failed","evidence":"command or observable result"}],"changed_paths":["relative/path"]}',
|
|
230
|
+
"Include every required criterion exactly once and copy the authoritative changed paths exactly.",
|
|
231
|
+
"Use decision approved only when every criterion passed; otherwise use rejected and identify each failure.",
|
|
232
|
+
"Do not rely on process exit alone and do not omit evidence.",
|
|
82
233
|
"",
|
|
83
234
|
"User request:",
|
|
84
235
|
input.request,
|
|
@@ -91,6 +242,12 @@ function roleConfig(role, title, instructions) {
|
|
|
91
242
|
instructions: role?.instructions?.length ? role.instructions : instructions
|
|
92
243
|
};
|
|
93
244
|
}
|
|
245
|
+
function waveRoleConfig(role, title, instructions) {
|
|
246
|
+
return {
|
|
247
|
+
title: role ? `${role.title} · Wave` : title,
|
|
248
|
+
instructions: role?.instructions?.length ? role.instructions : instructions
|
|
249
|
+
};
|
|
250
|
+
}
|
|
94
251
|
function instructionLines(instructions) {
|
|
95
252
|
if (instructions.length === 1) {
|
|
96
253
|
return [instructions[0]];
|
|
@@ -103,12 +260,18 @@ function featureLines(feature) {
|
|
|
103
260
|
}
|
|
104
261
|
return [
|
|
105
262
|
`Feature id: ${feature.featureId}`,
|
|
263
|
+
`Feature title: ${feature.featureTitle}`,
|
|
264
|
+
`Feature description: ${feature.featureDescription}`,
|
|
265
|
+
`Feature specification: ${feature.featureSpecPath}`,
|
|
266
|
+
`Feature dependencies: ${feature.featureDependencies.length > 0 ? feature.featureDependencies.join(", ") : "(none)"}`,
|
|
267
|
+
"Work only on this feature scope and honor completed dependency outputs.",
|
|
106
268
|
`Feature directory: ${feature.featureDir}`,
|
|
107
269
|
`Actor/Critic dialogue log: ${feature.dialoguePath}`,
|
|
108
270
|
`Actor feature worklog: ${feature.actorWorklogPath}`,
|
|
109
271
|
`Critic findings: ${feature.criticFindingsPath}`,
|
|
110
272
|
`Actor replies: ${feature.actorRepliesPath}`,
|
|
111
|
-
`Feature decisions: ${feature.decisionsPath}
|
|
273
|
+
`Feature decisions: ${feature.decisionsPath}`,
|
|
274
|
+
`Feature engine assignment: ${feature.assignmentPath}`
|
|
112
275
|
];
|
|
113
276
|
}
|
|
114
277
|
function turnLines(turn) {
|
|
@@ -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
|
-
|
|
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
|
}
|