ralphctl 0.4.3 → 0.4.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/dist/{add-MG26JWBP.mjs → add-DVPVHENV.mjs} +7 -7
- package/dist/{add-ZZYL4BSF.mjs → add-YVXM34RP.mjs} +6 -6
- package/dist/{chunk-LDSG7G2T.mjs → chunk-BSB4EDGR.mjs} +2 -2
- package/dist/{chunk-RQGD5WS6.mjs → chunk-CBMFRQ4Y.mjs} +3 -3
- package/dist/{chunk-Q4AVHUZL.mjs → chunk-FNAAA32W.mjs} +3 -3
- package/dist/{chunk-EGUFQNRB.mjs → chunk-GQ2WFKBN.mjs} +3 -3
- package/dist/{chunk-LCY32RW4.mjs → chunk-OFILN7QL.mjs} +183 -39
- package/dist/{chunk-MDE6KPJQ.mjs → chunk-OGEXYSFS.mjs} +5 -5
- package/dist/{chunk-TDBEEHTS.mjs → chunk-PYZEQ2VK.mjs} +5 -5
- package/dist/{chunk-57UWLHRH.mjs → chunk-VAZ3LJBI.mjs} +12 -1
- package/dist/{chunk-D2HWXEHH.mjs → chunk-WDMLPXOD.mjs} +2 -2
- package/dist/{chunk-WZTY77GY.mjs → chunk-XN2UIHBY.mjs} +10 -3
- package/dist/{chunk-WOMGKKZY.mjs → chunk-XPLYLRIM.mjs} +319 -15
- package/dist/{chunk-2FT37OZX.mjs → chunk-ZLWSPLWI.mjs} +53 -7
- package/dist/cli.mjs +19 -17
- package/dist/create-Z635FQKO.mjs +15 -0
- package/dist/{handle-SYVCFI6Y.mjs → handle-23EFF3BE.mjs} +1 -1
- package/dist/{mount-2ANLHHQE.mjs → mount-H2IH3MWE.mjs} +1455 -1193
- package/dist/{project-JF47ZWMF.mjs → project-DQHF4ISP.mjs} +3 -3
- package/dist/prompts/sprint-feedback.md +4 -0
- package/dist/prompts/task-evaluation.md +44 -2
- package/dist/prompts/task-execution.md +5 -0
- package/dist/{resolver-PG2DZEBX.mjs → resolver-OVPYVW6Q.mjs} +3 -3
- package/dist/{sprint-54DOSIJK.mjs → sprint-4E26AB5F.mjs} +4 -4
- package/dist/start-2WH4BTDB.mjs +19 -0
- package/package.json +1 -1
- package/dist/create-PQK6KKRD.mjs +0 -15
- package/dist/start-2SZTBKGF.mjs +0 -19
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
import {
|
|
3
3
|
addSingleTicketInteractive,
|
|
4
4
|
ticketAddCommand
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-OGEXYSFS.mjs";
|
|
6
|
+
import "./chunk-GQ2WFKBN.mjs";
|
|
7
7
|
import "./chunk-CFUVE2BP.mjs";
|
|
8
8
|
import "./chunk-747KW2RW.mjs";
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-BSB4EDGR.mjs";
|
|
10
|
+
import "./chunk-CBMFRQ4Y.mjs";
|
|
11
|
+
import "./chunk-XN2UIHBY.mjs";
|
|
12
12
|
import "./chunk-IWXBJD2D.mjs";
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-
|
|
13
|
+
import "./chunk-WDMLPXOD.mjs";
|
|
14
|
+
import "./chunk-VAZ3LJBI.mjs";
|
|
15
15
|
export {
|
|
16
16
|
addSingleTicketInteractive,
|
|
17
17
|
ticketAddCommand
|
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
import {
|
|
3
3
|
addCheckScriptToRepository,
|
|
4
4
|
projectAddCommand
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-PYZEQ2VK.mjs";
|
|
6
|
+
import "./chunk-ZLWSPLWI.mjs";
|
|
7
7
|
import "./chunk-CFUVE2BP.mjs";
|
|
8
8
|
import "./chunk-747KW2RW.mjs";
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-BSB4EDGR.mjs";
|
|
10
|
+
import "./chunk-XN2UIHBY.mjs";
|
|
11
11
|
import "./chunk-IWXBJD2D.mjs";
|
|
12
|
-
import "./chunk-
|
|
13
|
-
import "./chunk-
|
|
12
|
+
import "./chunk-WDMLPXOD.mjs";
|
|
13
|
+
import "./chunk-VAZ3LJBI.mjs";
|
|
14
14
|
export {
|
|
15
15
|
addCheckScriptToRepository,
|
|
16
16
|
projectAddCommand
|
|
@@ -8,13 +8,13 @@ import {
|
|
|
8
8
|
readValidatedJson,
|
|
9
9
|
validateProjectPath,
|
|
10
10
|
writeValidatedJson
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-WDMLPXOD.mjs";
|
|
12
12
|
import {
|
|
13
13
|
ParseError,
|
|
14
14
|
ProjectExistsError,
|
|
15
15
|
ProjectNotFoundError,
|
|
16
16
|
ValidationError
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-VAZ3LJBI.mjs";
|
|
18
18
|
|
|
19
19
|
// src/integration/persistence/project.ts
|
|
20
20
|
import { basename, resolve } from "path";
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
getCurrentSprint,
|
|
4
4
|
log
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-XN2UIHBY.mjs";
|
|
6
6
|
import {
|
|
7
7
|
SprintSchema,
|
|
8
8
|
TasksSchema,
|
|
@@ -21,14 +21,14 @@ import {
|
|
|
21
21
|
readValidatedJson,
|
|
22
22
|
removeDir,
|
|
23
23
|
writeValidatedJson
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-WDMLPXOD.mjs";
|
|
25
25
|
import {
|
|
26
26
|
LockError,
|
|
27
27
|
NoCurrentSprintError,
|
|
28
28
|
SprintNotFoundError,
|
|
29
29
|
SprintStatusError,
|
|
30
30
|
StorageError
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-VAZ3LJBI.mjs";
|
|
32
32
|
|
|
33
33
|
// src/integration/persistence/progress.ts
|
|
34
34
|
import { execSync } from "child_process";
|
|
@@ -8,10 +8,10 @@ import {
|
|
|
8
8
|
} from "./chunk-747KW2RW.mjs";
|
|
9
9
|
import {
|
|
10
10
|
listProjects
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-BSB4EDGR.mjs";
|
|
12
12
|
import {
|
|
13
13
|
createSprint
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-CBMFRQ4Y.mjs";
|
|
15
15
|
import {
|
|
16
16
|
emoji,
|
|
17
17
|
field,
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
showNextStep,
|
|
23
23
|
showRandomQuote,
|
|
24
24
|
showSuccess
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-XN2UIHBY.mjs";
|
|
26
26
|
|
|
27
27
|
// src/integration/cli/commands/sprint/create.ts
|
|
28
28
|
async function sprintCreateCommand(options = {}) {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
assertSprintStatus,
|
|
4
4
|
resolveSprintId
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-CBMFRQ4Y.mjs";
|
|
6
6
|
import {
|
|
7
7
|
unwrapOrThrow
|
|
8
8
|
} from "./chunk-IWXBJD2D.mjs";
|
|
@@ -12,11 +12,11 @@ import {
|
|
|
12
12
|
getSprintFilePath,
|
|
13
13
|
readValidatedJson,
|
|
14
14
|
writeValidatedJson
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-WDMLPXOD.mjs";
|
|
16
16
|
import {
|
|
17
17
|
IssueFetchError,
|
|
18
18
|
TicketNotFoundError
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-VAZ3LJBI.mjs";
|
|
20
20
|
|
|
21
21
|
// src/domain/strings.ts
|
|
22
22
|
function truncate(str, max) {
|
|
@@ -13,13 +13,13 @@ import {
|
|
|
13
13
|
buildTicketRefinePrompt,
|
|
14
14
|
getActiveProvider,
|
|
15
15
|
spawnInteractive
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-ZLWSPLWI.mjs";
|
|
17
17
|
import {
|
|
18
18
|
fetchIssueFromUrl,
|
|
19
19
|
formatIssueContext,
|
|
20
20
|
formatTicketDisplay,
|
|
21
21
|
truncate
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-GQ2WFKBN.mjs";
|
|
23
23
|
import {
|
|
24
24
|
EXIT_ERROR,
|
|
25
25
|
EXIT_NO_TASKS,
|
|
@@ -31,15 +31,16 @@ import {
|
|
|
31
31
|
} from "./chunk-747KW2RW.mjs";
|
|
32
32
|
import {
|
|
33
33
|
updateProject
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-BSB4EDGR.mjs";
|
|
35
35
|
import {
|
|
36
36
|
assertSprintStatus,
|
|
37
37
|
closeSprint,
|
|
38
38
|
getSprint,
|
|
39
39
|
resolveSprintId,
|
|
40
40
|
withFileLock
|
|
41
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-CBMFRQ4Y.mjs";
|
|
42
42
|
import {
|
|
43
|
+
isTTY,
|
|
43
44
|
log,
|
|
44
45
|
printHeader,
|
|
45
46
|
renderTable,
|
|
@@ -49,7 +50,7 @@ import {
|
|
|
49
50
|
showSuccess,
|
|
50
51
|
showWarning,
|
|
51
52
|
terminalBell
|
|
52
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-XN2UIHBY.mjs";
|
|
53
54
|
import {
|
|
54
55
|
ensureError,
|
|
55
56
|
unwrapOrThrow,
|
|
@@ -68,7 +69,7 @@ import {
|
|
|
68
69
|
getTasksFilePath,
|
|
69
70
|
readValidatedJson,
|
|
70
71
|
writeValidatedJson
|
|
71
|
-
} from "./chunk-
|
|
72
|
+
} from "./chunk-WDMLPXOD.mjs";
|
|
72
73
|
import {
|
|
73
74
|
BranchPreflightError,
|
|
74
75
|
DependencyCycleError,
|
|
@@ -81,7 +82,7 @@ import {
|
|
|
81
82
|
StepError,
|
|
82
83
|
StorageError,
|
|
83
84
|
TaskNotFoundError
|
|
84
|
-
} from "./chunk-
|
|
85
|
+
} from "./chunk-VAZ3LJBI.mjs";
|
|
85
86
|
|
|
86
87
|
// src/integration/persistence/task.ts
|
|
87
88
|
async function getTasks(sprintId) {
|
|
@@ -386,7 +387,9 @@ function unwrapError(result) {
|
|
|
386
387
|
async function executePipeline(pipeline2, initialContext) {
|
|
387
388
|
let ctx = { ...initialContext };
|
|
388
389
|
const stepResults = [];
|
|
390
|
+
let stepsRun = 0;
|
|
389
391
|
for (const step2 of pipeline2.steps) {
|
|
392
|
+
if (stepsRun > 0 && ctx.abortSignal?.aborted) break;
|
|
390
393
|
const startTime = Date.now();
|
|
391
394
|
try {
|
|
392
395
|
if (step2.hooks?.pre) {
|
|
@@ -439,6 +442,7 @@ async function executePipeline(pipeline2, initialContext) {
|
|
|
439
442
|
status: "success",
|
|
440
443
|
durationMs: Date.now() - startTime
|
|
441
444
|
});
|
|
445
|
+
stepsRun++;
|
|
442
446
|
} catch (err) {
|
|
443
447
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
444
448
|
stepResults.push({
|
|
@@ -1373,14 +1377,16 @@ async function runScheduler(opts, ctx) {
|
|
|
1373
1377
|
const itemKey = opts.itemKey ?? DEFAULT_ITEM_KEY;
|
|
1374
1378
|
const maxConcurrency = opts.strategy.maxConcurrency ?? DEFAULT_MAX_CONCURRENCY;
|
|
1375
1379
|
const mutexKeyFn = opts.strategy.mutexKey ?? identityMutexKey;
|
|
1376
|
-
const
|
|
1380
|
+
const abortSignal = ctx.abortSignal;
|
|
1381
|
+
const services = opts.createServices?.({ abortSignal }) ?? defaultServices();
|
|
1377
1382
|
const state = {
|
|
1378
1383
|
stats: {
|
|
1379
1384
|
completed: 0,
|
|
1380
1385
|
failed: 0,
|
|
1381
1386
|
requeued: 0,
|
|
1382
1387
|
inFlight: 0,
|
|
1383
|
-
pausedRepos: /* @__PURE__ */ new Set()
|
|
1388
|
+
pausedRepos: /* @__PURE__ */ new Set(),
|
|
1389
|
+
cancelled: false
|
|
1384
1390
|
},
|
|
1385
1391
|
retryNowQueue: [],
|
|
1386
1392
|
requeueQueue: [],
|
|
@@ -1394,6 +1400,10 @@ async function runScheduler(opts, ctx) {
|
|
|
1394
1400
|
let concurrencyLimit = -1;
|
|
1395
1401
|
try {
|
|
1396
1402
|
for (; ; ) {
|
|
1403
|
+
if (abortSignal?.aborted) {
|
|
1404
|
+
state.stats.cancelled = true;
|
|
1405
|
+
break;
|
|
1406
|
+
}
|
|
1397
1407
|
if (state.stopRequested) break;
|
|
1398
1408
|
if (state.terminalError) break;
|
|
1399
1409
|
if (opts.strategy.stopWhen?.(state.stats)) break;
|
|
@@ -1433,6 +1443,11 @@ async function runScheduler(opts, ctx) {
|
|
|
1433
1443
|
}
|
|
1434
1444
|
continue;
|
|
1435
1445
|
}
|
|
1446
|
+
if (abortSignal?.aborted) {
|
|
1447
|
+
state.stats.cancelled = true;
|
|
1448
|
+
opts.policies.onSettle?.(item, "failed");
|
|
1449
|
+
break;
|
|
1450
|
+
}
|
|
1436
1451
|
const attempt = (state.attempts.get(item) ?? 0) + 1;
|
|
1437
1452
|
state.attempts.set(item, attempt);
|
|
1438
1453
|
const action = opts.policies.retryPolicy(item, error, attempt);
|
|
@@ -1441,6 +1456,9 @@ async function runScheduler(opts, ctx) {
|
|
|
1441
1456
|
if (state.terminalError && state.shouldDrainOnFail && state.inFlight.size > 0) {
|
|
1442
1457
|
await Promise.allSettled(state.inFlight.values());
|
|
1443
1458
|
}
|
|
1459
|
+
if (state.stats.cancelled && state.inFlight.size > 0) {
|
|
1460
|
+
await Promise.allSettled(state.inFlight.values());
|
|
1461
|
+
}
|
|
1444
1462
|
} finally {
|
|
1445
1463
|
if (opts.disposeServices) {
|
|
1446
1464
|
opts.disposeServices(services);
|
|
@@ -1821,7 +1839,8 @@ ${instructions}`;
|
|
|
1821
1839
|
args,
|
|
1822
1840
|
env: this.aiSession.getSpawnEnv(),
|
|
1823
1841
|
maxRetries: options?.maxRetries,
|
|
1824
|
-
resumeSessionId: options?.resumeSessionId
|
|
1842
|
+
resumeSessionId: options?.resumeSessionId,
|
|
1843
|
+
abortSignal: options?.abortSignal
|
|
1825
1844
|
});
|
|
1826
1845
|
spinner.succeed(`${this.aiSession.getProviderDisplayName()} completed: ${task.name}`);
|
|
1827
1846
|
const ctx = { sprintId: sprint.id, taskId: task.id, projectPath: repoPath };
|
|
@@ -1958,7 +1977,8 @@ ${instructions}`;
|
|
|
1958
1977
|
cwd: repoPath,
|
|
1959
1978
|
args: ["--add-dir", sprintDir],
|
|
1960
1979
|
env: this.aiSession.getSpawnEnv(),
|
|
1961
|
-
maxTurns: options?.maxTurns
|
|
1980
|
+
maxTurns: options?.maxTurns,
|
|
1981
|
+
abortSignal: options?.abortSignal
|
|
1962
1982
|
});
|
|
1963
1983
|
spinner.succeed(`${this.aiSession.getProviderDisplayName()} completed: ${syntheticTask.name}`);
|
|
1964
1984
|
const ctx = { sprintId: sprint.id, taskId: syntheticTask.id, projectPath: repoPath };
|
|
@@ -2304,7 +2324,8 @@ function executeTask(deps) {
|
|
|
2304
2324
|
const result = await deps.useCase.executeOneTask(task, sprint, {
|
|
2305
2325
|
...deps.options,
|
|
2306
2326
|
...resumeSessionId ? { resumeSessionId } : {},
|
|
2307
|
-
...ctx.contractPath ? { contractPath: ctx.contractPath } : {}
|
|
2327
|
+
...ctx.contractPath ? { contractPath: ctx.contractPath } : {},
|
|
2328
|
+
...ctx.abortSignal ? { abortSignal: ctx.abortSignal } : {}
|
|
2308
2329
|
});
|
|
2309
2330
|
if (!result.success) {
|
|
2310
2331
|
return Result.error(new ParseError(`Task not completed: ${result.blocked ?? "Unknown reason"}`));
|
|
@@ -2555,7 +2576,8 @@ var EvaluateTaskUseCase = class {
|
|
|
2555
2576
|
result = await this.aiSession.spawnWithRetry(prompt, {
|
|
2556
2577
|
cwd: repoPath,
|
|
2557
2578
|
args,
|
|
2558
|
-
env: this.aiSession.getSpawnEnv()
|
|
2579
|
+
env: this.aiSession.getSpawnEnv(),
|
|
2580
|
+
abortSignal: options?.abortSignal
|
|
2559
2581
|
});
|
|
2560
2582
|
} catch (err) {
|
|
2561
2583
|
this.logger.warning(
|
|
@@ -2613,7 +2635,8 @@ var EvaluateTaskUseCase = class {
|
|
|
2613
2635
|
cwd: repoPath,
|
|
2614
2636
|
args: ["--add-dir", sprintDir],
|
|
2615
2637
|
env: this.aiSession.getSpawnEnv(),
|
|
2616
|
-
maxTurns: options?.maxTurns
|
|
2638
|
+
maxTurns: options?.maxTurns,
|
|
2639
|
+
abortSignal: options?.abortSignal
|
|
2617
2640
|
});
|
|
2618
2641
|
spinner.succeed(`Fix attempt completed: ${task.name}`);
|
|
2619
2642
|
const signals = this.parser.parseExecutionSignals(result.output);
|
|
@@ -2732,7 +2755,8 @@ function runEvaluatorLoopStep(useCase, options) {
|
|
|
2732
2755
|
const result = await useCase.execute(ctx.sprintId, ctx.taskId, {
|
|
2733
2756
|
iterations: options.iterations,
|
|
2734
2757
|
maxTurns: options.maxTurns,
|
|
2735
|
-
fallbackModel: ctx.generatorModel ?? void 0
|
|
2758
|
+
fallbackModel: ctx.generatorModel ?? void 0,
|
|
2759
|
+
abortSignal: ctx.abortSignal ?? options.abortSignal
|
|
2736
2760
|
});
|
|
2737
2761
|
if (!result.ok) {
|
|
2738
2762
|
return Result.error(result.error);
|
|
@@ -2781,13 +2805,15 @@ function evaluateTask(deps) {
|
|
|
2781
2805
|
},
|
|
2782
2806
|
{
|
|
2783
2807
|
iterations: evalCfg.iterations,
|
|
2784
|
-
maxTurns: deps.options.maxTurns
|
|
2808
|
+
maxTurns: deps.options.maxTurns,
|
|
2809
|
+
abortSignal: ctx.abortSignal
|
|
2785
2810
|
}
|
|
2786
2811
|
);
|
|
2787
2812
|
const innerCtx = {
|
|
2788
2813
|
sprintId: ctx.sprint.id,
|
|
2789
2814
|
taskId: ctx.task.id,
|
|
2790
|
-
generatorModel: ctx.generatorModel ?? null
|
|
2815
|
+
generatorModel: ctx.generatorModel ?? null,
|
|
2816
|
+
abortSignal: ctx.abortSignal
|
|
2791
2817
|
};
|
|
2792
2818
|
let stepNames = [];
|
|
2793
2819
|
try {
|
|
@@ -2933,11 +2959,59 @@ function withStepTrace(signalBus) {
|
|
|
2933
2959
|
});
|
|
2934
2960
|
}
|
|
2935
2961
|
|
|
2962
|
+
// src/business/pipelines/execute/resolve-dirty-tree.ts
|
|
2963
|
+
async function resolveDirtyTree(deps) {
|
|
2964
|
+
const { repoPath, options, prompt, isTTY: isTTY2, logger, external } = deps;
|
|
2965
|
+
let dirty;
|
|
2966
|
+
try {
|
|
2967
|
+
dirty = external.hasUncommittedChanges(repoPath);
|
|
2968
|
+
} catch {
|
|
2969
|
+
return;
|
|
2970
|
+
}
|
|
2971
|
+
if (!dirty) return;
|
|
2972
|
+
if (options.resetOnResume) {
|
|
2973
|
+
logger.warning(`Resetting working tree to HEAD in ${repoPath}...`);
|
|
2974
|
+
external.hardResetWorkingTree(repoPath);
|
|
2975
|
+
logger.success(`Working tree reset in ${repoPath}`);
|
|
2976
|
+
return;
|
|
2977
|
+
}
|
|
2978
|
+
if (options.resumeDirty) {
|
|
2979
|
+
logger.info(`Resuming with existing changes in ${repoPath}`);
|
|
2980
|
+
return;
|
|
2981
|
+
}
|
|
2982
|
+
if (isTTY2) {
|
|
2983
|
+
const keepDirty = await prompt.confirm({
|
|
2984
|
+
message: `Repository at ${repoPath} has uncommitted changes. Resume with existing changes?`,
|
|
2985
|
+
default: true
|
|
2986
|
+
});
|
|
2987
|
+
if (keepDirty) {
|
|
2988
|
+
logger.info(`Resuming with existing changes in ${repoPath}`);
|
|
2989
|
+
return;
|
|
2990
|
+
}
|
|
2991
|
+
const doReset = await prompt.confirm({
|
|
2992
|
+
message: "Reset to latest commit and resume?",
|
|
2993
|
+
default: false
|
|
2994
|
+
});
|
|
2995
|
+
if (doReset) {
|
|
2996
|
+
logger.warning(`Resetting working tree to HEAD in ${repoPath}...`);
|
|
2997
|
+
external.hardResetWorkingTree(repoPath);
|
|
2998
|
+
logger.success(`Working tree reset in ${repoPath}`);
|
|
2999
|
+
return;
|
|
3000
|
+
}
|
|
3001
|
+
throw new StorageError("Aborted: commit, stash, or discard changes before resuming.");
|
|
3002
|
+
}
|
|
3003
|
+
throw new StorageError(
|
|
3004
|
+
`Repository at ${repoPath} has uncommitted changes. Commit or stash them before starting.
|
|
3005
|
+
Hint: pass --resume-dirty to resume with the changes intact, or --reset-on-resume to discard them.`
|
|
3006
|
+
);
|
|
3007
|
+
}
|
|
3008
|
+
|
|
2936
3009
|
// src/business/pipelines/execute.ts
|
|
2937
3010
|
var EXIT_SUCCESS = 0;
|
|
2938
3011
|
var EXIT_ERROR2 = 1;
|
|
2939
3012
|
var EXIT_NO_TASKS2 = 2;
|
|
2940
3013
|
var EXIT_ALL_BLOCKED = 3;
|
|
3014
|
+
var EXIT_INTERRUPTED = 130;
|
|
2941
3015
|
var MAX_CONCURRENCY = 10;
|
|
2942
3016
|
var MAX_BRANCH_RETRIES = 3;
|
|
2943
3017
|
function checkPreconditionsStep(persistence, ui, logger, options) {
|
|
@@ -3111,7 +3185,7 @@ function prepareTasksStep(persistence) {
|
|
|
3111
3185
|
}
|
|
3112
3186
|
});
|
|
3113
3187
|
}
|
|
3114
|
-
function ensureBranchesStep(external, persistence, logger) {
|
|
3188
|
+
function ensureBranchesStep(external, persistence, logger, prompt, isTTY2, options) {
|
|
3115
3189
|
return step("ensure-branches", async (ctx) => {
|
|
3116
3190
|
if (ctx.proceedAfterPrecondition === false || ctx.tasksEmpty) {
|
|
3117
3191
|
const empty = {};
|
|
@@ -3146,17 +3220,14 @@ function ensureBranchesStep(external, persistence, logger) {
|
|
|
3146
3220
|
}
|
|
3147
3221
|
try {
|
|
3148
3222
|
for (const projectPath of uniquePaths) {
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
} catch (err) {
|
|
3158
|
-
if (err instanceof StorageError) return Result.error(err);
|
|
3159
|
-
}
|
|
3223
|
+
await resolveDirtyTree({
|
|
3224
|
+
repoPath: projectPath,
|
|
3225
|
+
options,
|
|
3226
|
+
prompt,
|
|
3227
|
+
isTTY: isTTY2(),
|
|
3228
|
+
logger,
|
|
3229
|
+
external
|
|
3230
|
+
});
|
|
3160
3231
|
}
|
|
3161
3232
|
for (const projectPath of uniquePaths) {
|
|
3162
3233
|
const currentBranch = external.getCurrentBranch(projectPath);
|
|
@@ -3228,6 +3299,7 @@ function executeTasksStep(deps, options) {
|
|
|
3228
3299
|
);
|
|
3229
3300
|
const taskSessionIds = /* @__PURE__ */ new Map();
|
|
3230
3301
|
const failedRepos = /* @__PURE__ */ new Set();
|
|
3302
|
+
const launchedTaskIds = /* @__PURE__ */ new Set();
|
|
3231
3303
|
let firstBlockedReason = null;
|
|
3232
3304
|
const forceSequential = options.session === true || options.step === true;
|
|
3233
3305
|
const uniqueRepoIds = new Set(allTasks.map((t) => t.repoId));
|
|
@@ -3360,6 +3432,7 @@ function executeTasksStep(deps, options) {
|
|
|
3360
3432
|
deps.signalBus.emit({ type: "rate-limit-resumed", timestamp: /* @__PURE__ */ new Date() });
|
|
3361
3433
|
},
|
|
3362
3434
|
onLaunch: (task) => {
|
|
3435
|
+
launchedTaskIds.add(task.id);
|
|
3363
3436
|
const resumeId = taskSessionIds.get(task.id);
|
|
3364
3437
|
const action = resumeId ? "Resuming" : "Starting";
|
|
3365
3438
|
deps.logger.info(`--- ${action} task ${String(task.order)}: ${task.name} ---`);
|
|
@@ -3386,9 +3459,36 @@ function executeTasksStep(deps, options) {
|
|
|
3386
3459
|
failed: 0,
|
|
3387
3460
|
requeued: 0,
|
|
3388
3461
|
inFlight: 0,
|
|
3389
|
-
pausedRepos: /* @__PURE__ */ new Set()
|
|
3462
|
+
pausedRepos: /* @__PURE__ */ new Set(),
|
|
3463
|
+
cancelled: false
|
|
3390
3464
|
};
|
|
3391
3465
|
const stats = schedResult.ok ? schedResult.value.schedulerStats ?? emptyStats : emptyStats;
|
|
3466
|
+
if (stats.cancelled && launchedTaskIds.size > 0) {
|
|
3467
|
+
try {
|
|
3468
|
+
const currentTasks = await deps.persistence.getTasks(sprint.id);
|
|
3469
|
+
const toCancel = currentTasks.filter((t) => launchedTaskIds.has(t.id) && t.status === "in_progress");
|
|
3470
|
+
if (toCancel.length > 0) {
|
|
3471
|
+
const updated = currentTasks.map(
|
|
3472
|
+
(t) => launchedTaskIds.has(t.id) && t.status === "in_progress" ? { ...t, status: "cancelled" } : t
|
|
3473
|
+
);
|
|
3474
|
+
await deps.persistence.saveTasks(updated, sprint.id);
|
|
3475
|
+
for (const t of toCancel) {
|
|
3476
|
+
deps.signalBus.emit({
|
|
3477
|
+
type: "task-finished",
|
|
3478
|
+
sprintId: sprint.id,
|
|
3479
|
+
taskId: t.id,
|
|
3480
|
+
status: "cancelled",
|
|
3481
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
3482
|
+
});
|
|
3483
|
+
}
|
|
3484
|
+
deps.logger.warning(`Cancelled ${String(toCancel.length)} in-progress task(s).`);
|
|
3485
|
+
}
|
|
3486
|
+
} catch (err) {
|
|
3487
|
+
deps.logger.warning(
|
|
3488
|
+
`Failed to flip in-progress tasks to cancelled: ${err instanceof Error ? err.message : String(err)}`
|
|
3489
|
+
);
|
|
3490
|
+
}
|
|
3491
|
+
}
|
|
3392
3492
|
const summary = await buildExecutionSummary({
|
|
3393
3493
|
persistence: deps.persistence,
|
|
3394
3494
|
sprintId: sprint.id,
|
|
@@ -3442,6 +3542,15 @@ async function buildExecutionSummary(args) {
|
|
|
3442
3542
|
const remaining = await persistence.getRemainingTasks(sprintId);
|
|
3443
3543
|
const currentTasks = await persistence.getTasks(sprintId);
|
|
3444
3544
|
const blocked = remaining.filter((t) => isBlocked(t, currentTasks));
|
|
3545
|
+
if (stats.cancelled) {
|
|
3546
|
+
return {
|
|
3547
|
+
completed: stats.completed,
|
|
3548
|
+
remaining: remaining.length,
|
|
3549
|
+
blocked: blocked.length,
|
|
3550
|
+
stopReason: "cancelled",
|
|
3551
|
+
exitCode: EXIT_INTERRUPTED
|
|
3552
|
+
};
|
|
3553
|
+
}
|
|
3445
3554
|
if (failedRepos.size > 0) {
|
|
3446
3555
|
logger.warning(`Repos with failed checks: ${[...failedRepos].join(", ")}`);
|
|
3447
3556
|
}
|
|
@@ -3562,7 +3671,7 @@ function createExecuteSprintPipeline(deps, options = {}) {
|
|
|
3562
3671
|
autoActivateStep(deps.persistence),
|
|
3563
3672
|
assertActiveStep(),
|
|
3564
3673
|
prepareTasksStep(deps.persistence),
|
|
3565
|
-
ensureBranchesStep(deps.external, deps.persistence, deps.logger),
|
|
3674
|
+
ensureBranchesStep(deps.external, deps.persistence, deps.logger, deps.prompt, deps.isTTY, options),
|
|
3566
3675
|
sprintStartCheckStep(deps.external, deps.persistence, deps.logger, options),
|
|
3567
3676
|
executeTasksStep(deps, options),
|
|
3568
3677
|
feedbackLoopStep(deps, options)
|
|
@@ -4013,10 +4122,12 @@ async function importTasksAppend(tasks, sprintId) {
|
|
|
4013
4122
|
name: taskInput.name,
|
|
4014
4123
|
description: taskInput.description,
|
|
4015
4124
|
steps: taskInput.steps ?? [],
|
|
4125
|
+
verificationCriteria: taskInput.verificationCriteria ?? [],
|
|
4016
4126
|
ticketId: taskInput.ticketId,
|
|
4017
4127
|
blockedBy: [],
|
|
4018
4128
|
// Set later
|
|
4019
|
-
repoId: taskInput.repoId
|
|
4129
|
+
repoId: taskInput.repoId,
|
|
4130
|
+
extraDimensions: taskInput.extraDimensions
|
|
4020
4131
|
},
|
|
4021
4132
|
sprintId
|
|
4022
4133
|
);
|
|
@@ -4069,7 +4180,8 @@ async function importTasksReplace(tasks, sprintId) {
|
|
|
4069
4180
|
// Set in second pass
|
|
4070
4181
|
repoId: taskInput.repoId,
|
|
4071
4182
|
evaluated: false,
|
|
4072
|
-
verified: false
|
|
4183
|
+
verified: false,
|
|
4184
|
+
extraDimensions: taskInput.extraDimensions
|
|
4073
4185
|
});
|
|
4074
4186
|
}
|
|
4075
4187
|
for (let i = 0; i < tasks.length; i++) {
|
|
@@ -4142,7 +4254,7 @@ async function runAiSession(workingDir, prompt, ticketTitle) {
|
|
|
4142
4254
|
}
|
|
4143
4255
|
|
|
4144
4256
|
// src/integration/ai/evaluator.ts
|
|
4145
|
-
var DIMENSION_LINE = /\*\*([A-Za-z][A-Za-z0-9]{2,29})\*\*\s*:\s*(PASS|FAIL)
|
|
4257
|
+
var DIMENSION_LINE = /\*\*([A-Za-z][A-Za-z0-9]{2,29})\*\*\s*:\s*(PASS|FAIL)(?:\s*(?:—|-)\s*([^\n]*\S))?/gi;
|
|
4146
4258
|
function parseDimensionScores(output) {
|
|
4147
4259
|
const scores = [];
|
|
4148
4260
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -4151,15 +4263,16 @@ function parseDimensionScores(output) {
|
|
|
4151
4263
|
while ((match = DIMENSION_LINE.exec(output)) !== null) {
|
|
4152
4264
|
const rawName = match[1];
|
|
4153
4265
|
const verdict = match[2];
|
|
4154
|
-
const finding = match[3];
|
|
4155
|
-
if (!rawName || !verdict
|
|
4266
|
+
const finding = (match[3] ?? "").trim();
|
|
4267
|
+
if (!rawName || !verdict) continue;
|
|
4156
4268
|
const name = rawName.toLowerCase();
|
|
4157
4269
|
if (seen.has(name)) continue;
|
|
4158
4270
|
seen.add(name);
|
|
4271
|
+
const hasJustification = finding.length > 0;
|
|
4159
4272
|
scores.push({
|
|
4160
4273
|
dimension: name,
|
|
4161
|
-
passed: verdict.toUpperCase() === "PASS",
|
|
4162
|
-
finding
|
|
4274
|
+
passed: verdict.toUpperCase() === "PASS" && hasJustification,
|
|
4275
|
+
finding
|
|
4163
4276
|
});
|
|
4164
4277
|
}
|
|
4165
4278
|
return scores;
|
|
@@ -4651,6 +4764,25 @@ function hasUncommittedChanges(cwd) {
|
|
|
4651
4764
|
}
|
|
4652
4765
|
return result.stdout.trim().length > 0;
|
|
4653
4766
|
}
|
|
4767
|
+
function hardResetWorkingTree(cwd) {
|
|
4768
|
+
assertSafeCwd(cwd);
|
|
4769
|
+
const reset = spawnSync2("git", ["reset", "--hard", "HEAD"], {
|
|
4770
|
+
cwd,
|
|
4771
|
+
encoding: "utf-8",
|
|
4772
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4773
|
+
});
|
|
4774
|
+
if (reset.status !== 0) {
|
|
4775
|
+
throw new StorageError(`Failed to reset working tree in ${cwd}: ${reset.stderr.trim() || reset.stdout.trim()}`);
|
|
4776
|
+
}
|
|
4777
|
+
const clean = spawnSync2("git", ["clean", "-fd"], {
|
|
4778
|
+
cwd,
|
|
4779
|
+
encoding: "utf-8",
|
|
4780
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4781
|
+
});
|
|
4782
|
+
if (clean.status !== 0) {
|
|
4783
|
+
throw new StorageError(`Failed to clean working tree in ${cwd}: ${clean.stderr.trim() || clean.stdout.trim()}`);
|
|
4784
|
+
}
|
|
4785
|
+
}
|
|
4654
4786
|
function autoCommit(cwd, message) {
|
|
4655
4787
|
assertSafeCwd(cwd);
|
|
4656
4788
|
const add = spawnSync2("git", ["add", "-A"], {
|
|
@@ -4729,6 +4861,9 @@ var DefaultExternalAdapter = class {
|
|
|
4729
4861
|
hasUncommittedChanges(projectPath) {
|
|
4730
4862
|
return hasUncommittedChanges(projectPath);
|
|
4731
4863
|
}
|
|
4864
|
+
hardResetWorkingTree(projectPath) {
|
|
4865
|
+
hardResetWorkingTree(projectPath);
|
|
4866
|
+
}
|
|
4732
4867
|
autoCommit(projectPath, message) {
|
|
4733
4868
|
autoCommit(projectPath, message);
|
|
4734
4869
|
return Promise.resolve();
|
|
@@ -5106,7 +5241,9 @@ function createExecuteSprintPipeline2(shared, options = {}) {
|
|
|
5106
5241
|
signalHandler: shared.signalHandler,
|
|
5107
5242
|
signalBus: shared.signalBus,
|
|
5108
5243
|
createRateLimitCoordinator: shared.createRateLimitCoordinator,
|
|
5109
|
-
processLifecycle: shared.processLifecycle
|
|
5244
|
+
processLifecycle: shared.processLifecycle,
|
|
5245
|
+
prompt: shared.prompt,
|
|
5246
|
+
isTTY
|
|
5110
5247
|
},
|
|
5111
5248
|
options
|
|
5112
5249
|
);
|
|
@@ -5186,10 +5323,17 @@ function parseArgs(args) {
|
|
|
5186
5323
|
options.noEvaluate = true;
|
|
5187
5324
|
} else if (arg === "--no-feedback") {
|
|
5188
5325
|
options.noFeedback = true;
|
|
5326
|
+
} else if (arg === "--resume-dirty") {
|
|
5327
|
+
options.resumeDirty = true;
|
|
5328
|
+
} else if (arg === "--reset-on-resume") {
|
|
5329
|
+
options.resetOnResume = true;
|
|
5189
5330
|
} else if (!arg?.startsWith("-")) {
|
|
5190
5331
|
sprintId = arg;
|
|
5191
5332
|
}
|
|
5192
5333
|
}
|
|
5334
|
+
if (options.resumeDirty && options.resetOnResume) {
|
|
5335
|
+
throw new Error("--resume-dirty and --reset-on-resume are mutually exclusive");
|
|
5336
|
+
}
|
|
5193
5337
|
return { sprintId, options };
|
|
5194
5338
|
}
|
|
5195
5339
|
async function sprintStartCommand(args) {
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
addTicket,
|
|
4
4
|
fetchIssueFromUrl,
|
|
5
5
|
truncate
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-GQ2WFKBN.mjs";
|
|
7
7
|
import {
|
|
8
8
|
EXIT_ERROR,
|
|
9
9
|
exitWithCode
|
|
@@ -13,10 +13,10 @@ import {
|
|
|
13
13
|
} from "./chunk-747KW2RW.mjs";
|
|
14
14
|
import {
|
|
15
15
|
getProjectById
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-BSB4EDGR.mjs";
|
|
17
17
|
import {
|
|
18
18
|
getCurrentSprintOrThrow
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-CBMFRQ4Y.mjs";
|
|
20
20
|
import {
|
|
21
21
|
createSpinner,
|
|
22
22
|
emoji,
|
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
showError,
|
|
30
30
|
showSuccess,
|
|
31
31
|
showWarning
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-XN2UIHBY.mjs";
|
|
33
33
|
import {
|
|
34
34
|
ensureError,
|
|
35
35
|
wrapAsync
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
import {
|
|
38
38
|
IOError,
|
|
39
39
|
SprintStatusError
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-VAZ3LJBI.mjs";
|
|
41
41
|
|
|
42
42
|
// src/integration/cli/commands/ticket/add.ts
|
|
43
43
|
import { Result as Result2 } from "typescript-result";
|