codeharness 0.36.5 → 0.36.6

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.
@@ -1774,7 +1774,7 @@ function checkDocker(opts) {
1774
1774
  }
1775
1775
  function setupDocker(opts) {
1776
1776
  try {
1777
- let state = { ...opts.state };
1777
+ const state = { ...opts.state };
1778
1778
  if (!opts.observability) {
1779
1779
  writeState(state, opts.projectDir);
1780
1780
  if (!opts.isJson) {
@@ -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.36.5" : "0.0.0-dev";
2898
+ var HARNESS_VERSION = true ? "0.36.6" : "0.0.0-dev";
2899
2899
  function failResult(opts, error) {
2900
2900
  return {
2901
2901
  status: "fail",
@@ -3013,7 +3013,7 @@ async function initProjectInner(opts) {
3013
3013
  result.beads = { status: "skipped", message: "beads removed" };
3014
3014
  const bmadResult = setupBmad({ projectDir, isJson });
3015
3015
  if (isOk(bmadResult)) result.bmad = bmadResult.data;
3016
- let state = getDefaultState(stack);
3016
+ const state = getDefaultState(stack);
3017
3017
  state.harness_version = HARNESS_VERSION;
3018
3018
  state.initialized = true;
3019
3019
  state.app_type = appType;
@@ -16,7 +16,7 @@ import {
16
16
  stopCollectorOnly,
17
17
  stopSharedStack,
18
18
  stopStack
19
- } from "./chunk-3UH6SVBO.js";
19
+ } from "./chunk-VB6M7AES.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-3UH6SVBO.js";
43
+ } from "./chunk-VB6M7AES.js";
44
44
 
45
45
  // src/index.ts
46
46
  import { Command } from "commander";
@@ -3171,7 +3171,7 @@ function formatCoverageContextMessage(coverage, target) {
3171
3171
  return `Coverage already verified by engine: ${coverage}% (target: ${target}%). No re-run needed.`;
3172
3172
  }
3173
3173
 
3174
- // src/lib/workflow-actors.ts
3174
+ // src/lib/workflow-constants.ts
3175
3175
  var TASK_PROMPTS = {
3176
3176
  "create-story": (key) => `Create the story spec for ${key}. Read the epic definitions and architecture docs. Write a complete story file with acceptance criteria, tasks, and dev notes. CRITICAL: Every AC must be testable by a blind QA agent using ONLY a user guide + browser/API/CLI access. No AC should reference source code, internal data structures, or implementation details like O(1) complexity. Each AC must describe observable behavior that can be verified through UI interaction (agent-browser), API calls (curl), CLI commands (docker exec), or log inspection (docker logs). Wrap output in <story-spec>...</story-spec> tags.`,
3177
3177
  "implement": (key) => `Implement story ${key}`,
@@ -3192,6 +3192,8 @@ var FILE_WRITE_TOOL_NAMES = /* @__PURE__ */ new Set([
3192
3192
  "WriteFile",
3193
3193
  "EditFile"
3194
3194
  ]);
3195
+
3196
+ // src/lib/workflow-actors.ts
3195
3197
  function buildCoverageDeduplicationContext(contract, projectDir) {
3196
3198
  if (!contract?.testResults) return null;
3197
3199
  const { coverage } = contract.testResults;
@@ -3199,8 +3201,7 @@ function buildCoverageDeduplicationContext(contract, projectDir) {
3199
3201
  try {
3200
3202
  const { state } = readStateWithBody(projectDir);
3201
3203
  if (!state.session_flags.coverage_met) return null;
3202
- const target = state.coverage.target ?? 90;
3203
- return formatCoverageContextMessage(coverage, target);
3204
+ return formatCoverageContextMessage(coverage, state.coverage.target ?? 90);
3204
3205
  } catch {
3205
3206
  return null;
3206
3207
  }
@@ -3215,14 +3216,7 @@ async function nullTaskCore(input) {
3215
3216
  }
3216
3217
  const startMs = Date.now();
3217
3218
  const workflowStartMs = workflowState.started ? new Date(workflowState.started).getTime() : startMs;
3218
- const ctx = {
3219
- storyKey,
3220
- taskName,
3221
- cost: accumulatedCostUsd,
3222
- durationMs: startMs - workflowStartMs,
3223
- outputContract: previousContract,
3224
- projectDir
3225
- };
3219
+ const ctx = { storyKey, taskName, cost: accumulatedCostUsd, durationMs: startMs - workflowStartMs, outputContract: previousContract, projectDir };
3226
3220
  let result;
3227
3221
  try {
3228
3222
  result = await handler(ctx);
@@ -3253,8 +3247,7 @@ async function nullTaskCore(input) {
3253
3247
  return { output: result.output ?? "", cost: 0, changedFiles: [], sessionId: "", contract, updatedState };
3254
3248
  }
3255
3249
  function propagateVerifyFlags(taskName, contract, projectDir) {
3256
- if (taskName !== "implement") return;
3257
- if (!contract?.testResults) return;
3250
+ if (taskName !== "implement" || !contract?.testResults) return;
3258
3251
  const { failed, coverage } = contract.testResults;
3259
3252
  try {
3260
3253
  const { state, body } = readStateWithBody(projectDir);
@@ -3264,8 +3257,7 @@ function propagateVerifyFlags(taskName, contract, projectDir) {
3264
3257
  }
3265
3258
  writeState(state, projectDir, body);
3266
3259
  } catch (err) {
3267
- const msg = err instanceof Error ? err.message : String(err);
3268
- warn(`workflow-actors: flag propagation failed for ${taskName}: ${msg}`);
3260
+ warn(`workflow-actors: flag propagation failed for ${taskName}: ${err instanceof Error ? err.message : String(err)}`);
3269
3261
  }
3270
3262
  }
3271
3263
  async function dispatchTaskCore(input) {
@@ -3290,14 +3282,7 @@ async function dispatchTaskCore(input) {
3290
3282
  } else {
3291
3283
  cwd = projectDir;
3292
3284
  }
3293
- let basePrompt;
3294
- if (customPrompt) {
3295
- basePrompt = customPrompt;
3296
- } else if (TASK_PROMPTS[taskName]) {
3297
- basePrompt = TASK_PROMPTS[taskName](storyKey);
3298
- } else {
3299
- basePrompt = `Execute task "${taskName}" for story ${storyKey}`;
3300
- }
3285
+ const basePrompt = customPrompt ?? (TASK_PROMPTS[taskName]?.(storyKey) ?? `Execute task "${taskName}" for story ${storyKey}`);
3301
3286
  let prompt = buildPromptWithContractContext(basePrompt, previousContract);
3302
3287
  const coverageDedup = buildCoverageDeduplicationContext(previousContract, projectDir);
3303
3288
  if (coverageDedup) prompt = `${prompt}
@@ -3396,8 +3381,7 @@ ${coverageDedup}`;
3396
3381
  };
3397
3382
  writeOutputContract(contract, join12(projectDir, ".codeharness", "contracts"));
3398
3383
  } catch (err) {
3399
- const msg = err instanceof Error ? err.message : String(err);
3400
- warn(`workflow-actors: failed to write output contract for ${taskName}/${storyKey}: ${msg}`);
3384
+ warn(`workflow-actors: failed to write output contract for ${taskName}/${storyKey}: ${err instanceof Error ? err.message : String(err)}`);
3401
3385
  contract = null;
3402
3386
  }
3403
3387
  writeWorkflowState(updatedState, projectDir);
@@ -3405,9 +3389,7 @@ ${coverageDedup}`;
3405
3389
  return { output, cost, changedFiles, sessionId: resultSessionId, contract, updatedState };
3406
3390
  }
3407
3391
  var dispatchActor = fromPromise(async ({ input }) => dispatchTaskCore(input));
3408
- var nullTaskDispatchActor = fromPromise(async ({ input }) => {
3409
- return nullTaskCore(input);
3410
- });
3392
+ var nullTaskDispatchActor = fromPromise(async ({ input }) => nullTaskCore(input));
3411
3393
 
3412
3394
  // src/lib/workflow-machine.ts
3413
3395
  var HALT_ERROR_CODES = /* @__PURE__ */ new Set(["RATE_LIMIT", "NETWORK", "SDK_INIT"]);
@@ -3520,8 +3502,9 @@ function isLoopBlock(step) {
3520
3502
  return typeof step === "object" && step !== null && "loop" in step;
3521
3503
  }
3522
3504
  var loopIterationActor = fromPromise2(async ({ input }) => {
3523
- const { loopBlock, config, workItems, storyFlowTasks, onStreamEvent, maxIterations } = input;
3524
- let { currentState, errors, tasksCompleted, lastContract, lastVerdict, accumulatedCostUsd } = input;
3505
+ const { loopBlock, config, workItems, storyFlowTasks, onStreamEvent, maxIterations: _maxIterations } = input;
3506
+ const { errors } = input;
3507
+ let { currentState, tasksCompleted, lastContract, lastVerdict, accumulatedCostUsd } = input;
3525
3508
  const projectDir = config.projectDir ?? process.cwd();
3526
3509
  const RUN_SENTINEL = "__run__";
3527
3510
  const lastAgentTaskInLoop = (() => {
@@ -3884,7 +3867,8 @@ var storyFlowActor = fromPromise2(async ({ input }) => {
3884
3867
  });
3885
3868
  var epicStepActor = fromPromise2(async ({ input }) => {
3886
3869
  const { epicId, epicItems, config, storyFlowTasks } = input;
3887
- let { workflowState: state, errors, tasksCompleted, storiesProcessed, lastContract, accumulatedCostUsd, halted, currentStepIndex } = input;
3870
+ const { errors, storiesProcessed, currentStepIndex } = input;
3871
+ let { workflowState: state, tasksCompleted, lastContract, accumulatedCostUsd, halted } = input;
3888
3872
  const projectDir = config.projectDir ?? process.cwd();
3889
3873
  const step = config.workflow.epicFlow[currentStepIndex];
3890
3874
  if (!step || halted || config.abortSignal?.aborted) {
@@ -4018,7 +4002,8 @@ var epicMachine = setup({
4018
4002
  });
4019
4003
  var runEpicActor = fromPromise2(async ({ input }) => {
4020
4004
  const { config, storyFlowTasks, epicEntries, currentEpicIndex } = input;
4021
- let { workflowState: state, errors, tasksCompleted, storiesProcessed, lastContract, accumulatedCostUsd, halted } = input;
4005
+ const { errors, storiesProcessed } = input;
4006
+ let { workflowState: state, tasksCompleted, lastContract, accumulatedCostUsd, halted } = input;
4022
4007
  if (currentEpicIndex >= epicEntries.length || halted || config.abortSignal?.aborted) {
4023
4008
  if (config.abortSignal?.aborted) {
4024
4009
  const projectDir = config.projectDir ?? process.cwd();
@@ -5062,12 +5047,12 @@ function LastThought({ text }) {
5062
5047
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: text })
5063
5048
  ] });
5064
5049
  }
5065
- function RetryNotice({ info: info3 }) {
5050
+ function RetryNotice({ info: info2 }) {
5066
5051
  return /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
5067
5052
  "\u23F3 API retry ",
5068
- info3.attempt,
5053
+ info2.attempt,
5069
5054
  " (waiting ",
5070
- info3.delay,
5055
+ info2.delay,
5071
5056
  "ms)"
5072
5057
  ] });
5073
5058
  }
@@ -5132,7 +5117,7 @@ function loopIteration(tasks, taskStates) {
5132
5117
  });
5133
5118
  return anyStarted ? 1 : 0;
5134
5119
  }
5135
- function WorkflowGraph({ flow, currentTask, taskStates, taskMeta }) {
5120
+ function WorkflowGraph({ flow, currentTask: _currentTask, taskStates, taskMeta }) {
5136
5121
  if (flow.length === 0 || Object.keys(taskStates).length === 0) {
5137
5122
  return null;
5138
5123
  }
@@ -5540,12 +5525,12 @@ function Separator() {
5540
5525
  function formatCost2(cost) {
5541
5526
  return `$${cost.toFixed(2)}`;
5542
5527
  }
5543
- function Header({ info: info3, laneCount }) {
5544
- if (!info3) return null;
5528
+ function Header({ info: info2, laneCount }) {
5529
+ if (!info2) return null;
5545
5530
  const parts = ["codeharness run"];
5546
5531
  if (laneCount != null && laneCount > 1) parts.push(`${laneCount} lanes`);
5547
- if (info3.elapsed) parts.push(`${info3.elapsed} elapsed`);
5548
- const displayCost = laneCount != null && laneCount > 1 && info3.laneTotalCost != null ? info3.laneTotalCost : info3.totalCost;
5532
+ if (info2.elapsed) parts.push(`${info2.elapsed} elapsed`);
5533
+ const displayCost = laneCount != null && laneCount > 1 && info2.laneTotalCost != null ? info2.laneTotalCost : info2.totalCost;
5549
5534
  if (displayCost != null) parts.push(`${formatCost2(displayCost)} spent`);
5550
5535
  const left = parts.join(" | ");
5551
5536
  const right = "[q to quit]";
@@ -5574,20 +5559,20 @@ function ProgressBar({ done, total, inProgress }) {
5574
5559
  ` ${label}`
5575
5560
  ] });
5576
5561
  }
5577
- function EpicInfo({ info: info3, stories }) {
5578
- if (!info3?.epicId) return null;
5579
- const title = info3.epicTitle ?? `Epic ${info3.epicId}`;
5580
- const epicPrefix2 = `${info3.epicId}-`;
5562
+ function EpicInfo({ info: info2, stories }) {
5563
+ if (!info2?.epicId) return null;
5564
+ const title = info2.epicTitle ?? `Epic ${info2.epicId}`;
5565
+ const epicPrefix2 = `${info2.epicId}-`;
5581
5566
  const epicStories = stories?.filter((s) => s.key.startsWith(epicPrefix2)) ?? [];
5582
5567
  const ipCount = epicStories.filter((s) => s.status === "in-progress").length;
5583
- const doneCount = info3.epicStoriesDone ?? 0;
5584
- const totalCount = info3.epicStoriesTotal ?? epicStories.length;
5568
+ const doneCount = info2.epicStoriesDone ?? 0;
5569
+ const totalCount = info2.epicStoriesTotal ?? epicStories.length;
5585
5570
  const progressParts = [];
5586
5571
  if (doneCount > 0) progressParts.push(`${doneCount} verified`);
5587
5572
  if (ipCount > 0) progressParts.push(`${ipCount} implemented`);
5588
5573
  const progress = totalCount > 0 ? ` \u2014 ${progressParts.join(", ")} / ${totalCount} stories` : "";
5589
5574
  return /* @__PURE__ */ jsxs8(Text8, { children: [
5590
- /* @__PURE__ */ jsx8(Text8, { bold: true, children: `Epic ${info3.epicId}: ${title}` }),
5575
+ /* @__PURE__ */ jsx8(Text8, { bold: true, children: `Epic ${info2.epicId}: ${title}` }),
5591
5576
  /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: progress })
5592
5577
  ] });
5593
5578
  }
@@ -6356,7 +6341,6 @@ function registerRunCommand(program) {
6356
6341
  }
6357
6342
  }
6358
6343
  if (isEpicTask) {
6359
- const epicStories = storyEntries.filter((s) => s.key.startsWith(`${epicId}-`));
6360
6344
  renderer.updateStories([...storyEntries]);
6361
6345
  }
6362
6346
  }
@@ -6534,7 +6518,7 @@ function registerRunCommand(program) {
6534
6518
  }
6535
6519
 
6536
6520
  // src/commands/verify.ts
6537
- import { existsSync as existsSync28, readFileSync as readFileSync25 } from "fs";
6521
+ import { existsSync as existsSync27, readFileSync as readFileSync25 } from "fs";
6538
6522
  import { join as join29 } from "path";
6539
6523
 
6540
6524
  // src/modules/verify/index.ts
@@ -7353,7 +7337,7 @@ function closeBeadsIssue(_storyId, _dir) {
7353
7337
  }
7354
7338
 
7355
7339
  // src/modules/verify/parser.ts
7356
- import { existsSync as existsSync23, readFileSync as readFileSync19 } from "fs";
7340
+ import { existsSync as existsSync22, readFileSync as readFileSync19 } from "fs";
7357
7341
 
7358
7342
  // src/modules/verify/parser-keywords.ts
7359
7343
  var UI_KEYWORDS = [
@@ -7397,7 +7381,7 @@ function classifyAC(description) {
7397
7381
  return "general";
7398
7382
  }
7399
7383
  function parseStoryACs(storyFilePath) {
7400
- if (!existsSync23(storyFilePath)) {
7384
+ if (!existsSync22(storyFilePath)) {
7401
7385
  throw new Error(
7402
7386
  `Story file not found: ${storyFilePath}. Ensure the story file exists at the expected path.`
7403
7387
  );
@@ -7613,7 +7597,7 @@ function normalizeSeverity(severity) {
7613
7597
  }
7614
7598
 
7615
7599
  // src/modules/observability/coverage.ts
7616
- import { readFileSync as readFileSync20, writeFileSync as writeFileSync11, renameSync as renameSync3, existsSync as existsSync24 } from "fs";
7600
+ import { readFileSync as readFileSync20, writeFileSync as writeFileSync11, renameSync as renameSync3, existsSync as existsSync23 } from "fs";
7617
7601
  import { join as join23 } from "path";
7618
7602
  var STATE_FILE2 = "sprint-state.json";
7619
7603
  var DEFAULT_STATIC_TARGET = 80;
@@ -7631,7 +7615,7 @@ function defaultCoverageState() {
7631
7615
  }
7632
7616
  function readStateFile(projectDir) {
7633
7617
  const fp = join23(projectDir, STATE_FILE2);
7634
- if (!existsSync24(fp)) {
7618
+ if (!existsSync23(fp)) {
7635
7619
  return ok2({});
7636
7620
  }
7637
7621
  try {
@@ -7702,7 +7686,7 @@ function parseGapArray(raw) {
7702
7686
  }
7703
7687
 
7704
7688
  // src/modules/observability/runtime-coverage.ts
7705
- import { readFileSync as readFileSync21, writeFileSync as writeFileSync12, renameSync as renameSync4, existsSync as existsSync25 } from "fs";
7689
+ import { readFileSync as readFileSync21, writeFileSync as writeFileSync12, renameSync as renameSync4, existsSync as existsSync24 } from "fs";
7706
7690
  import { join as join24 } from "path";
7707
7691
 
7708
7692
  // src/modules/observability/coverage-gate.ts
@@ -7906,7 +7890,7 @@ function parseLogEvents(text) {
7906
7890
 
7907
7891
  // src/modules/verify/browser.ts
7908
7892
  import { execFileSync as execFileSync3 } from "child_process";
7909
- import { existsSync as existsSync26, readFileSync as readFileSync22 } from "fs";
7893
+ import { existsSync as existsSync25, readFileSync as readFileSync22 } from "fs";
7910
7894
 
7911
7895
  // src/modules/verify/validation-ac-fr.ts
7912
7896
  var FR_ACS = [
@@ -8545,7 +8529,7 @@ function getACById(id) {
8545
8529
  // src/modules/verify/validation-runner.ts
8546
8530
  import { execSync as execSync5 } from "child_process";
8547
8531
  import { writeFileSync as writeFileSync13, mkdirSync as mkdirSync10 } from "fs";
8548
- import { join as join26, dirname as dirname3 } from "path";
8532
+ import { join as join26, dirname as dirname2 } from "path";
8549
8533
  var MAX_VALIDATION_ATTEMPTS = 10;
8550
8534
  var AC_COMMAND_TIMEOUT_MS = 3e4;
8551
8535
  var VAL_KEY_PREFIX = "val-";
@@ -8697,7 +8681,7 @@ function createFixStory(ac, error) {
8697
8681
  "Fix the root cause so the validation command passes.",
8698
8682
  ""
8699
8683
  ].join("\n");
8700
- mkdirSync10(dirname3(storyPath), { recursive: true });
8684
+ mkdirSync10(dirname2(storyPath), { recursive: true });
8701
8685
  writeFileSync13(storyPath, markdown, "utf-8");
8702
8686
  return ok2(storyKey);
8703
8687
  } catch (err) {
@@ -9024,7 +9008,7 @@ function runValidationCycle() {
9024
9008
 
9025
9009
  // src/modules/verify/env.ts
9026
9010
  import { execFileSync as execFileSync5 } from "child_process";
9027
- import { existsSync as existsSync27, mkdirSync as mkdirSync11, readdirSync as readdirSync7, readFileSync as readFileSync23, writeFileSync as writeFileSync14, cpSync, rmSync as rmSync3, statSync as statSync6 } from "fs";
9011
+ import { existsSync as existsSync26, mkdirSync as mkdirSync11, readdirSync as readdirSync7, readFileSync as readFileSync23, writeFileSync as writeFileSync14, cpSync, rmSync as rmSync3, statSync as statSync6 } from "fs";
9028
9012
  import { join as join28, basename as basename2 } from "path";
9029
9013
  import { createHash } from "crypto";
9030
9014
 
@@ -9079,7 +9063,7 @@ function isValidStoryKey(storyKey) {
9079
9063
  }
9080
9064
  function computeDistHash(projectDir) {
9081
9065
  const distDir = join28(projectDir, "dist");
9082
- if (!existsSync27(distDir)) return null;
9066
+ if (!existsSync26(distDir)) return null;
9083
9067
  const hash = createHash("sha256");
9084
9068
  const files = collectFiles(distDir).sort();
9085
9069
  for (const file of files) {
@@ -9127,7 +9111,7 @@ function detectProjectType(projectDir) {
9127
9111
  const rootDetection = allStacks.find((s) => s.dir === ".");
9128
9112
  const stack = rootDetection ? rootDetection.stack : null;
9129
9113
  if (stack && STACK_TO_PROJECT_TYPE[stack]) return STACK_TO_PROJECT_TYPE[stack];
9130
- if (existsSync27(join28(projectDir, ".claude-plugin", "plugin.json"))) return "plugin";
9114
+ if (existsSync26(join28(projectDir, ".claude-plugin", "plugin.json"))) return "plugin";
9131
9115
  return "generic";
9132
9116
  }
9133
9117
  function buildVerifyImage(options = {}) {
@@ -9225,15 +9209,15 @@ function prepareVerifyWorkspace(storyKey, projectDir) {
9225
9209
  throw new Error(`Invalid story key: ${storyKey}. Keys must contain only alphanumeric characters, hyphens, and underscores.`);
9226
9210
  }
9227
9211
  const storyFile = join28(root, STORY_DIR, `${storyKey}.md`);
9228
- if (!existsSync27(storyFile)) throw new Error(`Story file not found: ${storyFile}`);
9212
+ if (!existsSync26(storyFile)) throw new Error(`Story file not found: ${storyFile}`);
9229
9213
  const workspace = `${TEMP_PREFIX}${storyKey}`;
9230
- if (existsSync27(workspace)) rmSync3(workspace, { recursive: true, force: true });
9214
+ if (existsSync26(workspace)) rmSync3(workspace, { recursive: true, force: true });
9231
9215
  mkdirSync11(workspace, { recursive: true });
9232
9216
  cpSync(storyFile, join28(workspace, "story.md"));
9233
9217
  const readmePath = join28(root, "README.md");
9234
- if (existsSync27(readmePath)) cpSync(readmePath, join28(workspace, "README.md"));
9218
+ if (existsSync26(readmePath)) cpSync(readmePath, join28(workspace, "README.md"));
9235
9219
  const docsDir = join28(root, "docs");
9236
- if (existsSync27(docsDir) && statSync6(docsDir).isDirectory()) {
9220
+ if (existsSync26(docsDir) && statSync6(docsDir).isDirectory()) {
9237
9221
  cpSync(docsDir, join28(workspace, "docs"), { recursive: true });
9238
9222
  }
9239
9223
  mkdirSync11(join28(workspace, "verification"), { recursive: true });
@@ -9278,7 +9262,7 @@ function cleanupVerifyEnv(storyKey) {
9278
9262
  }
9279
9263
  const workspace = `${TEMP_PREFIX}${storyKey}`;
9280
9264
  const containerName = `codeharness-verify-${storyKey}`;
9281
- if (existsSync27(workspace)) rmSync3(workspace, { recursive: true, force: true });
9265
+ if (existsSync26(workspace)) rmSync3(workspace, { recursive: true, force: true });
9282
9266
  try {
9283
9267
  execFileSync5("docker", ["stop", containerName], { stdio: "pipe", timeout: 15e3 });
9284
9268
  } catch {
@@ -9296,7 +9280,7 @@ function buildPluginImage(projectDir) {
9296
9280
  cpSync(pluginDir, join28(buildContext, ".claude-plugin"), { recursive: true });
9297
9281
  for (const dir of ["commands", "hooks", "knowledge", "skills"]) {
9298
9282
  const src = join28(projectDir, dir);
9299
- if (existsSync27(src) && statSync6(src).isDirectory()) {
9283
+ if (existsSync26(src) && statSync6(src).isDirectory()) {
9300
9284
  cpSync(src, join28(buildContext, dir), { recursive: true });
9301
9285
  }
9302
9286
  }
@@ -9397,7 +9381,7 @@ function verifyRetro(opts, isJson, root) {
9397
9381
  }
9398
9382
  const retroFile = `epic-${epicNum}-retrospective.md`;
9399
9383
  const retroPath = join29(root, STORY_DIR2, retroFile);
9400
- if (!existsSync28(retroPath)) {
9384
+ if (!existsSync27(retroPath)) {
9401
9385
  if (isJson) {
9402
9386
  jsonOutput({ status: "fail", epic: epicNum, retroFile, message: `${retroFile} not found` });
9403
9387
  } else {
@@ -9426,7 +9410,7 @@ function verifyStory(storyId, isJson, root) {
9426
9410
  return;
9427
9411
  }
9428
9412
  const readmePath = join29(root, "README.md");
9429
- if (!existsSync28(readmePath)) {
9413
+ if (!existsSync27(readmePath)) {
9430
9414
  if (isJson) {
9431
9415
  jsonOutput({ status: "fail", message: "No README.md found \u2014 verification requires user documentation" });
9432
9416
  } else {
@@ -9436,7 +9420,7 @@ function verifyStory(storyId, isJson, root) {
9436
9420
  return;
9437
9421
  }
9438
9422
  const storyFilePath = join29(root, STORY_DIR2, `${storyId}.md`);
9439
- if (!existsSync28(storyFilePath)) {
9423
+ if (!existsSync27(storyFilePath)) {
9440
9424
  fail(`Story file not found: ${storyFilePath}`, { json: isJson });
9441
9425
  process.exitCode = 1;
9442
9426
  return;
@@ -9477,7 +9461,7 @@ function verifyStory(storyId, isJson, root) {
9477
9461
  }
9478
9462
  const storyTitle = extractStoryTitle(storyFilePath);
9479
9463
  const expectedProofPath = join29(root, "verification", `${storyId}-proof.md`);
9480
- const proofPath = existsSync28(expectedProofPath) ? expectedProofPath : createProofDocument(storyId, storyTitle, acs, root);
9464
+ const proofPath = existsSync27(expectedProofPath) ? expectedProofPath : createProofDocument(storyId, storyTitle, acs, root);
9481
9465
  const proofQuality = validateProofQuality(proofPath);
9482
9466
  if (!proofQuality.passed) {
9483
9467
  if (isJson) {
@@ -9647,11 +9631,11 @@ function resolveEndpoints(state) {
9647
9631
  }
9648
9632
 
9649
9633
  // src/lib/onboard-checks.ts
9650
- import { existsSync as existsSync32 } from "fs";
9634
+ import { existsSync as existsSync30 } from "fs";
9651
9635
  import { join as join32 } from "path";
9652
9636
 
9653
9637
  // src/lib/coverage/parser.ts
9654
- import { existsSync as existsSync29, readFileSync as readFileSync26 } from "fs";
9638
+ import { existsSync as existsSync28, readFileSync as readFileSync26 } from "fs";
9655
9639
  import { join as join30 } from "path";
9656
9640
  function parseTestCounts(output) {
9657
9641
  const vitestMatch = /Tests\s+(\d+)\s+passed(?:\s*\|\s*(\d+)\s+failed)?/i.exec(output);
@@ -9717,7 +9701,7 @@ function parseVitestCoverage(dir) {
9717
9701
  }
9718
9702
  function parsePythonCoverage(dir) {
9719
9703
  const reportPath = join30(dir, "coverage.json");
9720
- if (!existsSync29(reportPath)) {
9704
+ if (!existsSync28(reportPath)) {
9721
9705
  warn("Coverage report not found at coverage.json");
9722
9706
  return 0;
9723
9707
  }
@@ -9731,7 +9715,7 @@ function parsePythonCoverage(dir) {
9731
9715
  }
9732
9716
  function parseTarpaulinCoverage(dir) {
9733
9717
  const reportPath = join30(dir, "coverage", "tarpaulin-report.json");
9734
- if (!existsSync29(reportPath)) {
9718
+ if (!existsSync28(reportPath)) {
9735
9719
  warn("Tarpaulin report not found at coverage/tarpaulin-report.json");
9736
9720
  return 0;
9737
9721
  }
@@ -9749,14 +9733,14 @@ function findCoverageSummary(dir) {
9749
9733
  join30(dir, "src", "coverage", "coverage-summary.json")
9750
9734
  ];
9751
9735
  for (const p of candidates) {
9752
- if (existsSync29(p)) return p;
9736
+ if (existsSync28(p)) return p;
9753
9737
  }
9754
9738
  return null;
9755
9739
  }
9756
9740
 
9757
9741
  // src/lib/coverage/runner.ts
9758
9742
  import { execSync as execSync7 } from "child_process";
9759
- import { existsSync as existsSync30, readFileSync as readFileSync27 } from "fs";
9743
+ import { existsSync as existsSync29, readFileSync as readFileSync27 } from "fs";
9760
9744
  import { join as join31 } from "path";
9761
9745
  function detectCoverageTool(dir) {
9762
9746
  const baseDir = dir ?? process.cwd();
@@ -9813,14 +9797,14 @@ function getStateToolHint(dir) {
9813
9797
  }
9814
9798
  }
9815
9799
  function detectNodeCoverageTool(dir, stateHint) {
9816
- const hasVitestConfig = existsSync30(join31(dir, "vitest.config.ts")) || existsSync30(join31(dir, "vitest.config.js"));
9800
+ const hasVitestConfig = existsSync29(join31(dir, "vitest.config.ts")) || existsSync29(join31(dir, "vitest.config.js"));
9817
9801
  const pkgPath = join31(dir, "package.json");
9818
9802
  let hasVitestCoverageV8 = false;
9819
9803
  let hasVitestCoverageIstanbul = false;
9820
9804
  let hasC8 = false;
9821
9805
  let hasJest = false;
9822
9806
  let pkgScripts = {};
9823
- if (existsSync30(pkgPath)) {
9807
+ if (existsSync29(pkgPath)) {
9824
9808
  try {
9825
9809
  const pkg = JSON.parse(readFileSync27(pkgPath, "utf-8"));
9826
9810
  const allDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
@@ -9876,7 +9860,7 @@ function getNodeTestCommand(scripts, runner) {
9876
9860
  }
9877
9861
  function detectPythonCoverageTool(dir) {
9878
9862
  const reqPath = join31(dir, "requirements.txt");
9879
- if (existsSync30(reqPath)) {
9863
+ if (existsSync29(reqPath)) {
9880
9864
  try {
9881
9865
  const content = readFileSync27(reqPath, "utf-8");
9882
9866
  if (content.includes("pytest-cov") || content.includes("coverage")) {
@@ -9890,7 +9874,7 @@ function detectPythonCoverageTool(dir) {
9890
9874
  }
9891
9875
  }
9892
9876
  const pyprojectPath = join31(dir, "pyproject.toml");
9893
- if (existsSync30(pyprojectPath)) {
9877
+ if (existsSync29(pyprojectPath)) {
9894
9878
  try {
9895
9879
  const content = readFileSync27(pyprojectPath, "utf-8");
9896
9880
  if (content.includes("pytest-cov") || content.includes("coverage")) {
@@ -10102,7 +10086,7 @@ function printCoverageOutput(result, evaluation) {
10102
10086
  // src/lib/onboard-checks.ts
10103
10087
  function checkHarnessInitialized(dir) {
10104
10088
  const statePath2 = getStatePath(dir ?? process.cwd());
10105
- return { ok: existsSync32(statePath2) };
10089
+ return { ok: existsSync30(statePath2) };
10106
10090
  }
10107
10091
  function checkBmadInstalled(dir) {
10108
10092
  return { ok: isBmadInstalled(dir) };
@@ -10721,7 +10705,7 @@ function registerStatusCommand(program) {
10721
10705
  }
10722
10706
 
10723
10707
  // src/modules/audit/dimensions.ts
10724
- import { existsSync as existsSync33, readdirSync as readdirSync8 } from "fs";
10708
+ import { existsSync as existsSync31, readdirSync as readdirSync8 } from "fs";
10725
10709
  import { join as join33 } from "path";
10726
10710
  function gap(dimension, description, suggestedFix) {
10727
10711
  return { dimension, description, suggestedFix };
@@ -10835,10 +10819,10 @@ function checkVerification(projectDir) {
10835
10819
  try {
10836
10820
  const gaps = [];
10837
10821
  const sprintPath = join33(projectDir, "_bmad-output", "implementation-artifacts", "sprint-status.yaml");
10838
- if (!existsSync33(sprintPath)) return dimOk("verification", "warn", "no sprint data", [gap("verification", "No sprint-status.yaml found", "Run sprint planning to create sprint status")]);
10822
+ if (!existsSync31(sprintPath)) return dimOk("verification", "warn", "no sprint data", [gap("verification", "No sprint-status.yaml found", "Run sprint planning to create sprint status")]);
10839
10823
  const vDir = join33(projectDir, "verification");
10840
10824
  let proofCount = 0, totalChecked = 0;
10841
- if (existsSync33(vDir)) {
10825
+ if (existsSync31(vDir)) {
10842
10826
  for (const file of readdirSafe(vDir)) {
10843
10827
  if (!file.endsWith("-proof.md")) continue;
10844
10828
  totalChecked++;
@@ -10919,13 +10903,13 @@ function formatAuditJson(result) {
10919
10903
  }
10920
10904
 
10921
10905
  // src/modules/audit/fix-generator.ts
10922
- import { existsSync as existsSync34, writeFileSync as writeFileSync15, mkdirSync as mkdirSync12 } from "fs";
10923
- import { join as join34, dirname as dirname5 } from "path";
10906
+ import { existsSync as existsSync32, writeFileSync as writeFileSync15, mkdirSync as mkdirSync12 } from "fs";
10907
+ import { join as join34, dirname as dirname3 } from "path";
10924
10908
  function buildStoryKey(gap2, index) {
10925
10909
  const safeDimension = gap2.dimension.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
10926
10910
  return `audit-fix-${safeDimension}-${index}`;
10927
10911
  }
10928
- function buildStoryMarkdown(gap2, key) {
10912
+ function buildStoryMarkdown(gap2, _key) {
10929
10913
  return [
10930
10914
  `# Fix: ${gap2.dimension} \u2014 ${gap2.description}`,
10931
10915
  "",
@@ -10962,7 +10946,7 @@ function generateFixStories(auditResult) {
10962
10946
  const gap2 = dimension.gaps[i];
10963
10947
  const key = buildStoryKey(gap2, i + 1);
10964
10948
  const filePath = join34(artifactsDir, `${key}.md`);
10965
- if (existsSync34(filePath)) {
10949
+ if (existsSync32(filePath)) {
10966
10950
  stories.push({
10967
10951
  key,
10968
10952
  filePath,
@@ -10974,7 +10958,7 @@ function generateFixStories(auditResult) {
10974
10958
  continue;
10975
10959
  }
10976
10960
  const markdown = buildStoryMarkdown(gap2, key);
10977
- mkdirSync12(dirname5(filePath), { recursive: true });
10961
+ mkdirSync12(dirname3(filePath), { recursive: true });
10978
10962
  writeFileSync15(filePath, markdown, "utf-8");
10979
10963
  stories.push({ key, filePath, gap: gap2, skipped: false });
10980
10964
  created++;
@@ -11151,7 +11135,7 @@ function registerOnboardCommand(program) {
11151
11135
  }
11152
11136
 
11153
11137
  // src/commands/teardown.ts
11154
- import { existsSync as existsSync35, unlinkSync as unlinkSync3, readFileSync as readFileSync29, writeFileSync as writeFileSync16, rmSync as rmSync4 } from "fs";
11138
+ import { existsSync as existsSync33, unlinkSync as unlinkSync3, readFileSync as readFileSync29, writeFileSync as writeFileSync16, rmSync as rmSync4 } from "fs";
11155
11139
  import { join as join35 } from "path";
11156
11140
  function buildDefaultResult() {
11157
11141
  return {
@@ -11198,7 +11182,7 @@ function registerTeardownCommand(program) {
11198
11182
  } else if (otlpMode === "remote-routed") {
11199
11183
  if (!options.keepDocker) {
11200
11184
  try {
11201
- const { stopCollectorOnly: stopCollectorOnly2 } = await import("./docker-LNVG4NBV.js");
11185
+ const { stopCollectorOnly: stopCollectorOnly2 } = await import("./docker-W7JLB33I.js");
11202
11186
  stopCollectorOnly2();
11203
11187
  result.docker.stopped = true;
11204
11188
  if (!isJson) {
@@ -11230,7 +11214,7 @@ function registerTeardownCommand(program) {
11230
11214
  info("Shared stack: kept running (other projects may use it)");
11231
11215
  }
11232
11216
  } else if (isLegacyStack) {
11233
- const { isStackRunning: isStackRunning2, stopStack } = await import("./docker-LNVG4NBV.js");
11217
+ const { isStackRunning: isStackRunning2, stopStack } = await import("./docker-W7JLB33I.js");
11234
11218
  let stackRunning = false;
11235
11219
  try {
11236
11220
  stackRunning = isStackRunning2(composeFile);
@@ -11256,7 +11240,7 @@ function registerTeardownCommand(program) {
11256
11240
  }
11257
11241
  }
11258
11242
  const composeFilePath = join35(projectDir, composeFile);
11259
- if (existsSync35(composeFilePath)) {
11243
+ if (existsSync33(composeFilePath)) {
11260
11244
  unlinkSync3(composeFilePath);
11261
11245
  result.removed.push(composeFile);
11262
11246
  if (!isJson) {
@@ -11264,7 +11248,7 @@ function registerTeardownCommand(program) {
11264
11248
  }
11265
11249
  }
11266
11250
  const otelConfigPath = join35(projectDir, "otel-collector-config.yaml");
11267
- if (existsSync35(otelConfigPath)) {
11251
+ if (existsSync33(otelConfigPath)) {
11268
11252
  unlinkSync3(otelConfigPath);
11269
11253
  result.removed.push("otel-collector-config.yaml");
11270
11254
  if (!isJson) {
@@ -11284,7 +11268,7 @@ function registerTeardownCommand(program) {
11284
11268
  const stacks = state.stacks ?? (state.stack ? [state.stack] : []);
11285
11269
  if (state.otlp?.enabled && stacks.includes("nodejs")) {
11286
11270
  const pkgPath = join35(projectDir, "package.json");
11287
- if (existsSync35(pkgPath)) {
11271
+ if (existsSync33(pkgPath)) {
11288
11272
  try {
11289
11273
  const raw = readFileSync29(pkgPath, "utf-8");
11290
11274
  const pkg = JSON.parse(raw);
@@ -11327,7 +11311,7 @@ function registerTeardownCommand(program) {
11327
11311
  }
11328
11312
  }
11329
11313
  const harnessDir = join35(projectDir, ".harness");
11330
- if (existsSync35(harnessDir)) {
11314
+ if (existsSync33(harnessDir)) {
11331
11315
  rmSync4(harnessDir, { recursive: true, force: true });
11332
11316
  result.removed.push(".harness/");
11333
11317
  if (!isJson) {
@@ -11335,7 +11319,7 @@ function registerTeardownCommand(program) {
11335
11319
  }
11336
11320
  }
11337
11321
  const statePath2 = getStatePath(projectDir);
11338
- if (existsSync35(statePath2)) {
11322
+ if (existsSync33(statePath2)) {
11339
11323
  unlinkSync3(statePath2);
11340
11324
  result.removed.push(".claude/codeharness.local.md");
11341
11325
  if (!isJson) {
@@ -12016,7 +12000,7 @@ function registerQueryCommand(program) {
12016
12000
  }
12017
12001
 
12018
12002
  // src/commands/retro-import.ts
12019
- import { existsSync as existsSync37, readFileSync as readFileSync31 } from "fs";
12003
+ import { existsSync as existsSync35, readFileSync as readFileSync31 } from "fs";
12020
12004
  import { join as join37 } from "path";
12021
12005
 
12022
12006
  // src/lib/retro-parser.ts
@@ -12134,7 +12118,7 @@ function isDuplicate(newItem, existingTitles, threshold = 0.8) {
12134
12118
  }
12135
12119
 
12136
12120
  // src/lib/issue-tracker.ts
12137
- import { existsSync as existsSync36, readFileSync as readFileSync30, writeFileSync as writeFileSync17, mkdirSync as mkdirSync13 } from "fs";
12121
+ import { existsSync as existsSync34, readFileSync as readFileSync30, writeFileSync as writeFileSync17, mkdirSync as mkdirSync13 } from "fs";
12138
12122
  import { join as join36 } from "path";
12139
12123
  import { parse as parse6, stringify as stringify3 } from "yaml";
12140
12124
  var VALID_PRIORITIES = /* @__PURE__ */ new Set([
@@ -12149,7 +12133,7 @@ function issuesPath(dir) {
12149
12133
  }
12150
12134
  function readIssues(dir = process.cwd()) {
12151
12135
  const filePath = issuesPath(dir);
12152
- if (!existsSync36(filePath)) {
12136
+ if (!existsSync34(filePath)) {
12153
12137
  return { issues: [] };
12154
12138
  }
12155
12139
  const raw = readFileSync30(filePath, "utf-8");
@@ -12162,7 +12146,7 @@ function readIssues(dir = process.cwd()) {
12162
12146
  function writeIssues(data, dir = process.cwd()) {
12163
12147
  const filePath = issuesPath(dir);
12164
12148
  const dirPath = join36(dir, ".codeharness");
12165
- if (!existsSync36(dirPath)) {
12149
+ if (!existsSync34(dirPath)) {
12166
12150
  mkdirSync13(dirPath, { recursive: true });
12167
12151
  }
12168
12152
  writeFileSync17(filePath, stringify3(data, { nullStr: "" }), "utf-8");
@@ -12323,7 +12307,7 @@ function registerRetroImportCommand(program) {
12323
12307
  }
12324
12308
  const retroFile = `epic-${epicNum}-retrospective.md`;
12325
12309
  const retroPath = join37(root, STORY_DIR3, retroFile);
12326
- if (!existsSync37(retroPath)) {
12310
+ if (!existsSync35(retroPath)) {
12327
12311
  fail(`Retro file not found: ${retroFile}`, { json: isJson });
12328
12312
  process.exitCode = 1;
12329
12313
  return;
@@ -12822,7 +12806,7 @@ function registerValidateStateCommand(program) {
12822
12806
  }
12823
12807
 
12824
12808
  // src/commands/validate-schema.ts
12825
- import { readdirSync as readdirSync9, existsSync as existsSync38 } from "fs";
12809
+ import { readdirSync as readdirSync9, existsSync as existsSync36 } from "fs";
12826
12810
  import { join as join38, resolve as resolve6 } from "path";
12827
12811
  function renderSchemaResult(result, isJson) {
12828
12812
  if (isJson) {
@@ -12844,7 +12828,7 @@ function renderSchemaResult(result, isJson) {
12844
12828
  }
12845
12829
  function runSchemaValidation(projectDir) {
12846
12830
  const workflowsDir = join38(projectDir, ".codeharness", "workflows");
12847
- if (!existsSync38(workflowsDir)) {
12831
+ if (!existsSync36(workflowsDir)) {
12848
12832
  return {
12849
12833
  status: "fail",
12850
12834
  files: [{
@@ -13151,7 +13135,7 @@ function registerAuditCommand(program) {
13151
13135
  }
13152
13136
 
13153
13137
  // src/commands/stats.ts
13154
- import { existsSync as existsSync39, readdirSync as readdirSync10, readFileSync as readFileSync32, writeFileSync as writeFileSync18 } from "fs";
13138
+ import { existsSync as existsSync37, readdirSync as readdirSync10, readFileSync as readFileSync32, writeFileSync as writeFileSync18 } from "fs";
13155
13139
  import { join as join39 } from "path";
13156
13140
  var RATES = {
13157
13141
  input: 15,
@@ -13175,7 +13159,7 @@ function addToBucket(target, input, output, cacheRead, cacheWrite) {
13175
13159
  function parseLogFile(filePath, report) {
13176
13160
  const basename3 = filePath.split("/").pop() ?? "";
13177
13161
  const dateMatch = basename3.match(/(\d{4}-\d{2}-\d{2})/);
13178
- const date = dateMatch ? dateMatch[1] : "unknown";
13162
+ const _date = dateMatch ? dateMatch[1] : "unknown";
13179
13163
  let currentPhase = "orchestrator";
13180
13164
  let currentStory = "unknown";
13181
13165
  let currentTool = "";
@@ -13239,7 +13223,7 @@ function parseLogFile(filePath, report) {
13239
13223
  function generateReport3(projectDir, logsDir) {
13240
13224
  const ralphLogs = join39(projectDir, "ralph", "logs");
13241
13225
  const sessionLogs = join39(projectDir, "session-logs");
13242
- const resolvedLogsDir = logsDir ?? (existsSync39(ralphLogs) ? ralphLogs : sessionLogs);
13226
+ const resolvedLogsDir = logsDir ?? (existsSync37(ralphLogs) ? ralphLogs : sessionLogs);
13243
13227
  const logFiles = readdirSync10(resolvedLogsDir).filter((f) => f.endsWith(".log")).sort().map((f) => join39(resolvedLogsDir, f));
13244
13228
  const report = {
13245
13229
  byPhase: /* @__PURE__ */ new Map(),
@@ -13345,9 +13329,9 @@ function registerStatsCommand(program) {
13345
13329
  } else {
13346
13330
  const ralphLogs = join39(projectDir, "ralph", "logs");
13347
13331
  const sessionLogs = join39(projectDir, "session-logs");
13348
- logsDir = existsSync39(ralphLogs) ? ralphLogs : sessionLogs;
13332
+ logsDir = existsSync37(ralphLogs) ? ralphLogs : sessionLogs;
13349
13333
  }
13350
- if (!existsSync39(logsDir)) {
13334
+ if (!existsSync37(logsDir)) {
13351
13335
  fail("No logs directory found \u2014 checked ralph/logs/ and session-logs/. Run codeharness run first or use --logs-dir <path>");
13352
13336
  process.exitCode = 1;
13353
13337
  return;
@@ -13749,7 +13733,6 @@ function parseLine(line) {
13749
13733
  if (type === "item.started" && item) {
13750
13734
  const itemType = item.type;
13751
13735
  if (itemType === "command_execution") {
13752
- const cmd = item.command;
13753
13736
  return { type: "tool-start", name: "Bash", id: item.id ?? "" };
13754
13737
  }
13755
13738
  if (itemType === "file_edit") {
@@ -13763,7 +13746,6 @@ function parseLine(line) {
13763
13746
  if (type === "item.completed" && item) {
13764
13747
  const itemType = item.type;
13765
13748
  if (itemType === "command_execution") {
13766
- const cmd = item.command;
13767
13749
  return { type: "tool-complete" };
13768
13750
  }
13769
13751
  if (itemType === "agent_message") {
@@ -14217,7 +14199,7 @@ function registerDriversCommand(program) {
14217
14199
  }
14218
14200
 
14219
14201
  // src/index.ts
14220
- var VERSION = true ? "0.36.5" : "0.0.0-dev";
14202
+ var VERSION = true ? "0.36.6" : "0.0.0-dev";
14221
14203
  function createProgram() {
14222
14204
  const program = new Command();
14223
14205
  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.36.5",
3
+ "version": "0.36.6",
4
4
  "type": "module",
5
5
  "description": "CLI for codeharness — makes autonomous coding agents produce software that actually works",
6
6
  "bin": {