codeharness 0.16.1 → 0.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -464,6 +464,10 @@ var WEB_OTLP_PACKAGES = [
464
464
  var AGENT_OTLP_PACKAGES_NODE = ["@traceloop/node-server-sdk"];
465
465
  var AGENT_OTLP_PACKAGES_PYTHON = ["traceloop-sdk"];
466
466
  var NODE_REQUIRE_FLAG = "--require @opentelemetry/auto-instrumentations-node/register";
467
+ function truncateError(message, maxLength = 200) {
468
+ if (message.length <= maxLength) return message;
469
+ return message.slice(0, maxLength) + "... (truncated)";
470
+ }
467
471
  function installNodeOtlp(projectDir) {
468
472
  try {
469
473
  execFileSync2("npm", ["install", ...NODE_OTLP_PACKAGES], { cwd: projectDir, stdio: "pipe", timeout: 3e5 });
@@ -480,7 +484,7 @@ function installNodeOtlp(projectDir) {
480
484
  packages_installed: false,
481
485
  start_script_patched: false,
482
486
  env_vars_configured: false,
483
- error: `Failed to install Node.js OTLP packages: ${message}`
487
+ error: `Failed to install Node.js OTLP packages: ${truncateError(message)}`
484
488
  };
485
489
  }
486
490
  }
@@ -489,8 +493,14 @@ function patchNodeStartScript(projectDir) {
489
493
  if (!existsSync3(pkgPath)) {
490
494
  return false;
491
495
  }
492
- const raw = readFileSync3(pkgPath, "utf-8");
493
- const pkg = JSON.parse(raw);
496
+ let raw;
497
+ let pkg;
498
+ try {
499
+ raw = readFileSync3(pkgPath, "utf-8");
500
+ pkg = JSON.parse(raw);
501
+ } catch {
502
+ return false;
503
+ }
494
504
  const scripts = pkg["scripts"];
495
505
  if (!scripts) {
496
506
  return false;
@@ -554,6 +564,14 @@ function configureWeb(projectDir, stack) {
554
564
  } catch {
555
565
  }
556
566
  }
567
+ let endpoint = "http://localhost:4318";
568
+ try {
569
+ const currentState = readState(projectDir);
570
+ if (currentState.otlp?.endpoint) {
571
+ endpoint = currentState.otlp.endpoint;
572
+ }
573
+ } catch {
574
+ }
557
575
  const snippet = `// OpenTelemetry Web SDK initialization \u2014 generated by codeharness
558
576
  import { WebTracerProvider } from '@opentelemetry/sdk-trace-web';
559
577
  import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
@@ -563,7 +581,7 @@ import { XMLHttpRequestInstrumentation } from '@opentelemetry/instrumentation-xm
563
581
  import { registerInstrumentations } from '@opentelemetry/instrumentation';
564
582
 
565
583
  const exporter = new OTLPTraceExporter({
566
- url: 'http://localhost:4318/v1/traces',
584
+ url: '${endpoint}/v1/traces',
567
585
  });
568
586
 
569
587
  const provider = new WebTracerProvider();
@@ -675,12 +693,12 @@ function instrumentProject(projectDir, stack, opts) {
675
693
  error: "Unsupported stack for OTLP instrumentation"
676
694
  };
677
695
  }
696
+ configureOtlpEnvVars(projectDir, stack, { appType });
697
+ result.env_vars_configured = true;
698
+ if (!isJson) {
699
+ ok("OTLP: environment variables configured");
700
+ }
678
701
  if (result.status === "configured") {
679
- configureOtlpEnvVars(projectDir, stack, { appType });
680
- result.env_vars_configured = true;
681
- if (!isJson) {
682
- ok("OTLP: environment variables configured");
683
- }
684
702
  if (appType === "cli") {
685
703
  configureCli(projectDir);
686
704
  if (!isJson) {
@@ -874,6 +892,11 @@ ${patchContent}
874
892
  ${markers.end}`;
875
893
  const startIdx = content.indexOf(markers.start);
876
894
  const endIdx = content.indexOf(markers.end);
895
+ if (startIdx !== -1 !== (endIdx !== -1)) {
896
+ throw new Error(
897
+ `Corrupted patch markers for '${patchName}': only ${startIdx !== -1 ? "start" : "end"} marker found in ${filePath}`
898
+ );
899
+ }
877
900
  if (startIdx !== -1 && endIdx !== -1) {
878
901
  if (endIdx < startIdx) {
879
902
  throw new Error(
@@ -1060,8 +1083,8 @@ var BmadError = class extends Error {
1060
1083
  };
1061
1084
  var PATCH_TARGETS = {
1062
1085
  "story-verification": "bmm/workflows/4-implementation/create-story/template.md",
1063
- "dev-enforcement": "bmm/workflows/4-implementation/dev-story/checklist.md",
1064
- "review-enforcement": "bmm/workflows/4-implementation/code-review/checklist.md",
1086
+ "dev-enforcement": "bmm/workflows/4-implementation/dev-story/instructions.xml",
1087
+ "review-enforcement": "bmm/workflows/4-implementation/code-review/instructions.xml",
1065
1088
  "retro-enforcement": "bmm/workflows/4-implementation/retrospective/instructions.md",
1066
1089
  "sprint-beads": "bmm/workflows/4-implementation/sprint-planning/checklist.md",
1067
1090
  "sprint-retro": "bmm/workflows/4-implementation/sprint-planning/instructions.md"
@@ -1110,9 +1133,9 @@ function installBmad(dir) {
1110
1133
  patches_applied: []
1111
1134
  };
1112
1135
  }
1113
- const cmdStr = "npx bmad-method init";
1136
+ const cmdStr = "npx bmad-method install";
1114
1137
  try {
1115
- execFileSync4("npx", ["bmad-method", "init"], {
1138
+ execFileSync4("npx", ["bmad-method", "install"], {
1116
1139
  stdio: "pipe",
1117
1140
  timeout: 6e4,
1118
1141
  cwd: root
@@ -1121,6 +1144,9 @@ function installBmad(dir) {
1121
1144
  const message = err instanceof Error ? err.message : String(err);
1122
1145
  throw new BmadError(cmdStr, message);
1123
1146
  }
1147
+ if (!isBmadInstalled(root)) {
1148
+ throw new BmadError(cmdStr, "_bmad/ directory was not created after successful npx bmad-method install");
1149
+ }
1124
1150
  const version = detectBmadVersion(root);
1125
1151
  return {
1126
1152
  status: "installed",
@@ -1177,6 +1203,21 @@ function applyAllPatches(dir) {
1177
1203
  }
1178
1204
  return results;
1179
1205
  }
1206
+ function detectBmalph(dir) {
1207
+ const root = dir ?? process.cwd();
1208
+ const files = [];
1209
+ const ralphRcPath = join5(root, ".ralph", ".ralphrc");
1210
+ if (existsSync5(ralphRcPath)) {
1211
+ files.push(".ralph/.ralphrc");
1212
+ }
1213
+ const dotRalphDir = join5(root, ".ralph");
1214
+ if (existsSync5(dotRalphDir)) {
1215
+ if (files.length === 0) {
1216
+ files.push(".ralph/");
1217
+ }
1218
+ }
1219
+ return { detected: files.length > 0, files };
1220
+ }
1180
1221
  function generateStoryKey(epicNumber, storyNumber, title) {
1181
1222
  const slug = title.toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
1182
1223
  return `${epicNumber}-${storyNumber}-${slug}`;
@@ -1402,7 +1443,7 @@ function getInstallCommand(stack) {
1402
1443
  }
1403
1444
 
1404
1445
  // src/commands/init.ts
1405
- var HARNESS_VERSION = true ? "0.16.1" : "0.0.0-dev";
1446
+ var HARNESS_VERSION = true ? "0.17.1" : "0.0.0-dev";
1406
1447
  function getProjectName(projectDir) {
1407
1448
  try {
1408
1449
  const pkgPath = join6(projectDir, "package.json");
@@ -1498,7 +1539,7 @@ function generateDocsIndexContent() {
1498
1539
  }
1499
1540
  var DO_NOT_EDIT_HEADER = "<!-- DO NOT EDIT MANUALLY -->\n";
1500
1541
  function registerInitCommand(program) {
1501
- program.command("init").description("Initialize the harness in a project").option("--no-frontend", "Disable frontend enforcement").option("--no-database", "Disable database enforcement").option("--no-api", "Disable API enforcement").option("--otel-endpoint <url>", "Remote OTLP endpoint (skips local Docker stack)").option("--logs-url <url>", "Remote VictoriaLogs URL").option("--metrics-url <url>", "Remote VictoriaMetrics URL").option("--traces-url <url>", "Remote Jaeger/VictoriaTraces URL").action(async (options, cmd) => {
1542
+ program.command("init").description("Initialize the harness in a project").option("--no-frontend", "Disable frontend enforcement").option("--no-database", "Disable database enforcement").option("--no-api", "Disable API enforcement").option("--no-observability", "Skip OTLP package installation").option("--otel-endpoint <url>", "Remote OTLP endpoint (skips local Docker stack)").option("--logs-url <url>", "Remote VictoriaLogs URL").option("--metrics-url <url>", "Remote VictoriaMetrics URL").option("--traces-url <url>", "Remote Jaeger/VictoriaTraces URL").action(async (options, cmd) => {
1502
1543
  const globalOpts = cmd.optsWithGlobals();
1503
1544
  const isJson = globalOpts.json === true;
1504
1545
  const projectDir = process.cwd();
@@ -1527,6 +1568,47 @@ function registerInitCommand(program) {
1527
1568
  result.documentation.agents_md = "exists";
1528
1569
  result.documentation.docs_scaffold = "exists";
1529
1570
  result.documentation.readme = "exists";
1571
+ const depResults = [];
1572
+ for (const spec of DEPENDENCY_REGISTRY) {
1573
+ const check = checkInstalled(spec);
1574
+ const depResult = {
1575
+ name: spec.name,
1576
+ displayName: spec.displayName,
1577
+ status: check.installed ? "already-installed" : "failed",
1578
+ version: check.version
1579
+ };
1580
+ depResults.push(depResult);
1581
+ if (!isJson) {
1582
+ if (check.installed) {
1583
+ const versionStr = check.version ? ` (v${check.version})` : "";
1584
+ ok(`${spec.displayName}: already installed${versionStr}`);
1585
+ } else {
1586
+ fail(`${spec.displayName}: not found`);
1587
+ }
1588
+ }
1589
+ }
1590
+ result.dependencies = depResults;
1591
+ if (isBmadInstalled(projectDir)) {
1592
+ try {
1593
+ const patchResults = applyAllPatches(projectDir);
1594
+ const patchNames = patchResults.filter((r) => r.applied).map((r) => r.patchName);
1595
+ const version = detectBmadVersion(projectDir);
1596
+ const bmalpHDetection = detectBmalph(projectDir);
1597
+ result.bmad = {
1598
+ status: "already-installed",
1599
+ version,
1600
+ patches_applied: patchNames,
1601
+ bmalph_detected: bmalpHDetection.detected
1602
+ };
1603
+ if (!isJson) {
1604
+ info("BMAD: already installed, patches verified");
1605
+ if (bmalpHDetection.detected) {
1606
+ warn("bmalph detected \u2014 superseded files noted for cleanup");
1607
+ }
1608
+ }
1609
+ } catch {
1610
+ }
1611
+ }
1530
1612
  if (isJson) {
1531
1613
  jsonOutput(result);
1532
1614
  } else {
@@ -1661,10 +1743,11 @@ function registerInitCommand(program) {
1661
1743
  result.bmad = {
1662
1744
  status: "already-installed",
1663
1745
  version,
1664
- patches_applied: patchNames
1746
+ patches_applied: patchNames,
1747
+ bmalph_detected: false
1665
1748
  };
1666
1749
  if (!isJson) {
1667
- info("BMAD: existing installation detected, patches applied");
1750
+ info("BMAD: already installed, patches verified");
1668
1751
  }
1669
1752
  } else {
1670
1753
  const installResult = installBmad(projectDir);
@@ -1673,18 +1756,27 @@ function registerInitCommand(program) {
1673
1756
  result.bmad = {
1674
1757
  status: installResult.status,
1675
1758
  version: installResult.version,
1676
- patches_applied: patchNames
1759
+ patches_applied: patchNames,
1760
+ bmalph_detected: false
1677
1761
  };
1678
1762
  if (!isJson) {
1679
1763
  ok(`BMAD: installed (v${installResult.version ?? "unknown"}), harness patches applied`);
1680
1764
  }
1681
1765
  }
1766
+ const bmalpHDetection = detectBmalph(projectDir);
1767
+ if (bmalpHDetection.detected && result.bmad) {
1768
+ result.bmad.bmalph_detected = true;
1769
+ if (!isJson) {
1770
+ warn("bmalph detected \u2014 superseded files noted for cleanup");
1771
+ }
1772
+ }
1682
1773
  } catch (err) {
1683
1774
  if (err instanceof BmadError) {
1684
1775
  result.bmad = {
1685
1776
  status: "failed",
1686
1777
  version: null,
1687
1778
  patches_applied: [],
1779
+ bmalph_detected: false,
1688
1780
  error: err.message
1689
1781
  };
1690
1782
  if (!isJson) {
@@ -1757,7 +1849,20 @@ function registerInitCommand(program) {
1757
1849
  ok("Documentation: README.md created");
1758
1850
  }
1759
1851
  }
1760
- const otlpResult = instrumentProject(projectDir, stack, { json: isJson, appType });
1852
+ let otlpResult;
1853
+ if (!options.observability) {
1854
+ otlpResult = {
1855
+ status: "skipped",
1856
+ packages_installed: false,
1857
+ start_script_patched: false,
1858
+ env_vars_configured: false
1859
+ };
1860
+ if (!isJson) {
1861
+ info("OTLP: skipped (--no-observability)");
1862
+ }
1863
+ } else {
1864
+ otlpResult = instrumentProject(projectDir, stack, { json: isJson, appType });
1865
+ }
1761
1866
  result.otlp = otlpResult;
1762
1867
  try {
1763
1868
  const updatedState = readState(projectDir);
@@ -2680,14 +2785,8 @@ var DB_KEYWORDS = [
2680
2785
  "table"
2681
2786
  ];
2682
2787
  var INTEGRATION_KEYWORDS = [
2683
- "sprint planning",
2684
- "workflow",
2685
- "run /command",
2686
- "user session",
2687
- "multi-step",
2688
2788
  "external system",
2689
2789
  "real infrastructure",
2690
- "integration test",
2691
2790
  "manual verification"
2692
2791
  ];
2693
2792
  var ESCALATE_KEYWORDS = [
@@ -3398,6 +3497,10 @@ function checkBlackBoxEnforcement(proofContent) {
3398
3497
  acsMissingDockerExec
3399
3498
  };
3400
3499
  }
3500
+ function hasFailVerdict(section) {
3501
+ const withoutCodeBlocks = section.replace(/```[\s\S]*?```/g, "");
3502
+ return withoutCodeBlocks.includes("[FAIL]");
3503
+ }
3401
3504
  function checkPreconditions(dir, storyId) {
3402
3505
  const state = readState(dir);
3403
3506
  const failures = [];
@@ -3510,6 +3613,10 @@ function validateProofQuality(proofPath) {
3510
3613
  escalated++;
3511
3614
  continue;
3512
3615
  }
3616
+ if (hasFailVerdict(section)) {
3617
+ pending++;
3618
+ continue;
3619
+ }
3513
3620
  const hasEvidence = section.includes("<!-- /showboat exec -->") || section.includes("<!-- showboat image:") || /```(?:bash|shell)\n[\s\S]*?```\n+```output\n/m.test(section);
3514
3621
  if (hasEvidence) {
3515
3622
  verified++;
@@ -3545,6 +3652,8 @@ function validateProofQuality(proofPath) {
3545
3652
  const bulletText = bulletMatch[1].toLowerCase();
3546
3653
  if (bulletText.includes("n/a") || bulletText.includes("escalat") || bulletText.includes("superseded")) {
3547
3654
  bEscalated++;
3655
+ } else if (bulletText.includes("fail")) {
3656
+ bPending++;
3548
3657
  } else {
3549
3658
  bVerified++;
3550
3659
  }
@@ -3570,6 +3679,8 @@ function validateProofQuality(proofPath) {
3570
3679
  const section = content.slice(regionStart, regionEnd);
3571
3680
  if (section.includes("[ESCALATE]")) {
3572
3681
  escalated++;
3682
+ } else if (hasFailVerdict(section)) {
3683
+ pending++;
3573
3684
  } else if (/```output/m.test(section)) {
3574
3685
  verified++;
3575
3686
  } else {
@@ -3597,6 +3708,8 @@ function validateProofQuality(proofPath) {
3597
3708
  const section = content.slice(acIdx, nextMatch ? nextMatch.index : content.length);
3598
3709
  if (section.includes("[ESCALATE]")) {
3599
3710
  escalated++;
3711
+ } else if (hasFailVerdict(section)) {
3712
+ pending++;
3600
3713
  } else if (/```output\n/m.test(section)) {
3601
3714
  verified++;
3602
3715
  } else {
@@ -7675,7 +7788,7 @@ function handleStatus(dir, isJson, filterStory) {
7675
7788
  }
7676
7789
 
7677
7790
  // src/index.ts
7678
- var VERSION = true ? "0.16.1" : "0.0.0-dev";
7791
+ var VERSION = true ? "0.17.1" : "0.0.0-dev";
7679
7792
  function createProgram() {
7680
7793
  const program = new Command();
7681
7794
  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.16.1",
3
+ "version": "0.17.1",
4
4
  "type": "module",
5
5
  "description": "CLI for codeharness — makes autonomous coding agents produce software that actually works",
6
6
  "bin": {
@@ -48,6 +48,9 @@ driver_valid_tools() {
48
48
  "Bash(showboat *)"
49
49
  "Bash(codeharness *)"
50
50
  "NotebookEdit"
51
+ "Skill"
52
+ "Agent"
53
+ "ToolSearch"
51
54
  )
52
55
  }
53
56