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 +139 -26
- package/package.json +1 -1
- package/ralph/drivers/claude-code.sh +3 -0
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
|
-
|
|
493
|
-
|
|
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: '
|
|
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/
|
|
1064
|
-
"review-enforcement": "bmm/workflows/4-implementation/code-review/
|
|
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
|
|
1136
|
+
const cmdStr = "npx bmad-method install";
|
|
1114
1137
|
try {
|
|
1115
|
-
execFileSync4("npx", ["bmad-method", "
|
|
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.
|
|
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:
|
|
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
|
-
|
|
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.
|
|
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