chati-dev 4.5.15 → 4.5.27
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/README.md +8 -3
- package/bin/chati.js +124 -66
- package/framework/agents/build/dev.md +5 -5
- package/framework/agents/deploy/devops.md +7 -7
- package/framework/agents/discover/brief.md +4 -4
- package/framework/agents/discover/brownfield-wu.md +3 -3
- package/framework/agents/discover/greenfield-wu.md +3 -3
- package/framework/agents/plan/architect.md +2 -2
- package/framework/agents/plan/detail.md +4 -4
- package/framework/agents/plan/phases.md +2 -2
- package/framework/agents/plan/tasks.md +2 -2
- package/framework/agents/plan/ux.md +2 -2
- package/framework/agents/quality/qa-implementation.md +11 -10
- package/framework/agents/quality/qa-planning.md +3 -3
- package/framework/agents/quality/qa-visual.md +1 -1
- package/framework/config.yaml +3 -3
- package/framework/constitution.md +18 -18
- package/framework/context/protocols.md +2 -2
- package/framework/context/quality.md +1 -1
- package/framework/context/root.md +4 -4
- package/framework/data/entity-registry.yaml +1 -1
- package/framework/domains/agents/orchestrator.yaml +2 -2
- package/framework/domains/constitution.yaml +1 -1
- package/framework/hooks/advance-trigger.js +4 -6
- package/framework/hooks/git-push-authority.js +45 -37
- package/framework/hooks/mode-governance.js +149 -40
- package/framework/hooks/model-governance.js +13 -20
- package/framework/hooks/prism-engine.js +74 -92
- package/framework/hooks/reasoning-escalator.js +23 -40
- package/framework/hooks/session-digest.js +12 -13
- package/framework/hooks/session-reader.js +224 -0
- package/framework/hooks/session-writer.js +195 -0
- package/framework/hooks/team-quality-gate.js +34 -24
- package/framework/i18n/en.yaml +2 -2
- package/framework/i18n/es.yaml +2 -2
- package/framework/i18n/fr.yaml +2 -2
- package/framework/i18n/pt.yaml +2 -2
- package/framework/intelligence/context-engine.md +4 -4
- package/framework/intelligence/memory-layer.md +1 -1
- package/framework/manifest.json +129 -119
- package/framework/manifest.sig +1 -1
- package/framework/orchestrator/chati-router.js +278 -24
- package/framework/orchestrator/chati.md +250 -68
- package/framework/schemas/session.schema.json +21 -1
- package/framework/tasks/orchestrator-deviation.md +1 -1
- package/framework/tasks/orchestrator-escalate.md +1 -1
- package/framework/tasks/orchestrator-handoff.md +6 -6
- package/framework/tasks/orchestrator-health.md +5 -9
- package/framework/tasks/orchestrator-mode-switch.md +3 -7
- package/framework/tasks/orchestrator-resume.md +10 -14
- package/framework/tasks/orchestrator-route.md +3 -3
- package/framework/tasks/orchestrator-spawn-terminal.md +1 -1
- package/framework/tasks/orchestrator-status.md +9 -9
- package/framework/tasks/orchestrator-suggest-mode.md +1 -1
- package/framework/tasks/qa-impl-consolidate.md +2 -2
- package/framework/tasks/qa-impl-performance-test.md +4 -4
- package/framework/tasks/qa-impl-regression-check.md +4 -4
- package/framework/tasks/qa-impl-sast-scan.md +1 -1
- package/framework/tasks/qa-impl-test-execute.md +1 -1
- package/framework/tasks/qa-impl-verdict.md +2 -2
- package/framework/tasks/qa-planning-consolidate.md +3 -3
- package/framework/tasks/qa-planning-coverage-plan.md +2 -2
- package/framework/tasks/qa-planning-gate-define.md +4 -4
- package/framework/tasks/qa-planning-risk-matrix.md +4 -4
- package/framework/tasks/qa-planning-test-strategy.md +2 -2
- package/node_modules/@chati/browser-capability/src/index.js +12 -2
- package/node_modules/@chati/planning/src/index.js +24 -4
- package/node_modules/@chati/provider-registry/src/index.js +11 -0
- package/node_modules/@chati/rail/src/index.js +1967 -83
- package/node_modules/@chati/release-lane/README.md +12 -8
- package/node_modules/@chati/release-lane/package.json +1 -1
- package/node_modules/@chati/release-lane/src/index.js +1426 -58
- package/node_modules/@chati/review-council/src/index.js +63 -0
- package/node_modules/@chati/tracking-clickup/README.md +13 -0
- package/node_modules/@chati/tracking-clickup/src/index.js +690 -30
- package/package-artifact-manifest.json +1 -0
- package/package-artifact-manifest.sig +1 -0
- package/package.json +16 -7
- package/scripts/verify-real-harness-e2e.js +1581 -0
- package/src/config/framework-adapter.js +4 -4
- package/src/installer/core.js +157 -56
- package/src/installer/manifest.js +140 -9
- package/src/installer/package-artifact.js +249 -0
- package/src/installer/templates.js +91 -23
- package/src/installer-v2/catalog-client.js +531 -23
- package/src/installer-v2/index.js +91 -34
- package/src/installer-v2/installation-authority.js +327 -0
- package/src/installer-v2/provider-executable.js +235 -0
- package/src/installer-v2/wizard-installation.js +1 -1
- package/src/intelligence/timeline.js +3 -1
- package/src/orchestrator/browser-runtime.js +44 -13
- package/src/orchestrator/cli.js +1616 -151
- package/src/orchestrator/clickup-projection.js +43 -1
- package/src/orchestrator/clickup-runtime.js +355 -39
- package/src/orchestrator/doctor.js +87 -4
- package/src/orchestrator/index.js +16 -0
- package/src/orchestrator/planning-runtime.js +20 -2
- package/src/orchestrator/rail-adjudication-evidence.js +234 -0
- package/src/orchestrator/rail-evidence-authority.js +147 -0
- package/src/orchestrator/rail-execution-evidence.js +45 -0
- package/src/orchestrator/rail-runtime.js +836 -56
- package/src/orchestrator/release-runtime.js +65 -6
- package/src/orchestrator/review-runtime.js +186 -41
- package/src/orchestrator/runtime-installation-v2.js +236 -20
- package/src/orchestrator/session-manager.js +1249 -52
- package/src/terminal/adapters/claude-adapter.js +3 -1
- package/src/terminal/adapters/codex-adapter.js +2 -0
- package/src/terminal/adapters/grok-adapter.js +7 -4
- package/src/terminal/handoff-parser.js +19 -1
- package/src/terminal/prompt-builder.js +10 -1
- package/src/terminal/provider-preflight.js +36 -3
- package/src/terminal/rail-execution-worktree.js +213 -0
- package/src/terminal/rail-prompts.js +169 -0
- package/src/terminal/rail-readonly-workspace.js +324 -0
- package/src/terminal/run-agent.js +434 -16
- package/src/terminal/run-parallel.js +5 -0
- package/src/terminal/run-rail-adjudication.js +323 -0
- package/src/terminal/run-rail-review.js +291 -0
- package/src/terminal/run-rail-rework.js +325 -0
- package/src/terminal/run-rail-task.js +380 -0
- package/src/terminal/run-team.js +5 -0
- package/src/terminal/spawner.js +1225 -77
- package/src/utils/schema-validator.js +5 -0
- package/src/wizard/index.js +3 -2
|
@@ -16,10 +16,17 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import { existsSync, readFileSync } from 'fs';
|
|
19
|
-
import {
|
|
19
|
+
import { randomUUID } from 'crypto';
|
|
20
|
+
import { join, resolve } from 'path';
|
|
21
|
+
import { readHookSession } from '../hooks/session-reader.js';
|
|
20
22
|
|
|
21
23
|
const PROJECT_DIR = process.cwd();
|
|
22
|
-
const
|
|
24
|
+
const RAW_ROUTER_ARGS = process.argv.slice(2);
|
|
25
|
+
const harnessFlagIndex = RAW_ROUTER_ARGS.indexOf('--harness');
|
|
26
|
+
const ACTIVE_HARNESS = harnessFlagIndex >= 0 ? RAW_ROUTER_ARGS[harnessFlagIndex + 1] : null;
|
|
27
|
+
const ROUTER_ARGS = harnessFlagIndex >= 0
|
|
28
|
+
? RAW_ROUTER_ARGS.filter((_, index) => index !== harnessFlagIndex && index !== harnessFlagIndex + 1)
|
|
29
|
+
: RAW_ROUTER_ARGS;
|
|
23
30
|
|
|
24
31
|
async function readEnabledHarnesses(projectDir) {
|
|
25
32
|
const installationPath = join(projectDir, '.chati', 'v2', 'installation.json');
|
|
@@ -40,6 +47,7 @@ async function readEnabledHarnesses(projectDir) {
|
|
|
40
47
|
|
|
41
48
|
const detected = [];
|
|
42
49
|
if (existsSync(join(projectDir, '.claude', 'commands', 'chati.md'))) detected.push('claude');
|
|
50
|
+
if (existsSync(join(projectDir, '.gemini', 'commands', 'chati.toml'))) detected.push('gemini');
|
|
43
51
|
if (existsSync(join(projectDir, '.agents', 'skills', 'chati', 'SKILL.md'))) detected.push('codex');
|
|
44
52
|
if (existsSync(join(projectDir, '.grok', 'commands', 'chati.md'))) detected.push('grok');
|
|
45
53
|
return { harnesses: detected, error: null };
|
|
@@ -64,7 +72,7 @@ export async function validateProviderIntegrity(projectDir) {
|
|
|
64
72
|
const { harnesses, error } = await readEnabledHarnesses(projectDir);
|
|
65
73
|
|
|
66
74
|
if (error) missing.push(error);
|
|
67
|
-
const supportedHarnesses = new Set(['claude', 'codex', 'grok']);
|
|
75
|
+
const supportedHarnesses = new Set(['claude', 'gemini', 'codex', 'grok']);
|
|
68
76
|
for (const harness of harnesses) {
|
|
69
77
|
if (!supportedHarnesses.has(harness)) missing.push(`unsupported harness ${harness}`);
|
|
70
78
|
}
|
|
@@ -132,9 +140,40 @@ export async function validateProviderIntegrity(projectDir) {
|
|
|
132
140
|
}
|
|
133
141
|
}
|
|
134
142
|
|
|
143
|
+
if (harnesses.includes('gemini')) {
|
|
144
|
+
for (const [label, relativePath] of [
|
|
145
|
+
['gemini command', '.gemini/commands/chati.toml'],
|
|
146
|
+
['gemini session lock', '.gemini/session-lock.md'],
|
|
147
|
+
['gemini governance context', '.gemini/context/governance.md'],
|
|
148
|
+
['gemini license hook file', '.gemini/hooks/license-guard.js'],
|
|
149
|
+
]) {
|
|
150
|
+
if (!hasContent(join(projectDir, relativePath))) missing.push(label);
|
|
151
|
+
}
|
|
152
|
+
const settingsPath = join(projectDir, '.gemini', 'settings.json');
|
|
153
|
+
if (!hasContent(settingsPath)) {
|
|
154
|
+
missing.push('gemini settings');
|
|
155
|
+
} else {
|
|
156
|
+
try {
|
|
157
|
+
const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
|
|
158
|
+
const firstHook = settings.hooks?.BeforeModel?.[0]?.hooks?.[0];
|
|
159
|
+
if (firstHook?.name !== 'license-guard'
|
|
160
|
+
|| firstHook?.command !== 'node .gemini/hooks/license-guard.js') {
|
|
161
|
+
missing.push('gemini license hook');
|
|
162
|
+
}
|
|
163
|
+
} catch {
|
|
164
|
+
missing.push('gemini settings invalid');
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
135
169
|
if (harnesses.includes('grok')) {
|
|
136
170
|
if (!hasContent(join(projectDir, '.grok', 'commands', 'chati.md'))) missing.push('grok command');
|
|
137
171
|
if (!hasContent(join(projectDir, '.grok', 'session-lock.md'))) missing.push('grok session lock');
|
|
172
|
+
const governancePath = join(projectDir, '.grok', 'rules', 'chati-governance.md');
|
|
173
|
+
if (!hasContent(governancePath)
|
|
174
|
+
|| !readFileSync(governancePath, 'utf-8').includes('chati-router.js --harness grok')) {
|
|
175
|
+
missing.push('grok governance rule');
|
|
176
|
+
}
|
|
138
177
|
}
|
|
139
178
|
|
|
140
179
|
return { valid: missing.length === 0, missing };
|
|
@@ -312,9 +351,23 @@ const VALID_ORCHESTRATE_SUBS = new Set([
|
|
|
312
351
|
'spawn-team', 'team-status', 'team-dissolve',
|
|
313
352
|
'wait-for-capture', 'wave-status', 'assess-coverage',
|
|
314
353
|
'wait-for-license', 'doctor', 'run-script-task',
|
|
354
|
+
'activate-harness',
|
|
315
355
|
]);
|
|
316
356
|
|
|
317
|
-
const VALID_RAIL_SUBS = new Set([
|
|
357
|
+
const VALID_RAIL_SUBS = new Set([
|
|
358
|
+
'rail-next',
|
|
359
|
+
'rail-dispatch',
|
|
360
|
+
'rail-review-run',
|
|
361
|
+
'rail-rework',
|
|
362
|
+
'rail-adjudicate',
|
|
363
|
+
'rail-recover',
|
|
364
|
+
'rail-complete',
|
|
365
|
+
'rail-release-ready',
|
|
366
|
+
'rail-tracking-reconcile',
|
|
367
|
+
'rail-tracking-pending',
|
|
368
|
+
'rail-tracking-ack',
|
|
369
|
+
'rail-tracking-lookup',
|
|
370
|
+
]);
|
|
318
371
|
|
|
319
372
|
/**
|
|
320
373
|
* Parse --flag value pairs and standalone --flag bools from argv.
|
|
@@ -380,8 +433,17 @@ async function dispatchRail(subCommand, argv) {
|
|
|
380
433
|
}
|
|
381
434
|
|
|
382
435
|
const flags = parseFlagArgs(argv);
|
|
436
|
+
const parseJsonFlag = (name, { required = false } = {}) => {
|
|
437
|
+
const raw = flags[name];
|
|
438
|
+
if (raw === undefined) {
|
|
439
|
+
if (required) throw Object.assign(new Error(`Missing --${name}`), { code: 'RAIL_INPUT_REQUIRED' });
|
|
440
|
+
return undefined;
|
|
441
|
+
}
|
|
442
|
+
try { return JSON.parse(raw); }
|
|
443
|
+
catch (error) { throw Object.assign(new Error(`Invalid JSON in --${name}: ${error.message}`), { code: 'RAIL_INPUT_INVALID' }); }
|
|
444
|
+
};
|
|
383
445
|
if (subCommand === 'rail-next') {
|
|
384
|
-
if (!flags['handoff-id']) return { ok: false, error: 'Missing --handoff-id' };
|
|
446
|
+
if (!flags['handoff-id']) return { ok: false, action: 'error', error: 'RAIL_INPUT_REQUIRED', detail: 'Missing --handoff-id' };
|
|
385
447
|
try {
|
|
386
448
|
const { selectReadyRailTasks } = await import(runtimePath);
|
|
387
449
|
return selectReadyRailTasks({ projectDir: PROJECT_DIR, handoff_id: flags['handoff-id'] });
|
|
@@ -389,6 +451,173 @@ async function dispatchRail(subCommand, argv) {
|
|
|
389
451
|
return { ok: false, action: 'error', error: error.code || error.message };
|
|
390
452
|
}
|
|
391
453
|
}
|
|
454
|
+
if (subCommand === 'rail-dispatch') {
|
|
455
|
+
if (!flags['handoff-id'] || !flags['task-id']) return { ok: false, action: 'error', error: 'RAIL_INPUT_REQUIRED', detail: 'Missing --handoff-id or --task-id' };
|
|
456
|
+
const attemptId = flags['attempt-id'] || `attempt-${randomUUID()}`;
|
|
457
|
+
try {
|
|
458
|
+
const runnerPath = join(PROJECT_DIR, fwDir, '_cli', 'terminal', 'run-rail-task.js');
|
|
459
|
+
if (!existsSync(runnerPath)) return { ok: false, action: 'error', error: 'rail_dispatch_runtime_not_found' };
|
|
460
|
+
const { executeReadyRailTask } = await import(runnerPath);
|
|
461
|
+
const browserRequest = flags['browser-request-json']
|
|
462
|
+
? JSON.parse(flags['browser-request-json'])
|
|
463
|
+
: undefined;
|
|
464
|
+
return await executeReadyRailTask({
|
|
465
|
+
projectDir: PROJECT_DIR,
|
|
466
|
+
handoff_id: flags['handoff-id'],
|
|
467
|
+
task_id: flags['task-id'],
|
|
468
|
+
attempt_id: attemptId,
|
|
469
|
+
operator_id: flags['operator-id'] || `operator://chati/${ACTIVE_HARNESS || 'runtime'}`,
|
|
470
|
+
...(flags.timeout ? { timeout: Number(flags.timeout) } : {}),
|
|
471
|
+
...(browserRequest === undefined ? {} : { browser_request: browserRequest }),
|
|
472
|
+
});
|
|
473
|
+
} catch (error) {
|
|
474
|
+
let nextRequiredAction = 'rail_retry_same_attempt';
|
|
475
|
+
try {
|
|
476
|
+
const runtime = await import(runtimePath);
|
|
477
|
+
runtime.resolveRailAttemptContext({ projectDir: PROJECT_DIR, handoff_id: flags['handoff-id'], attempt_id: attemptId });
|
|
478
|
+
nextRequiredAction = 'rail_recover_same_attempt_after_lease_expiry';
|
|
479
|
+
} catch {
|
|
480
|
+
// The dispatch never created a canonical claim.
|
|
481
|
+
}
|
|
482
|
+
return {
|
|
483
|
+
ok: false, action: 'error', error: error.code || 'RAIL_DISPATCH_FAILED',
|
|
484
|
+
detail: error.message, attempt_id: error.details?.attempt_id || attemptId,
|
|
485
|
+
next_required_action: nextRequiredAction,
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
if (subCommand === 'rail-recover') {
|
|
490
|
+
if (!flags['handoff-id'] || !flags['attempt-id'] || !flags['operator-id']) {
|
|
491
|
+
return { ok: false, action: 'error', error: 'RAIL_INPUT_REQUIRED', detail: 'Missing --handoff-id, --attempt-id or --operator-id' };
|
|
492
|
+
}
|
|
493
|
+
const attemptId = flags['attempt-id'];
|
|
494
|
+
try {
|
|
495
|
+
const runnerPath = join(PROJECT_DIR, fwDir, '_cli', 'terminal', 'run-rail-task.js');
|
|
496
|
+
if (!existsSync(runnerPath)) return { ok: false, action: 'error', error: 'rail_dispatch_runtime_not_found' };
|
|
497
|
+
const runtime = await import(runtimePath);
|
|
498
|
+
const { executeReadyRailTask } = await import(runnerPath);
|
|
499
|
+
const context = runtime.resolveRailAttemptContext({
|
|
500
|
+
projectDir: PROJECT_DIR,
|
|
501
|
+
handoff_id: flags['handoff-id'],
|
|
502
|
+
attempt_id: attemptId,
|
|
503
|
+
});
|
|
504
|
+
const browserRequest = flags['browser-request-json']
|
|
505
|
+
? JSON.parse(flags['browser-request-json'])
|
|
506
|
+
: undefined;
|
|
507
|
+
return await executeReadyRailTask({
|
|
508
|
+
projectDir: PROJECT_DIR,
|
|
509
|
+
handoff_id: flags['handoff-id'],
|
|
510
|
+
task_id: context.task.task_id,
|
|
511
|
+
attempt_id: attemptId,
|
|
512
|
+
operator_id: flags['operator-id'],
|
|
513
|
+
recover_expired: true,
|
|
514
|
+
...(flags.timeout ? { timeout: Number(flags.timeout) } : {}),
|
|
515
|
+
...(browserRequest === undefined ? {} : { browser_request: browserRequest }),
|
|
516
|
+
});
|
|
517
|
+
} catch (error) {
|
|
518
|
+
return {
|
|
519
|
+
ok: false, action: 'error', error: error.code || 'RAIL_RECOVERY_FAILED',
|
|
520
|
+
detail: error.message, attempt_id: error.details?.attempt_id || attemptId,
|
|
521
|
+
next_required_action: 'rail_recover_and_dispatch_same_attempt',
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
if (subCommand === 'rail-review-run') {
|
|
526
|
+
if (!flags['handoff-id'] || !flags['attempt-id']) return { ok: false, action: 'error', error: 'Missing --handoff-id or --attempt-id' };
|
|
527
|
+
try {
|
|
528
|
+
const runnerPath = join(PROJECT_DIR, fwDir, '_cli', 'terminal', 'run-rail-review.js');
|
|
529
|
+
if (!existsSync(runnerPath)) return { ok: false, action: 'error', error: 'rail_review_runtime_not_found' };
|
|
530
|
+
const { executeRailReview } = await import(runnerPath);
|
|
531
|
+
return await executeRailReview({
|
|
532
|
+
projectDir: PROJECT_DIR,
|
|
533
|
+
handoff_id: flags['handoff-id'],
|
|
534
|
+
attempt_id: flags['attempt-id'],
|
|
535
|
+
...(flags.timeout ? { timeout: Number(flags.timeout) } : {}),
|
|
536
|
+
});
|
|
537
|
+
} catch (error) {
|
|
538
|
+
return { ok: false, action: 'error', error: error.code || 'RAIL_REVIEW_FAILED', detail: error.message };
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
if (subCommand === 'rail-rework') {
|
|
542
|
+
if (!flags['handoff-id'] || !flags['attempt-id']) return { ok: false, action: 'error', error: 'Missing --handoff-id or --attempt-id' };
|
|
543
|
+
try {
|
|
544
|
+
const runnerPath = join(PROJECT_DIR, fwDir, '_cli', 'terminal', 'run-rail-rework.js');
|
|
545
|
+
if (!existsSync(runnerPath)) return { ok: false, action: 'error', error: 'rail_rework_runtime_not_found' };
|
|
546
|
+
const { executeRailRework } = await import(runnerPath);
|
|
547
|
+
return await executeRailRework({
|
|
548
|
+
projectDir: PROJECT_DIR,
|
|
549
|
+
handoff_id: flags['handoff-id'],
|
|
550
|
+
attempt_id: flags['attempt-id'],
|
|
551
|
+
...(flags.timeout ? { timeout: Number(flags.timeout) } : {}),
|
|
552
|
+
});
|
|
553
|
+
} catch (error) {
|
|
554
|
+
return { ok: false, action: 'error', error: error.code || 'RAIL_REWORK_FAILED', detail: error.message };
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
if (subCommand === 'rail-adjudicate') {
|
|
558
|
+
if (!flags['handoff-id'] || !flags['attempt-id']) return { ok: false, action: 'error', error: 'Missing --handoff-id or --attempt-id' };
|
|
559
|
+
try {
|
|
560
|
+
const runnerPath = join(PROJECT_DIR, fwDir, '_cli', 'terminal', 'run-rail-adjudication.js');
|
|
561
|
+
if (!existsSync(runnerPath)) return { ok: false, action: 'error', error: 'rail_adjudication_runtime_not_found' };
|
|
562
|
+
const { executeRailAdjudication } = await import(runnerPath);
|
|
563
|
+
return await executeRailAdjudication({
|
|
564
|
+
projectDir: PROJECT_DIR,
|
|
565
|
+
handoff_id: flags['handoff-id'],
|
|
566
|
+
attempt_id: flags['attempt-id'],
|
|
567
|
+
...(flags.timeout ? { timeout: Number(flags.timeout) } : {}),
|
|
568
|
+
});
|
|
569
|
+
} catch (error) {
|
|
570
|
+
return { ok: false, action: 'error', error: error.code || 'RAIL_ADJUDICATION_FAILED', detail: error.message };
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
try {
|
|
574
|
+
const runtime = await import(runtimePath);
|
|
575
|
+
if (subCommand === 'rail-complete') {
|
|
576
|
+
const completion = parseJsonFlag('completion-json', { required: true });
|
|
577
|
+
const { completion_id, attempt_id, claim_id, operator_id, state, git, ci, reviews, acceptance_evidence_refs, acceptance_authority_receipt, completed_at, metrics } = completion;
|
|
578
|
+
return runtime.completeRailTask({
|
|
579
|
+
projectDir: PROJECT_DIR, handoff_id: flags['handoff-id'], completion_id, attempt_id, claim_id, operator_id, state, git, ci,
|
|
580
|
+
reviews, acceptance_evidence_refs, acceptance_authority_receipt, ...(completed_at === undefined ? {} : { completed_at }), ...(metrics === undefined ? {} : { metrics }),
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
if (subCommand === 'rail-release-ready') {
|
|
584
|
+
return runtime.selectRailReleaseReadiness({ projectDir: PROJECT_DIR, handoff_id: flags['handoff-id'] });
|
|
585
|
+
}
|
|
586
|
+
if (subCommand === 'rail-tracking-reconcile') {
|
|
587
|
+
return runtime.reconcileProjectClickUpTracking({ projectDir: PROJECT_DIR, handoff_id: flags['handoff-id'] });
|
|
588
|
+
}
|
|
589
|
+
if (subCommand === 'rail-tracking-pending') {
|
|
590
|
+
const clickUpRuntimePath = join(PROJECT_DIR, fwDir, '_cli', 'orchestrator', 'clickup-runtime.js');
|
|
591
|
+
const clickUpRuntime = await import(clickUpRuntimePath);
|
|
592
|
+
return clickUpRuntime.preparePendingClickUpProjections({ projectDir: PROJECT_DIR });
|
|
593
|
+
}
|
|
594
|
+
if (subCommand === 'rail-tracking-ack') {
|
|
595
|
+
if (!flags['handoff-id'] || !flags['projection-id'] || !flags.receipt) {
|
|
596
|
+
throw Object.assign(new Error('Missing --handoff-id, --projection-id or --receipt'), { code: 'RAIL_INPUT_REQUIRED' });
|
|
597
|
+
}
|
|
598
|
+
const clickUpRuntimePath = join(PROJECT_DIR, fwDir, '_cli', 'orchestrator', 'clickup-runtime.js');
|
|
599
|
+
const clickUpRuntime = await import(clickUpRuntimePath);
|
|
600
|
+
const receipt = JSON.parse(readFileSync(resolve(flags.receipt), 'utf8'));
|
|
601
|
+
return clickUpRuntime.acknowledgeClickUpProjection({
|
|
602
|
+
projectDir: PROJECT_DIR, handoff_id: flags['handoff-id'],
|
|
603
|
+
projection_id: flags['projection-id'], receipt,
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
if (subCommand === 'rail-tracking-lookup') {
|
|
607
|
+
if (!flags['handoff-id'] || !flags['projection-id'] || !flags.result) {
|
|
608
|
+
throw Object.assign(new Error('Missing --handoff-id, --projection-id or --result'), { code: 'RAIL_INPUT_REQUIRED' });
|
|
609
|
+
}
|
|
610
|
+
const clickUpRuntimePath = join(PROJECT_DIR, fwDir, '_cli', 'orchestrator', 'clickup-runtime.js');
|
|
611
|
+
const clickUpRuntime = await import(clickUpRuntimePath);
|
|
612
|
+
const result = JSON.parse(readFileSync(resolve(flags.result), 'utf8'));
|
|
613
|
+
return clickUpRuntime.resolveClickUpProjectionLookup({
|
|
614
|
+
projectDir: PROJECT_DIR, handoff_id: flags['handoff-id'],
|
|
615
|
+
projection_id: flags['projection-id'], result,
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
} catch (error) {
|
|
619
|
+
return { ok: false, action: 'error', error: error.code || 'RAIL_RUNTIME_FAILED', detail: error.message };
|
|
620
|
+
}
|
|
392
621
|
return { ok: false, action: 'error', error: `Unsupported RAIL command: ${subCommand}` };
|
|
393
622
|
}
|
|
394
623
|
|
|
@@ -486,30 +715,49 @@ async function main() {
|
|
|
486
715
|
pipeline: null,
|
|
487
716
|
team: null,
|
|
488
717
|
error: null,
|
|
718
|
+
active_harness: ACTIVE_HARNESS,
|
|
489
719
|
};
|
|
490
720
|
|
|
721
|
+
const sessionRead = readHookSession(PROJECT_DIR);
|
|
722
|
+
if (!sessionRead.loaded && !sessionRead.missing) {
|
|
723
|
+
result.ok = false;
|
|
724
|
+
result.action = 'error';
|
|
725
|
+
result.error = `unsafe_session: ${sessionRead.error}`;
|
|
726
|
+
console.log(JSON.stringify(result));
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
if (ACTIVE_HARNESS && sessionRead.loaded) {
|
|
730
|
+
const cliMod = await findCliModule(PROJECT_DIR);
|
|
731
|
+
if (!cliMod) {
|
|
732
|
+
result.ok = false;
|
|
733
|
+
result.action = 'error';
|
|
734
|
+
result.error = 'cli_module_not_found';
|
|
735
|
+
console.log(JSON.stringify(result));
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
const activation = await captureOrchestrate(cliMod, 'activate-harness', ['--harness', ACTIVE_HARNESS], PROJECT_DIR);
|
|
739
|
+
if (!activation.activated) {
|
|
740
|
+
result.ok = false;
|
|
741
|
+
result.action = 'error';
|
|
742
|
+
result.error = activation.error || 'harness_activation_failed';
|
|
743
|
+
console.log(JSON.stringify(result));
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
|
|
491
748
|
// -----------------------------------------------------------------------
|
|
492
749
|
// 1. Session state
|
|
493
750
|
// -----------------------------------------------------------------------
|
|
494
|
-
|
|
495
|
-
const
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
project_name: extract('name') || '',
|
|
505
|
-
project_type: extract('type') || 'greenfield',
|
|
506
|
-
state: extract('state') || 'discover',
|
|
507
|
-
current_agent: extract('current_agent') || '',
|
|
508
|
-
workflow: extract('workflow') || 'full',
|
|
509
|
-
};
|
|
510
|
-
}
|
|
511
|
-
} catch {
|
|
512
|
-
result.session = null;
|
|
751
|
+
if (sessionRead.loaded) {
|
|
752
|
+
const session = sessionRead.session;
|
|
753
|
+
result.session = {
|
|
754
|
+
language: session.language || 'en',
|
|
755
|
+
project_name: session.project?.name || '',
|
|
756
|
+
project_type: session.project?.type || session.project_type || 'greenfield',
|
|
757
|
+
state: session.project?.state || session.mode || 'discover',
|
|
758
|
+
current_agent: session.current_agent || '',
|
|
759
|
+
workflow: session.workflow || 'full',
|
|
760
|
+
};
|
|
513
761
|
}
|
|
514
762
|
|
|
515
763
|
// -----------------------------------------------------------------------
|
|
@@ -521,14 +769,20 @@ async function main() {
|
|
|
521
769
|
const cliMod = await findCliModule(PROJECT_DIR);
|
|
522
770
|
if (findCliModule._mismatch) result.version_mismatch = findCliModule._mismatch;
|
|
523
771
|
if (!cliMod) {
|
|
772
|
+
result.ok = false;
|
|
524
773
|
result.action = 'error';
|
|
525
774
|
result.error = 'cli_module_not_found';
|
|
526
775
|
result.reason = 'Bundled CLI missing in .chati.dev/_cli/. Reinstall with: npx chati-dev upgrade';
|
|
527
776
|
} else {
|
|
528
777
|
result.pipeline = await captureOrchestrate(cliMod, 'next', [], PROJECT_DIR);
|
|
529
778
|
result.action = result.pipeline.action;
|
|
779
|
+
if (result.action === 'error' || result.pipeline?.ok === false) {
|
|
780
|
+
result.ok = false;
|
|
781
|
+
result.error = result.pipeline?.error || result.pipeline?.code || 'pipeline_routing_failed';
|
|
782
|
+
}
|
|
530
783
|
}
|
|
531
784
|
} catch (err) {
|
|
785
|
+
result.ok = false;
|
|
532
786
|
result.action = 'error';
|
|
533
787
|
result.error = `pipeline_routing_failed: ${err.message?.slice(0, 200)}`;
|
|
534
788
|
}
|