codeharness 0.29.4 → 0.30.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.
|
@@ -2895,7 +2895,7 @@ function generateDockerfileTemplate(projectDir, stackOrDetections) {
|
|
|
2895
2895
|
}
|
|
2896
2896
|
|
|
2897
2897
|
// src/modules/infra/init-project.ts
|
|
2898
|
-
var HARNESS_VERSION = true ? "0.
|
|
2898
|
+
var HARNESS_VERSION = true ? "0.30.1" : "0.0.0-dev";
|
|
2899
2899
|
function failResult(opts, error) {
|
|
2900
2900
|
return {
|
|
2901
2901
|
status: "fail",
|
package/dist/index.js
CHANGED
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
validateDockerfile,
|
|
41
41
|
warn,
|
|
42
42
|
writeState
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-QLY7NJIB.js";
|
|
44
44
|
|
|
45
45
|
// src/index.ts
|
|
46
46
|
import { Command } from "commander";
|
|
@@ -3882,75 +3882,165 @@ async function executeWorkflow(config) {
|
|
|
3882
3882
|
let halted = false;
|
|
3883
3883
|
let lastOutputContract = null;
|
|
3884
3884
|
let accumulatedCostUsd = 0;
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
}
|
|
3894
|
-
if (isLoopBlock(step)) {
|
|
3895
|
-
const loopResult = await executeLoopBlock(step, state, config, workItems, lastOutputContract);
|
|
3896
|
-
state = loopResult.state;
|
|
3897
|
-
errors.push(...loopResult.errors);
|
|
3898
|
-
tasksCompleted += loopResult.tasksCompleted;
|
|
3899
|
-
lastOutputContract = loopResult.lastContract;
|
|
3900
|
-
for (const item of workItems) {
|
|
3901
|
-
processedStories.add(item.key);
|
|
3885
|
+
if (config.storyPipeline) {
|
|
3886
|
+
const preTasks = [];
|
|
3887
|
+
let loopIdx = -1;
|
|
3888
|
+
for (let i = 0; i < config.workflow.storyFlow.length; i++) {
|
|
3889
|
+
const s = config.workflow.storyFlow[i];
|
|
3890
|
+
if (isLoopBlock(s)) {
|
|
3891
|
+
loopIdx = i;
|
|
3892
|
+
break;
|
|
3902
3893
|
}
|
|
3903
|
-
if (
|
|
3894
|
+
if (typeof s === "string") preTasks.push(s);
|
|
3895
|
+
}
|
|
3896
|
+
for (const item of workItems) {
|
|
3897
|
+
if (halted || config.abortSignal?.aborted) {
|
|
3898
|
+
if (config.abortSignal?.aborted) {
|
|
3899
|
+
info("Execution interrupted \u2014 saving state");
|
|
3900
|
+
state = { ...state, phase: "interrupted" };
|
|
3901
|
+
writeWorkflowState(state, projectDir);
|
|
3902
|
+
}
|
|
3904
3903
|
halted = true;
|
|
3904
|
+
break;
|
|
3905
3905
|
}
|
|
3906
|
-
|
|
3907
|
-
|
|
3906
|
+
processedStories.add(item.key);
|
|
3907
|
+
for (const taskName of preTasks) {
|
|
3908
|
+
if (halted || config.abortSignal?.aborted) {
|
|
3909
|
+
halted = true;
|
|
3910
|
+
break;
|
|
3911
|
+
}
|
|
3912
|
+
const task = config.workflow.tasks[taskName];
|
|
3913
|
+
if (!task || task.agent === null) continue;
|
|
3914
|
+
const definition = config.agents[task.agent];
|
|
3915
|
+
if (!definition) {
|
|
3916
|
+
warn(`workflow-engine: agent "${task.agent}" not found for "${taskName}"`);
|
|
3917
|
+
continue;
|
|
3918
|
+
}
|
|
3919
|
+
if (isTaskCompleted(state, taskName, item.key)) continue;
|
|
3920
|
+
try {
|
|
3921
|
+
const dr = await dispatchTaskWithResult(task, taskName, item.key, definition, state, config, void 0, lastOutputContract ?? void 0);
|
|
3922
|
+
state = dr.updatedState;
|
|
3923
|
+
lastOutputContract = dr.contract;
|
|
3924
|
+
propagateVerifyFlags(taskName, dr.contract, projectDir);
|
|
3925
|
+
accumulatedCostUsd += dr.contract?.cost_usd ?? 0;
|
|
3926
|
+
tasksCompleted++;
|
|
3927
|
+
} catch (err) {
|
|
3928
|
+
const engineError = handleDispatchError(err, taskName, item.key);
|
|
3929
|
+
errors.push(engineError);
|
|
3930
|
+
if (config.onEvent) {
|
|
3931
|
+
config.onEvent({ type: "dispatch-error", taskName, storyKey: item.key, error: { code: engineError.code, message: engineError.message } });
|
|
3932
|
+
} else {
|
|
3933
|
+
warn(`[${taskName}] ${item.key} \u2014 ERROR: [${engineError.code}] ${engineError.message}`);
|
|
3934
|
+
}
|
|
3935
|
+
state = recordErrorInState(state, taskName, item.key, engineError);
|
|
3936
|
+
writeWorkflowState(state, projectDir);
|
|
3937
|
+
if (err instanceof DispatchError && HALT_ERROR_CODES.has(err.code)) {
|
|
3938
|
+
halted = true;
|
|
3939
|
+
}
|
|
3940
|
+
break;
|
|
3941
|
+
}
|
|
3908
3942
|
}
|
|
3909
|
-
continue;
|
|
3910
3943
|
}
|
|
3911
|
-
const
|
|
3912
|
-
const
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3944
|
+
const remaining = loopIdx >= 0 ? config.workflow.storyFlow.slice(loopIdx) : [];
|
|
3945
|
+
for (const step of remaining) {
|
|
3946
|
+
if (halted || config.abortSignal?.aborted) {
|
|
3947
|
+
halted = true;
|
|
3948
|
+
break;
|
|
3949
|
+
}
|
|
3950
|
+
if (isLoopBlock(step)) {
|
|
3951
|
+
const loopResult = await executeLoopBlock(step, state, config, workItems, lastOutputContract);
|
|
3952
|
+
state = loopResult.state;
|
|
3953
|
+
errors.push(...loopResult.errors);
|
|
3954
|
+
tasksCompleted += loopResult.tasksCompleted;
|
|
3955
|
+
lastOutputContract = loopResult.lastContract;
|
|
3956
|
+
for (const item of workItems) processedStories.add(item.key);
|
|
3957
|
+
if (loopResult.halted || state.phase === "max-iterations" || state.phase === "circuit-breaker") {
|
|
3958
|
+
halted = true;
|
|
3922
3959
|
}
|
|
3960
|
+
continue;
|
|
3961
|
+
}
|
|
3962
|
+
const taskName = step;
|
|
3963
|
+
const task = config.workflow.tasks[taskName];
|
|
3964
|
+
if (!task) continue;
|
|
3965
|
+
if (task.agent === null) continue;
|
|
3966
|
+
const definition = config.agents[task.agent];
|
|
3967
|
+
if (!definition) continue;
|
|
3968
|
+
if (task.scope === "per-run" || task.scope === "per-epic") {
|
|
3969
|
+
if (isTaskCompleted(state, taskName, PER_RUN_SENTINEL)) continue;
|
|
3923
3970
|
try {
|
|
3924
|
-
const
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
PER_RUN_SENTINEL,
|
|
3928
|
-
state,
|
|
3929
|
-
config,
|
|
3930
|
-
lastOutputContract ?? void 0,
|
|
3931
|
-
accumulatedCostUsd
|
|
3932
|
-
);
|
|
3933
|
-
state = nullResult.updatedState;
|
|
3934
|
-
lastOutputContract = nullResult.contract;
|
|
3971
|
+
const dr = await dispatchTaskWithResult(task, taskName, PER_RUN_SENTINEL, definition, state, config, void 0, lastOutputContract ?? void 0);
|
|
3972
|
+
state = dr.updatedState;
|
|
3973
|
+
lastOutputContract = dr.contract;
|
|
3935
3974
|
tasksCompleted++;
|
|
3936
3975
|
} catch (err) {
|
|
3937
|
-
const engineError =
|
|
3976
|
+
const engineError = handleDispatchError(err, taskName, PER_RUN_SENTINEL);
|
|
3938
3977
|
errors.push(engineError);
|
|
3939
3978
|
state = recordErrorInState(state, taskName, PER_RUN_SENTINEL, engineError);
|
|
3940
3979
|
writeWorkflowState(state, projectDir);
|
|
3941
3980
|
}
|
|
3942
3981
|
} else {
|
|
3982
|
+
for (const item of workItems) {
|
|
3983
|
+
if (halted || config.abortSignal?.aborted) break;
|
|
3984
|
+
if (isTaskCompleted(state, taskName, item.key)) continue;
|
|
3985
|
+
try {
|
|
3986
|
+
const dr = await dispatchTaskWithResult(task, taskName, item.key, definition, state, config, void 0, lastOutputContract ?? void 0);
|
|
3987
|
+
state = dr.updatedState;
|
|
3988
|
+
lastOutputContract = dr.contract;
|
|
3989
|
+
tasksCompleted++;
|
|
3990
|
+
} catch (err) {
|
|
3991
|
+
const engineError = handleDispatchError(err, taskName, item.key);
|
|
3992
|
+
errors.push(engineError);
|
|
3993
|
+
state = recordErrorInState(state, taskName, item.key, engineError);
|
|
3994
|
+
writeWorkflowState(state, projectDir);
|
|
3995
|
+
}
|
|
3996
|
+
}
|
|
3997
|
+
}
|
|
3998
|
+
}
|
|
3999
|
+
}
|
|
4000
|
+
if (!config.storyPipeline)
|
|
4001
|
+
for (const step of config.workflow.storyFlow) {
|
|
4002
|
+
if (halted) break;
|
|
4003
|
+
if (config.abortSignal?.aborted) {
|
|
4004
|
+
info("Execution interrupted \u2014 saving state");
|
|
4005
|
+
state = { ...state, phase: "interrupted" };
|
|
4006
|
+
writeWorkflowState(state, projectDir);
|
|
4007
|
+
halted = true;
|
|
4008
|
+
break;
|
|
4009
|
+
}
|
|
4010
|
+
if (isLoopBlock(step)) {
|
|
4011
|
+
const loopResult = await executeLoopBlock(step, state, config, workItems, lastOutputContract);
|
|
4012
|
+
state = loopResult.state;
|
|
4013
|
+
errors.push(...loopResult.errors);
|
|
4014
|
+
tasksCompleted += loopResult.tasksCompleted;
|
|
4015
|
+
lastOutputContract = loopResult.lastContract;
|
|
3943
4016
|
for (const item of workItems) {
|
|
3944
4017
|
processedStories.add(item.key);
|
|
3945
|
-
|
|
3946
|
-
|
|
4018
|
+
}
|
|
4019
|
+
if (loopResult.halted) {
|
|
4020
|
+
halted = true;
|
|
4021
|
+
}
|
|
4022
|
+
if (state.phase === "max-iterations" || state.phase === "circuit-breaker") {
|
|
4023
|
+
halted = true;
|
|
4024
|
+
}
|
|
4025
|
+
continue;
|
|
4026
|
+
}
|
|
4027
|
+
const taskName = step;
|
|
4028
|
+
const task = config.workflow.tasks[taskName];
|
|
4029
|
+
if (!task) {
|
|
4030
|
+
warn(`workflow-engine: task "${taskName}" not found in workflow tasks, skipping`);
|
|
4031
|
+
continue;
|
|
4032
|
+
}
|
|
4033
|
+
if (task.agent === null) {
|
|
4034
|
+
if (task.scope === "per-run") {
|
|
4035
|
+
if (isTaskCompleted(state, taskName, PER_RUN_SENTINEL)) {
|
|
4036
|
+
warn(`workflow-engine: skipping completed task ${taskName} for ${PER_RUN_SENTINEL}`);
|
|
3947
4037
|
continue;
|
|
3948
4038
|
}
|
|
3949
4039
|
try {
|
|
3950
4040
|
const nullResult = await executeNullTask(
|
|
3951
4041
|
task,
|
|
3952
4042
|
taskName,
|
|
3953
|
-
|
|
4043
|
+
PER_RUN_SENTINEL,
|
|
3954
4044
|
state,
|
|
3955
4045
|
config,
|
|
3956
4046
|
lastOutputContract ?? void 0,
|
|
@@ -3960,71 +4050,56 @@ async function executeWorkflow(config) {
|
|
|
3960
4050
|
lastOutputContract = nullResult.contract;
|
|
3961
4051
|
tasksCompleted++;
|
|
3962
4052
|
} catch (err) {
|
|
3963
|
-
const engineError = isEngineError(err) ? err : handleDispatchError(err, taskName,
|
|
4053
|
+
const engineError = isEngineError(err) ? err : handleDispatchError(err, taskName, PER_RUN_SENTINEL);
|
|
3964
4054
|
errors.push(engineError);
|
|
3965
|
-
state = recordErrorInState(state, taskName,
|
|
4055
|
+
state = recordErrorInState(state, taskName, PER_RUN_SENTINEL, engineError);
|
|
3966
4056
|
writeWorkflowState(state, projectDir);
|
|
3967
4057
|
}
|
|
4058
|
+
} else {
|
|
4059
|
+
for (const item of workItems) {
|
|
4060
|
+
processedStories.add(item.key);
|
|
4061
|
+
if (isTaskCompleted(state, taskName, item.key)) {
|
|
4062
|
+
warn(`workflow-engine: skipping completed task ${taskName} for ${item.key}`);
|
|
4063
|
+
continue;
|
|
4064
|
+
}
|
|
4065
|
+
try {
|
|
4066
|
+
const nullResult = await executeNullTask(
|
|
4067
|
+
task,
|
|
4068
|
+
taskName,
|
|
4069
|
+
item.key,
|
|
4070
|
+
state,
|
|
4071
|
+
config,
|
|
4072
|
+
lastOutputContract ?? void 0,
|
|
4073
|
+
accumulatedCostUsd
|
|
4074
|
+
);
|
|
4075
|
+
state = nullResult.updatedState;
|
|
4076
|
+
lastOutputContract = nullResult.contract;
|
|
4077
|
+
tasksCompleted++;
|
|
4078
|
+
} catch (err) {
|
|
4079
|
+
const engineError = isEngineError(err) ? err : handleDispatchError(err, taskName, item.key);
|
|
4080
|
+
errors.push(engineError);
|
|
4081
|
+
state = recordErrorInState(state, taskName, item.key, engineError);
|
|
4082
|
+
writeWorkflowState(state, projectDir);
|
|
4083
|
+
}
|
|
4084
|
+
}
|
|
3968
4085
|
}
|
|
3969
|
-
}
|
|
3970
|
-
continue;
|
|
3971
|
-
}
|
|
3972
|
-
const definition = config.agents[task.agent];
|
|
3973
|
-
if (!definition) {
|
|
3974
|
-
warn(`workflow-engine: agent "${task.agent}" not found for task "${taskName}", skipping`);
|
|
3975
|
-
continue;
|
|
3976
|
-
}
|
|
3977
|
-
if (task.scope === "per-run") {
|
|
3978
|
-
if (isTaskCompleted(state, taskName, PER_RUN_SENTINEL)) {
|
|
3979
|
-
warn(`workflow-engine: skipping completed task ${taskName} for ${PER_RUN_SENTINEL}`);
|
|
3980
4086
|
continue;
|
|
3981
4087
|
}
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
PER_RUN_SENTINEL,
|
|
3987
|
-
definition,
|
|
3988
|
-
state,
|
|
3989
|
-
config,
|
|
3990
|
-
void 0,
|
|
3991
|
-
lastOutputContract ?? void 0
|
|
3992
|
-
);
|
|
3993
|
-
state = dispatchResult.updatedState;
|
|
3994
|
-
lastOutputContract = dispatchResult.contract;
|
|
3995
|
-
propagateVerifyFlags(taskName, dispatchResult.contract, projectDir);
|
|
3996
|
-
accumulatedCostUsd += dispatchResult.contract?.cost_usd ?? 0;
|
|
3997
|
-
tasksCompleted++;
|
|
3998
|
-
} catch (err) {
|
|
3999
|
-
const engineError = handleDispatchError(err, taskName, PER_RUN_SENTINEL);
|
|
4000
|
-
errors.push(engineError);
|
|
4001
|
-
if (config.onEvent) {
|
|
4002
|
-
config.onEvent({ type: "dispatch-error", taskName, storyKey: PER_RUN_SENTINEL, error: { code: engineError.code, message: engineError.message } });
|
|
4003
|
-
} else {
|
|
4004
|
-
warn(`[${taskName}] ${PER_RUN_SENTINEL} \u2014 ERROR: [${engineError.code}] ${engineError.message}`);
|
|
4005
|
-
}
|
|
4006
|
-
state = recordErrorInState(state, taskName, PER_RUN_SENTINEL, engineError);
|
|
4007
|
-
writeWorkflowState(state, projectDir);
|
|
4008
|
-
if (err instanceof DispatchError && HALT_ERROR_CODES.has(err.code)) {
|
|
4009
|
-
halted = true;
|
|
4010
|
-
}
|
|
4088
|
+
const definition = config.agents[task.agent];
|
|
4089
|
+
if (!definition) {
|
|
4090
|
+
warn(`workflow-engine: agent "${task.agent}" not found for task "${taskName}", skipping`);
|
|
4091
|
+
continue;
|
|
4011
4092
|
}
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
halted = true;
|
|
4016
|
-
break;
|
|
4017
|
-
}
|
|
4018
|
-
processedStories.add(item.key);
|
|
4019
|
-
if (isTaskCompleted(state, taskName, item.key)) {
|
|
4020
|
-
warn(`workflow-engine: skipping completed task ${taskName} for ${item.key}`);
|
|
4093
|
+
if (task.scope === "per-run") {
|
|
4094
|
+
if (isTaskCompleted(state, taskName, PER_RUN_SENTINEL)) {
|
|
4095
|
+
warn(`workflow-engine: skipping completed task ${taskName} for ${PER_RUN_SENTINEL}`);
|
|
4021
4096
|
continue;
|
|
4022
4097
|
}
|
|
4023
4098
|
try {
|
|
4024
4099
|
const dispatchResult = await dispatchTaskWithResult(
|
|
4025
4100
|
task,
|
|
4026
4101
|
taskName,
|
|
4027
|
-
|
|
4102
|
+
PER_RUN_SENTINEL,
|
|
4028
4103
|
definition,
|
|
4029
4104
|
state,
|
|
4030
4105
|
config,
|
|
@@ -4037,24 +4112,65 @@ async function executeWorkflow(config) {
|
|
|
4037
4112
|
accumulatedCostUsd += dispatchResult.contract?.cost_usd ?? 0;
|
|
4038
4113
|
tasksCompleted++;
|
|
4039
4114
|
} catch (err) {
|
|
4040
|
-
const engineError = handleDispatchError(err, taskName,
|
|
4115
|
+
const engineError = handleDispatchError(err, taskName, PER_RUN_SENTINEL);
|
|
4041
4116
|
errors.push(engineError);
|
|
4042
4117
|
if (config.onEvent) {
|
|
4043
|
-
config.onEvent({ type: "dispatch-error", taskName, storyKey:
|
|
4118
|
+
config.onEvent({ type: "dispatch-error", taskName, storyKey: PER_RUN_SENTINEL, error: { code: engineError.code, message: engineError.message } });
|
|
4044
4119
|
} else {
|
|
4045
|
-
warn(`[${taskName}] ${
|
|
4120
|
+
warn(`[${taskName}] ${PER_RUN_SENTINEL} \u2014 ERROR: [${engineError.code}] ${engineError.message}`);
|
|
4046
4121
|
}
|
|
4047
|
-
state = recordErrorInState(state, taskName,
|
|
4122
|
+
state = recordErrorInState(state, taskName, PER_RUN_SENTINEL, engineError);
|
|
4048
4123
|
writeWorkflowState(state, projectDir);
|
|
4049
4124
|
if (err instanceof DispatchError && HALT_ERROR_CODES.has(err.code)) {
|
|
4050
4125
|
halted = true;
|
|
4126
|
+
}
|
|
4127
|
+
}
|
|
4128
|
+
} else {
|
|
4129
|
+
for (const item of workItems) {
|
|
4130
|
+
if (config.abortSignal?.aborted) {
|
|
4131
|
+
halted = true;
|
|
4051
4132
|
break;
|
|
4052
4133
|
}
|
|
4053
|
-
|
|
4134
|
+
processedStories.add(item.key);
|
|
4135
|
+
if (isTaskCompleted(state, taskName, item.key)) {
|
|
4136
|
+
warn(`workflow-engine: skipping completed task ${taskName} for ${item.key}`);
|
|
4137
|
+
continue;
|
|
4138
|
+
}
|
|
4139
|
+
try {
|
|
4140
|
+
const dispatchResult = await dispatchTaskWithResult(
|
|
4141
|
+
task,
|
|
4142
|
+
taskName,
|
|
4143
|
+
item.key,
|
|
4144
|
+
definition,
|
|
4145
|
+
state,
|
|
4146
|
+
config,
|
|
4147
|
+
void 0,
|
|
4148
|
+
lastOutputContract ?? void 0
|
|
4149
|
+
);
|
|
4150
|
+
state = dispatchResult.updatedState;
|
|
4151
|
+
lastOutputContract = dispatchResult.contract;
|
|
4152
|
+
propagateVerifyFlags(taskName, dispatchResult.contract, projectDir);
|
|
4153
|
+
accumulatedCostUsd += dispatchResult.contract?.cost_usd ?? 0;
|
|
4154
|
+
tasksCompleted++;
|
|
4155
|
+
} catch (err) {
|
|
4156
|
+
const engineError = handleDispatchError(err, taskName, item.key);
|
|
4157
|
+
errors.push(engineError);
|
|
4158
|
+
if (config.onEvent) {
|
|
4159
|
+
config.onEvent({ type: "dispatch-error", taskName, storyKey: item.key, error: { code: engineError.code, message: engineError.message } });
|
|
4160
|
+
} else {
|
|
4161
|
+
warn(`[${taskName}] ${item.key} \u2014 ERROR: [${engineError.code}] ${engineError.message}`);
|
|
4162
|
+
}
|
|
4163
|
+
state = recordErrorInState(state, taskName, item.key, engineError);
|
|
4164
|
+
writeWorkflowState(state, projectDir);
|
|
4165
|
+
if (err instanceof DispatchError && HALT_ERROR_CODES.has(err.code)) {
|
|
4166
|
+
halted = true;
|
|
4167
|
+
break;
|
|
4168
|
+
}
|
|
4169
|
+
continue;
|
|
4170
|
+
}
|
|
4054
4171
|
}
|
|
4055
4172
|
}
|
|
4056
4173
|
}
|
|
4057
|
-
}
|
|
4058
4174
|
if (state.phase === "interrupted") {
|
|
4059
4175
|
} else if (errors.length === 0 && state.phase !== "max-iterations" && state.phase !== "circuit-breaker") {
|
|
4060
4176
|
state = { ...state, phase: "completed" };
|
|
@@ -4930,15 +5046,29 @@ function CompletedTool({ entry }) {
|
|
|
4930
5046
|
entry.driver && /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` (${entry.driver})` })
|
|
4931
5047
|
] });
|
|
4932
5048
|
}
|
|
4933
|
-
var
|
|
4934
|
-
function CompletedTools({ tools }) {
|
|
4935
|
-
const
|
|
5049
|
+
var DEFAULT_VISIBLE_TOOLS = 5;
|
|
5050
|
+
function CompletedTools({ tools, maxVisible }) {
|
|
5051
|
+
const limit = maxVisible ?? DEFAULT_VISIBLE_TOOLS;
|
|
5052
|
+
const visible = tools.slice(-limit);
|
|
4936
5053
|
const hidden = tools.length - visible.length;
|
|
4937
5054
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
4938
5055
|
hidden > 0 && /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \u2026 ${hidden} earlier tools` }),
|
|
4939
5056
|
visible.map((entry, i) => /* @__PURE__ */ jsx(CompletedTool, { entry }, i))
|
|
4940
5057
|
] });
|
|
4941
5058
|
}
|
|
5059
|
+
function ActivitySection({ completedTools, activeTool, activeDriverName, lastThought, retryInfo, availableHeight }) {
|
|
5060
|
+
let reserved = 0;
|
|
5061
|
+
if (activeTool) reserved++;
|
|
5062
|
+
if (lastThought) reserved++;
|
|
5063
|
+
if (retryInfo) reserved++;
|
|
5064
|
+
const toolsHeight = Math.max(1, availableHeight - reserved - 1);
|
|
5065
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingLeft: 1, children: [
|
|
5066
|
+
/* @__PURE__ */ jsx(CompletedTools, { tools: completedTools, maxVisible: toolsHeight }),
|
|
5067
|
+
activeTool && /* @__PURE__ */ jsx(ActiveTool, { name: activeTool.name, driverName: activeDriverName }),
|
|
5068
|
+
lastThought && /* @__PURE__ */ jsx(LastThought, { text: lastThought }),
|
|
5069
|
+
retryInfo && /* @__PURE__ */ jsx(RetryNotice, { info: retryInfo })
|
|
5070
|
+
] });
|
|
5071
|
+
}
|
|
4942
5072
|
function ActiveTool({ name, driverName }) {
|
|
4943
5073
|
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
4944
5074
|
/* @__PURE__ */ jsx(Text, { color: "yellow", children: "\u26A1 " }),
|
|
@@ -4964,13 +5094,6 @@ function RetryNotice({ info: info3 }) {
|
|
|
4964
5094
|
"ms)"
|
|
4965
5095
|
] });
|
|
4966
5096
|
}
|
|
4967
|
-
function DriverCostSummary({ driverCosts }) {
|
|
4968
|
-
if (!driverCosts) return null;
|
|
4969
|
-
const entries = Object.entries(driverCosts).sort(([a], [b]) => a.localeCompare(b));
|
|
4970
|
-
if (entries.length === 0) return null;
|
|
4971
|
-
const parts = entries.map(([driver, cost]) => `${driver} $${cost.toFixed(2)}`).join(", ");
|
|
4972
|
-
return /* @__PURE__ */ jsx(Text, { dimColor: true, children: `Cost: ${parts}` });
|
|
4973
|
-
}
|
|
4974
5097
|
|
|
4975
5098
|
// src/lib/ink-app.tsx
|
|
4976
5099
|
import { Box as Box7, Static, Text as Text7, useInput } from "ink";
|
|
@@ -5430,39 +5553,49 @@ function LaneActivityHeader({ activeLaneId, laneCount }) {
|
|
|
5430
5553
|
if (laneCount <= 1 || !activeLaneId) return null;
|
|
5431
5554
|
return /* @__PURE__ */ jsx7(Text7, { children: /* @__PURE__ */ jsx7(Text7, { color: "cyan", children: `[Lane ${activeLaneId} \u25B8]` }) });
|
|
5432
5555
|
}
|
|
5433
|
-
function App({ state, onCycleLane }) {
|
|
5556
|
+
function App({ state, onCycleLane, onQuit }) {
|
|
5434
5557
|
const lanes = state.lanes;
|
|
5435
5558
|
const laneCount = lanes?.length ?? 0;
|
|
5436
5559
|
const terminalWidth = process.stdout.columns || 80;
|
|
5437
|
-
useInput((
|
|
5438
|
-
if (key.ctrl &&
|
|
5560
|
+
useInput((input, key) => {
|
|
5561
|
+
if (key.ctrl && input === "l" && onCycleLane && laneCount > 1) {
|
|
5439
5562
|
onCycleLane();
|
|
5440
5563
|
}
|
|
5564
|
+
if (input === "q" && onQuit) {
|
|
5565
|
+
onQuit();
|
|
5566
|
+
}
|
|
5441
5567
|
}, { isActive: typeof process.stdin.setRawMode === "function" });
|
|
5442
5568
|
const activeLaneCount = state.laneCount ?? 0;
|
|
5569
|
+
const termRows = process.stdout.rows || 24;
|
|
5570
|
+
const fixedHeight = 10;
|
|
5571
|
+
const availableHeight = Math.max(3, termRows - fixedHeight);
|
|
5443
5572
|
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
5444
5573
|
/* @__PURE__ */ jsx7(Static, { items: state.messages, children: (msg, i) => /* @__PURE__ */ jsx7(StoryMessageLine, { msg }, i) }),
|
|
5445
5574
|
/* @__PURE__ */ jsx7(Header, { info: state.sprintInfo, laneCount: laneCount > 1 ? laneCount : void 0 }),
|
|
5446
|
-
laneCount > 1 ? /* @__PURE__ */
|
|
5447
|
-
/* @__PURE__ */ jsx7(
|
|
5448
|
-
/* @__PURE__ */
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5575
|
+
laneCount > 1 ? /* @__PURE__ */ jsxs7(Fragment, { children: [
|
|
5576
|
+
/* @__PURE__ */ jsx7(LaneContainer, { lanes, terminalWidth }),
|
|
5577
|
+
state.summaryBar && /* @__PURE__ */ jsxs7(Fragment, { children: [
|
|
5578
|
+
/* @__PURE__ */ jsx7(Separator, {}),
|
|
5579
|
+
/* @__PURE__ */ jsx7(SummaryBar, { ...state.summaryBar })
|
|
5580
|
+
] }),
|
|
5581
|
+
state.mergeState && /* @__PURE__ */ jsxs7(Fragment, { children: [
|
|
5582
|
+
/* @__PURE__ */ jsx7(Separator, {}),
|
|
5583
|
+
/* @__PURE__ */ jsx7(MergeStatus, { mergeState: state.mergeState })
|
|
5584
|
+
] }),
|
|
5452
5585
|
/* @__PURE__ */ jsx7(Separator, {}),
|
|
5453
|
-
/* @__PURE__ */
|
|
5454
|
-
|
|
5455
|
-
|
|
5586
|
+
/* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", paddingLeft: 1, children: [
|
|
5587
|
+
/* @__PURE__ */ jsx7(LaneActivityHeader, { activeLaneId: state.activeLaneId ?? null, laneCount: activeLaneCount }),
|
|
5588
|
+
/* @__PURE__ */ jsx7(ActivitySection, { completedTools: state.completedTools, activeTool: state.activeTool, activeDriverName: state.activeDriverName, lastThought: state.lastThought, retryInfo: state.retryInfo, availableHeight })
|
|
5589
|
+
] })
|
|
5590
|
+
] }) : /* @__PURE__ */ jsxs7(Fragment, { children: [
|
|
5456
5591
|
/* @__PURE__ */ jsx7(Separator, {}),
|
|
5457
|
-
/* @__PURE__ */ jsx7(
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
/* @__PURE__ */ jsx7(
|
|
5462
|
-
/* @__PURE__ */ jsx7(
|
|
5463
|
-
|
|
5464
|
-
state.lastThought && /* @__PURE__ */ jsx7(LastThought, { text: state.lastThought }),
|
|
5465
|
-
state.retryInfo && /* @__PURE__ */ jsx7(RetryNotice, { info: state.retryInfo })
|
|
5592
|
+
/* @__PURE__ */ jsx7(ProgressBar, { done: state.sprintInfo?.done ?? 0, total: state.sprintInfo?.total ?? 0 }),
|
|
5593
|
+
/* @__PURE__ */ jsx7(EpicInfo, { info: state.sprintInfo }),
|
|
5594
|
+
/* @__PURE__ */ jsx7(StoryContext, { entries: state.storyContext ?? [] }),
|
|
5595
|
+
/* @__PURE__ */ jsx7(Separator, {}),
|
|
5596
|
+
/* @__PURE__ */ jsx7(WorkflowGraph, { flow: state.workflowFlow, currentTask: state.currentTaskName, taskStates: state.taskStates }),
|
|
5597
|
+
/* @__PURE__ */ jsx7(Separator, {}),
|
|
5598
|
+
/* @__PURE__ */ jsx7(ActivitySection, { completedTools: state.completedTools, activeTool: state.activeTool, activeDriverName: state.activeDriverName, lastThought: state.lastThought, retryInfo: state.retryInfo, availableHeight })
|
|
5466
5599
|
] })
|
|
5467
5600
|
] });
|
|
5468
5601
|
}
|
|
@@ -5483,133 +5616,48 @@ function formatCost3(cost) {
|
|
|
5483
5616
|
function Header({ info: info3, laneCount }) {
|
|
5484
5617
|
if (!info3) return null;
|
|
5485
5618
|
const parts = ["codeharness run"];
|
|
5486
|
-
if (laneCount != null && laneCount > 1) {
|
|
5487
|
-
|
|
5488
|
-
}
|
|
5489
|
-
if (info3.iterationCount != null) {
|
|
5490
|
-
parts.push(`iteration ${info3.iterationCount}`);
|
|
5491
|
-
}
|
|
5492
|
-
if (info3.elapsed) {
|
|
5493
|
-
parts.push(`${info3.elapsed} elapsed`);
|
|
5494
|
-
}
|
|
5619
|
+
if (laneCount != null && laneCount > 1) parts.push(`${laneCount} lanes`);
|
|
5620
|
+
if (info3.elapsed) parts.push(`${info3.elapsed} elapsed`);
|
|
5495
5621
|
const displayCost = laneCount != null && laneCount > 1 && info3.laneTotalCost != null ? info3.laneTotalCost : info3.totalCost;
|
|
5496
|
-
if (displayCost != null) {
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
const
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
}
|
|
5506
|
-
if (info3.currentCommand) {
|
|
5507
|
-
phaseLine += ` (${info3.currentCommand})`;
|
|
5508
|
-
}
|
|
5509
|
-
}
|
|
5510
|
-
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
5511
|
-
/* @__PURE__ */ jsx8(Text8, { children: headerLine }),
|
|
5512
|
-
/* @__PURE__ */ jsx8(Separator, {}),
|
|
5513
|
-
/* @__PURE__ */ jsx8(Text8, { children: `Story: ${info3.storyKey || "(waiting)"}` }),
|
|
5514
|
-
phaseLine && /* @__PURE__ */ jsx8(Text8, { children: phaseLine })
|
|
5622
|
+
if (displayCost != null) parts.push(`${formatCost3(displayCost)} spent`);
|
|
5623
|
+
const left = parts.join(" | ");
|
|
5624
|
+
const right = "[q to quit]";
|
|
5625
|
+
const width = process.stdout.columns || 80;
|
|
5626
|
+
const pad = Math.max(0, width - left.length - right.length);
|
|
5627
|
+
return /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
5628
|
+
left,
|
|
5629
|
+
" ".repeat(pad),
|
|
5630
|
+
/* @__PURE__ */ jsx8(Text8, { dimColor: true, children: right })
|
|
5515
5631
|
] });
|
|
5516
5632
|
}
|
|
5517
|
-
function
|
|
5518
|
-
|
|
5519
|
-
const
|
|
5520
|
-
const
|
|
5521
|
-
const
|
|
5522
|
-
const
|
|
5523
|
-
|
|
5524
|
-
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
}
|
|
5542
|
-
}
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
const doneItems = done.map((s) => {
|
|
5546
|
-
let item = `${shortKey(s.key)} \u2713`;
|
|
5547
|
-
if (s.costByDriver && Object.keys(s.costByDriver).length > 0) {
|
|
5548
|
-
const costParts = Object.keys(s.costByDriver).sort().map(
|
|
5549
|
-
(driver) => `${driver} ${formatCost3(s.costByDriver[driver])}`
|
|
5550
|
-
);
|
|
5551
|
-
item += ` ${costParts.join(", ")}`;
|
|
5552
|
-
}
|
|
5553
|
-
return item;
|
|
5554
|
-
}).join(" ");
|
|
5555
|
-
lines.push(
|
|
5556
|
-
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
5557
|
-
/* @__PURE__ */ jsx8(Text8, { color: "green", children: "Done: " }),
|
|
5558
|
-
/* @__PURE__ */ jsx8(Text8, { color: "green", children: doneItems })
|
|
5559
|
-
] }, "done")
|
|
5560
|
-
);
|
|
5561
|
-
}
|
|
5562
|
-
if (inProgress.length > 0) {
|
|
5563
|
-
for (const s of inProgress) {
|
|
5564
|
-
let thisText = `${shortKey(s.key)} \u25C6`;
|
|
5565
|
-
if (sprintInfo && sprintInfo.storyKey && shortKey(s.key) === shortKey(sprintInfo.storyKey)) {
|
|
5566
|
-
if (sprintInfo.phase) thisText += ` ${sprintInfo.phase}`;
|
|
5567
|
-
if (sprintInfo.acProgress) thisText += ` (${sprintInfo.acProgress} ACs)`;
|
|
5568
|
-
}
|
|
5569
|
-
lines.push(
|
|
5570
|
-
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
5571
|
-
/* @__PURE__ */ jsx8(Text8, { color: "cyan", children: "This: " }),
|
|
5572
|
-
/* @__PURE__ */ jsx8(Text8, { color: "cyan", children: thisText })
|
|
5573
|
-
] }, `this-${s.key}`)
|
|
5574
|
-
);
|
|
5575
|
-
}
|
|
5576
|
-
}
|
|
5577
|
-
if (pending.length > 0) {
|
|
5578
|
-
lines.push(
|
|
5579
|
-
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
5580
|
-
/* @__PURE__ */ jsx8(Text8, { children: "Next: " }),
|
|
5581
|
-
/* @__PURE__ */ jsx8(Text8, { children: shortKey(pending[0].key) }),
|
|
5582
|
-
pending.length > 1 && /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: ` (+${pending.length - 1} more)` })
|
|
5583
|
-
] }, "next")
|
|
5584
|
-
);
|
|
5585
|
-
}
|
|
5586
|
-
if (blocked.length > 0) {
|
|
5587
|
-
const blockedItems = blocked.map((s) => {
|
|
5588
|
-
let item = `${shortKey(s.key)} \u2715`;
|
|
5589
|
-
if (s.retryCount != null && s.maxRetries != null) item += ` (${s.retryCount}/${s.maxRetries})`;
|
|
5590
|
-
return item;
|
|
5591
|
-
}).join(" ");
|
|
5592
|
-
lines.push(
|
|
5593
|
-
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
5594
|
-
/* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "Blocked: " }),
|
|
5595
|
-
/* @__PURE__ */ jsx8(Text8, { color: "yellow", children: blockedItems })
|
|
5596
|
-
] }, "blocked")
|
|
5597
|
-
);
|
|
5598
|
-
}
|
|
5599
|
-
if (failed.length > 0) {
|
|
5600
|
-
const failedItems = failed.map((s) => {
|
|
5601
|
-
let item = `${shortKey(s.key)} \u2717`;
|
|
5602
|
-
if (s.retryCount != null && s.maxRetries != null) item += ` (${s.retryCount}/${s.maxRetries})`;
|
|
5603
|
-
return item;
|
|
5604
|
-
}).join(" ");
|
|
5605
|
-
lines.push(
|
|
5606
|
-
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
5607
|
-
/* @__PURE__ */ jsx8(Text8, { color: "red", children: "Failed: " }),
|
|
5608
|
-
/* @__PURE__ */ jsx8(Text8, { color: "red", children: failedItems })
|
|
5609
|
-
] }, "failed")
|
|
5610
|
-
);
|
|
5611
|
-
}
|
|
5612
|
-
return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", children: lines });
|
|
5633
|
+
function ProgressBar({ done, total }) {
|
|
5634
|
+
const width = Math.max(10, (process.stdout.columns || 80) - 30);
|
|
5635
|
+
const pct = total > 0 ? done / total : 0;
|
|
5636
|
+
const filled = Math.round(width * pct);
|
|
5637
|
+
const bar = "\u2588".repeat(filled) + "\u2591".repeat(width - filled);
|
|
5638
|
+
const pctStr = total > 0 ? `${Math.round(pct * 100)}%` : "0%";
|
|
5639
|
+
return /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
5640
|
+
"Progress: ",
|
|
5641
|
+
/* @__PURE__ */ jsx8(Text8, { color: "green", children: bar }),
|
|
5642
|
+
` ${done}/${total} stories (${pctStr})`
|
|
5643
|
+
] });
|
|
5644
|
+
}
|
|
5645
|
+
function EpicInfo({ info: info3 }) {
|
|
5646
|
+
if (!info3?.epicId) return null;
|
|
5647
|
+
const title = info3.epicTitle ?? `Epic ${info3.epicId}`;
|
|
5648
|
+
const progress = info3.epicStoriesTotal ? ` \u2014 ${info3.epicStoriesDone ?? 0}/${info3.epicStoriesTotal} stories done` : "";
|
|
5649
|
+
return /* @__PURE__ */ jsxs8(Text8, { children: [
|
|
5650
|
+
/* @__PURE__ */ jsx8(Text8, { bold: true, children: `Epic ${info3.epicId}: ${title}` }),
|
|
5651
|
+
/* @__PURE__ */ jsx8(Text8, { dimColor: true, children: progress })
|
|
5652
|
+
] });
|
|
5653
|
+
}
|
|
5654
|
+
function StoryContext({ entries }) {
|
|
5655
|
+
if (entries.length === 0) return null;
|
|
5656
|
+
return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", children: entries.map((e, i) => {
|
|
5657
|
+
if (e.role === "prev") return /* @__PURE__ */ jsx8(Text8, { children: /* @__PURE__ */ jsx8(Text8, { color: "green", children: ` Prev: ${shortKey(e.key)} \u2713` }) }, i);
|
|
5658
|
+
if (e.role === "current") return /* @__PURE__ */ jsx8(Text8, { children: /* @__PURE__ */ jsx8(Text8, { color: "cyan", children: ` This: ${shortKey(e.key)} \u25C6 ${e.task ?? ""}` }) }, i);
|
|
5659
|
+
return /* @__PURE__ */ jsx8(Text8, { children: /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: ` Next: ${shortKey(e.key)}` }) }, i);
|
|
5660
|
+
}) });
|
|
5613
5661
|
}
|
|
5614
5662
|
|
|
5615
5663
|
// src/lib/ink-renderer.tsx
|
|
@@ -5652,6 +5700,7 @@ function startRenderer(options) {
|
|
|
5652
5700
|
taskMeta: {},
|
|
5653
5701
|
activeDriverName: null,
|
|
5654
5702
|
driverCosts: {},
|
|
5703
|
+
storyContext: [],
|
|
5655
5704
|
activeLaneId: null,
|
|
5656
5705
|
laneCount: 0
|
|
5657
5706
|
};
|
|
@@ -5661,7 +5710,8 @@ function startRenderer(options) {
|
|
|
5661
5710
|
let lastStoryKey = state.sprintInfo?.storyKey ?? null;
|
|
5662
5711
|
const pendingStoryCosts = /* @__PURE__ */ new Map();
|
|
5663
5712
|
let cleaned = false;
|
|
5664
|
-
const
|
|
5713
|
+
const onQuit = options?.onQuit;
|
|
5714
|
+
const inkInstance = inkRender(/* @__PURE__ */ jsx9(App, { state, onCycleLane: () => cycleLane(), onQuit: onQuit ? () => onQuit() : void 0 }), {
|
|
5665
5715
|
exitOnCtrlC: false,
|
|
5666
5716
|
patchConsole: !options?._forceTTY,
|
|
5667
5717
|
maxFps: 15
|
|
@@ -5669,7 +5719,7 @@ function startRenderer(options) {
|
|
|
5669
5719
|
function rerender() {
|
|
5670
5720
|
if (!cleaned) {
|
|
5671
5721
|
state = { ...state };
|
|
5672
|
-
inkInstance.rerender(/* @__PURE__ */ jsx9(App, { state, onCycleLane: () => cycleLane() }));
|
|
5722
|
+
inkInstance.rerender(/* @__PURE__ */ jsx9(App, { state, onCycleLane: () => cycleLane(), onQuit: onQuit ? () => onQuit() : void 0 }));
|
|
5673
5723
|
}
|
|
5674
5724
|
}
|
|
5675
5725
|
const heartbeat = setInterval(() => {
|
|
@@ -6005,6 +6055,24 @@ function startRenderer(options) {
|
|
|
6005
6055
|
lastStoryKey = currentKey;
|
|
6006
6056
|
}
|
|
6007
6057
|
state.stories = updatedStories;
|
|
6058
|
+
const ctx = [];
|
|
6059
|
+
const currentStory = currentKey ?? "";
|
|
6060
|
+
const currentTask = state.currentTaskName ?? "";
|
|
6061
|
+
let foundCurrent = false;
|
|
6062
|
+
let prevKey = null;
|
|
6063
|
+
for (const s of updatedStories) {
|
|
6064
|
+
if (s.key === currentStory) {
|
|
6065
|
+
if (prevKey) ctx.push({ key: prevKey, role: "prev" });
|
|
6066
|
+
ctx.push({ key: s.key, role: "current", task: currentTask });
|
|
6067
|
+
foundCurrent = true;
|
|
6068
|
+
} else if (foundCurrent && s.status === "pending") {
|
|
6069
|
+
ctx.push({ key: s.key, role: "next" });
|
|
6070
|
+
break;
|
|
6071
|
+
} else if (s.status === "done") {
|
|
6072
|
+
prevKey = s.key;
|
|
6073
|
+
}
|
|
6074
|
+
}
|
|
6075
|
+
state.storyContext = ctx;
|
|
6008
6076
|
rerender();
|
|
6009
6077
|
}
|
|
6010
6078
|
function addMessage(msg) {
|
|
@@ -6175,17 +6243,8 @@ function registerRunCommand(program) {
|
|
|
6175
6243
|
}
|
|
6176
6244
|
}
|
|
6177
6245
|
const abortController = new AbortController();
|
|
6178
|
-
const renderer = startRenderer({
|
|
6179
|
-
quiet: !!options.quiet || isJson,
|
|
6180
|
-
sprintState: {
|
|
6181
|
-
storyKey: "",
|
|
6182
|
-
phase: "executing",
|
|
6183
|
-
done: counts.done,
|
|
6184
|
-
total: counts.total,
|
|
6185
|
-
totalCost: 0
|
|
6186
|
-
}
|
|
6187
|
-
});
|
|
6188
6246
|
let interrupted = false;
|
|
6247
|
+
let renderer;
|
|
6189
6248
|
const onInterrupt = () => {
|
|
6190
6249
|
if (interrupted) {
|
|
6191
6250
|
process.exit(1);
|
|
@@ -6195,6 +6254,17 @@ function registerRunCommand(program) {
|
|
|
6195
6254
|
abortController.abort();
|
|
6196
6255
|
info("Interrupted \u2014 waiting for current task to finish...", outputOpts);
|
|
6197
6256
|
};
|
|
6257
|
+
renderer = startRenderer({
|
|
6258
|
+
quiet: !!options.quiet || isJson,
|
|
6259
|
+
sprintState: {
|
|
6260
|
+
storyKey: "",
|
|
6261
|
+
phase: "executing",
|
|
6262
|
+
done: counts.done,
|
|
6263
|
+
total: counts.total,
|
|
6264
|
+
totalCost: 0
|
|
6265
|
+
},
|
|
6266
|
+
onQuit: () => onInterrupt()
|
|
6267
|
+
});
|
|
6198
6268
|
process.on("SIGINT", onInterrupt);
|
|
6199
6269
|
process.on("SIGTERM", onInterrupt);
|
|
6200
6270
|
const sessionStartMs = Date.now();
|
|
@@ -6204,15 +6274,28 @@ function registerRunCommand(program) {
|
|
|
6204
6274
|
let currentTaskName = "";
|
|
6205
6275
|
const headerRefresh = setInterval(() => {
|
|
6206
6276
|
if (interrupted) return;
|
|
6277
|
+
const epicId = currentStoryKey ? extractEpicId2(currentStoryKey) : "";
|
|
6278
|
+
const epic = epicId ? epicData[epicId] : void 0;
|
|
6207
6279
|
renderer.updateSprintState({
|
|
6208
6280
|
storyKey: currentStoryKey,
|
|
6209
6281
|
phase: currentTaskName,
|
|
6210
6282
|
done: storiesDone,
|
|
6211
6283
|
total: counts.total,
|
|
6212
6284
|
totalCost: totalCostUsd,
|
|
6213
|
-
elapsed: formatElapsed(Date.now() - sessionStartMs)
|
|
6285
|
+
elapsed: formatElapsed(Date.now() - sessionStartMs),
|
|
6286
|
+
epicId: epicId || void 0,
|
|
6287
|
+
epicStoriesDone: epic?.storiesDone,
|
|
6288
|
+
epicStoriesTotal: epic?.storiesTotal
|
|
6214
6289
|
});
|
|
6215
6290
|
}, 2e3);
|
|
6291
|
+
const epicData = {};
|
|
6292
|
+
const sprintStateResult = getSprintState2();
|
|
6293
|
+
if (sprintStateResult.success) {
|
|
6294
|
+
for (const [epicKey, epic] of Object.entries(sprintStateResult.data.epics ?? {})) {
|
|
6295
|
+
const epicId = epicKey.replace("epic-", "");
|
|
6296
|
+
epicData[epicId] = { storiesDone: epic.storiesDone ?? 0, storiesTotal: epic.storiesTotal ?? 0 };
|
|
6297
|
+
}
|
|
6298
|
+
}
|
|
6216
6299
|
const taskStates = {};
|
|
6217
6300
|
const taskMeta = {};
|
|
6218
6301
|
for (const [tn, task] of Object.entries(parsedWorkflow.tasks)) {
|
|
@@ -6235,13 +6318,18 @@ function registerRunCommand(program) {
|
|
|
6235
6318
|
if (event.type === "dispatch-start") {
|
|
6236
6319
|
currentStoryKey = event.storyKey;
|
|
6237
6320
|
currentTaskName = event.taskName;
|
|
6321
|
+
const epicId = extractEpicId2(event.storyKey);
|
|
6322
|
+
const epic = epicData[epicId];
|
|
6238
6323
|
renderer.updateSprintState({
|
|
6239
6324
|
storyKey: event.storyKey,
|
|
6240
6325
|
phase: event.taskName,
|
|
6241
6326
|
done: storiesDone,
|
|
6242
6327
|
total: counts.total,
|
|
6243
6328
|
totalCost: totalCostUsd,
|
|
6244
|
-
elapsed: formatElapsed(Date.now() - sessionStartMs)
|
|
6329
|
+
elapsed: formatElapsed(Date.now() - sessionStartMs),
|
|
6330
|
+
epicId,
|
|
6331
|
+
epicStoriesDone: epic?.storiesDone ?? 0,
|
|
6332
|
+
epicStoriesTotal: epic?.storiesTotal ?? 0
|
|
6245
6333
|
});
|
|
6246
6334
|
taskStates[event.taskName] = "active";
|
|
6247
6335
|
renderer.updateWorkflowState(parsedWorkflow.flow, event.taskName, { ...taskStates }, { ...taskMeta });
|
|
@@ -6286,6 +6374,7 @@ function registerRunCommand(program) {
|
|
|
6286
6374
|
runId: `run-${Date.now()}`,
|
|
6287
6375
|
projectDir,
|
|
6288
6376
|
abortSignal: abortController.signal,
|
|
6377
|
+
storyPipeline: true,
|
|
6289
6378
|
maxIterations,
|
|
6290
6379
|
onEvent
|
|
6291
6380
|
};
|
|
@@ -11167,7 +11256,7 @@ function registerTeardownCommand(program) {
|
|
|
11167
11256
|
} else if (otlpMode === "remote-routed") {
|
|
11168
11257
|
if (!options.keepDocker) {
|
|
11169
11258
|
try {
|
|
11170
|
-
const { stopCollectorOnly: stopCollectorOnly2 } = await import("./docker-
|
|
11259
|
+
const { stopCollectorOnly: stopCollectorOnly2 } = await import("./docker-P65B7Z3S.js");
|
|
11171
11260
|
stopCollectorOnly2();
|
|
11172
11261
|
result.docker.stopped = true;
|
|
11173
11262
|
if (!isJson) {
|
|
@@ -11199,7 +11288,7 @@ function registerTeardownCommand(program) {
|
|
|
11199
11288
|
info("Shared stack: kept running (other projects may use it)");
|
|
11200
11289
|
}
|
|
11201
11290
|
} else if (isLegacyStack) {
|
|
11202
|
-
const { isStackRunning: isStackRunning2, stopStack } = await import("./docker-
|
|
11291
|
+
const { isStackRunning: isStackRunning2, stopStack } = await import("./docker-P65B7Z3S.js");
|
|
11203
11292
|
let stackRunning = false;
|
|
11204
11293
|
try {
|
|
11205
11294
|
stackRunning = isStackRunning2(composeFile);
|
|
@@ -14095,7 +14184,7 @@ function registerDriversCommand(program) {
|
|
|
14095
14184
|
}
|
|
14096
14185
|
|
|
14097
14186
|
// src/index.ts
|
|
14098
|
-
var VERSION = true ? "0.
|
|
14187
|
+
var VERSION = true ? "0.30.1" : "0.0.0-dev";
|
|
14099
14188
|
function createProgram() {
|
|
14100
14189
|
const program = new Command();
|
|
14101
14190
|
program.name("codeharness").description("Makes autonomous coding agents produce software that actually works").version(VERSION).option("--json", "Output in machine-readable JSON format");
|