@coralai/sps-cli 0.10.2 → 0.11.1
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 +63 -23
- package/dist/commands/workerDashboard.d.ts.map +1 -1
- package/dist/commands/workerDashboard.js +39 -11
- package/dist/commands/workerDashboard.js.map +1 -1
- package/dist/core/config.d.ts +1 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +1 -0
- package/dist/core/config.js.map +1 -1
- package/dist/core/state.d.ts +10 -0
- package/dist/core/state.d.ts.map +1 -1
- package/dist/core/state.js +5 -0
- package/dist/core/state.js.map +1 -1
- package/dist/engines/CloseoutEngine.d.ts.map +1 -1
- package/dist/engines/CloseoutEngine.js +79 -28
- package/dist/engines/CloseoutEngine.js.map +1 -1
- package/dist/engines/ExecutionEngine.d.ts +5 -0
- package/dist/engines/ExecutionEngine.d.ts.map +1 -1
- package/dist/engines/ExecutionEngine.js +80 -16
- package/dist/engines/ExecutionEngine.js.map +1 -1
- package/dist/engines/MonitorEngine.d.ts.map +1 -1
- package/dist/engines/MonitorEngine.js +6 -1
- package/dist/engines/MonitorEngine.js.map +1 -1
- package/dist/interfaces/WorkerProvider.d.ts +68 -15
- package/dist/interfaces/WorkerProvider.d.ts.map +1 -1
- package/dist/models/types.d.ts +3 -1
- package/dist/models/types.d.ts.map +1 -1
- package/dist/providers/ClaudePrintProvider.d.ts +54 -0
- package/dist/providers/ClaudePrintProvider.d.ts.map +1 -0
- package/dist/providers/ClaudePrintProvider.js +279 -0
- package/dist/providers/ClaudePrintProvider.js.map +1 -0
- package/dist/providers/ClaudeTmuxProvider.d.ts +94 -0
- package/dist/providers/ClaudeTmuxProvider.d.ts.map +1 -0
- package/dist/providers/ClaudeTmuxProvider.js +331 -0
- package/dist/providers/ClaudeTmuxProvider.js.map +1 -0
- package/dist/providers/ClaudeWorkerProvider.d.ts +5 -93
- package/dist/providers/ClaudeWorkerProvider.d.ts.map +1 -1
- package/dist/providers/ClaudeWorkerProvider.js +3 -303
- package/dist/providers/ClaudeWorkerProvider.js.map +1 -1
- package/dist/providers/CodexExecProvider.d.ts +36 -0
- package/dist/providers/CodexExecProvider.d.ts.map +1 -0
- package/dist/providers/CodexExecProvider.js +238 -0
- package/dist/providers/CodexExecProvider.js.map +1 -0
- package/dist/providers/CodexTmuxProvider.d.ts +71 -0
- package/dist/providers/CodexTmuxProvider.d.ts.map +1 -0
- package/dist/providers/CodexTmuxProvider.js +351 -0
- package/dist/providers/CodexTmuxProvider.js.map +1 -0
- package/dist/providers/CodexWorkerProvider.d.ts +5 -70
- package/dist/providers/CodexWorkerProvider.d.ts.map +1 -1
- package/dist/providers/CodexWorkerProvider.js +3 -328
- package/dist/providers/CodexWorkerProvider.js.map +1 -1
- package/dist/providers/outputParser.d.ts +50 -0
- package/dist/providers/outputParser.d.ts.map +1 -0
- package/dist/providers/outputParser.js +219 -0
- package/dist/providers/outputParser.js.map +1 -0
- package/dist/providers/registry.d.ts.map +1 -1
- package/dist/providers/registry.js +18 -6
- package/dist/providers/registry.js.map +1 -1
- package/dist/providers/streamRenderer.d.ts +13 -0
- package/dist/providers/streamRenderer.d.ts.map +1 -0
- package/dist/providers/streamRenderer.js +106 -0
- package/dist/providers/streamRenderer.js.map +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkerProvider.d.ts","sourceRoot":"","sources":["../../src/interfaces/WorkerProvider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"WorkerProvider.d.ts","sourceRoot":"","sources":["../../src/interfaces/WorkerProvider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,iFAAiF;AACjF,MAAM,WAAW,YAAY;IAC3B,yDAAyD;IACzD,GAAG,EAAE,MAAM,CAAC;IACZ,qEAAqE;IACrE,UAAU,EAAE,MAAM,CAAC;IACnB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,qEAAqE;IACrE,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzD;;;;;;;;;;OAUG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAErF;;;;OAIG;IACH,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;QAChC,KAAK,EAAE,OAAO,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC,CAAC;IAEH;;;;OAIG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAExF;;;;OAIG;IACH,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAEpG;;;OAGG;IACH,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEjD;;;;;;;;OAQG;IACH,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAEpG;;;;;;;OAOG;IACH,eAAe,CACb,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAEhC;;;;OAIG;IACH,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAExC;;;;OAIG;IACH,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAErC,iDAAiD;IACjD,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAClD"}
|
package/dist/models/types.d.ts
CHANGED
|
@@ -11,7 +11,9 @@ export interface Card {
|
|
|
11
11
|
export type CardState = 'Planning' | 'Backlog' | 'Todo' | 'Inprogress' | 'QA' | 'Done';
|
|
12
12
|
export type AuxiliaryState = 'BLOCKED' | 'NEEDS-FIX' | 'WAITING-CONFIRMATION' | 'CONFLICT' | 'STALE-RUNTIME';
|
|
13
13
|
/** Worker detection result */
|
|
14
|
-
export type WorkerStatus = 'ALIVE' | 'COMPLETED' | 'NEEDS_INPUT' | 'AUTO_CONFIRM' | 'BLOCKED' | 'DEAD' | 'DEAD_EXCEEDED'
|
|
14
|
+
export type WorkerStatus = 'ALIVE' | 'COMPLETED' | 'NEEDS_INPUT' | 'AUTO_CONFIRM' | 'BLOCKED' | 'DEAD' | 'DEAD_EXCEEDED'
|
|
15
|
+
/** Process exited (code 0) but no artifacts found (no commits/MR). Worker gave up or hit token limit. */
|
|
16
|
+
| 'EXITED_INCOMPLETE';
|
|
15
17
|
/** MR status from repo backend */
|
|
16
18
|
export interface MrStatus {
|
|
17
19
|
exists: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/models/types.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,SAAS,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED,MAAM,MAAM,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,MAAM,GAAG,YAAY,GAAG,IAAI,GAAG,MAAM,CAAC;AAEvF,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,WAAW,GAAG,sBAAsB,GAAG,UAAU,GAAG,eAAe,CAAC;AAE7G,8BAA8B;AAC9B,MAAM,MAAM,YAAY,GACpB,OAAO,GACP,WAAW,GACX,aAAa,GACb,cAAc,GACd,SAAS,GACT,MAAM,GACN,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/models/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { ProjectConfig } from '../core/config.js';
|
|
2
|
+
import type { WorkerProvider, LaunchResult } from '../interfaces/WorkerProvider.js';
|
|
3
|
+
import type { WorkerStatus } from '../models/types.js';
|
|
4
|
+
export declare class ClaudePrintProvider implements WorkerProvider {
|
|
5
|
+
private readonly config;
|
|
6
|
+
constructor(config: ProjectConfig);
|
|
7
|
+
prepareEnv(worktree: string, _seq: string): Promise<void>;
|
|
8
|
+
/**
|
|
9
|
+
* Spawn `claude -p` with the prompt piped via stdin.
|
|
10
|
+
* Returns immediately — the process runs in background.
|
|
11
|
+
*/
|
|
12
|
+
launch(session: string, worktree: string, promptFile: string): Promise<LaunchResult>;
|
|
13
|
+
inspect(session: string): Promise<{
|
|
14
|
+
alive: boolean;
|
|
15
|
+
paneText: string;
|
|
16
|
+
pid?: number;
|
|
17
|
+
exitCode?: number;
|
|
18
|
+
}>;
|
|
19
|
+
detectCompleted(session: string, logDir: string, branch: string): Promise<WorkerStatus>;
|
|
20
|
+
/**
|
|
21
|
+
* Print mode with --dangerously-skip-permissions never waits for input.
|
|
22
|
+
*/
|
|
23
|
+
detectWaiting(_session: string): Promise<{
|
|
24
|
+
waiting: boolean;
|
|
25
|
+
destructive: boolean;
|
|
26
|
+
prompt: string;
|
|
27
|
+
}>;
|
|
28
|
+
detectBlocked(_session: string): Promise<boolean>;
|
|
29
|
+
/**
|
|
30
|
+
* Send a fix prompt by spawning a NEW claude -p with --resume.
|
|
31
|
+
* Returns a new LaunchResult with the new process info.
|
|
32
|
+
*/
|
|
33
|
+
sendFix(session: string, fixPrompt: string, resumeSessionId?: string): Promise<LaunchResult>;
|
|
34
|
+
resolveConflict(session: string, worktree: string, branch: string, resumeSessionId?: string): Promise<LaunchResult>;
|
|
35
|
+
/**
|
|
36
|
+
* No-op in print mode — process already exited.
|
|
37
|
+
*/
|
|
38
|
+
release(_session: string): Promise<void>;
|
|
39
|
+
stop(session: string): Promise<void>;
|
|
40
|
+
collectSummary(session: string): Promise<string>;
|
|
41
|
+
private spawnClaude;
|
|
42
|
+
/**
|
|
43
|
+
* Try to extract session ID from output file after process starts.
|
|
44
|
+
* Called asynchronously — updates the tracking entry.
|
|
45
|
+
*/
|
|
46
|
+
extractSessionIdAsync(session: string): Promise<string | null>;
|
|
47
|
+
/**
|
|
48
|
+
* Look up worker slot info from state.json by tmuxSession name.
|
|
49
|
+
* Used as fallback when activeProcesses map is empty (SPS restarted).
|
|
50
|
+
*/
|
|
51
|
+
private findSlotBySession;
|
|
52
|
+
private log;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=ClaudePrintProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ClaudePrintProvider.d.ts","sourceRoot":"","sources":["../../src/providers/ClaudePrintProvider.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AA0BvD,qBAAa,mBAAoB,YAAW,cAAc;IACxD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;gBAE3B,MAAM,EAAE,aAAa;IAI3B,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAa/D;;;OAGG;IACG,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAcpF,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;QACtC,KAAK,EAAE,OAAO,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IA8BI,eAAe,CACnB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,YAAY,CAAC;IAkExB;;OAEG;IACG,aAAa,CACjB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAIhE,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIvD;;;OAGG;IACG,OAAO,CACX,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,YAAY,CAAC;IAalB,eAAe,CACnB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,YAAY,CAAC;IAmBxB;;OAEG;IACG,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKxC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWpC,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAQtD,OAAO,CAAC,WAAW;IAkEnB;;;OAGG;IACG,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAQpE;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAgCzB,OAAO,CAAC,GAAG;CAGZ"}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ClaudePrintProvider — one-shot print-mode worker using `claude -p`.
|
|
3
|
+
*
|
|
4
|
+
* Eliminates all tmux interaction. Process lifecycle = task lifecycle.
|
|
5
|
+
* Uses --resume <sessionId> for context continuity across tasks.
|
|
6
|
+
*/
|
|
7
|
+
import { spawn, execFileSync } from 'node:child_process';
|
|
8
|
+
import { existsSync, readFileSync, createWriteStream } from 'node:fs';
|
|
9
|
+
import { resolve } from 'node:path';
|
|
10
|
+
import { tailFile, parseClaudeSessionId, isProcessAlive, killProcessGroup, extractLastAssistantText, branchCommitsAhead, branchPushed, } from './outputParser.js';
|
|
11
|
+
import { readState } from '../core/state.js';
|
|
12
|
+
/** Completion indicators in the final assistant message. */
|
|
13
|
+
const COMPLETION_KEYWORDS = /\b(done|完成|全部完成|MR created|merge request|已提交|已推送)\b|🎉/i;
|
|
14
|
+
/**
|
|
15
|
+
* Track spawned child processes by session name.
|
|
16
|
+
* Needed because inspect()/detectCompleted() receive session name, not PID.
|
|
17
|
+
*/
|
|
18
|
+
const activeProcesses = new Map();
|
|
19
|
+
export class ClaudePrintProvider {
|
|
20
|
+
config;
|
|
21
|
+
constructor(config) {
|
|
22
|
+
this.config = config;
|
|
23
|
+
}
|
|
24
|
+
async prepareEnv(worktree, _seq) {
|
|
25
|
+
if (!existsSync(worktree)) {
|
|
26
|
+
throw new Error(`Worktree directory does not exist: ${worktree}`);
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
execFileSync('git', ['-C', worktree, 'rev-parse', '--is-inside-work-tree'], {
|
|
30
|
+
encoding: 'utf-8', timeout: 5_000, stdio: ['ignore', 'pipe', 'pipe'],
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
throw new Error(`Directory is not a git worktree: ${worktree}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Spawn `claude -p` with the prompt piped via stdin.
|
|
39
|
+
* Returns immediately — the process runs in background.
|
|
40
|
+
*/
|
|
41
|
+
async launch(session, worktree, promptFile) {
|
|
42
|
+
if (!existsSync(promptFile)) {
|
|
43
|
+
throw new Error(`Prompt file does not exist: ${promptFile}`);
|
|
44
|
+
}
|
|
45
|
+
const prompt = readFileSync(promptFile, 'utf-8').trim();
|
|
46
|
+
const outputFile = resolve(this.config.raw.LOGS_DIR || `/tmp/sps-${this.config.PROJECT_NAME}`, `${session}-${Date.now()}.jsonl`);
|
|
47
|
+
return this.spawnClaude(session, worktree, prompt, outputFile);
|
|
48
|
+
}
|
|
49
|
+
async inspect(session) {
|
|
50
|
+
// Try in-memory tracking first
|
|
51
|
+
const proc = activeProcesses.get(session);
|
|
52
|
+
if (proc) {
|
|
53
|
+
const pid = proc.child.pid ?? 0;
|
|
54
|
+
const alive = pid > 0 && isProcessAlive(pid);
|
|
55
|
+
return {
|
|
56
|
+
alive,
|
|
57
|
+
paneText: tailFile(proc.outputFile, 50),
|
|
58
|
+
pid,
|
|
59
|
+
exitCode: proc.exitCode ?? undefined,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
// Fallback: recover from state.json (SPS process may have restarted)
|
|
63
|
+
const slotInfo = this.findSlotBySession(session);
|
|
64
|
+
if (slotInfo?.pid) {
|
|
65
|
+
const alive = isProcessAlive(slotInfo.pid);
|
|
66
|
+
const paneText = slotInfo.outputFile ? tailFile(slotInfo.outputFile, 50) : '';
|
|
67
|
+
return {
|
|
68
|
+
alive,
|
|
69
|
+
paneText,
|
|
70
|
+
pid: slotInfo.pid,
|
|
71
|
+
exitCode: alive ? undefined : (slotInfo.exitCode ?? undefined),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
return { alive: false, paneText: '', pid: undefined, exitCode: undefined };
|
|
75
|
+
}
|
|
76
|
+
async detectCompleted(session, logDir, branch) {
|
|
77
|
+
// Priority 1: marker file
|
|
78
|
+
const markerPath = `${logDir}/task_completed`;
|
|
79
|
+
if (existsSync(markerPath)) {
|
|
80
|
+
return 'COMPLETED';
|
|
81
|
+
}
|
|
82
|
+
// Resolve process info — in-memory or state.json fallback
|
|
83
|
+
const proc = activeProcesses.get(session);
|
|
84
|
+
const slotInfo = !proc ? this.findSlotBySession(session) : null;
|
|
85
|
+
const pid = proc?.child.pid ?? slotInfo?.pid ?? 0;
|
|
86
|
+
const outputFile = proc?.outputFile ?? slotInfo?.outputFile ?? null;
|
|
87
|
+
const exitCode = proc?.exitCode ?? slotInfo?.exitCode ?? null;
|
|
88
|
+
if (!pid && !proc) {
|
|
89
|
+
// No process tracked at all — treat as dead
|
|
90
|
+
return 'DEAD';
|
|
91
|
+
}
|
|
92
|
+
// Priority 2: process still running
|
|
93
|
+
if (pid > 0 && isProcessAlive(pid)) {
|
|
94
|
+
return 'ALIVE';
|
|
95
|
+
}
|
|
96
|
+
// ── Process has exited — verify with artifacts ──
|
|
97
|
+
// The key question: did the worker actually complete the task?
|
|
98
|
+
// Exit code 0 alone is NOT enough — worker may have:
|
|
99
|
+
// - Hit token/budget limit and exited gracefully
|
|
100
|
+
// - Said "I can't do this" and exited
|
|
101
|
+
// - Completed coding but not pushed / not created MR
|
|
102
|
+
// Check git artifacts: branch pushed with commits ahead of base
|
|
103
|
+
const worktree = slotInfo?.worktree ?? null;
|
|
104
|
+
const baseBranch = this.config.GITLAB_MERGE_BRANCH;
|
|
105
|
+
if (worktree && branch) {
|
|
106
|
+
const pushed = branchPushed(worktree, branch);
|
|
107
|
+
const commitsAhead = pushed
|
|
108
|
+
? branchCommitsAhead(worktree, branch, baseBranch)
|
|
109
|
+
: 0;
|
|
110
|
+
if (pushed && commitsAhead > 0) {
|
|
111
|
+
// Branch has been pushed with new commits — worker did real work.
|
|
112
|
+
// ExecutionEngine will additionally check MR existence before moving to QA.
|
|
113
|
+
return 'COMPLETED';
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Check output text for completion keywords as secondary signal
|
|
117
|
+
if (outputFile) {
|
|
118
|
+
const lastText = extractLastAssistantText(outputFile);
|
|
119
|
+
if (COMPLETION_KEYWORDS.test(lastText)) {
|
|
120
|
+
return 'COMPLETED';
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Process exited but no artifacts found
|
|
124
|
+
if (exitCode === 0) {
|
|
125
|
+
// Graceful exit but nothing to show for it
|
|
126
|
+
return 'EXITED_INCOMPLETE';
|
|
127
|
+
}
|
|
128
|
+
// Non-zero exit — worker crashed
|
|
129
|
+
return 'DEAD';
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Print mode with --dangerously-skip-permissions never waits for input.
|
|
133
|
+
*/
|
|
134
|
+
async detectWaiting(_session) {
|
|
135
|
+
return { waiting: false, destructive: false, prompt: '' };
|
|
136
|
+
}
|
|
137
|
+
async detectBlocked(_session) {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Send a fix prompt by spawning a NEW claude -p with --resume.
|
|
142
|
+
* Returns a new LaunchResult with the new process info.
|
|
143
|
+
*/
|
|
144
|
+
async sendFix(session, fixPrompt, resumeSessionId) {
|
|
145
|
+
// Find worktree from state.json
|
|
146
|
+
const slotInfo = this.findSlotBySession(session);
|
|
147
|
+
const worktree = slotInfo?.worktree || '.';
|
|
148
|
+
const outputFile = resolve(this.config.raw.LOGS_DIR || `/tmp/sps-${this.config.PROJECT_NAME}`, `${session}-fix-${Date.now()}.jsonl`);
|
|
149
|
+
return this.spawnClaude(session, worktree, fixPrompt, outputFile, resumeSessionId);
|
|
150
|
+
}
|
|
151
|
+
async resolveConflict(session, worktree, branch, resumeSessionId) {
|
|
152
|
+
const instruction = [
|
|
153
|
+
`There is a merge conflict on branch ${branch}.`,
|
|
154
|
+
`Working directory: ${worktree}`,
|
|
155
|
+
'Please resolve the conflict:',
|
|
156
|
+
`1. Run: git fetch origin && git rebase origin/${this.config.GITLAB_MERGE_BRANCH}`,
|
|
157
|
+
'2. Resolve any conflicts in the affected files',
|
|
158
|
+
'3. Run: git add . && git rebase --continue',
|
|
159
|
+
'4. Run: git push --force-with-lease',
|
|
160
|
+
].join('\n');
|
|
161
|
+
const outputFile = resolve(this.config.raw.LOGS_DIR || `/tmp/sps-${this.config.PROJECT_NAME}`, `${session}-conflict-${Date.now()}.jsonl`);
|
|
162
|
+
return this.spawnClaude(session, worktree, instruction, outputFile, resumeSessionId);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* No-op in print mode — process already exited.
|
|
166
|
+
*/
|
|
167
|
+
async release(_session) {
|
|
168
|
+
// Clean up tracking entry
|
|
169
|
+
activeProcesses.delete(_session);
|
|
170
|
+
}
|
|
171
|
+
async stop(session) {
|
|
172
|
+
const proc = activeProcesses.get(session);
|
|
173
|
+
if (!proc)
|
|
174
|
+
return;
|
|
175
|
+
const pid = proc.child.pid;
|
|
176
|
+
if (pid && isProcessAlive(pid)) {
|
|
177
|
+
await killProcessGroup(pid);
|
|
178
|
+
}
|
|
179
|
+
activeProcesses.delete(session);
|
|
180
|
+
}
|
|
181
|
+
async collectSummary(session) {
|
|
182
|
+
const proc = activeProcesses.get(session);
|
|
183
|
+
if (!proc)
|
|
184
|
+
return '';
|
|
185
|
+
return tailFile(proc.outputFile, 100);
|
|
186
|
+
}
|
|
187
|
+
// ─── Internal ────────────────────────────────────────────────────
|
|
188
|
+
spawnClaude(session, worktree, prompt, outputFile, resumeSessionId) {
|
|
189
|
+
const args = [
|
|
190
|
+
'-p', // print mode: reads prompt from stdin when no prompt arg given
|
|
191
|
+
'--output-format', 'stream-json',
|
|
192
|
+
'--dangerously-skip-permissions',
|
|
193
|
+
];
|
|
194
|
+
if (resumeSessionId) {
|
|
195
|
+
args.push('--resume', resumeSessionId);
|
|
196
|
+
}
|
|
197
|
+
// Ensure output directory exists
|
|
198
|
+
const { mkdirSync } = require('node:fs');
|
|
199
|
+
const { dirname } = require('node:path');
|
|
200
|
+
try {
|
|
201
|
+
mkdirSync(dirname(outputFile), { recursive: true });
|
|
202
|
+
}
|
|
203
|
+
catch { /* exists */ }
|
|
204
|
+
const outStream = createWriteStream(outputFile, { flags: 'a' });
|
|
205
|
+
const child = spawn('claude', args, {
|
|
206
|
+
cwd: worktree,
|
|
207
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
208
|
+
detached: true, // own process group for killProcessGroup()
|
|
209
|
+
env: { ...process.env },
|
|
210
|
+
});
|
|
211
|
+
// Pipe stdout (stream-json) to output file
|
|
212
|
+
child.stdout?.pipe(outStream);
|
|
213
|
+
// Also capture stderr to output file
|
|
214
|
+
child.stderr?.on('data', (chunk) => {
|
|
215
|
+
outStream.write(chunk);
|
|
216
|
+
});
|
|
217
|
+
// Write prompt to stdin and close
|
|
218
|
+
child.stdin?.write(prompt);
|
|
219
|
+
child.stdin?.end();
|
|
220
|
+
// Track process
|
|
221
|
+
const entry = { child, outputFile, exitCode: null };
|
|
222
|
+
activeProcesses.set(session, entry);
|
|
223
|
+
child.on('exit', (code) => {
|
|
224
|
+
entry.exitCode = code ?? 1;
|
|
225
|
+
outStream.end();
|
|
226
|
+
});
|
|
227
|
+
// Don't let this child block the parent from exiting
|
|
228
|
+
child.unref();
|
|
229
|
+
// Parse session ID from output once available (async)
|
|
230
|
+
const sessionId = resumeSessionId || undefined;
|
|
231
|
+
this.log(`Spawned claude -p for ${session} (pid=${child.pid}), output=${outputFile}`);
|
|
232
|
+
return {
|
|
233
|
+
pid: child.pid ?? 0,
|
|
234
|
+
outputFile,
|
|
235
|
+
sessionId,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Try to extract session ID from output file after process starts.
|
|
240
|
+
* Called asynchronously — updates the tracking entry.
|
|
241
|
+
*/
|
|
242
|
+
async extractSessionIdAsync(session) {
|
|
243
|
+
// Wait a bit for output to be written
|
|
244
|
+
await new Promise((r) => setTimeout(r, 3_000));
|
|
245
|
+
const proc = activeProcesses.get(session);
|
|
246
|
+
if (!proc)
|
|
247
|
+
return null;
|
|
248
|
+
return parseClaudeSessionId(proc.outputFile);
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Look up worker slot info from state.json by tmuxSession name.
|
|
252
|
+
* Used as fallback when activeProcesses map is empty (SPS restarted).
|
|
253
|
+
*/
|
|
254
|
+
findSlotBySession(session) {
|
|
255
|
+
try {
|
|
256
|
+
const stateFile = resolve(process.env.HOME || '~', '.projects', this.config.PROJECT_NAME, 'runtime', 'state.json');
|
|
257
|
+
if (!existsSync(stateFile))
|
|
258
|
+
return null;
|
|
259
|
+
const state = readState(stateFile, this.config.MAX_CONCURRENT_WORKERS);
|
|
260
|
+
for (const slot of Object.values(state.workers)) {
|
|
261
|
+
if (slot.tmuxSession === session && slot.mode === 'print') {
|
|
262
|
+
return {
|
|
263
|
+
pid: slot.pid ?? null,
|
|
264
|
+
outputFile: slot.outputFile ?? null,
|
|
265
|
+
exitCode: slot.exitCode ?? null,
|
|
266
|
+
sessionId: slot.sessionId ?? null,
|
|
267
|
+
worktree: slot.worktree ?? null,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
catch { /* state read error */ }
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
log(msg) {
|
|
276
|
+
process.stderr.write(`[claude-print] ${msg}\n`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
//# sourceMappingURL=ClaudePrintProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ClaudePrintProvider.js","sourceRoot":"","sources":["../../src/providers/ClaudePrintProvider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { ProjectConfig } from '../core/config.js';
|
|
2
|
+
import type { WorkerProvider, LaunchResult } from '../interfaces/WorkerProvider.js';
|
|
3
|
+
import type { WorkerStatus } from '../models/types.js';
|
|
4
|
+
export declare class ClaudeTmuxProvider implements WorkerProvider {
|
|
5
|
+
private readonly config;
|
|
6
|
+
constructor(config: ProjectConfig);
|
|
7
|
+
/**
|
|
8
|
+
* Ensure worktree directory exists and is clean.
|
|
9
|
+
* This is largely a no-op when the worktree is already prepared.
|
|
10
|
+
*/
|
|
11
|
+
prepareEnv(worktree: string, _seq: string): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Launch a Claude Code worker inside a tmux session.
|
|
14
|
+
*
|
|
15
|
+
* Session reuse strategy (WORKER_SESSION_REUSE=true):
|
|
16
|
+
* 1. Session exists + Claude running → reuse: /clear + cd worktree (keep context hot)
|
|
17
|
+
* 2. Session exists + Claude not running → reuse session: cd + start claude
|
|
18
|
+
* 3. No session → create new session + start claude
|
|
19
|
+
*/
|
|
20
|
+
launch(session: string, worktree: string, promptFile: string): Promise<LaunchResult>;
|
|
21
|
+
private log;
|
|
22
|
+
/**
|
|
23
|
+
* Poll tmux pane text until Claude's ready prompt appears.
|
|
24
|
+
* Default timeout: 30 seconds, poll interval: 2 seconds.
|
|
25
|
+
*/
|
|
26
|
+
waitReady(session: string, timeoutMs?: number): Promise<boolean>;
|
|
27
|
+
/**
|
|
28
|
+
* Send a task prompt file to the Claude session.
|
|
29
|
+
*/
|
|
30
|
+
sendTask(session: string, promptFile: string): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Inspect a tmux session: check if alive and capture pane text.
|
|
33
|
+
*/
|
|
34
|
+
inspect(session: string): Promise<{
|
|
35
|
+
alive: boolean;
|
|
36
|
+
paneText: string;
|
|
37
|
+
}>;
|
|
38
|
+
/**
|
|
39
|
+
* Multi-layer completion detection chain.
|
|
40
|
+
*
|
|
41
|
+
* Priority order:
|
|
42
|
+
* 1. task_completed marker file in logDir
|
|
43
|
+
* 2. Waiting for confirmation prompts (delegates to detectWaiting)
|
|
44
|
+
* 3. Completion keywords in pane text
|
|
45
|
+
* 4. MR exists on GitLab (skipped — returns ALIVE)
|
|
46
|
+
* 5. tmux session alive → ALIVE
|
|
47
|
+
* 6. Session dead + restart limit exceeded → DEAD_EXCEEDED
|
|
48
|
+
*/
|
|
49
|
+
detectCompleted(session: string, logDir: string, _branch: string): Promise<WorkerStatus>;
|
|
50
|
+
/**
|
|
51
|
+
* Detect whether the worker is waiting for user confirmation.
|
|
52
|
+
* Returns whether the prompt is destructive (delete/remove/drop etc.).
|
|
53
|
+
*/
|
|
54
|
+
detectWaiting(session: string): Promise<{
|
|
55
|
+
waiting: boolean;
|
|
56
|
+
destructive: boolean;
|
|
57
|
+
prompt: string;
|
|
58
|
+
}>;
|
|
59
|
+
/**
|
|
60
|
+
* Check pane text for blocked indicators (errors, stuck states).
|
|
61
|
+
*/
|
|
62
|
+
detectBlocked(session: string): Promise<boolean>;
|
|
63
|
+
/**
|
|
64
|
+
* Send a fix prompt to the Claude session (e.g. after CI failure).
|
|
65
|
+
*/
|
|
66
|
+
sendFix(session: string, fixPrompt: string, _resumeSessionId?: string): Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* Send conflict resolution instructions to the Claude session.
|
|
69
|
+
*/
|
|
70
|
+
resolveConflict(session: string, worktree: string, branch: string, _resumeSessionId?: string): Promise<void>;
|
|
71
|
+
/**
|
|
72
|
+
* Release a worker session after task completion.
|
|
73
|
+
*
|
|
74
|
+
* WORKER_SESSION_REUSE=true: do nothing — keep Claude running so the
|
|
75
|
+
* next task can hot-reuse the session via /clear + cd (preserves
|
|
76
|
+
* session state, env vars, loaded MCP servers, etc.).
|
|
77
|
+
*
|
|
78
|
+
* WORKER_SESSION_REUSE=false: exit Claude but keep tmux session alive
|
|
79
|
+
* (next launch will restart Claude in the existing session).
|
|
80
|
+
*/
|
|
81
|
+
release(session: string): Promise<void>;
|
|
82
|
+
/**
|
|
83
|
+
* Force-stop a worker session (error recovery, cleanup).
|
|
84
|
+
* Always exits Claude and kills the tmux session.
|
|
85
|
+
*/
|
|
86
|
+
stop(session: string): Promise<void>;
|
|
87
|
+
/**
|
|
88
|
+
* Capture the last 100 lines of pane text as a summary.
|
|
89
|
+
*/
|
|
90
|
+
collectSummary(session: string): Promise<string>;
|
|
91
|
+
/** Helper: sleep for the given milliseconds. */
|
|
92
|
+
private sleep;
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=ClaudeTmuxProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ClaudeTmuxProvider.d.ts","sourceRoot":"","sources":["../../src/providers/ClaudeTmuxProvider.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AA6DvD,qBAAa,kBAAmB,YAAW,cAAc;IACvD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;gBAE3B,MAAM,EAAE,aAAa;IAIjC;;;OAGG;IACG,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB/D;;;;;;;OAOG;IACG,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAqC1F,OAAO,CAAC,GAAG,CAAwE;IAEnF;;;OAGG;IACG,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC;IA4BtE;;OAEG;IACG,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoClE;;OAEG;IACG,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAM7E;;;;;;;;;;OAUG;IACG,eAAe,CACnB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,YAAY,CAAC;IAoCxB;;;OAGG;IACG,aAAa,CACjB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAiBtE;;OAEG;IACG,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKtD;;OAEG;IACG,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM3F;;OAEG;IACG,eAAe,CACnB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,gBAAgB,CAAC,EAAE,MAAM,GACxB,OAAO,CAAC,IAAI,CAAC;IAchB;;;;;;;;;OASG;IACG,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAa7C;;;OAGG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW1C;;OAEG;IACG,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAItD,gDAAgD;IAChD,OAAO,CAAC,KAAK;CAGd"}
|