codeharness 0.29.2 → 0.29.3

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.29.2" : "0.0.0-dev";
2898
+ var HARNESS_VERSION = true ? "0.29.3" : "0.0.0-dev";
2899
2899
  function failResult(opts, error) {
2900
2900
  return {
2901
2901
  status: "fail",
@@ -16,7 +16,7 @@ import {
16
16
  stopCollectorOnly,
17
17
  stopSharedStack,
18
18
  stopStack
19
- } from "./chunk-3ZSXMCZV.js";
19
+ } from "./chunk-4QLSEKNP.js";
20
20
  export {
21
21
  checkRemoteEndpoint,
22
22
  cleanupOrphanedContainers,
package/dist/index.js CHANGED
@@ -40,7 +40,7 @@ import {
40
40
  validateDockerfile,
41
41
  warn,
42
42
  writeState
43
- } from "./chunk-3ZSXMCZV.js";
43
+ } from "./chunk-4QLSEKNP.js";
44
44
 
45
45
  // src/index.ts
46
46
  import { Command } from "commander";
@@ -3884,6 +3884,13 @@ async function executeWorkflow(config) {
3884
3884
  let accumulatedCostUsd = 0;
3885
3885
  for (const step of config.workflow.storyFlow) {
3886
3886
  if (halted) break;
3887
+ if (config.abortSignal?.aborted) {
3888
+ info("Execution interrupted \u2014 saving state");
3889
+ state = { ...state, phase: "interrupted" };
3890
+ writeWorkflowState(state, projectDir);
3891
+ halted = true;
3892
+ break;
3893
+ }
3887
3894
  if (isLoopBlock(step)) {
3888
3895
  const loopResult = await executeLoopBlock(step, state, config, workItems, lastOutputContract);
3889
3896
  state = loopResult.state;
@@ -4004,6 +4011,10 @@ async function executeWorkflow(config) {
4004
4011
  }
4005
4012
  } else {
4006
4013
  for (const item of workItems) {
4014
+ if (config.abortSignal?.aborted) {
4015
+ halted = true;
4016
+ break;
4017
+ }
4007
4018
  processedStories.add(item.key);
4008
4019
  if (isTaskCompleted(state, taskName, item.key)) {
4009
4020
  warn(`workflow-engine: skipping completed task ${taskName} for ${item.key}`);
@@ -4044,13 +4055,14 @@ async function executeWorkflow(config) {
4044
4055
  }
4045
4056
  }
4046
4057
  }
4047
- if (errors.length === 0 && state.phase !== "max-iterations" && state.phase !== "circuit-breaker") {
4058
+ if (state.phase === "interrupted") {
4059
+ } else if (errors.length === 0 && state.phase !== "max-iterations" && state.phase !== "circuit-breaker") {
4048
4060
  state = { ...state, phase: "completed" };
4049
4061
  writeWorkflowState(state, projectDir);
4050
4062
  }
4051
4063
  const loopTerminated = state.phase === "max-iterations" || state.phase === "circuit-breaker";
4052
4064
  return {
4053
- success: errors.length === 0 && !loopTerminated,
4065
+ success: errors.length === 0 && !loopTerminated && state.phase !== "interrupted",
4054
4066
  tasksCompleted,
4055
4067
  storiesProcessed: processedStories.size,
4056
4068
  errors,
@@ -6160,6 +6172,7 @@ function registerRunCommand(program) {
6160
6172
  info("Resuming after circuit breaker \u2014 previous findings preserved", outputOpts);
6161
6173
  }
6162
6174
  }
6175
+ const abortController = new AbortController();
6163
6176
  const renderer = startRenderer({
6164
6177
  quiet: !!options.quiet || isJson,
6165
6178
  sprintState: {
@@ -6170,6 +6183,17 @@ function registerRunCommand(program) {
6170
6183
  totalCost: 0
6171
6184
  }
6172
6185
  });
6186
+ let interrupted = false;
6187
+ const onInterrupt = () => {
6188
+ if (interrupted) {
6189
+ renderer.cleanup();
6190
+ process.exit(1);
6191
+ }
6192
+ interrupted = true;
6193
+ abortController.abort();
6194
+ };
6195
+ process.on("SIGINT", onInterrupt);
6196
+ process.on("SIGTERM", onInterrupt);
6173
6197
  let totalCostUsd = 0;
6174
6198
  let storiesDone = counts.done;
6175
6199
  const taskStates = {};
@@ -6241,6 +6265,7 @@ function registerRunCommand(program) {
6241
6265
  issuesPath: join15(projectDir, ".codeharness", "issues.yaml"),
6242
6266
  runId: `run-${Date.now()}`,
6243
6267
  projectDir,
6268
+ abortSignal: abortController.signal,
6244
6269
  maxIterations,
6245
6270
  onEvent
6246
6271
  };
@@ -6320,8 +6345,13 @@ function registerRunCommand(program) {
6320
6345
  } else {
6321
6346
  try {
6322
6347
  const result = await executeWorkflow(config);
6348
+ process.removeListener("SIGINT", onInterrupt);
6349
+ process.removeListener("SIGTERM", onInterrupt);
6323
6350
  renderer.cleanup();
6324
- if (result.success) {
6351
+ if (interrupted) {
6352
+ info(`Interrupted \u2014 ${result.storiesProcessed} stories processed, ${result.tasksCompleted} tasks completed. State saved \u2014 run again to resume.`, outputOpts);
6353
+ process.exitCode = 130;
6354
+ } else if (result.success) {
6325
6355
  ok(`Workflow completed \u2014 ${result.storiesProcessed} stories processed, ${result.tasksCompleted} tasks completed in ${formatElapsed(result.durationMs)}`, outputOpts);
6326
6356
  } else {
6327
6357
  fail(`Workflow failed \u2014 ${result.storiesProcessed} stories processed, ${result.tasksCompleted} tasks completed, ${result.errors.length} error(s) in ${formatElapsed(result.durationMs)}`, outputOpts);
@@ -11115,7 +11145,7 @@ function registerTeardownCommand(program) {
11115
11145
  } else if (otlpMode === "remote-routed") {
11116
11146
  if (!options.keepDocker) {
11117
11147
  try {
11118
- const { stopCollectorOnly: stopCollectorOnly2 } = await import("./docker-QJGQIPTO.js");
11148
+ const { stopCollectorOnly: stopCollectorOnly2 } = await import("./docker-XNAP7B5H.js");
11119
11149
  stopCollectorOnly2();
11120
11150
  result.docker.stopped = true;
11121
11151
  if (!isJson) {
@@ -11147,7 +11177,7 @@ function registerTeardownCommand(program) {
11147
11177
  info("Shared stack: kept running (other projects may use it)");
11148
11178
  }
11149
11179
  } else if (isLegacyStack) {
11150
- const { isStackRunning: isStackRunning2, stopStack } = await import("./docker-QJGQIPTO.js");
11180
+ const { isStackRunning: isStackRunning2, stopStack } = await import("./docker-XNAP7B5H.js");
11151
11181
  let stackRunning = false;
11152
11182
  try {
11153
11183
  stackRunning = isStackRunning2(composeFile);
@@ -14025,7 +14055,7 @@ function registerDriversCommand(program) {
14025
14055
  }
14026
14056
 
14027
14057
  // src/index.ts
14028
- var VERSION = true ? "0.29.2" : "0.0.0-dev";
14058
+ var VERSION = true ? "0.29.3" : "0.0.0-dev";
14029
14059
  function createProgram() {
14030
14060
  const program = new Command();
14031
14061
  program.name("codeharness").description("Makes autonomous coding agents produce software that actually works").version(VERSION).option("--json", "Output in machine-readable JSON format");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeharness",
3
- "version": "0.29.2",
3
+ "version": "0.29.3",
4
4
  "type": "module",
5
5
  "description": "CLI for codeharness — makes autonomous coding agents produce software that actually works",
6
6
  "bin": {