codeharness 0.26.4 → 0.27.0
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/{chunk-KZQETPQS.js → chunk-JMYDBV6O.js} +127 -430
- package/dist/{docker-JEC7THRT.js → docker-5LUADX2H.js} +1 -1
- package/dist/index.js +5150 -2054
- package/package.json +5 -3
- package/patches/AGENTS.md +1 -1
- package/patches/dev/enforcement.md +16 -7
- package/patches/retro/enforcement.md +2 -2
- package/patches/review/enforcement.md +24 -3
- package/patches/verify/story-verification.md +25 -11
- package/templates/agents/analyst.yaml +10 -0
- package/templates/agents/architect.yaml +11 -0
- package/templates/agents/dev.yaml +10 -0
- package/templates/agents/evaluator.yaml +92 -0
- package/templates/agents/pm.yaml +12 -0
- package/templates/agents/qa.yaml +15 -0
- package/templates/agents/sm.yaml +10 -0
- package/templates/agents/tech-writer.yaml +11 -0
- package/templates/agents/ux-designer.yaml +13 -0
- package/templates/workflows/default.yaml +23 -0
- package/ralph/AGENTS.md +0 -48
- package/ralph/bridge.sh +0 -424
- package/ralph/db_schema_gen.sh +0 -109
- package/ralph/drivers/claude-code.sh +0 -140
- package/ralph/exec_plans.sh +0 -252
- package/ralph/harness_status.sh +0 -147
- package/ralph/lib/circuit_breaker.sh +0 -210
- package/ralph/lib/date_utils.sh +0 -60
- package/ralph/lib/timeout_utils.sh +0 -77
- package/ralph/onboard.sh +0 -83
- package/ralph/ralph.sh +0 -1402
- package/ralph/validate_epic_docs.sh +0 -129
- package/ralph/verify_gates.sh +0 -210
|
@@ -371,8 +371,8 @@ function getRunningServices(composeFile, projectName) {
|
|
|
371
371
|
}
|
|
372
372
|
|
|
373
373
|
// src/modules/infra/init-project.ts
|
|
374
|
-
import { existsSync as
|
|
375
|
-
import { basename as basename3, join as
|
|
374
|
+
import { copyFileSync, existsSync as existsSync13, mkdirSync as mkdirSync4 } from "fs";
|
|
375
|
+
import { basename as basename3, dirname as dirname3, join as join14 } from "path";
|
|
376
376
|
|
|
377
377
|
// src/lib/output.ts
|
|
378
378
|
function ok(message, options) {
|
|
@@ -1096,25 +1096,33 @@ RUN cargo build --release
|
|
|
1096
1096
|
// ── Task 16: getProjectName ───────────────────────────────────────────
|
|
1097
1097
|
// ── getVerifyDockerfileSection ──────────────────────────────────────
|
|
1098
1098
|
getVerifyDockerfileSection(projectDir) {
|
|
1099
|
+
let needsBevy = false;
|
|
1100
|
+
const cargoContent = readTextSafe(join6(projectDir, "Cargo.toml"));
|
|
1101
|
+
if (cargoContent) {
|
|
1102
|
+
const depsSection = getCargoDepsSection(cargoContent);
|
|
1103
|
+
needsBevy = hasCargoDep(depsSection, "bevy");
|
|
1104
|
+
}
|
|
1105
|
+
const aptPackages = ["build-essential", "pkg-config", "libssl-dev"];
|
|
1106
|
+
if (needsBevy) {
|
|
1107
|
+
aptPackages.push(
|
|
1108
|
+
"libudev-dev",
|
|
1109
|
+
"libasound2-dev",
|
|
1110
|
+
"libwayland-dev",
|
|
1111
|
+
"libxkbcommon-dev",
|
|
1112
|
+
"libfontconfig1-dev",
|
|
1113
|
+
"libx11-dev"
|
|
1114
|
+
);
|
|
1115
|
+
}
|
|
1099
1116
|
const lines = [
|
|
1100
1117
|
"# --- Rust tooling ---",
|
|
1118
|
+
"RUN apt-get update && apt-get install -y --no-install-recommends \\",
|
|
1119
|
+
` ${aptPackages.join(" ")} \\`,
|
|
1120
|
+
" && rm -rf /var/lib/apt/lists/*",
|
|
1101
1121
|
'RUN curl --proto "=https" --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable',
|
|
1102
1122
|
'ENV PATH="/root/.cargo/bin:$PATH"',
|
|
1103
1123
|
"RUN rustup component add clippy",
|
|
1104
1124
|
"RUN cargo install cargo-tarpaulin"
|
|
1105
1125
|
];
|
|
1106
|
-
const cargoContent = readTextSafe(join6(projectDir, "Cargo.toml"));
|
|
1107
|
-
if (cargoContent) {
|
|
1108
|
-
const depsSection = getCargoDepsSection(cargoContent);
|
|
1109
|
-
if (hasCargoDep(depsSection, "bevy")) {
|
|
1110
|
-
lines.push(
|
|
1111
|
-
"RUN apt-get update && apt-get install -y --no-install-recommends \\",
|
|
1112
|
-
" libudev-dev libasound2-dev libwayland-dev libxkbcommon-dev \\",
|
|
1113
|
-
" libfontconfig1-dev libx11-dev \\",
|
|
1114
|
-
" && rm -rf /var/lib/apt/lists/*"
|
|
1115
|
-
);
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1118
1126
|
return lines.join("\n");
|
|
1119
1127
|
}
|
|
1120
1128
|
getProjectName(dir) {
|
|
@@ -1337,9 +1345,8 @@ function parseValue(raw) {
|
|
|
1337
1345
|
if (raw === "true") return true;
|
|
1338
1346
|
if (raw === "false") return false;
|
|
1339
1347
|
if (raw === "null") return null;
|
|
1340
|
-
const
|
|
1341
|
-
|
|
1342
|
-
return raw;
|
|
1348
|
+
const n = Number(raw);
|
|
1349
|
+
return !Number.isNaN(n) && raw.trim() !== "" ? n : raw;
|
|
1343
1350
|
}
|
|
1344
1351
|
|
|
1345
1352
|
// src/lib/observability/instrument.ts
|
|
@@ -1932,9 +1939,7 @@ function handleLocalShared(opts, state) {
|
|
|
1932
1939
|
}
|
|
1933
1940
|
if (!opts.isJson) {
|
|
1934
1941
|
fail("Observability stack: failed to start");
|
|
1935
|
-
if (startResult.error) {
|
|
1936
|
-
info(`Error: ${startResult.error}`);
|
|
1937
|
-
}
|
|
1942
|
+
if (startResult.error) info(`Error: ${startResult.error}`);
|
|
1938
1943
|
}
|
|
1939
1944
|
const docker = {
|
|
1940
1945
|
compose_file: sharedComposeFile,
|
|
@@ -2124,78 +2129,12 @@ function verifyDeps(isJson) {
|
|
|
2124
2129
|
}
|
|
2125
2130
|
|
|
2126
2131
|
// src/lib/bmad.ts
|
|
2127
|
-
import { execFileSync as
|
|
2128
|
-
import { existsSync as
|
|
2129
|
-
import { join as
|
|
2130
|
-
|
|
2131
|
-
// src/lib/patch-engine.ts
|
|
2132
|
-
import { readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
|
|
2133
|
-
function validatePatchName(patchName) {
|
|
2134
|
-
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(patchName)) {
|
|
2135
|
-
throw new Error(
|
|
2136
|
-
`Invalid patch name '${patchName}': must be kebab-case (lowercase letters, digits, hyphens)`
|
|
2137
|
-
);
|
|
2138
|
-
}
|
|
2139
|
-
}
|
|
2140
|
-
function getPatchMarkers(patchName) {
|
|
2141
|
-
validatePatchName(patchName);
|
|
2142
|
-
return {
|
|
2143
|
-
start: `<!-- CODEHARNESS-PATCH-START:${patchName} -->`,
|
|
2144
|
-
end: `<!-- CODEHARNESS-PATCH-END:${patchName} -->`
|
|
2145
|
-
};
|
|
2146
|
-
}
|
|
2147
|
-
function applyPatch(filePath, patchName, patchContent) {
|
|
2148
|
-
let content = readFileSync9(filePath, "utf-8");
|
|
2149
|
-
const markers = getPatchMarkers(patchName);
|
|
2150
|
-
const markerBlock = `${markers.start}
|
|
2151
|
-
${patchContent}
|
|
2152
|
-
${markers.end}`;
|
|
2153
|
-
const startIdx = content.indexOf(markers.start);
|
|
2154
|
-
const endIdx = content.indexOf(markers.end);
|
|
2155
|
-
if (startIdx !== -1 !== (endIdx !== -1)) {
|
|
2156
|
-
throw new Error(
|
|
2157
|
-
`Corrupted patch markers for '${patchName}': only ${startIdx !== -1 ? "start" : "end"} marker found in ${filePath}`
|
|
2158
|
-
);
|
|
2159
|
-
}
|
|
2160
|
-
if (startIdx !== -1 && endIdx !== -1) {
|
|
2161
|
-
if (endIdx < startIdx) {
|
|
2162
|
-
throw new Error(
|
|
2163
|
-
`Corrupted patch markers for '${patchName}': end marker appears before start marker in ${filePath}`
|
|
2164
|
-
);
|
|
2165
|
-
}
|
|
2166
|
-
const before = content.slice(0, startIdx);
|
|
2167
|
-
const after = content.slice(endIdx + markers.end.length);
|
|
2168
|
-
content = before + markerBlock + after;
|
|
2169
|
-
writeFileSync7(filePath, content, "utf-8");
|
|
2170
|
-
return { applied: true, updated: true };
|
|
2171
|
-
}
|
|
2172
|
-
const trimmed = content.trimEnd();
|
|
2173
|
-
content = trimmed + "\n\n" + markerBlock + "\n";
|
|
2174
|
-
writeFileSync7(filePath, content, "utf-8");
|
|
2175
|
-
return { applied: true, updated: false };
|
|
2176
|
-
}
|
|
2177
|
-
function removePatch(filePath, patchName) {
|
|
2178
|
-
let content = readFileSync9(filePath, "utf-8");
|
|
2179
|
-
const markers = getPatchMarkers(patchName);
|
|
2180
|
-
const startIdx = content.indexOf(markers.start);
|
|
2181
|
-
const endIdx = content.indexOf(markers.end);
|
|
2182
|
-
if (startIdx === -1 || endIdx === -1) {
|
|
2183
|
-
return false;
|
|
2184
|
-
}
|
|
2185
|
-
if (endIdx < startIdx) {
|
|
2186
|
-
throw new Error(
|
|
2187
|
-
`Corrupted patch markers for '${patchName}': end marker appears before start marker in ${filePath}`
|
|
2188
|
-
);
|
|
2189
|
-
}
|
|
2190
|
-
const before = content.slice(0, startIdx);
|
|
2191
|
-
const after = content.slice(endIdx + markers.end.length);
|
|
2192
|
-
content = before.trimEnd() + "\n" + after.trimStart();
|
|
2193
|
-
writeFileSync7(filePath, content, "utf-8");
|
|
2194
|
-
return true;
|
|
2195
|
-
}
|
|
2132
|
+
import { execFileSync as execFileSync9 } from "child_process";
|
|
2133
|
+
import { existsSync as existsSync10, readFileSync as readFileSync10 } from "fs";
|
|
2134
|
+
import { join as join11 } from "path";
|
|
2196
2135
|
|
|
2197
2136
|
// src/templates/bmad-patches.ts
|
|
2198
|
-
import { readFileSync as
|
|
2137
|
+
import { readFileSync as readFileSync9, existsSync as existsSync9 } from "fs";
|
|
2199
2138
|
import { join as join10, dirname as dirname2 } from "path";
|
|
2200
2139
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2201
2140
|
var __dirname2 = dirname2(fileURLToPath2(import.meta.url));
|
|
@@ -2203,7 +2142,7 @@ function readPatchFile(role, name) {
|
|
|
2203
2142
|
const patchPath = join10(__dirname2, "..", "..", "patches", role, `${name}.md`);
|
|
2204
2143
|
try {
|
|
2205
2144
|
if (existsSync9(patchPath)) {
|
|
2206
|
-
return
|
|
2145
|
+
return readFileSync9(patchPath, "utf-8").trim();
|
|
2207
2146
|
}
|
|
2208
2147
|
} catch {
|
|
2209
2148
|
}
|
|
@@ -2278,154 +2217,6 @@ var PATCH_TEMPLATES = {
|
|
|
2278
2217
|
"sprint-retro": sprintPlanningRetroPatch
|
|
2279
2218
|
};
|
|
2280
2219
|
|
|
2281
|
-
// src/lib/beads.ts
|
|
2282
|
-
import { execFileSync as execFileSync9 } from "child_process";
|
|
2283
|
-
import { existsSync as existsSync10, readdirSync as readdirSync2 } from "fs";
|
|
2284
|
-
import { join as join11 } from "path";
|
|
2285
|
-
var BeadsError = class extends Error {
|
|
2286
|
-
constructor(command, originalMessage) {
|
|
2287
|
-
super(`Beads failed: ${originalMessage}. Command: ${command}`);
|
|
2288
|
-
this.command = command;
|
|
2289
|
-
this.originalMessage = originalMessage;
|
|
2290
|
-
this.name = "BeadsError";
|
|
2291
|
-
}
|
|
2292
|
-
};
|
|
2293
|
-
function isBeadsCLIInstalled() {
|
|
2294
|
-
try {
|
|
2295
|
-
execFileSync9("which", ["bd"], { stdio: "pipe", timeout: 5e3 });
|
|
2296
|
-
return true;
|
|
2297
|
-
} catch {
|
|
2298
|
-
return false;
|
|
2299
|
-
}
|
|
2300
|
-
}
|
|
2301
|
-
function bdCommand(args) {
|
|
2302
|
-
const cmdStr = `bd ${args.join(" ")}`;
|
|
2303
|
-
let text;
|
|
2304
|
-
try {
|
|
2305
|
-
const output = execFileSync9("bd", args, {
|
|
2306
|
-
stdio: "pipe",
|
|
2307
|
-
timeout: 3e4
|
|
2308
|
-
});
|
|
2309
|
-
text = output.toString().trim();
|
|
2310
|
-
} catch (err) {
|
|
2311
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
2312
|
-
throw new BeadsError(cmdStr, message);
|
|
2313
|
-
}
|
|
2314
|
-
if (!text) return void 0;
|
|
2315
|
-
try {
|
|
2316
|
-
return JSON.parse(text);
|
|
2317
|
-
} catch {
|
|
2318
|
-
throw new BeadsError(cmdStr, `Invalid JSON output from bd: ${text}`);
|
|
2319
|
-
}
|
|
2320
|
-
}
|
|
2321
|
-
function createIssue(title, opts) {
|
|
2322
|
-
const args = ["create", title, "--json"];
|
|
2323
|
-
if (opts?.type) {
|
|
2324
|
-
args.push("--type", opts.type);
|
|
2325
|
-
}
|
|
2326
|
-
if (opts?.priority !== void 0) {
|
|
2327
|
-
args.push("--priority", String(opts.priority));
|
|
2328
|
-
}
|
|
2329
|
-
if (opts?.description) {
|
|
2330
|
-
args.push("--description", opts.description);
|
|
2331
|
-
}
|
|
2332
|
-
if (opts?.deps && opts.deps.length > 0) {
|
|
2333
|
-
for (const dep of opts.deps) {
|
|
2334
|
-
args.push("--dep", dep);
|
|
2335
|
-
}
|
|
2336
|
-
}
|
|
2337
|
-
return bdCommand(args);
|
|
2338
|
-
}
|
|
2339
|
-
function closeIssue(id) {
|
|
2340
|
-
bdCommand(["close", id, "--json"]);
|
|
2341
|
-
}
|
|
2342
|
-
function updateIssue(id, opts) {
|
|
2343
|
-
const args = ["update", id, "--json"];
|
|
2344
|
-
if (opts.status !== void 0) {
|
|
2345
|
-
args.push("--status", opts.status);
|
|
2346
|
-
}
|
|
2347
|
-
if (opts.priority !== void 0) {
|
|
2348
|
-
args.push("--priority", String(opts.priority));
|
|
2349
|
-
}
|
|
2350
|
-
bdCommand(args);
|
|
2351
|
-
}
|
|
2352
|
-
function listIssues() {
|
|
2353
|
-
return bdCommand(["list", "--json"]);
|
|
2354
|
-
}
|
|
2355
|
-
function isBeadsInitialized(dir) {
|
|
2356
|
-
const beadsDir = join11(dir ?? process.cwd(), ".beads");
|
|
2357
|
-
return existsSync10(beadsDir);
|
|
2358
|
-
}
|
|
2359
|
-
function initBeads(dir) {
|
|
2360
|
-
if (isBeadsInitialized(dir)) {
|
|
2361
|
-
return;
|
|
2362
|
-
}
|
|
2363
|
-
const cmdStr = "bd init";
|
|
2364
|
-
try {
|
|
2365
|
-
execFileSync9("bd", ["init"], {
|
|
2366
|
-
stdio: "pipe",
|
|
2367
|
-
timeout: 3e4,
|
|
2368
|
-
cwd: dir ?? process.cwd()
|
|
2369
|
-
});
|
|
2370
|
-
} catch (err) {
|
|
2371
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
2372
|
-
throw new BeadsError(cmdStr, message);
|
|
2373
|
-
}
|
|
2374
|
-
}
|
|
2375
|
-
function detectBeadsHooks(dir) {
|
|
2376
|
-
const hooksDir = join11(dir ?? process.cwd(), ".beads", "hooks");
|
|
2377
|
-
if (!existsSync10(hooksDir)) {
|
|
2378
|
-
return { hasHooks: false, hookTypes: [] };
|
|
2379
|
-
}
|
|
2380
|
-
try {
|
|
2381
|
-
const entries = readdirSync2(hooksDir);
|
|
2382
|
-
const hookTypes = entries.filter((e) => !e.startsWith("."));
|
|
2383
|
-
return {
|
|
2384
|
-
hasHooks: hookTypes.length > 0,
|
|
2385
|
-
hookTypes
|
|
2386
|
-
};
|
|
2387
|
-
} catch {
|
|
2388
|
-
return { hasHooks: false, hookTypes: [] };
|
|
2389
|
-
}
|
|
2390
|
-
}
|
|
2391
|
-
function buildGapId(category, identifier) {
|
|
2392
|
-
return `[gap:${category}:${identifier}]`;
|
|
2393
|
-
}
|
|
2394
|
-
function findExistingByGapId(gapId, issues) {
|
|
2395
|
-
return issues.find(
|
|
2396
|
-
(issue) => issue.status !== "done" && issue.description?.includes(gapId)
|
|
2397
|
-
);
|
|
2398
|
-
}
|
|
2399
|
-
function appendGapId(existingDescription, gapId) {
|
|
2400
|
-
if (!existingDescription) {
|
|
2401
|
-
return gapId;
|
|
2402
|
-
}
|
|
2403
|
-
return `${existingDescription}
|
|
2404
|
-
${gapId}`;
|
|
2405
|
-
}
|
|
2406
|
-
function createOrFindIssue(title, gapId, opts) {
|
|
2407
|
-
const issues = listIssues();
|
|
2408
|
-
const existing = findExistingByGapId(gapId, issues);
|
|
2409
|
-
if (existing) {
|
|
2410
|
-
return { issue: existing, created: false };
|
|
2411
|
-
}
|
|
2412
|
-
const issue = createIssue(title, {
|
|
2413
|
-
...opts,
|
|
2414
|
-
description: appendGapId(opts?.description, gapId)
|
|
2415
|
-
});
|
|
2416
|
-
return { issue, created: true };
|
|
2417
|
-
}
|
|
2418
|
-
function configureHookCoexistence(dir) {
|
|
2419
|
-
const detection = detectBeadsHooks(dir);
|
|
2420
|
-
if (!detection.hasHooks) {
|
|
2421
|
-
return;
|
|
2422
|
-
}
|
|
2423
|
-
const gitHooksDir = join11(dir ?? process.cwd(), ".git", "hooks");
|
|
2424
|
-
if (!existsSync10(gitHooksDir)) {
|
|
2425
|
-
return;
|
|
2426
|
-
}
|
|
2427
|
-
}
|
|
2428
|
-
|
|
2429
2220
|
// src/lib/bmad.ts
|
|
2430
2221
|
var BmadError = class extends Error {
|
|
2431
2222
|
constructor(command, originalMessage) {
|
|
@@ -2444,15 +2235,15 @@ var PATCH_TARGETS = {
|
|
|
2444
2235
|
"sprint-retro": "bmm/workflows/4-implementation/sprint-planning/instructions.md"
|
|
2445
2236
|
};
|
|
2446
2237
|
function isBmadInstalled(dir) {
|
|
2447
|
-
const bmadDir =
|
|
2448
|
-
return
|
|
2238
|
+
const bmadDir = join11(dir ?? process.cwd(), "_bmad");
|
|
2239
|
+
return existsSync10(bmadDir);
|
|
2449
2240
|
}
|
|
2450
2241
|
function detectBmadVersion(dir) {
|
|
2451
2242
|
const root = dir ?? process.cwd();
|
|
2452
|
-
const moduleYamlPath =
|
|
2453
|
-
if (
|
|
2243
|
+
const moduleYamlPath = join11(root, "_bmad", "core", "module.yaml");
|
|
2244
|
+
if (existsSync10(moduleYamlPath)) {
|
|
2454
2245
|
try {
|
|
2455
|
-
const content =
|
|
2246
|
+
const content = readFileSync10(moduleYamlPath, "utf-8");
|
|
2456
2247
|
const versionMatch = content.match(/version:\s*["']?([^\s"']+)["']?/);
|
|
2457
2248
|
if (versionMatch) {
|
|
2458
2249
|
return versionMatch[1];
|
|
@@ -2460,17 +2251,17 @@ function detectBmadVersion(dir) {
|
|
|
2460
2251
|
} catch {
|
|
2461
2252
|
}
|
|
2462
2253
|
}
|
|
2463
|
-
const versionFilePath =
|
|
2464
|
-
if (
|
|
2254
|
+
const versionFilePath = join11(root, "_bmad", "VERSION");
|
|
2255
|
+
if (existsSync10(versionFilePath)) {
|
|
2465
2256
|
try {
|
|
2466
|
-
return
|
|
2257
|
+
return readFileSync10(versionFilePath, "utf-8").trim() || null;
|
|
2467
2258
|
} catch {
|
|
2468
2259
|
}
|
|
2469
2260
|
}
|
|
2470
|
-
const packageJsonPath =
|
|
2471
|
-
if (
|
|
2261
|
+
const packageJsonPath = join11(root, "_bmad", "package.json");
|
|
2262
|
+
if (existsSync10(packageJsonPath)) {
|
|
2472
2263
|
try {
|
|
2473
|
-
const pkg = JSON.parse(
|
|
2264
|
+
const pkg = JSON.parse(readFileSync10(packageJsonPath, "utf-8"));
|
|
2474
2265
|
return pkg.version ?? null;
|
|
2475
2266
|
} catch {
|
|
2476
2267
|
}
|
|
@@ -2489,7 +2280,7 @@ function installBmad(dir) {
|
|
|
2489
2280
|
}
|
|
2490
2281
|
const cmdStr = "npx bmad-method install --yes --tools claude-code";
|
|
2491
2282
|
try {
|
|
2492
|
-
|
|
2283
|
+
execFileSync9("npx", ["bmad-method", "install", "--yes", "--tools", "claude-code"], {
|
|
2493
2284
|
stdio: "pipe",
|
|
2494
2285
|
timeout: 12e4,
|
|
2495
2286
|
// 2 min — npx may need to download the package first time
|
|
@@ -2514,8 +2305,8 @@ function applyAllPatches(dir, options) {
|
|
|
2514
2305
|
const silent = options?.silent ?? false;
|
|
2515
2306
|
const results = [];
|
|
2516
2307
|
for (const [patchName, relativePath] of Object.entries(PATCH_TARGETS)) {
|
|
2517
|
-
const targetFile =
|
|
2518
|
-
if (!
|
|
2308
|
+
const targetFile = join11(root, "_bmad", relativePath);
|
|
2309
|
+
if (!existsSync10(targetFile)) {
|
|
2519
2310
|
if (!silent) warn(`Patch target not found: ${relativePath}`);
|
|
2520
2311
|
results.push({
|
|
2521
2312
|
patchName,
|
|
@@ -2538,13 +2329,13 @@ function applyAllPatches(dir, options) {
|
|
|
2538
2329
|
continue;
|
|
2539
2330
|
}
|
|
2540
2331
|
try {
|
|
2541
|
-
const
|
|
2542
|
-
const patchResult = applyPatch(targetFile, patchName, patchContent);
|
|
2332
|
+
const _patchContent = templateFn();
|
|
2543
2333
|
results.push({
|
|
2544
2334
|
patchName,
|
|
2545
2335
|
targetFile,
|
|
2546
|
-
applied:
|
|
2547
|
-
updated:
|
|
2336
|
+
applied: false,
|
|
2337
|
+
updated: false,
|
|
2338
|
+
error: "Patch engine removed (Story 1.2) \u2014 pending v2 rebuild"
|
|
2548
2339
|
});
|
|
2549
2340
|
} catch (err) {
|
|
2550
2341
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -2562,12 +2353,12 @@ function applyAllPatches(dir, options) {
|
|
|
2562
2353
|
function detectBmalph(dir) {
|
|
2563
2354
|
const root = dir ?? process.cwd();
|
|
2564
2355
|
const files = [];
|
|
2565
|
-
const ralphRcPath =
|
|
2566
|
-
if (
|
|
2356
|
+
const ralphRcPath = join11(root, ".ralph", ".ralphrc");
|
|
2357
|
+
if (existsSync10(ralphRcPath)) {
|
|
2567
2358
|
files.push(".ralph/.ralphrc");
|
|
2568
2359
|
}
|
|
2569
|
-
const dotRalphDir =
|
|
2570
|
-
if (
|
|
2360
|
+
const dotRalphDir = join11(root, ".ralph");
|
|
2361
|
+
if (existsSync10(dotRalphDir)) {
|
|
2571
2362
|
if (files.length === 0) {
|
|
2572
2363
|
files.push(".ralph/");
|
|
2573
2364
|
}
|
|
@@ -2582,10 +2373,10 @@ function getStoryFilePath(storyKey) {
|
|
|
2582
2373
|
return `_bmad-output/implementation-artifacts/${storyKey}.md`;
|
|
2583
2374
|
}
|
|
2584
2375
|
function parseEpicsFile(filePath) {
|
|
2585
|
-
if (!
|
|
2376
|
+
if (!existsSync10(filePath)) {
|
|
2586
2377
|
return [];
|
|
2587
2378
|
}
|
|
2588
|
-
const content =
|
|
2379
|
+
const content = readFileSync10(filePath, "utf-8");
|
|
2589
2380
|
if (!content.trim()) {
|
|
2590
2381
|
return [];
|
|
2591
2382
|
}
|
|
@@ -2671,78 +2462,6 @@ function finalizeStory(raw) {
|
|
|
2671
2462
|
technicalNotes
|
|
2672
2463
|
};
|
|
2673
2464
|
}
|
|
2674
|
-
function importStoriesToBeads(stories, opts, beadsFns) {
|
|
2675
|
-
const results = [];
|
|
2676
|
-
let existingIssues = [];
|
|
2677
|
-
try {
|
|
2678
|
-
existingIssues = beadsFns.listIssues();
|
|
2679
|
-
} catch {
|
|
2680
|
-
}
|
|
2681
|
-
const lastBeadsIdByEpic = /* @__PURE__ */ new Map();
|
|
2682
|
-
let priority = 1;
|
|
2683
|
-
for (const story of stories) {
|
|
2684
|
-
const storyFilePath = getStoryFilePath(story.key);
|
|
2685
|
-
const gapId = buildGapId("bridge", `${story.epicNumber}.${story.storyNumber}`);
|
|
2686
|
-
const existingIssue = findExistingByGapId(gapId, existingIssues);
|
|
2687
|
-
if (existingIssue) {
|
|
2688
|
-
lastBeadsIdByEpic.set(story.epicNumber, existingIssue.id);
|
|
2689
|
-
results.push({
|
|
2690
|
-
storyKey: story.key,
|
|
2691
|
-
title: story.title,
|
|
2692
|
-
beadsId: existingIssue.id,
|
|
2693
|
-
status: "exists",
|
|
2694
|
-
storyFilePath
|
|
2695
|
-
});
|
|
2696
|
-
priority++;
|
|
2697
|
-
continue;
|
|
2698
|
-
}
|
|
2699
|
-
if (opts.dryRun) {
|
|
2700
|
-
results.push({
|
|
2701
|
-
storyKey: story.key,
|
|
2702
|
-
title: story.title,
|
|
2703
|
-
beadsId: null,
|
|
2704
|
-
status: "skipped",
|
|
2705
|
-
storyFilePath
|
|
2706
|
-
});
|
|
2707
|
-
priority++;
|
|
2708
|
-
continue;
|
|
2709
|
-
}
|
|
2710
|
-
const deps = [];
|
|
2711
|
-
const prevId = lastBeadsIdByEpic.get(story.epicNumber);
|
|
2712
|
-
if (prevId) {
|
|
2713
|
-
deps.push(prevId);
|
|
2714
|
-
}
|
|
2715
|
-
try {
|
|
2716
|
-
const description = appendGapId(storyFilePath, gapId);
|
|
2717
|
-
const issue = beadsFns.createIssue(story.title, {
|
|
2718
|
-
type: "story",
|
|
2719
|
-
priority,
|
|
2720
|
-
description,
|
|
2721
|
-
deps: deps.length > 0 ? deps : void 0
|
|
2722
|
-
});
|
|
2723
|
-
lastBeadsIdByEpic.set(story.epicNumber, issue.id);
|
|
2724
|
-
results.push({
|
|
2725
|
-
storyKey: story.key,
|
|
2726
|
-
title: story.title,
|
|
2727
|
-
beadsId: issue.id,
|
|
2728
|
-
status: "created",
|
|
2729
|
-
storyFilePath
|
|
2730
|
-
});
|
|
2731
|
-
} catch (err) {
|
|
2732
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
2733
|
-
results.push({
|
|
2734
|
-
storyKey: story.key,
|
|
2735
|
-
title: story.title,
|
|
2736
|
-
beadsId: null,
|
|
2737
|
-
status: "failed",
|
|
2738
|
-
storyFilePath,
|
|
2739
|
-
error: message
|
|
2740
|
-
});
|
|
2741
|
-
}
|
|
2742
|
-
priority++;
|
|
2743
|
-
}
|
|
2744
|
-
return results;
|
|
2745
|
-
}
|
|
2746
2465
|
|
|
2747
2466
|
// src/modules/infra/bmad-setup.ts
|
|
2748
2467
|
function setupBmad(opts) {
|
|
@@ -2826,49 +2545,9 @@ function verifyBmadOnRerun(projectDir, isJson) {
|
|
|
2826
2545
|
}
|
|
2827
2546
|
}
|
|
2828
2547
|
|
|
2829
|
-
// src/modules/infra/beads-init.ts
|
|
2830
|
-
function initializeBeads(projectDir, isJson) {
|
|
2831
|
-
try {
|
|
2832
|
-
let beadsResult;
|
|
2833
|
-
if (isBeadsInitialized(projectDir)) {
|
|
2834
|
-
beadsResult = { status: "already-initialized", hooks_detected: false };
|
|
2835
|
-
if (!isJson) {
|
|
2836
|
-
info("Beads: .beads/ already exists");
|
|
2837
|
-
}
|
|
2838
|
-
} else {
|
|
2839
|
-
initBeads(projectDir);
|
|
2840
|
-
beadsResult = { status: "initialized", hooks_detected: false };
|
|
2841
|
-
if (!isJson) {
|
|
2842
|
-
ok("Beads: initialized (.beads/ created)");
|
|
2843
|
-
}
|
|
2844
|
-
}
|
|
2845
|
-
const hookDetection = detectBeadsHooks(projectDir);
|
|
2846
|
-
beadsResult = { ...beadsResult, hooks_detected: hookDetection.hasHooks };
|
|
2847
|
-
if (hookDetection.hasHooks) {
|
|
2848
|
-
configureHookCoexistence(projectDir);
|
|
2849
|
-
if (!isJson) {
|
|
2850
|
-
info("Beads hooks detected \u2014 coexistence configured");
|
|
2851
|
-
}
|
|
2852
|
-
}
|
|
2853
|
-
return beadsResult;
|
|
2854
|
-
} catch (err) {
|
|
2855
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
2856
|
-
const beadsResult = {
|
|
2857
|
-
status: "failed",
|
|
2858
|
-
hooks_detected: false,
|
|
2859
|
-
error: message
|
|
2860
|
-
};
|
|
2861
|
-
if (!isJson) {
|
|
2862
|
-
warn(`Beads init failed: ${message}`);
|
|
2863
|
-
info("Beads is optional \u2014 continuing without it");
|
|
2864
|
-
}
|
|
2865
|
-
return beadsResult;
|
|
2866
|
-
}
|
|
2867
|
-
}
|
|
2868
|
-
|
|
2869
2548
|
// src/modules/infra/docs-scaffold.ts
|
|
2870
|
-
import { existsSync as
|
|
2871
|
-
import { join as
|
|
2549
|
+
import { existsSync as existsSync11, readFileSync as readFileSync11 } from "fs";
|
|
2550
|
+
import { join as join12, basename as basename2 } from "path";
|
|
2872
2551
|
|
|
2873
2552
|
// src/templates/readme.ts
|
|
2874
2553
|
function readmeTemplate(config) {
|
|
@@ -2900,9 +2579,9 @@ function getSingleInstallCommand(stack) {
|
|
|
2900
2579
|
var DO_NOT_EDIT_HEADER = "<!-- DO NOT EDIT MANUALLY -->\n";
|
|
2901
2580
|
function getProjectName(projectDir) {
|
|
2902
2581
|
try {
|
|
2903
|
-
const pkgPath =
|
|
2904
|
-
if (
|
|
2905
|
-
const pkg = JSON.parse(
|
|
2582
|
+
const pkgPath = join12(projectDir, "package.json");
|
|
2583
|
+
if (existsSync11(pkgPath)) {
|
|
2584
|
+
const pkg = JSON.parse(readFileSync11(pkgPath, "utf-8"));
|
|
2906
2585
|
if (pkg.name && typeof pkg.name === "string") {
|
|
2907
2586
|
return pkg.name;
|
|
2908
2587
|
}
|
|
@@ -2910,9 +2589,9 @@ function getProjectName(projectDir) {
|
|
|
2910
2589
|
} catch {
|
|
2911
2590
|
}
|
|
2912
2591
|
try {
|
|
2913
|
-
const cargoPath =
|
|
2914
|
-
if (
|
|
2915
|
-
const content =
|
|
2592
|
+
const cargoPath = join12(projectDir, "Cargo.toml");
|
|
2593
|
+
if (existsSync11(cargoPath)) {
|
|
2594
|
+
const content = readFileSync11(cargoPath, "utf-8");
|
|
2916
2595
|
const packageMatch = content.match(/\[package\]([\s\S]*?)(?=\n\[|$)/s);
|
|
2917
2596
|
if (packageMatch) {
|
|
2918
2597
|
const nameMatch = packageMatch[1].match(/^\s*name\s*=\s*["']([^"']+)["']/m);
|
|
@@ -3073,8 +2752,8 @@ async function scaffoldDocs(opts) {
|
|
|
3073
2752
|
let agentsMd = "skipped";
|
|
3074
2753
|
let docsScaffold = "skipped";
|
|
3075
2754
|
let readme = "skipped";
|
|
3076
|
-
const agentsMdPath =
|
|
3077
|
-
if (!
|
|
2755
|
+
const agentsMdPath = join12(opts.projectDir, "AGENTS.md");
|
|
2756
|
+
if (!existsSync11(agentsMdPath)) {
|
|
3078
2757
|
const stackArg = opts.stacks && opts.stacks.length > 1 ? opts.stacks : opts.stack;
|
|
3079
2758
|
const content = generateAgentsMdContent(opts.projectDir, stackArg);
|
|
3080
2759
|
generateFile(agentsMdPath, content);
|
|
@@ -3082,23 +2761,23 @@ async function scaffoldDocs(opts) {
|
|
|
3082
2761
|
} else {
|
|
3083
2762
|
agentsMd = "exists";
|
|
3084
2763
|
}
|
|
3085
|
-
const docsDir =
|
|
3086
|
-
if (!
|
|
3087
|
-
generateFile(
|
|
3088
|
-
generateFile(
|
|
3089
|
-
generateFile(
|
|
3090
|
-
generateFile(
|
|
3091
|
-
generateFile(
|
|
2764
|
+
const docsDir = join12(opts.projectDir, "docs");
|
|
2765
|
+
if (!existsSync11(docsDir)) {
|
|
2766
|
+
generateFile(join12(docsDir, "index.md"), generateDocsIndexContent());
|
|
2767
|
+
generateFile(join12(docsDir, "exec-plans", "active", ".gitkeep"), "");
|
|
2768
|
+
generateFile(join12(docsDir, "exec-plans", "completed", ".gitkeep"), "");
|
|
2769
|
+
generateFile(join12(docsDir, "quality", ".gitkeep"), DO_NOT_EDIT_HEADER);
|
|
2770
|
+
generateFile(join12(docsDir, "generated", ".gitkeep"), DO_NOT_EDIT_HEADER);
|
|
3092
2771
|
docsScaffold = "created";
|
|
3093
2772
|
} else {
|
|
3094
2773
|
docsScaffold = "exists";
|
|
3095
2774
|
}
|
|
3096
|
-
const readmePath =
|
|
3097
|
-
if (!
|
|
2775
|
+
const readmePath = join12(opts.projectDir, "README.md");
|
|
2776
|
+
if (!existsSync11(readmePath)) {
|
|
3098
2777
|
let cliHelpOutput = "";
|
|
3099
2778
|
try {
|
|
3100
|
-
const { execFileSync:
|
|
3101
|
-
cliHelpOutput =
|
|
2779
|
+
const { execFileSync: execFileSync12 } = await import("child_process");
|
|
2780
|
+
cliHelpOutput = execFileSync12(process.execPath, [process.argv[1], "--help"], {
|
|
3102
2781
|
stdio: "pipe",
|
|
3103
2782
|
timeout: 1e4
|
|
3104
2783
|
}).toString();
|
|
@@ -3133,8 +2812,8 @@ async function scaffoldDocs(opts) {
|
|
|
3133
2812
|
}
|
|
3134
2813
|
|
|
3135
2814
|
// src/modules/infra/dockerfile-template.ts
|
|
3136
|
-
import { existsSync as
|
|
3137
|
-
import { join as
|
|
2815
|
+
import { existsSync as existsSync12, writeFileSync as writeFileSync7 } from "fs";
|
|
2816
|
+
import { join as join13 } from "path";
|
|
3138
2817
|
function genericTemplate() {
|
|
3139
2818
|
return renderTemplateFile("templates/dockerfiles/Dockerfile.generic");
|
|
3140
2819
|
}
|
|
@@ -3171,15 +2850,15 @@ function generateDockerfileTemplate(projectDir, stackOrDetections) {
|
|
|
3171
2850
|
if (!projectDir) {
|
|
3172
2851
|
return fail2("projectDir is required");
|
|
3173
2852
|
}
|
|
3174
|
-
const dockerfilePath =
|
|
3175
|
-
if (
|
|
2853
|
+
const dockerfilePath = join13(projectDir, "Dockerfile");
|
|
2854
|
+
if (existsSync12(dockerfilePath)) {
|
|
3176
2855
|
return fail2("Dockerfile already exists");
|
|
3177
2856
|
}
|
|
3178
2857
|
if (Array.isArray(stackOrDetections) && stackOrDetections.length > 1) {
|
|
3179
2858
|
const content2 = multiStageTemplate(stackOrDetections);
|
|
3180
2859
|
const stacks = stackOrDetections.map((d) => d.stack);
|
|
3181
2860
|
try {
|
|
3182
|
-
|
|
2861
|
+
writeFileSync7(dockerfilePath, content2, "utf-8");
|
|
3183
2862
|
} catch (err) {
|
|
3184
2863
|
const message = err instanceof Error ? err.message : String(err);
|
|
3185
2864
|
return fail2(`Failed to write Dockerfile: ${message}`);
|
|
@@ -3198,7 +2877,7 @@ function generateDockerfileTemplate(projectDir, stackOrDetections) {
|
|
|
3198
2877
|
resolvedStack = "generic";
|
|
3199
2878
|
}
|
|
3200
2879
|
try {
|
|
3201
|
-
|
|
2880
|
+
writeFileSync7(dockerfilePath, content, "utf-8");
|
|
3202
2881
|
} catch (err) {
|
|
3203
2882
|
const message = err instanceof Error ? err.message : String(err);
|
|
3204
2883
|
return fail2(`Failed to write Dockerfile: ${message}`);
|
|
@@ -3207,7 +2886,7 @@ function generateDockerfileTemplate(projectDir, stackOrDetections) {
|
|
|
3207
2886
|
}
|
|
3208
2887
|
|
|
3209
2888
|
// src/modules/infra/init-project.ts
|
|
3210
|
-
var HARNESS_VERSION = true ? "0.
|
|
2889
|
+
var HARNESS_VERSION = true ? "0.27.0" : "0.0.0-dev";
|
|
3211
2890
|
function failResult(opts, error) {
|
|
3212
2891
|
return {
|
|
3213
2892
|
status: "fail",
|
|
@@ -3272,6 +2951,19 @@ async function initProjectInner(opts) {
|
|
|
3272
2951
|
}
|
|
3273
2952
|
info(`App type: ${appType}`);
|
|
3274
2953
|
}
|
|
2954
|
+
const workflowSrc = join14(getPackageRoot(), "templates/workflows/default.yaml");
|
|
2955
|
+
const workflowDest = join14(projectDir, ".codeharness/workflows/default.yaml");
|
|
2956
|
+
const workflowRelPath = ".codeharness/workflows/default.yaml";
|
|
2957
|
+
if (existsSync13(workflowDest) && !opts.force) {
|
|
2958
|
+
result.workflow = { status: "exists", path: workflowRelPath };
|
|
2959
|
+
if (!isJson) info(`Workflow: ${workflowRelPath} already exists`);
|
|
2960
|
+
} else {
|
|
2961
|
+
const overwriting = existsSync13(workflowDest);
|
|
2962
|
+
mkdirSync4(dirname3(workflowDest), { recursive: true });
|
|
2963
|
+
copyFileSync(workflowSrc, workflowDest);
|
|
2964
|
+
result.workflow = { status: overwriting ? "overwritten" : "created", path: workflowRelPath };
|
|
2965
|
+
if (!isJson) ok(`Workflow: ${workflowRelPath} ${overwriting ? "overwritten" : "created"}`);
|
|
2966
|
+
}
|
|
3275
2967
|
const dfResult = generateDockerfileTemplate(projectDir, allStacks);
|
|
3276
2968
|
if (isOk(dfResult)) {
|
|
3277
2969
|
result.dockerfile = { generated: true, stack: dfResult.data.stack, stacks: dfResult.data.stacks };
|
|
@@ -3309,7 +3001,7 @@ async function initProjectInner(opts) {
|
|
|
3309
3001
|
return ok2(result);
|
|
3310
3002
|
}
|
|
3311
3003
|
result.dependencies = depResult.data;
|
|
3312
|
-
result.beads =
|
|
3004
|
+
result.beads = { status: "skipped", message: "beads removed" };
|
|
3313
3005
|
const bmadResult = setupBmad({ projectDir, isJson });
|
|
3314
3006
|
if (isOk(bmadResult)) result.bmad = bmadResult.data;
|
|
3315
3007
|
let state = getDefaultState(stack);
|
|
@@ -3348,7 +3040,7 @@ async function initProjectInner(opts) {
|
|
|
3348
3040
|
if (!isJson) info("OTLP: skipped (--no-observability)");
|
|
3349
3041
|
} else {
|
|
3350
3042
|
for (const detection of allStacks) {
|
|
3351
|
-
const stackDir = detection.dir === "." ? projectDir :
|
|
3043
|
+
const stackDir = detection.dir === "." ? projectDir : join14(projectDir, detection.dir);
|
|
3352
3044
|
const stackOtlp = instrumentProject(stackDir, detection.stack, { json: isJson, appType });
|
|
3353
3045
|
if (detection.dir === "." && detection.stack === stack) {
|
|
3354
3046
|
result.otlp = stackOtlp;
|
|
@@ -3393,7 +3085,7 @@ async function initProjectInner(opts) {
|
|
|
3393
3085
|
}
|
|
3394
3086
|
function handleRerun(opts, result) {
|
|
3395
3087
|
const { projectDir, json: isJson = false } = opts;
|
|
3396
|
-
if (!
|
|
3088
|
+
if (!existsSync13(getStatePath(projectDir))) return null;
|
|
3397
3089
|
try {
|
|
3398
3090
|
const existingState = readState(projectDir);
|
|
3399
3091
|
const legacyObsDisabled = existingState.enforcement.observability === false;
|
|
@@ -3405,6 +3097,23 @@ function handleRerun(opts, result) {
|
|
|
3405
3097
|
result.stacks = existingState.stacks ?? [];
|
|
3406
3098
|
result.enforcement = existingState.enforcement;
|
|
3407
3099
|
result.documentation = { agents_md: "exists", docs_scaffold: "exists", readme: "exists" };
|
|
3100
|
+
const workflowRelPath = ".codeharness/workflows/default.yaml";
|
|
3101
|
+
const workflowPath = join14(projectDir, workflowRelPath);
|
|
3102
|
+
if (existsSync13(workflowPath) && !opts.force) {
|
|
3103
|
+
result.workflow = { status: "exists", path: workflowRelPath };
|
|
3104
|
+
} else if (existsSync13(workflowPath) && opts.force) {
|
|
3105
|
+
const workflowSrc = join14(getPackageRoot(), "templates/workflows/default.yaml");
|
|
3106
|
+
mkdirSync4(dirname3(workflowPath), { recursive: true });
|
|
3107
|
+
copyFileSync(workflowSrc, workflowPath);
|
|
3108
|
+
result.workflow = { status: "overwritten", path: workflowRelPath };
|
|
3109
|
+
if (!isJson) ok(`Workflow: ${workflowRelPath} overwritten`);
|
|
3110
|
+
} else {
|
|
3111
|
+
const workflowSrc = join14(getPackageRoot(), "templates/workflows/default.yaml");
|
|
3112
|
+
mkdirSync4(dirname3(workflowPath), { recursive: true });
|
|
3113
|
+
copyFileSync(workflowSrc, workflowPath);
|
|
3114
|
+
result.workflow = { status: "created", path: workflowRelPath };
|
|
3115
|
+
if (!isJson) ok(`Workflow: ${workflowRelPath} created`);
|
|
3116
|
+
}
|
|
3408
3117
|
result.dependencies = verifyDeps(isJson);
|
|
3409
3118
|
result.docker = existingState.docker ? { compose_file: existingState.docker.compose_file, stack_running: existingState.docker.stack_running, services: [], ports: existingState.docker.ports } : null;
|
|
3410
3119
|
const bmadResult = verifyBmadOnRerun(projectDir, isJson);
|
|
@@ -3436,10 +3145,10 @@ function validateRemoteUrls(opts) {
|
|
|
3436
3145
|
}
|
|
3437
3146
|
|
|
3438
3147
|
// src/modules/infra/stack-management.ts
|
|
3439
|
-
import { execFileSync as
|
|
3148
|
+
import { execFileSync as execFileSync10 } from "child_process";
|
|
3440
3149
|
|
|
3441
3150
|
// src/modules/infra/container-cleanup.ts
|
|
3442
|
-
import { execFileSync as
|
|
3151
|
+
import { execFileSync as execFileSync11 } from "child_process";
|
|
3443
3152
|
var STALE_PATTERNS = ["codeharness-shared-", "codeharness-collector-", "codeharness-verify-"];
|
|
3444
3153
|
function cleanupContainers() {
|
|
3445
3154
|
try {
|
|
@@ -3449,7 +3158,7 @@ function cleanupContainers() {
|
|
|
3449
3158
|
const staleNames = [];
|
|
3450
3159
|
for (const pattern of STALE_PATTERNS) {
|
|
3451
3160
|
try {
|
|
3452
|
-
const output =
|
|
3161
|
+
const output = execFileSync11(
|
|
3453
3162
|
"docker",
|
|
3454
3163
|
[
|
|
3455
3164
|
"ps",
|
|
@@ -3476,7 +3185,7 @@ function cleanupContainers() {
|
|
|
3476
3185
|
const removed = [];
|
|
3477
3186
|
for (const name of staleNames) {
|
|
3478
3187
|
try {
|
|
3479
|
-
|
|
3188
|
+
execFileSync11("docker", ["rm", "-f", name], {
|
|
3480
3189
|
stdio: "pipe",
|
|
3481
3190
|
timeout: 1e4
|
|
3482
3191
|
});
|
|
@@ -3497,8 +3206,8 @@ var DEFAULT_METRICS_URL = `http://localhost:${DEFAULT_PORTS.metrics}`;
|
|
|
3497
3206
|
var DEFAULT_TRACES_URL = `http://localhost:${DEFAULT_PORTS.traces}`;
|
|
3498
3207
|
|
|
3499
3208
|
// src/modules/infra/dockerfile-validator.ts
|
|
3500
|
-
import { existsSync as
|
|
3501
|
-
import { join as
|
|
3209
|
+
import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
|
|
3210
|
+
import { join as join15 } from "path";
|
|
3502
3211
|
var DEFAULT_RULES = {
|
|
3503
3212
|
requirePinnedFrom: true,
|
|
3504
3213
|
requireBinaryOnPath: true,
|
|
@@ -3513,8 +3222,8 @@ function dfGap(rule, description, suggestedFix, line) {
|
|
|
3513
3222
|
return g;
|
|
3514
3223
|
}
|
|
3515
3224
|
function loadRules(projectDir) {
|
|
3516
|
-
const rulesPath =
|
|
3517
|
-
if (!
|
|
3225
|
+
const rulesPath = join15(projectDir, "patches", "infra", "dockerfile-rules.md");
|
|
3226
|
+
if (!existsSync14(rulesPath)) {
|
|
3518
3227
|
return {
|
|
3519
3228
|
rules: DEFAULT_RULES,
|
|
3520
3229
|
warnings: ["dockerfile-rules.md not found -- using defaults."]
|
|
@@ -3596,13 +3305,13 @@ function checkCacheCleanup(lines) {
|
|
|
3596
3305
|
return [];
|
|
3597
3306
|
}
|
|
3598
3307
|
function validateDockerfile(projectDir) {
|
|
3599
|
-
const dfPath =
|
|
3600
|
-
if (!
|
|
3308
|
+
const dfPath = join15(projectDir, "Dockerfile");
|
|
3309
|
+
if (!existsSync14(dfPath)) {
|
|
3601
3310
|
return fail2("No Dockerfile found");
|
|
3602
3311
|
}
|
|
3603
3312
|
let content;
|
|
3604
3313
|
try {
|
|
3605
|
-
content =
|
|
3314
|
+
content = readFileSync12(dfPath, "utf-8");
|
|
3606
3315
|
} catch {
|
|
3607
3316
|
return fail2("Dockerfile exists but could not be read");
|
|
3608
3317
|
}
|
|
@@ -3651,8 +3360,6 @@ export {
|
|
|
3651
3360
|
getStackProvider,
|
|
3652
3361
|
detectStacks,
|
|
3653
3362
|
detectStack,
|
|
3654
|
-
getPackageRoot,
|
|
3655
|
-
renderTemplateFile,
|
|
3656
3363
|
getStatePath,
|
|
3657
3364
|
writeState,
|
|
3658
3365
|
readState,
|
|
@@ -3684,19 +3391,9 @@ export {
|
|
|
3684
3391
|
stopCollectorOnly,
|
|
3685
3392
|
cleanupOrphanedContainers,
|
|
3686
3393
|
cleanupVerifyEnv,
|
|
3687
|
-
removePatch,
|
|
3688
|
-
isBeadsCLIInstalled,
|
|
3689
|
-
createIssue,
|
|
3690
|
-
closeIssue,
|
|
3691
|
-
updateIssue,
|
|
3692
|
-
listIssues,
|
|
3693
|
-
isBeadsInitialized,
|
|
3694
|
-
buildGapId,
|
|
3695
|
-
createOrFindIssue,
|
|
3696
|
-
PATCH_TARGETS,
|
|
3697
3394
|
isBmadInstalled,
|
|
3395
|
+
getStoryFilePath,
|
|
3698
3396
|
parseEpicsFile,
|
|
3699
|
-
importStoriesToBeads,
|
|
3700
3397
|
validateDockerfile,
|
|
3701
3398
|
initProject2 as initProject,
|
|
3702
3399
|
cleanupContainers2 as cleanupContainers
|