chati-dev 4.5.16 → 4.5.28

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 (122) hide show
  1. package/README.md +8 -3
  2. package/bin/chati.js +124 -66
  3. package/framework/agents/build/dev.md +5 -5
  4. package/framework/agents/deploy/devops.md +7 -7
  5. package/framework/agents/discover/brief.md +4 -4
  6. package/framework/agents/discover/brownfield-wu.md +3 -3
  7. package/framework/agents/discover/greenfield-wu.md +3 -3
  8. package/framework/agents/plan/architect.md +2 -2
  9. package/framework/agents/plan/detail.md +4 -4
  10. package/framework/agents/plan/phases.md +2 -2
  11. package/framework/agents/plan/tasks.md +2 -2
  12. package/framework/agents/plan/ux.md +2 -2
  13. package/framework/agents/quality/qa-implementation.md +11 -10
  14. package/framework/agents/quality/qa-planning.md +3 -3
  15. package/framework/agents/quality/qa-visual.md +1 -1
  16. package/framework/config.yaml +3 -3
  17. package/framework/constitution.md +18 -18
  18. package/framework/context/protocols.md +1 -1
  19. package/framework/context/quality.md +1 -1
  20. package/framework/context/root.md +4 -4
  21. package/framework/data/entity-registry.yaml +1 -1
  22. package/framework/domains/agents/orchestrator.yaml +2 -2
  23. package/framework/domains/constitution.yaml +1 -1
  24. package/framework/hooks/advance-trigger.js +4 -6
  25. package/framework/hooks/git-push-authority.js +45 -37
  26. package/framework/hooks/mode-governance.js +149 -40
  27. package/framework/hooks/model-governance.js +13 -20
  28. package/framework/hooks/prism-engine.js +74 -92
  29. package/framework/hooks/reasoning-escalator.js +23 -40
  30. package/framework/hooks/session-digest.js +12 -13
  31. package/framework/hooks/session-reader.js +224 -0
  32. package/framework/hooks/session-writer.js +195 -0
  33. package/framework/hooks/team-quality-gate.js +34 -24
  34. package/framework/i18n/en.yaml +2 -2
  35. package/framework/i18n/es.yaml +2 -2
  36. package/framework/i18n/fr.yaml +2 -2
  37. package/framework/i18n/pt.yaml +2 -2
  38. package/framework/intelligence/context-engine.md +4 -4
  39. package/framework/intelligence/memory-layer.md +1 -1
  40. package/framework/manifest.json +129 -119
  41. package/framework/manifest.sig +1 -1
  42. package/framework/orchestrator/chati-router.js +252 -25
  43. package/framework/orchestrator/chati.md +238 -70
  44. package/framework/schemas/session.schema.json +1 -1
  45. package/framework/tasks/orchestrator-deviation.md +1 -1
  46. package/framework/tasks/orchestrator-escalate.md +1 -1
  47. package/framework/tasks/orchestrator-handoff.md +6 -6
  48. package/framework/tasks/orchestrator-health.md +5 -9
  49. package/framework/tasks/orchestrator-mode-switch.md +3 -7
  50. package/framework/tasks/orchestrator-resume.md +10 -14
  51. package/framework/tasks/orchestrator-route.md +3 -3
  52. package/framework/tasks/orchestrator-spawn-terminal.md +1 -1
  53. package/framework/tasks/orchestrator-status.md +9 -9
  54. package/framework/tasks/orchestrator-suggest-mode.md +1 -1
  55. package/framework/tasks/qa-impl-consolidate.md +2 -2
  56. package/framework/tasks/qa-impl-performance-test.md +4 -4
  57. package/framework/tasks/qa-impl-regression-check.md +4 -4
  58. package/framework/tasks/qa-impl-sast-scan.md +1 -1
  59. package/framework/tasks/qa-impl-test-execute.md +1 -1
  60. package/framework/tasks/qa-impl-verdict.md +2 -2
  61. package/framework/tasks/qa-planning-consolidate.md +3 -3
  62. package/framework/tasks/qa-planning-coverage-plan.md +2 -2
  63. package/framework/tasks/qa-planning-gate-define.md +4 -4
  64. package/framework/tasks/qa-planning-risk-matrix.md +4 -4
  65. package/framework/tasks/qa-planning-test-strategy.md +2 -2
  66. package/node_modules/@chati/browser-capability/src/index.js +12 -2
  67. package/node_modules/@chati/planning/src/index.js +24 -4
  68. package/node_modules/@chati/provider-registry/src/index.js +11 -0
  69. package/node_modules/@chati/rail/src/index.js +1967 -83
  70. package/node_modules/@chati/release-lane/README.md +12 -8
  71. package/node_modules/@chati/release-lane/package.json +1 -1
  72. package/node_modules/@chati/release-lane/src/index.js +1426 -58
  73. package/node_modules/@chati/review-council/src/index.js +63 -0
  74. package/node_modules/@chati/tracking-clickup/README.md +13 -0
  75. package/node_modules/@chati/tracking-clickup/src/index.js +690 -30
  76. package/package-artifact-manifest.json +1 -0
  77. package/package-artifact-manifest.sig +1 -0
  78. package/package.json +16 -7
  79. package/scripts/verify-real-harness-e2e.js +1581 -0
  80. package/src/config/framework-adapter.js +4 -4
  81. package/src/installer/core.js +148 -53
  82. package/src/installer/manifest.js +140 -9
  83. package/src/installer/package-artifact.js +249 -0
  84. package/src/installer/templates.js +65 -20
  85. package/src/installer-v2/catalog-client.js +531 -23
  86. package/src/installer-v2/index.js +91 -34
  87. package/src/installer-v2/installation-authority.js +327 -0
  88. package/src/installer-v2/provider-executable.js +247 -0
  89. package/src/installer-v2/wizard-installation.js +1 -1
  90. package/src/orchestrator/browser-runtime.js +44 -13
  91. package/src/orchestrator/cli.js +1545 -174
  92. package/src/orchestrator/clickup-projection.js +43 -1
  93. package/src/orchestrator/clickup-runtime.js +355 -39
  94. package/src/orchestrator/doctor.js +16 -3
  95. package/src/orchestrator/index.js +16 -0
  96. package/src/orchestrator/planning-runtime.js +20 -2
  97. package/src/orchestrator/rail-adjudication-evidence.js +234 -0
  98. package/src/orchestrator/rail-evidence-authority.js +147 -0
  99. package/src/orchestrator/rail-execution-evidence.js +45 -0
  100. package/src/orchestrator/rail-runtime.js +836 -56
  101. package/src/orchestrator/release-runtime.js +65 -6
  102. package/src/orchestrator/review-runtime.js +186 -41
  103. package/src/orchestrator/runtime-installation-v2.js +236 -20
  104. package/src/orchestrator/session-manager.js +1167 -53
  105. package/src/terminal/adapters/claude-adapter.js +3 -1
  106. package/src/terminal/adapters/codex-adapter.js +2 -0
  107. package/src/terminal/adapters/grok-adapter.js +7 -4
  108. package/src/terminal/handoff-parser.js +19 -1
  109. package/src/terminal/prompt-builder.js +9 -1
  110. package/src/terminal/provider-preflight.js +36 -3
  111. package/src/terminal/rail-execution-worktree.js +213 -0
  112. package/src/terminal/rail-prompts.js +169 -0
  113. package/src/terminal/rail-readonly-workspace.js +324 -0
  114. package/src/terminal/run-agent.js +384 -24
  115. package/src/terminal/run-parallel.js +5 -0
  116. package/src/terminal/run-rail-adjudication.js +323 -0
  117. package/src/terminal/run-rail-review.js +291 -0
  118. package/src/terminal/run-rail-rework.js +325 -0
  119. package/src/terminal/run-rail-task.js +380 -0
  120. package/src/terminal/run-team.js +5 -0
  121. package/src/terminal/spawner.js +1225 -77
  122. package/src/wizard/index.js +3 -2
@@ -0,0 +1,325 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { randomUUID } from 'node:crypto';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { resolve } from 'node:path';
6
+ import {
7
+ buildSpawnCommand, getTerminalCompletionAuthorityReceipt,
8
+ getTerminalLaunchAuthorityReceipt, isTransientFailure, killTerminal, spawnTerminal, waitForTerminal,
9
+ } from './spawner.js';
10
+ import { checkProviderReadiness } from './provider-preflight.js';
11
+ import { getRateLimiter } from './rate-limiter.js';
12
+ import { startClaimHeartbeat } from './run-rail-task.js';
13
+ import {
14
+ acquireRailTaskSupervisor, listRailExecutionEvidence, recordRailExecutionEvidence,
15
+ recordRailExecutionDispatch, releaseRailReworkReservation, relinquishRailTask,
16
+ reserveRailRework, reserveRailReworkLaunch, resolveRailAttemptContext,
17
+ } from '../orchestrator/rail-runtime.js';
18
+ import { parseRailExecutionEnvelope } from '../orchestrator/rail-execution-evidence.js';
19
+ import { listRailReviewEvidence } from '../orchestrator/review-runtime.js';
20
+ import { buildRailReworkPrompt } from './rail-prompts.js';
21
+ import { sha256 } from '@chati/core';
22
+ import {
23
+ loadRuntimeInstallationV2, resolveRuntimeExecutableBindingV2,
24
+ } from '../orchestrator/runtime-installation-v2.js';
25
+
26
+ export { buildRailReworkPrompt } from './rail-prompts.js';
27
+
28
+ function fail(code, message, details = {}) { const error = new Error(message); error.code = code; error.details = details; throw error; }
29
+
30
+ function terminalEvidence(handle, runNumber) {
31
+ const stdout = (handle.stdout || []).join('');
32
+ const stderr = (handle.stderr || []).join('');
33
+ if (Buffer.byteLength(stdout) > 1_000_000 || Buffer.byteLength(stderr) > 100_000) {
34
+ fail('RAIL_EXECUTION_OUTPUT_TOO_LARGE', 'RAIL terminal output exceeds the journal-bound evidence limit');
35
+ }
36
+ return Object.freeze({
37
+ terminal_id: handle.id,
38
+ provider: handle.provider,
39
+ model: handle.model,
40
+ reasoning_configuration: handle.reasoningConfiguration,
41
+ exit_status: handle.exitCode,
42
+ retry_count: handle.retryCount || 0,
43
+ run_number: runNumber,
44
+ started_at: handle.startedAt,
45
+ stdout,
46
+ stderr,
47
+ terminal_authority_receipt: getTerminalCompletionAuthorityReceipt(handle),
48
+ });
49
+ }
50
+
51
+ function latestReworkAuthorization(context) {
52
+ const decisions = context.records.filter((record) => ['review_decided', 'adjudication_decided'].includes(record.type)
53
+ && record.payload?.attempt_id === context.attempt.attempt_id);
54
+ const latest = decisions.at(-1);
55
+ if (latest?.type === 'review_decided' && latest.payload?.decision?.decision === 'normal-rework') {
56
+ return Object.freeze({ source_decision_ref: latest.payload.review_ref, review_ref: latest.payload.review_ref });
57
+ }
58
+ if (latest?.type === 'adjudication_decided' && latest.payload?.decision?.outcome === 'bounded-correction') {
59
+ return Object.freeze({
60
+ source_decision_ref: latest.payload.adjudication_ref,
61
+ review_ref: latest.payload.review_ref,
62
+ source_adjudication: Object.freeze({
63
+ adjudication_ref: latest.payload.adjudication_ref,
64
+ review_ref: latest.payload.review_ref,
65
+ decision: latest.payload.decision,
66
+ evidence: latest.payload.evidence,
67
+ }),
68
+ });
69
+ }
70
+ return fail('RAIL_REWORK_NOT_AUTHORIZED', 'the latest canonical decision does not authorize bounded rework');
71
+ }
72
+
73
+ export async function executeRailRework({
74
+ projectDir,
75
+ handoff_id,
76
+ attempt_id,
77
+ timeout = 30 * 60 * 1000,
78
+ max_retries = 2,
79
+ lease_ttl_ms = 5 * 60 * 1000,
80
+ } = {}) {
81
+ if (!projectDir || !handoff_id || !attempt_id) fail('RAIL_REWORK_INPUT_REQUIRED', 'projectDir, handoff_id and attempt_id are required');
82
+ let context = resolveRailAttemptContext({ projectDir, handoff_id, attempt_id });
83
+ if (context.state !== 'active') fail('RAIL_REWORK_ATTEMPT_INACTIVE', `attempt is ${context.state}`);
84
+ const authorization = latestReworkAuthorization(context);
85
+ const reviewEntry = listRailReviewEvidence({ projectDir, attempt_id })
86
+ .find((entry) => `rail-review://${handoff_id}/${attempt_id}/${entry.review?.review_id}` === authorization.review_ref);
87
+ if (!reviewEntry) fail('RAIL_REWORK_REVIEW_EVIDENCE_MISSING', 'the authorizing review evidence is unavailable or does not match the journal');
88
+ const executionHistory = listRailExecutionEvidence({ projectDir, attempt_id });
89
+ if (executionHistory.length === 0) fail('RAIL_EXECUTION_EVIDENCE_REQUIRED', 'rework requires prior immutable execution evidence');
90
+ if (executionHistory.some((entry) => typeof entry.result_commit_ref === 'string'
91
+ && entry.source_decision_ref === authorization.source_decision_ref)) {
92
+ fail('RAIL_REWORK_ALREADY_EXECUTED', 'this decision already has a successful rework execution');
93
+ }
94
+
95
+ const originalInvocation = executionHistory[0].invocation;
96
+ const binding = context.task.execution_binding;
97
+ const providerExecutableBinding = resolveRuntimeExecutableBindingV2({
98
+ artifact: loadRuntimeInstallationV2(projectDir),
99
+ provider_id: binding.provider_id,
100
+ harness_id: binding.harness_id,
101
+ });
102
+ if (originalInvocation.provider_id !== binding.provider_id
103
+ || originalInvocation.harness_id !== binding.harness_id
104
+ || originalInvocation.model_pin?.model_id !== binding.model_id
105
+ || originalInvocation.model_pin?.reasoning_configuration !== binding.reasoning_configuration) {
106
+ fail('RAIL_REWORK_BINDING_MISMATCH', 'original execution evidence does not match the sealed task binding');
107
+ }
108
+ const readiness = checkProviderReadiness(binding.harness_id, {
109
+ workingDir: projectDir, executableBinding: providerExecutableBinding,
110
+ });
111
+ if (!readiness.ready) fail(readiness.code || 'PROVIDER_NOT_READY', readiness.detail || `${binding.harness_id} is not ready`);
112
+ const base_commit_ref = executionHistory.filter((entry) => typeof entry.result_commit_ref === 'string').at(-1)?.result_commit_ref;
113
+ if (!base_commit_ref) fail('RAIL_REWORK_BASE_COMMIT_REQUIRED', 'latest immutable execution evidence has no result commit');
114
+ const ownership = acquireRailTaskSupervisor({ projectDir, handoff_id, attempt_id, lease_ttl_ms });
115
+ context = ownership.context;
116
+ const renewed = ownership.renewed;
117
+ const reservation_id = `rework-reservation-${randomUUID()}`;
118
+ let reservationAcquired = false;
119
+ let reservationReleased = false;
120
+ let dispatchAwaitingEvidence = false;
121
+ let reworkSucceeded = false;
122
+ let invocation;
123
+ let handle;
124
+ let heartbeat;
125
+ try {
126
+ reserveRailRework({
127
+ projectDir, handoff_id, attempt_id,
128
+ source_decision_ref: authorization.source_decision_ref,
129
+ reservation_id,
130
+ });
131
+ reservationAcquired = true;
132
+ heartbeat = startClaimHeartbeat({
133
+ projectDir, handoff_id, attempt_id, claim_id: context.claim.claim_id,
134
+ operator_id: ownership.operator_id, lease_ttl_ms, initial_lease: renewed.lease,
135
+ activeHandle: () => handle,
136
+ });
137
+ await getRateLimiter(binding.harness_id).waitForSlot();
138
+ if (heartbeat.evidence().failure) fail('RAIL_LEASE_HEARTBEAT_FAILED', 'rework lost its RAIL claim heartbeat');
139
+ const launch = async () => {
140
+ const promptExecutionHistory = listRailExecutionEvidence({ projectDir, attempt_id });
141
+ const invocationDraft = {
142
+ ...originalInvocation,
143
+ invocation_id: `rail-rework-${attempt_id}-${executionHistory.length + 1}-${randomUUID()}`,
144
+ attempt_id,
145
+ launch_nonce: randomUUID(),
146
+ prompt_kind: 'rework',
147
+ source_decision_ref: authorization.source_decision_ref,
148
+ rework_reservation_id: reservation_id,
149
+ language: context.language,
150
+ };
151
+ const prompt = buildRailReworkPrompt({
152
+ projectDir, context, sourceReview: reviewEntry.review, executionHistory: promptExecutionHistory,
153
+ sourceAdjudication: authorization.source_adjudication,
154
+ baseCommitRef: base_commit_ref, invocation: invocationDraft,
155
+ });
156
+ invocation = Object.freeze({ ...invocationDraft, prompt_digest: sha256(prompt) });
157
+ const config = {
158
+ agent: 'rail-rework', taskId: context.task.task_id,
159
+ provider: binding.harness_id, providerId: binding.provider_id,
160
+ model: binding.model_id, reasoningConfiguration: binding.reasoning_configuration,
161
+ catalogSnapshotRef: invocation.model_pin.catalog_snapshot_ref,
162
+ providerExecutableBinding,
163
+ prompt, workingDir: projectDir, timeout, writeScope: ['.'],
164
+ executionBaseCommitRef: base_commit_ref,
165
+ authorityContext: {
166
+ authority_kind: 'rail_execution', invocation_id: invocation.invocation_id,
167
+ launch_nonce: invocation.launch_nonce, provider_id: invocation.provider_id,
168
+ },
169
+ beforeAuthoritySpawn: () => {
170
+ reserveRailReworkLaunch({
171
+ projectDir, handoff_id, attempt_id,
172
+ source_decision_ref: authorization.source_decision_ref,
173
+ reservation_id,
174
+ invocation,
175
+ });
176
+ // From the final pre-spawn durable launch intent onward, absence of
177
+ // canonical completion evidence remains unresolved after a crash.
178
+ dispatchAwaitingEvidence = true;
179
+ },
180
+ };
181
+ const built = buildSpawnCommand(config);
182
+ if (built.actualProvider !== config.provider || built.actualModel !== config.model
183
+ || built.actualReasoningConfiguration !== config.reasoningConfiguration) {
184
+ fail('RAIL_REWORK_ROUTE_MISMATCH', 'terminal adapter changed the sealed rework binding');
185
+ }
186
+ handle = spawnTerminal(config);
187
+ try {
188
+ recordRailExecutionDispatch({
189
+ projectDir, handoff_id, attempt_id, invocation,
190
+ launch_receipt: getTerminalLaunchAuthorityReceipt(handle),
191
+ prompt, run_kind: 'rework',
192
+ });
193
+ } catch (error) {
194
+ await killTerminal(handle);
195
+ throw error;
196
+ }
197
+ return handle;
198
+ };
199
+ await launch();
200
+ let retries = 0;
201
+ const failedAttemptRefs = [];
202
+ let persisted;
203
+ let execution;
204
+ const recordFailedAttempt = () => {
205
+ handle.retryCount = retries;
206
+ const failedExecution = terminalEvidence(handle, executionHistory.length + failedAttemptRefs.length + 1);
207
+ persisted = recordRailExecutionEvidence({
208
+ projectDir, handoff_id, task_id: context.task.task_id, attempt_id,
209
+ invocation, execution: failedExecution, base_commit_ref, result_commit_ref: null,
210
+ run_kind: 'rework', source_decision_ref: authorization.source_decision_ref,
211
+ });
212
+ execution = failedExecution;
213
+ dispatchAwaitingEvidence = false;
214
+ failedAttemptRefs.push(persisted.evidence_ref);
215
+ };
216
+ try {
217
+ await waitForTerminal(handle);
218
+ while (handle.exitCode !== 0 && retries < max_retries && isTransientFailure(handle.exitCode, [...handle.stdout, ...handle.stderr])) {
219
+ if (heartbeat.evidence().failure) break;
220
+ recordFailedAttempt();
221
+ retries += 1;
222
+ await new Promise((done) => setTimeout(done, 2_000 * (2 ** (retries - 1))));
223
+ if (heartbeat.evidence().failure) break;
224
+ await getRateLimiter(binding.harness_id).waitForSlot();
225
+ if (heartbeat.evidence().failure) break;
226
+ await launch();
227
+ await waitForTerminal(handle);
228
+ }
229
+ } finally { heartbeat.stop(); }
230
+ const heartbeatEvidence = heartbeat.evidence();
231
+ let result_commit_ref = null;
232
+ if (dispatchAwaitingEvidence) {
233
+ handle.retryCount = retries;
234
+ execution = terminalEvidence(handle, executionHistory.length + failedAttemptRefs.length + 1);
235
+ const invalidOutput = execution.terminal_authority_receipt.execution_result_status === 'invalid_output';
236
+ const envelope = execution.exit_status === 0 && !invalidOutput
237
+ ? parseRailExecutionEnvelope((handle.stdout || []).join(''))
238
+ : null;
239
+ if (envelope && envelope.launch_nonce !== invocation.launch_nonce) {
240
+ fail('RAIL_EXECUTION_LAUNCH_NONCE_MISMATCH', 'rework output does not echo the canonical launch nonce');
241
+ }
242
+ result_commit_ref = envelope?.result_commit_ref ?? null;
243
+ persisted = recordRailExecutionEvidence({
244
+ projectDir, handoff_id, task_id: context.task.task_id, attempt_id,
245
+ invocation, execution, base_commit_ref, result_commit_ref,
246
+ run_kind: 'rework', source_decision_ref: authorization.source_decision_ref,
247
+ });
248
+ dispatchAwaitingEvidence = false;
249
+ }
250
+ if (heartbeatEvidence.failure) fail('RAIL_LEASE_HEARTBEAT_FAILED', 'rework lost its RAIL claim heartbeat');
251
+ reworkSucceeded = persisted.outcome === 'succeeded';
252
+ if (!reworkSucceeded && execution.exit_status === 0) {
253
+ fail(
254
+ persisted.failure_code || 'RAIL_EXECUTION_OUTPUT_INVALID',
255
+ 'RAIL rework completed without a valid canonical result commit',
256
+ { execution_evidence_ref: persisted.evidence_ref },
257
+ );
258
+ }
259
+ return Object.freeze({
260
+ ok: reworkSucceeded,
261
+ action: reworkSucceeded ? 'rail_rework_executed' : 'rail_rework_failed',
262
+ handoff_id, task_id: context.task.task_id, attempt_id,
263
+ source_decision_ref: authorization.source_decision_ref,
264
+ ...(authorization.source_decision_ref === authorization.review_ref ? { source_review_ref: authorization.review_ref } : {}),
265
+ invocation, execution, base_commit_ref, result_commit_ref,
266
+ execution_evidence_ref: persisted.evidence_ref,
267
+ failed_execution_evidence_refs: Object.freeze(failedAttemptRefs),
268
+ retry_count: retries,
269
+ lease_heartbeat_count: heartbeatEvidence.heartbeat_count,
270
+ next_required_action: reworkSucceeded ? 'independent_review' : 'rail_retry_same_rework',
271
+ });
272
+ } finally {
273
+ try {
274
+ heartbeat?.stop();
275
+ if (handle) await killTerminal(handle);
276
+ if (reservationAcquired && !dispatchAwaitingEvidence) {
277
+ releaseRailReworkReservation({
278
+ projectDir, handoff_id, attempt_id,
279
+ source_decision_ref: authorization.source_decision_ref,
280
+ reservation_id,
281
+ reason: reworkSucceeded ? 'canonical_success_recorded' : 'canonical_failure_recorded',
282
+ });
283
+ reservationReleased = true;
284
+ }
285
+ } finally {
286
+ if ((reservationAcquired && reservationReleased) || (!reservationAcquired && ownership.recovered)) {
287
+ relinquishRailTask({
288
+ projectDir, handoff_id, attempt_id,
289
+ claim_id: context.claim.claim_id,
290
+ operator_id: ownership.operator_id,
291
+ reason: 'action_complete',
292
+ });
293
+ }
294
+ }
295
+ }
296
+ }
297
+
298
+ function parseArgs(argv) {
299
+ const args = {};
300
+ for (let index = 2; index < argv.length; index += 1) {
301
+ const token = argv[index];
302
+ if (!token.startsWith('--')) continue;
303
+ const value = argv[index + 1];
304
+ if (value && !value.startsWith('--')) { args[token.slice(2)] = value; index += 1; } else args[token.slice(2)] = true;
305
+ }
306
+ return args;
307
+ }
308
+
309
+ async function main() {
310
+ const args = parseArgs(process.argv);
311
+ try {
312
+ const result = await executeRailRework({
313
+ projectDir: resolve(args['project-dir'] || process.cwd()),
314
+ handoff_id: args['handoff-id'], attempt_id: args['attempt-id'],
315
+ ...(args.timeout ? { timeout: Number(args.timeout) } : {}),
316
+ });
317
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
318
+ if (!result.ok) process.exitCode = 1;
319
+ } catch (error) {
320
+ process.stdout.write(`${JSON.stringify({ ok: false, action: 'error', code: error.code || 'RAIL_REWORK_FAILED', error: error.message })}\n`);
321
+ process.exitCode = 1;
322
+ }
323
+ }
324
+
325
+ if (process.argv[1] && resolve(process.argv[1]) === resolve(fileURLToPath(import.meta.url))) await main();