cleargate 0.6.1 → 0.7.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/MANIFEST.json +2 -2
- package/dist/cli.cjs +526 -460
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +535 -470
- package/dist/cli.js.map +1 -1
- package/dist/templates/cleargate-planning/MANIFEST.json +2 -2
- package/package.json +1 -1
- package/templates/cleargate-planning/MANIFEST.json +2 -2
package/dist/cli.cjs
CHANGED
|
@@ -627,7 +627,7 @@ var import_commander = require("commander");
|
|
|
627
627
|
// package.json
|
|
628
628
|
var package_default = {
|
|
629
629
|
name: "cleargate",
|
|
630
|
-
version: "0.
|
|
630
|
+
version: "0.7.0",
|
|
631
631
|
private: false,
|
|
632
632
|
type: "module",
|
|
633
633
|
description: "Planning framework for Claude Code agents \u2014 sprint/epic/story protocol, four-agent loop (architect/developer/qa/reporter), Karpathy-style awareness wiki.",
|
|
@@ -1897,8 +1897,8 @@ async function stampHandler(file, opts, cli) {
|
|
|
1897
1897
|
|
|
1898
1898
|
// src/commands/init.ts
|
|
1899
1899
|
init_cjs_shims();
|
|
1900
|
-
var
|
|
1901
|
-
var
|
|
1900
|
+
var fs16 = __toESM(require("fs"), 1);
|
|
1901
|
+
var path17 = __toESM(require("path"), 1);
|
|
1902
1902
|
var import_node_url5 = require("url");
|
|
1903
1903
|
var import_node_child_process3 = require("child_process");
|
|
1904
1904
|
|
|
@@ -1911,6 +1911,7 @@ var HOOK_FILES_WITH_PIN = /* @__PURE__ */ new Set([
|
|
|
1911
1911
|
".claude/hooks/stamp-and-gate.sh",
|
|
1912
1912
|
".claude/hooks/session-start.sh"
|
|
1913
1913
|
]);
|
|
1914
|
+
var SKIP_FILES = /* @__PURE__ */ new Set(["CLAUDE.md"]);
|
|
1914
1915
|
function listFilesRecursive(dir) {
|
|
1915
1916
|
const results = [];
|
|
1916
1917
|
function walk(current, rel) {
|
|
@@ -1933,7 +1934,7 @@ function copyPayload(payloadDir, targetCwd, opts) {
|
|
|
1933
1934
|
if (!fs7.existsSync(payloadDir)) {
|
|
1934
1935
|
throw new Error(`copyPayload: payloadDir does not exist: ${payloadDir}`);
|
|
1935
1936
|
}
|
|
1936
|
-
const files = listFilesRecursive(payloadDir);
|
|
1937
|
+
const files = listFilesRecursive(payloadDir).filter((r) => !SKIP_FILES.has(r));
|
|
1937
1938
|
for (const relPath of files) {
|
|
1938
1939
|
const srcPath = path7.join(payloadDir, relPath);
|
|
1939
1940
|
const dstPath = path7.join(targetCwd, relPath);
|
|
@@ -1944,9 +1945,14 @@ function copyPayload(payloadDir, targetCwd, opts) {
|
|
|
1944
1945
|
srcContent = text;
|
|
1945
1946
|
}
|
|
1946
1947
|
const srcBuffer = typeof srcContent === "string" ? Buffer.from(srcContent, "utf8") : srcContent;
|
|
1948
|
+
const srcMode = fs7.statSync(srcPath).mode;
|
|
1949
|
+
const needsExec = (srcMode & 73) !== 0 || relPath.endsWith(".sh");
|
|
1947
1950
|
if (fs7.existsSync(dstPath)) {
|
|
1948
1951
|
const dstContent = fs7.readFileSync(dstPath);
|
|
1949
1952
|
if (srcBuffer.equals(dstContent)) {
|
|
1953
|
+
if (needsExec && process.platform !== "win32") {
|
|
1954
|
+
fs7.chmodSync(dstPath, 493);
|
|
1955
|
+
}
|
|
1950
1956
|
report.skipped++;
|
|
1951
1957
|
report.actions.push({ action: "skipped", relPath });
|
|
1952
1958
|
continue;
|
|
@@ -1957,10 +1963,16 @@ function copyPayload(payloadDir, targetCwd, opts) {
|
|
|
1957
1963
|
continue;
|
|
1958
1964
|
}
|
|
1959
1965
|
fs7.writeFileSync(dstPath, srcBuffer);
|
|
1966
|
+
if (needsExec && process.platform !== "win32") {
|
|
1967
|
+
fs7.chmodSync(dstPath, 493);
|
|
1968
|
+
}
|
|
1960
1969
|
report.overwritten++;
|
|
1961
1970
|
report.actions.push({ action: "overwritten", relPath });
|
|
1962
1971
|
} else {
|
|
1963
1972
|
fs7.writeFileSync(dstPath, srcBuffer);
|
|
1973
|
+
if (needsExec && process.platform !== "win32") {
|
|
1974
|
+
fs7.chmodSync(dstPath, 493);
|
|
1975
|
+
}
|
|
1964
1976
|
report.created++;
|
|
1965
1977
|
report.actions.push({ action: "created", relPath });
|
|
1966
1978
|
}
|
|
@@ -2029,15 +2041,49 @@ function injectClaudeMd(existing, block) {
|
|
|
2029
2041
|
return existing.trimEnd() + "\n\n" + block + "\n";
|
|
2030
2042
|
}
|
|
2031
2043
|
|
|
2044
|
+
// src/init/inject-mcp-json.ts
|
|
2045
|
+
init_cjs_shims();
|
|
2046
|
+
var fs8 = __toESM(require("fs"), 1);
|
|
2047
|
+
var path8 = __toESM(require("path"), 1);
|
|
2048
|
+
function mergeMcpJson(existing, entry) {
|
|
2049
|
+
const next = existing ? { ...existing } : {};
|
|
2050
|
+
const servers = { ...next.mcpServers ?? {} };
|
|
2051
|
+
servers.cleargate = entry;
|
|
2052
|
+
next.mcpServers = servers;
|
|
2053
|
+
return JSON.stringify(next, null, 2) + "\n";
|
|
2054
|
+
}
|
|
2055
|
+
function injectMcpJson(cwd, url) {
|
|
2056
|
+
const dst = path8.join(cwd, ".mcp.json");
|
|
2057
|
+
const entry = { type: "http", url };
|
|
2058
|
+
let existing = null;
|
|
2059
|
+
let existingRaw = null;
|
|
2060
|
+
if (fs8.existsSync(dst)) {
|
|
2061
|
+
existingRaw = fs8.readFileSync(dst, "utf8");
|
|
2062
|
+
try {
|
|
2063
|
+
existing = JSON.parse(existingRaw);
|
|
2064
|
+
} catch {
|
|
2065
|
+
throw new Error(
|
|
2066
|
+
`inject-mcp-json: ${dst} is not valid JSON; refusing to overwrite. Fix or remove the file and re-run init.`
|
|
2067
|
+
);
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
const next = mergeMcpJson(existing, entry);
|
|
2071
|
+
if (existingRaw !== null && existingRaw === next) {
|
|
2072
|
+
return "unchanged";
|
|
2073
|
+
}
|
|
2074
|
+
fs8.writeFileSync(dst, next);
|
|
2075
|
+
return existingRaw === null ? "created" : "updated";
|
|
2076
|
+
}
|
|
2077
|
+
|
|
2032
2078
|
// src/commands/wiki-build.ts
|
|
2033
2079
|
init_cjs_shims();
|
|
2034
|
-
var
|
|
2035
|
-
var
|
|
2080
|
+
var fs14 = __toESM(require("fs"), 1);
|
|
2081
|
+
var path14 = __toESM(require("path"), 1);
|
|
2036
2082
|
|
|
2037
2083
|
// src/wiki/scan.ts
|
|
2038
2084
|
init_cjs_shims();
|
|
2039
|
-
var
|
|
2040
|
-
var
|
|
2085
|
+
var fs9 = __toESM(require("fs"), 1);
|
|
2086
|
+
var path9 = __toESM(require("path"), 1);
|
|
2041
2087
|
|
|
2042
2088
|
// src/wiki/derive-bucket.ts
|
|
2043
2089
|
init_cjs_shims();
|
|
@@ -2082,19 +2128,19 @@ var EXCLUDED_SUFFIXES = [
|
|
|
2082
2128
|
function scanRawItems(deliveryRoot, repoRoot) {
|
|
2083
2129
|
const results = [];
|
|
2084
2130
|
for (const subdir of ["pending-sync", "archive"]) {
|
|
2085
|
-
const dir =
|
|
2086
|
-
if (!
|
|
2087
|
-
const entries =
|
|
2131
|
+
const dir = path9.join(deliveryRoot, subdir);
|
|
2132
|
+
if (!fs9.existsSync(dir)) continue;
|
|
2133
|
+
const entries = fs9.readdirSync(dir, { recursive: true, encoding: "utf8" });
|
|
2088
2134
|
for (const rel of entries) {
|
|
2089
2135
|
if (!rel.endsWith(".md")) continue;
|
|
2090
2136
|
if (rel.includes("~") || rel.startsWith(".")) continue;
|
|
2091
|
-
const absPath =
|
|
2092
|
-
const stat =
|
|
2137
|
+
const absPath = path9.join(dir, rel);
|
|
2138
|
+
const stat = fs9.statSync(absPath);
|
|
2093
2139
|
if (!stat.isFile()) continue;
|
|
2094
|
-
const rawPath =
|
|
2140
|
+
const rawPath = path9.relative(repoRoot, absPath).replace(/\\/g, "/");
|
|
2095
2141
|
const isExcluded = EXCLUDED_SUFFIXES.some((excl) => rawPath.startsWith(excl));
|
|
2096
2142
|
if (isExcluded) continue;
|
|
2097
|
-
const filename =
|
|
2143
|
+
const filename = path9.basename(absPath);
|
|
2098
2144
|
let bucketInfo;
|
|
2099
2145
|
try {
|
|
2100
2146
|
bucketInfo = deriveBucket(filename);
|
|
@@ -2107,7 +2153,7 @@ function scanRawItems(deliveryRoot, repoRoot) {
|
|
|
2107
2153
|
} catch {
|
|
2108
2154
|
continue;
|
|
2109
2155
|
}
|
|
2110
|
-
const raw =
|
|
2156
|
+
const raw = fs9.readFileSync(absPath, "utf8");
|
|
2111
2157
|
let fm;
|
|
2112
2158
|
let body;
|
|
2113
2159
|
try {
|
|
@@ -2222,8 +2268,8 @@ function parseFmRaw(raw) {
|
|
|
2222
2268
|
|
|
2223
2269
|
// src/wiki/synthesis/active-sprint.ts
|
|
2224
2270
|
init_cjs_shims();
|
|
2225
|
-
var
|
|
2226
|
-
var
|
|
2271
|
+
var fs10 = __toESM(require("fs"), 1);
|
|
2272
|
+
var path10 = __toESM(require("path"), 1);
|
|
2227
2273
|
var import_node_url = require("url");
|
|
2228
2274
|
|
|
2229
2275
|
// src/wiki/synthesis/render.ts
|
|
@@ -2263,7 +2309,7 @@ function renderSection(template, ctx) {
|
|
|
2263
2309
|
// src/wiki/synthesis/active-sprint.ts
|
|
2264
2310
|
function compile(state2, templateDir) {
|
|
2265
2311
|
const tplDir = templateDir ?? resolveDefaultTemplateDir();
|
|
2266
|
-
const tpl =
|
|
2312
|
+
const tpl = fs10.readFileSync(path10.join(tplDir, "active-sprint.md"), "utf8");
|
|
2267
2313
|
const sprints = state2.filter((i) => i.bucket === "sprints");
|
|
2268
2314
|
const active = sprints.filter((s) => isSet(s.fm["activated_at"]) && !isSet(s.fm["completed_at"]));
|
|
2269
2315
|
const completed = sprints.filter((s) => isSet(s.fm["completed_at"]));
|
|
@@ -2287,18 +2333,18 @@ function isSet(val) {
|
|
|
2287
2333
|
return s !== "" && s !== "null";
|
|
2288
2334
|
}
|
|
2289
2335
|
function resolveDefaultTemplateDir() {
|
|
2290
|
-
const __dirname =
|
|
2291
|
-
return
|
|
2336
|
+
const __dirname = path10.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
|
|
2337
|
+
return path10.resolve(__dirname, "..", "templates", "synthesis");
|
|
2292
2338
|
}
|
|
2293
2339
|
|
|
2294
2340
|
// src/wiki/synthesis/open-gates.ts
|
|
2295
2341
|
init_cjs_shims();
|
|
2296
|
-
var
|
|
2297
|
-
var
|
|
2342
|
+
var fs11 = __toESM(require("fs"), 1);
|
|
2343
|
+
var path11 = __toESM(require("path"), 1);
|
|
2298
2344
|
var import_node_url2 = require("url");
|
|
2299
2345
|
function compile2(state2, templateDir) {
|
|
2300
2346
|
const tplDir = templateDir ?? resolveDefaultTemplateDir2();
|
|
2301
|
-
const tpl =
|
|
2347
|
+
const tpl = fs11.readFileSync(path11.join(tplDir, "open-gates.md"), "utf8");
|
|
2302
2348
|
const gate1 = state2.filter((i) => {
|
|
2303
2349
|
if (i.bucket !== "proposals") return false;
|
|
2304
2350
|
const status = String(i.fm["status"] ?? "");
|
|
@@ -2327,18 +2373,18 @@ function compile2(state2, templateDir) {
|
|
|
2327
2373
|
return renderTemplate(tpl, data);
|
|
2328
2374
|
}
|
|
2329
2375
|
function resolveDefaultTemplateDir2() {
|
|
2330
|
-
const __dirname =
|
|
2331
|
-
return
|
|
2376
|
+
const __dirname = path11.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
|
|
2377
|
+
return path11.resolve(__dirname, "..", "templates", "synthesis");
|
|
2332
2378
|
}
|
|
2333
2379
|
|
|
2334
2380
|
// src/wiki/synthesis/product-state.ts
|
|
2335
2381
|
init_cjs_shims();
|
|
2336
|
-
var
|
|
2337
|
-
var
|
|
2382
|
+
var fs12 = __toESM(require("fs"), 1);
|
|
2383
|
+
var path12 = __toESM(require("path"), 1);
|
|
2338
2384
|
var import_node_url3 = require("url");
|
|
2339
2385
|
function compile3(state2, templateDir) {
|
|
2340
2386
|
const tplDir = templateDir ?? resolveDefaultTemplateDir3();
|
|
2341
|
-
const tpl =
|
|
2387
|
+
const tpl = fs12.readFileSync(path12.join(tplDir, "product-state.md"), "utf8");
|
|
2342
2388
|
function countBucket(bucket) {
|
|
2343
2389
|
return state2.filter((i) => i.bucket === bucket);
|
|
2344
2390
|
}
|
|
@@ -2385,18 +2431,18 @@ function compile3(state2, templateDir) {
|
|
|
2385
2431
|
return renderTemplate(tpl, data);
|
|
2386
2432
|
}
|
|
2387
2433
|
function resolveDefaultTemplateDir3() {
|
|
2388
|
-
const __dirname =
|
|
2389
|
-
return
|
|
2434
|
+
const __dirname = path12.dirname((0, import_node_url3.fileURLToPath)(importMetaUrl));
|
|
2435
|
+
return path12.resolve(__dirname, "..", "templates", "synthesis");
|
|
2390
2436
|
}
|
|
2391
2437
|
|
|
2392
2438
|
// src/wiki/synthesis/roadmap.ts
|
|
2393
2439
|
init_cjs_shims();
|
|
2394
|
-
var
|
|
2395
|
-
var
|
|
2440
|
+
var fs13 = __toESM(require("fs"), 1);
|
|
2441
|
+
var path13 = __toESM(require("path"), 1);
|
|
2396
2442
|
var import_node_url4 = require("url");
|
|
2397
2443
|
function compile4(state2, templateDir) {
|
|
2398
2444
|
const tplDir = templateDir ?? resolveDefaultTemplateDir4();
|
|
2399
|
-
const tpl =
|
|
2445
|
+
const tpl = fs13.readFileSync(path13.join(tplDir, "roadmap.md"), "utf8");
|
|
2400
2446
|
const sprints = state2.filter((i) => i.bucket === "sprints");
|
|
2401
2447
|
const epics = state2.filter((i) => i.bucket === "epics");
|
|
2402
2448
|
const inFlightSprints = sprints.filter(
|
|
@@ -2449,8 +2495,8 @@ function isShippedStatus(status) {
|
|
|
2449
2495
|
return status === "Completed" || status === "Approved";
|
|
2450
2496
|
}
|
|
2451
2497
|
function resolveDefaultTemplateDir4() {
|
|
2452
|
-
const __dirname =
|
|
2453
|
-
return
|
|
2498
|
+
const __dirname = path13.dirname((0, import_node_url4.fileURLToPath)(importMetaUrl));
|
|
2499
|
+
return path13.resolve(__dirname, "..", "templates", "synthesis");
|
|
2454
2500
|
}
|
|
2455
2501
|
|
|
2456
2502
|
// src/commands/wiki-build.ts
|
|
@@ -2475,16 +2521,16 @@ async function wikiBuildHandler(opts = {}) {
|
|
|
2475
2521
|
const exit = opts.exit ?? ((c) => process.exit(c));
|
|
2476
2522
|
const gitRunner = opts.gitRunner;
|
|
2477
2523
|
const templateDir = opts.templateDir;
|
|
2478
|
-
const deliveryRoot =
|
|
2479
|
-
const wikiRoot =
|
|
2480
|
-
if (!
|
|
2524
|
+
const deliveryRoot = path14.join(cwd, ".cleargate", "delivery");
|
|
2525
|
+
const wikiRoot = path14.join(cwd, ".cleargate", "wiki");
|
|
2526
|
+
if (!fs14.existsSync(deliveryRoot)) {
|
|
2481
2527
|
stderr(`wiki build: .cleargate/delivery/ not found at ${deliveryRoot}
|
|
2482
2528
|
`);
|
|
2483
2529
|
exit(1);
|
|
2484
2530
|
return;
|
|
2485
2531
|
}
|
|
2486
2532
|
for (const bucket of BUCKET_ORDER) {
|
|
2487
|
-
|
|
2533
|
+
fs14.mkdirSync(path14.join(wikiRoot, bucket), { recursive: true });
|
|
2488
2534
|
}
|
|
2489
2535
|
const items = scanRawItems(deliveryRoot, cwd);
|
|
2490
2536
|
const timestamp = now();
|
|
@@ -2507,19 +2553,19 @@ async function wikiBuildHandler(opts = {}) {
|
|
|
2507
2553
|
};
|
|
2508
2554
|
const body = buildPageBody(item, wikiPage);
|
|
2509
2555
|
const content = serializePage(wikiPage, body);
|
|
2510
|
-
const pageDir =
|
|
2511
|
-
|
|
2512
|
-
|
|
2556
|
+
const pageDir = path14.join(wikiRoot, item.bucket);
|
|
2557
|
+
fs14.mkdirSync(pageDir, { recursive: true });
|
|
2558
|
+
fs14.writeFileSync(path14.join(pageDir, `${item.id}.md`), content, "utf8");
|
|
2513
2559
|
pagesWritten++;
|
|
2514
2560
|
}
|
|
2515
2561
|
const indexContent = buildIndex(items);
|
|
2516
|
-
|
|
2562
|
+
fs14.writeFileSync(path14.join(wikiRoot, "index.md"), indexContent, "utf8");
|
|
2517
2563
|
const logContent = buildLog(items, timestamp);
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2564
|
+
fs14.writeFileSync(path14.join(wikiRoot, "log.md"), logContent, "utf8");
|
|
2565
|
+
fs14.writeFileSync(path14.join(wikiRoot, "active-sprint.md"), compile(items, templateDir), "utf8");
|
|
2566
|
+
fs14.writeFileSync(path14.join(wikiRoot, "open-gates.md"), compile2(items, templateDir), "utf8");
|
|
2567
|
+
fs14.writeFileSync(path14.join(wikiRoot, "product-state.md"), compile3(items, templateDir), "utf8");
|
|
2568
|
+
fs14.writeFileSync(path14.join(wikiRoot, "roadmap.md"), compile4(items, templateDir), "utf8");
|
|
2523
2569
|
stdout(`wiki build: OK (${pagesWritten} pages written)
|
|
2524
2570
|
`);
|
|
2525
2571
|
}
|
|
@@ -2686,7 +2732,7 @@ function buildLog(items, timestamp) {
|
|
|
2686
2732
|
init_cjs_shims();
|
|
2687
2733
|
var import_promises2 = require("fs/promises");
|
|
2688
2734
|
var import_node_fs = require("fs");
|
|
2689
|
-
var
|
|
2735
|
+
var path15 = __toESM(require("path"), 1);
|
|
2690
2736
|
|
|
2691
2737
|
// src/lib/sha256.ts
|
|
2692
2738
|
init_cjs_shims();
|
|
@@ -2710,23 +2756,23 @@ function shortHash(full) {
|
|
|
2710
2756
|
// src/lib/manifest.ts
|
|
2711
2757
|
function resolveDefaultPackageRoot() {
|
|
2712
2758
|
const here = new URL(".", importMetaUrl).pathname;
|
|
2713
|
-
const distCandidate =
|
|
2759
|
+
const distCandidate = path15.join(here, "MANIFEST.json");
|
|
2714
2760
|
if ((0, import_node_fs.existsSync)(distCandidate)) {
|
|
2715
2761
|
return here;
|
|
2716
2762
|
}
|
|
2717
|
-
const oneLevelUp =
|
|
2763
|
+
const oneLevelUp = path15.join(here, "..", "MANIFEST.json");
|
|
2718
2764
|
if ((0, import_node_fs.existsSync)(oneLevelUp)) {
|
|
2719
|
-
return
|
|
2765
|
+
return path15.join(here, "..");
|
|
2720
2766
|
}
|
|
2721
|
-
const devCandidate =
|
|
2767
|
+
const devCandidate = path15.join(here, "..", "..", "..", "cleargate-planning", "MANIFEST.json");
|
|
2722
2768
|
if ((0, import_node_fs.existsSync)(devCandidate)) {
|
|
2723
|
-
return
|
|
2769
|
+
return path15.join(here, "..", "..", "..", "cleargate-planning");
|
|
2724
2770
|
}
|
|
2725
2771
|
return here;
|
|
2726
2772
|
}
|
|
2727
2773
|
function loadPackageManifest(opts) {
|
|
2728
2774
|
const packageRoot = opts?.packageRoot ?? resolveDefaultPackageRoot();
|
|
2729
|
-
const manifestPath =
|
|
2775
|
+
const manifestPath = path15.join(packageRoot, "MANIFEST.json");
|
|
2730
2776
|
if (!(0, import_node_fs.existsSync)(manifestPath)) {
|
|
2731
2777
|
throw new Error(
|
|
2732
2778
|
`MANIFEST.json not found at ${manifestPath}; run 'npm run build' to generate it.`
|
|
@@ -2743,7 +2789,7 @@ function loadPackageManifest(opts) {
|
|
|
2743
2789
|
return JSON.parse(raw);
|
|
2744
2790
|
}
|
|
2745
2791
|
async function loadInstallSnapshot(projectRoot) {
|
|
2746
|
-
const snapshotPath =
|
|
2792
|
+
const snapshotPath = path15.join(projectRoot, ".cleargate", ".install-manifest.json");
|
|
2747
2793
|
try {
|
|
2748
2794
|
const raw = await (0, import_promises2.readFile)(snapshotPath, "utf-8");
|
|
2749
2795
|
return JSON.parse(raw);
|
|
@@ -2752,7 +2798,7 @@ async function loadInstallSnapshot(projectRoot) {
|
|
|
2752
2798
|
}
|
|
2753
2799
|
}
|
|
2754
2800
|
async function computeCurrentSha(file, projectRoot) {
|
|
2755
|
-
const filePath =
|
|
2801
|
+
const filePath = path15.join(projectRoot, file.path);
|
|
2756
2802
|
try {
|
|
2757
2803
|
const raw = await (0, import_promises2.readFile)(filePath);
|
|
2758
2804
|
return hashNormalized(raw);
|
|
@@ -2781,8 +2827,8 @@ function classify(pkgSha, installSha, currentSha, tier) {
|
|
|
2781
2827
|
return "both-changed";
|
|
2782
2828
|
}
|
|
2783
2829
|
async function writeDriftState(projectRoot, state2, opts) {
|
|
2784
|
-
const cleargatDir =
|
|
2785
|
-
const finalPath =
|
|
2830
|
+
const cleargatDir = path15.join(projectRoot, ".cleargate");
|
|
2831
|
+
const finalPath = path15.join(cleargatDir, ".drift-state.json");
|
|
2786
2832
|
const tmpPath = `${finalPath}.tmp`;
|
|
2787
2833
|
const lastRefreshed = opts?.lastRefreshed ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
2788
2834
|
const fileContent = { last_refreshed: lastRefreshed, drift: state2 };
|
|
@@ -2791,7 +2837,7 @@ async function writeDriftState(projectRoot, state2, opts) {
|
|
|
2791
2837
|
await (0, import_promises2.rename)(tmpPath, finalPath);
|
|
2792
2838
|
}
|
|
2793
2839
|
async function readDriftState(projectRoot) {
|
|
2794
|
-
const driftPath =
|
|
2840
|
+
const driftPath = path15.join(projectRoot, ".cleargate", ".drift-state.json");
|
|
2795
2841
|
try {
|
|
2796
2842
|
const raw = await (0, import_promises2.readFile)(driftPath, "utf-8");
|
|
2797
2843
|
const parsed = JSON.parse(raw);
|
|
@@ -2866,24 +2912,24 @@ async function promptEmail(question, defaultValue, opts) {
|
|
|
2866
2912
|
|
|
2867
2913
|
// src/lib/identity.ts
|
|
2868
2914
|
init_cjs_shims();
|
|
2869
|
-
var
|
|
2870
|
-
var
|
|
2915
|
+
var fs15 = __toESM(require("fs"), 1);
|
|
2916
|
+
var path16 = __toESM(require("path"), 1);
|
|
2871
2917
|
var fsPromises = __toESM(require("fs/promises"), 1);
|
|
2872
2918
|
var os5 = __toESM(require("os"), 1);
|
|
2873
2919
|
var import_node_child_process2 = require("child_process");
|
|
2874
2920
|
function readParticipant(projectRoot) {
|
|
2875
|
-
const filePath =
|
|
2921
|
+
const filePath = path16.join(projectRoot, ".cleargate", ".participant.json");
|
|
2876
2922
|
try {
|
|
2877
|
-
const raw =
|
|
2923
|
+
const raw = fs15.readFileSync(filePath, "utf8");
|
|
2878
2924
|
return JSON.parse(raw);
|
|
2879
2925
|
} catch {
|
|
2880
2926
|
return null;
|
|
2881
2927
|
}
|
|
2882
2928
|
}
|
|
2883
2929
|
async function writeParticipant(projectRoot, email, source, now = () => (/* @__PURE__ */ new Date()).toISOString()) {
|
|
2884
|
-
const cleargateDir =
|
|
2930
|
+
const cleargateDir = path16.join(projectRoot, ".cleargate");
|
|
2885
2931
|
await fsPromises.mkdir(cleargateDir, { recursive: true });
|
|
2886
|
-
const filePath =
|
|
2932
|
+
const filePath = path16.join(cleargateDir, ".participant.json");
|
|
2887
2933
|
const tmpPath = filePath + ".tmp." + Date.now();
|
|
2888
2934
|
const content = {
|
|
2889
2935
|
email,
|
|
@@ -2959,16 +3005,16 @@ var HOOK_ADDITION = {
|
|
|
2959
3005
|
};
|
|
2960
3006
|
function resolveDefaultPayloadDir() {
|
|
2961
3007
|
const thisFile = (0, import_node_url5.fileURLToPath)(importMetaUrl);
|
|
2962
|
-
const pkgRoot =
|
|
2963
|
-
return
|
|
3008
|
+
const pkgRoot = path17.resolve(path17.dirname(thisFile), "..");
|
|
3009
|
+
return path17.join(pkgRoot, "templates", "cleargate-planning");
|
|
2964
3010
|
}
|
|
2965
3011
|
function countDeliveryItems(cwd) {
|
|
2966
|
-
const pendingSync =
|
|
2967
|
-
const archive =
|
|
3012
|
+
const pendingSync = path17.join(cwd, ".cleargate", "delivery", "pending-sync");
|
|
3013
|
+
const archive = path17.join(cwd, ".cleargate", "delivery", "archive");
|
|
2968
3014
|
let count = 0;
|
|
2969
3015
|
for (const dir of [pendingSync, archive]) {
|
|
2970
|
-
if (!
|
|
2971
|
-
const entries =
|
|
3016
|
+
if (!fs16.existsSync(dir)) continue;
|
|
3017
|
+
const entries = fs16.readdirSync(dir);
|
|
2972
3018
|
for (const f of entries) {
|
|
2973
3019
|
if (f.endsWith(".md") && f !== ".gitkeep") count++;
|
|
2974
3020
|
}
|
|
@@ -2977,12 +3023,12 @@ function countDeliveryItems(cwd) {
|
|
|
2977
3023
|
}
|
|
2978
3024
|
function writeAtomic(filePath, content) {
|
|
2979
3025
|
const tmpPath = filePath + ".tmp." + Date.now();
|
|
2980
|
-
|
|
2981
|
-
|
|
3026
|
+
fs16.writeFileSync(tmpPath, content, "utf8");
|
|
3027
|
+
fs16.renameSync(tmpPath, filePath);
|
|
2982
3028
|
}
|
|
2983
3029
|
function readPackageVersion(packageJsonPath) {
|
|
2984
3030
|
try {
|
|
2985
|
-
const raw =
|
|
3031
|
+
const raw = fs16.readFileSync(packageJsonPath, "utf8");
|
|
2986
3032
|
const pkg = JSON.parse(raw);
|
|
2987
3033
|
if (typeof pkg.version === "string" && pkg.version.length > 0) {
|
|
2988
3034
|
return pkg.version;
|
|
@@ -3002,16 +3048,16 @@ async function initHandler(opts = {}) {
|
|
|
3002
3048
|
const promptYesNoFn = opts.promptYesNo ?? promptYesNo;
|
|
3003
3049
|
const promptEmailFn = opts.promptEmail ?? promptEmail;
|
|
3004
3050
|
const spawnSyncFn = opts.spawnSyncFn ?? import_node_child_process3.spawnSync;
|
|
3005
|
-
if (!
|
|
3051
|
+
if (!fs16.existsSync(cwd)) {
|
|
3006
3052
|
stderr(`[cleargate init] ERROR: target directory does not exist: ${cwd}
|
|
3007
3053
|
`);
|
|
3008
3054
|
exit(1);
|
|
3009
3055
|
return;
|
|
3010
3056
|
}
|
|
3011
|
-
const testWritePath =
|
|
3057
|
+
const testWritePath = path17.join(cwd, `.cleargate-init-write-test-${Date.now()}`);
|
|
3012
3058
|
try {
|
|
3013
|
-
|
|
3014
|
-
|
|
3059
|
+
fs16.writeFileSync(testWritePath, "");
|
|
3060
|
+
fs16.unlinkSync(testWritePath);
|
|
3015
3061
|
} catch {
|
|
3016
3062
|
stderr(`[cleargate init] ERROR: target directory is not writable: ${cwd}
|
|
3017
3063
|
`);
|
|
@@ -3021,7 +3067,7 @@ async function initHandler(opts = {}) {
|
|
|
3021
3067
|
stdout(`[cleargate init] Target: ${cwd}
|
|
3022
3068
|
`);
|
|
3023
3069
|
const payloadDir = opts.payloadDir ?? resolveDefaultPayloadDir();
|
|
3024
|
-
if (!
|
|
3070
|
+
if (!fs16.existsSync(payloadDir)) {
|
|
3025
3071
|
stderr(`[cleargate init] ERROR: payload directory not found: ${payloadDir}
|
|
3026
3072
|
`);
|
|
3027
3073
|
stderr(`[cleargate init] Run \`npm run prebuild\` to copy the payload first.
|
|
@@ -3029,12 +3075,12 @@ async function initHandler(opts = {}) {
|
|
|
3029
3075
|
exit(1);
|
|
3030
3076
|
return;
|
|
3031
3077
|
}
|
|
3032
|
-
const uninstalledMarkerPath =
|
|
3078
|
+
const uninstalledMarkerPath = path17.join(cwd, ".cleargate", ".uninstalled");
|
|
3033
3079
|
let uninstalledMarker = null;
|
|
3034
3080
|
let userChoseRestore = false;
|
|
3035
|
-
if (
|
|
3081
|
+
if (fs16.existsSync(uninstalledMarkerPath)) {
|
|
3036
3082
|
try {
|
|
3037
|
-
const raw =
|
|
3083
|
+
const raw = fs16.readFileSync(uninstalledMarkerPath, "utf8");
|
|
3038
3084
|
uninstalledMarker = JSON.parse(raw);
|
|
3039
3085
|
} catch {
|
|
3040
3086
|
stderr(`[cleargate init] WARNING: .uninstalled marker is malformed; ignoring it.
|
|
@@ -3046,8 +3092,8 @@ async function initHandler(opts = {}) {
|
|
|
3046
3092
|
userChoseRestore = await promptYesNoFn(question, true);
|
|
3047
3093
|
if (userChoseRestore) {
|
|
3048
3094
|
for (const preservedPath of preserved) {
|
|
3049
|
-
const absPreserved =
|
|
3050
|
-
if (
|
|
3095
|
+
const absPreserved = path17.isAbsolute(preservedPath) ? preservedPath : path17.join(cwd, preservedPath);
|
|
3096
|
+
if (fs16.existsSync(absPreserved)) {
|
|
3051
3097
|
stdout(`[cleargate init] [preserved] ${preservedPath}
|
|
3052
3098
|
`);
|
|
3053
3099
|
} else {
|
|
@@ -3067,8 +3113,8 @@ async function initHandler(opts = {}) {
|
|
|
3067
3113
|
if (opts.pin) {
|
|
3068
3114
|
pinVersion = opts.pin;
|
|
3069
3115
|
} else {
|
|
3070
|
-
const payloadParent =
|
|
3071
|
-
pinVersion = readPackageVersion(
|
|
3116
|
+
const payloadParent = path17.resolve(payloadDir, "..", "..");
|
|
3117
|
+
pinVersion = readPackageVersion(path17.join(payloadParent, "package.json")) ?? readPackageVersion(path17.join(path17.dirname((0, import_node_url5.fileURLToPath)(importMetaUrl)), "..", "package.json")) ?? "latest";
|
|
3072
3118
|
}
|
|
3073
3119
|
const copyReport = copyPayload(payloadDir, cwd, { force, pinVersion });
|
|
3074
3120
|
for (const action of copyReport.actions) {
|
|
@@ -3076,33 +3122,33 @@ async function initHandler(opts = {}) {
|
|
|
3076
3122
|
stdout(`[cleargate init] ${verb} ${action.relPath}
|
|
3077
3123
|
`);
|
|
3078
3124
|
}
|
|
3079
|
-
const settingsPath =
|
|
3125
|
+
const settingsPath = path17.join(cwd, ".claude", "settings.json");
|
|
3080
3126
|
let existingSettings = null;
|
|
3081
|
-
if (
|
|
3127
|
+
if (fs16.existsSync(settingsPath)) {
|
|
3082
3128
|
try {
|
|
3083
|
-
existingSettings = JSON.parse(
|
|
3129
|
+
existingSettings = JSON.parse(fs16.readFileSync(settingsPath, "utf8"));
|
|
3084
3130
|
} catch {
|
|
3085
3131
|
stderr(`[cleargate init] WARNING: could not parse ${settingsPath}; treating as empty.
|
|
3086
3132
|
`);
|
|
3087
3133
|
}
|
|
3088
3134
|
}
|
|
3089
3135
|
const mergedSettings = mergeSettings(existingSettings, HOOK_ADDITION);
|
|
3090
|
-
|
|
3136
|
+
fs16.mkdirSync(path17.dirname(settingsPath), { recursive: true });
|
|
3091
3137
|
writeAtomic(settingsPath, JSON.stringify(mergedSettings, null, 2) + "\n");
|
|
3092
3138
|
stdout(`[cleargate init] Updated .claude/settings.json: merged PostToolUse hook
|
|
3093
3139
|
`);
|
|
3094
|
-
const claudeMdPath =
|
|
3095
|
-
const claudeMdSrcPath =
|
|
3140
|
+
const claudeMdPath = path17.join(cwd, "CLAUDE.md");
|
|
3141
|
+
const claudeMdSrcPath = path17.join(payloadDir, "CLAUDE.md");
|
|
3096
3142
|
let claudeMdBlock;
|
|
3097
3143
|
try {
|
|
3098
|
-
const claudeMdSrc =
|
|
3144
|
+
const claudeMdSrc = fs16.readFileSync(claudeMdSrcPath, "utf8");
|
|
3099
3145
|
claudeMdBlock = extractBlock(claudeMdSrc);
|
|
3100
3146
|
} catch (e) {
|
|
3101
3147
|
stderr(`[cleargate init] WARNING: could not read CLAUDE.md block from payload: ${String(e)}
|
|
3102
3148
|
`);
|
|
3103
3149
|
claudeMdBlock = "<!-- CLEARGATE:START -->\n<!-- CLEARGATE:END -->";
|
|
3104
3150
|
}
|
|
3105
|
-
const existingClaudeMd =
|
|
3151
|
+
const existingClaudeMd = fs16.existsSync(claudeMdPath) ? fs16.readFileSync(claudeMdPath, "utf8") : null;
|
|
3106
3152
|
const newClaudeMd = injectClaudeMd(existingClaudeMd, claudeMdBlock);
|
|
3107
3153
|
writeAtomic(claudeMdPath, newClaudeMd);
|
|
3108
3154
|
if (existingClaudeMd === null) {
|
|
@@ -3113,6 +3159,26 @@ async function initHandler(opts = {}) {
|
|
|
3113
3159
|
`);
|
|
3114
3160
|
} else {
|
|
3115
3161
|
stdout(`[cleargate init] CLAUDE.md unchanged (block already up to date)
|
|
3162
|
+
`);
|
|
3163
|
+
}
|
|
3164
|
+
try {
|
|
3165
|
+
const action = injectMcpJson(cwd, "https://cleargate-mcp.soula.ge/mcp");
|
|
3166
|
+
if (action === "created") {
|
|
3167
|
+
stdout(
|
|
3168
|
+
`[cleargate init] Created .mcp.json (cleargate MCP server registered) \u2014 restart Claude Code to load it.
|
|
3169
|
+
`
|
|
3170
|
+
);
|
|
3171
|
+
} else if (action === "updated") {
|
|
3172
|
+
stdout(
|
|
3173
|
+
`[cleargate init] Updated .mcp.json (cleargate MCP server entry merged) \u2014 restart Claude Code to pick up changes.
|
|
3174
|
+
`
|
|
3175
|
+
);
|
|
3176
|
+
} else {
|
|
3177
|
+
stdout(`[cleargate init] .mcp.json unchanged (cleargate entry already present)
|
|
3178
|
+
`);
|
|
3179
|
+
}
|
|
3180
|
+
} catch (e) {
|
|
3181
|
+
stderr(`[cleargate init] WARNING: ${String(e instanceof Error ? e.message : e)}
|
|
3116
3182
|
`);
|
|
3117
3183
|
}
|
|
3118
3184
|
const itemCount = countDeliveryItems(cwd);
|
|
@@ -3126,9 +3192,9 @@ async function initHandler(opts = {}) {
|
|
|
3126
3192
|
stdout(`[cleargate init] Bootstrap: no items to ingest, skipping build
|
|
3127
3193
|
`);
|
|
3128
3194
|
}
|
|
3129
|
-
const cleargateDir =
|
|
3130
|
-
|
|
3131
|
-
const snapshotPath =
|
|
3195
|
+
const cleargateDir = path17.join(cwd, ".cleargate");
|
|
3196
|
+
fs16.mkdirSync(cleargateDir, { recursive: true });
|
|
3197
|
+
const snapshotPath = path17.join(cleargateDir, ".install-manifest.json");
|
|
3132
3198
|
try {
|
|
3133
3199
|
const readManifest = opts.readInstallManifest ?? (() => loadPackageManifest({ packageRoot: payloadDir }));
|
|
3134
3200
|
const pkgManifest = readManifest();
|
|
@@ -3143,19 +3209,19 @@ async function initHandler(opts = {}) {
|
|
|
3143
3209
|
stderr(`[cleargate init] WARNING: could not write install snapshot: ${String(e)}
|
|
3144
3210
|
`);
|
|
3145
3211
|
}
|
|
3146
|
-
if (uninstalledMarker !== null &&
|
|
3212
|
+
if (uninstalledMarker !== null && fs16.existsSync(uninstalledMarkerPath)) {
|
|
3147
3213
|
try {
|
|
3148
|
-
|
|
3214
|
+
fs16.unlinkSync(uninstalledMarkerPath);
|
|
3149
3215
|
} catch (e) {
|
|
3150
3216
|
stderr(`[cleargate init] WARNING: could not remove .uninstalled marker: ${String(e)}
|
|
3151
3217
|
`);
|
|
3152
3218
|
}
|
|
3153
3219
|
}
|
|
3154
3220
|
{
|
|
3155
|
-
const distCliPath =
|
|
3221
|
+
const distCliPath = path17.join(cwd, "cleargate-cli", "dist", "cli.js");
|
|
3156
3222
|
let branch = null;
|
|
3157
3223
|
let branchLabel = "";
|
|
3158
|
-
if (
|
|
3224
|
+
if (fs16.existsSync(distCliPath)) {
|
|
3159
3225
|
branch = { cmd: "node", args: [distCliPath, "--version"] };
|
|
3160
3226
|
branchLabel = `local dist (${distCliPath})`;
|
|
3161
3227
|
} else {
|
|
@@ -3227,8 +3293,8 @@ async function initHandler(opts = {}) {
|
|
|
3227
3293
|
|
|
3228
3294
|
// src/commands/wiki-ingest.ts
|
|
3229
3295
|
init_cjs_shims();
|
|
3230
|
-
var
|
|
3231
|
-
var
|
|
3296
|
+
var fs17 = __toESM(require("fs"), 1);
|
|
3297
|
+
var path18 = __toESM(require("path"), 1);
|
|
3232
3298
|
var import_node_child_process4 = require("child_process");
|
|
3233
3299
|
var EXCLUDED_SUFFIXES2 = [
|
|
3234
3300
|
".cleargate/knowledge/",
|
|
@@ -3254,16 +3320,16 @@ async function wikiIngestHandler(opts) {
|
|
|
3254
3320
|
const stderr = opts.stderr ?? ((s) => process.stderr.write(s));
|
|
3255
3321
|
const exit = opts.exit ?? ((c) => process.exit(c));
|
|
3256
3322
|
const gitRunner = opts.gitRunner;
|
|
3257
|
-
const rename12 = opts.rename ??
|
|
3323
|
+
const rename12 = opts.rename ?? fs17.renameSync;
|
|
3258
3324
|
const templateDir = opts.templateDir;
|
|
3259
3325
|
const rawPath = opts.rawPath;
|
|
3260
|
-
const absRawPath =
|
|
3261
|
-
const relRawPath =
|
|
3262
|
-
const deliveryRoot =
|
|
3326
|
+
const absRawPath = path18.isAbsolute(rawPath) ? rawPath : path18.resolve(cwd, rawPath);
|
|
3327
|
+
const relRawPath = path18.relative(cwd, absRawPath).replace(/\\/g, "/");
|
|
3328
|
+
const deliveryRoot = path18.join(cwd, ".cleargate", "delivery");
|
|
3263
3329
|
const deliveryRootNorm = deliveryRoot.replace(/\\/g, "/");
|
|
3264
3330
|
const absDeliveryRoot = deliveryRoot;
|
|
3265
|
-
const relToDelivery =
|
|
3266
|
-
if (relToDelivery.startsWith("..") ||
|
|
3331
|
+
const relToDelivery = path18.relative(absDeliveryRoot, absRawPath);
|
|
3332
|
+
if (relToDelivery.startsWith("..") || path18.isAbsolute(relToDelivery)) {
|
|
3267
3333
|
stderr(`wiki ingest: ${rawPath} not under .cleargate/delivery/
|
|
3268
3334
|
`);
|
|
3269
3335
|
exit(2);
|
|
@@ -3277,7 +3343,7 @@ async function wikiIngestHandler(opts) {
|
|
|
3277
3343
|
exit(0);
|
|
3278
3344
|
return;
|
|
3279
3345
|
}
|
|
3280
|
-
const filename =
|
|
3346
|
+
const filename = path18.basename(absRawPath);
|
|
3281
3347
|
let bucketInfo;
|
|
3282
3348
|
try {
|
|
3283
3349
|
bucketInfo = deriveBucket(filename);
|
|
@@ -3297,12 +3363,12 @@ async function wikiIngestHandler(opts) {
|
|
|
3297
3363
|
return;
|
|
3298
3364
|
}
|
|
3299
3365
|
const { type, id, bucket } = bucketInfo;
|
|
3300
|
-
const wikiRoot =
|
|
3301
|
-
const pageDir =
|
|
3302
|
-
const pagePath =
|
|
3366
|
+
const wikiRoot = path18.join(cwd, ".cleargate", "wiki");
|
|
3367
|
+
const pageDir = path18.join(wikiRoot, bucket);
|
|
3368
|
+
const pagePath = path18.join(pageDir, `${id}.md`);
|
|
3303
3369
|
let rawContent;
|
|
3304
3370
|
try {
|
|
3305
|
-
rawContent =
|
|
3371
|
+
rawContent = fs17.readFileSync(absRawPath, "utf8");
|
|
3306
3372
|
} catch (e) {
|
|
3307
3373
|
stderr(`wiki ingest: cannot read ${rawPath}: ${e.message}
|
|
3308
3374
|
`);
|
|
@@ -3322,11 +3388,11 @@ async function wikiIngestHandler(opts) {
|
|
|
3322
3388
|
return;
|
|
3323
3389
|
}
|
|
3324
3390
|
const currentSha = getGitSha(absRawPath, gitRunner) ?? "";
|
|
3325
|
-
const pageExists =
|
|
3391
|
+
const pageExists = fs17.existsSync(pagePath);
|
|
3326
3392
|
if (pageExists && currentSha !== "") {
|
|
3327
3393
|
let isNoOp = false;
|
|
3328
3394
|
try {
|
|
3329
|
-
const existingPageContent =
|
|
3395
|
+
const existingPageContent = fs17.readFileSync(pagePath, "utf8");
|
|
3330
3396
|
const existingPage = parsePage(existingPageContent);
|
|
3331
3397
|
if (existingPage.last_ingest_commit === currentSha) {
|
|
3332
3398
|
const contentUnchanged = checkContentUnchanged(absRawPath, currentSha, relRawPath, gitRunner);
|
|
@@ -3361,8 +3427,8 @@ async function wikiIngestHandler(opts) {
|
|
|
3361
3427
|
};
|
|
3362
3428
|
const pageBody = buildPageBody2({ id, fm, body });
|
|
3363
3429
|
const pageContent = serializePage(wikiPage, pageBody);
|
|
3364
|
-
|
|
3365
|
-
|
|
3430
|
+
fs17.mkdirSync(pageDir, { recursive: true });
|
|
3431
|
+
fs17.writeFileSync(pagePath, pageContent, "utf8");
|
|
3366
3432
|
appendLogEntry(wikiRoot, { timestamp, action, id, relRawPath });
|
|
3367
3433
|
updateIndex(wikiRoot, { id, type, status: wikiPage.status, relRawPath, rename: rename12 });
|
|
3368
3434
|
recompileSynthesis(wikiRoot, cwd, templateDir);
|
|
@@ -3374,7 +3440,7 @@ function checkContentUnchanged(absRawPath, sha, relRawPath, gitRunner) {
|
|
|
3374
3440
|
const run = gitRunner ?? defaultGitRunner;
|
|
3375
3441
|
const gitContent = run("git", ["show", `${sha}:${relRawPath}`]);
|
|
3376
3442
|
if (!gitContent && gitContent !== "") return false;
|
|
3377
|
-
const currentContent =
|
|
3443
|
+
const currentContent = fs17.readFileSync(absRawPath, "utf8");
|
|
3378
3444
|
return gitContent === currentContent;
|
|
3379
3445
|
} catch {
|
|
3380
3446
|
return false;
|
|
@@ -3427,7 +3493,7 @@ function buildPageBody2(item) {
|
|
|
3427
3493
|
].join("\n");
|
|
3428
3494
|
}
|
|
3429
3495
|
function appendLogEntry(wikiRoot, entry) {
|
|
3430
|
-
const logPath =
|
|
3496
|
+
const logPath = path18.join(wikiRoot, "log.md");
|
|
3431
3497
|
const logEntry = [
|
|
3432
3498
|
`- timestamp: "${entry.timestamp}"`,
|
|
3433
3499
|
` actor: "cleargate wiki ingest"`,
|
|
@@ -3435,25 +3501,25 @@ function appendLogEntry(wikiRoot, entry) {
|
|
|
3435
3501
|
` target: "${entry.id}"`,
|
|
3436
3502
|
` path: "${entry.relRawPath}"`
|
|
3437
3503
|
].join("\n");
|
|
3438
|
-
if (
|
|
3439
|
-
const existing =
|
|
3504
|
+
if (fs17.existsSync(logPath)) {
|
|
3505
|
+
const existing = fs17.readFileSync(logPath, "utf8");
|
|
3440
3506
|
const newContent = existing.trimEnd() + "\n" + logEntry + "\n";
|
|
3441
|
-
|
|
3507
|
+
fs17.writeFileSync(logPath, newContent, "utf8");
|
|
3442
3508
|
} else {
|
|
3443
|
-
|
|
3444
|
-
|
|
3509
|
+
fs17.mkdirSync(wikiRoot, { recursive: true });
|
|
3510
|
+
fs17.writeFileSync(logPath, `# Wiki Event Log
|
|
3445
3511
|
|
|
3446
3512
|
${logEntry}
|
|
3447
3513
|
`, "utf8");
|
|
3448
3514
|
}
|
|
3449
3515
|
}
|
|
3450
3516
|
function updateIndex(wikiRoot, opts) {
|
|
3451
|
-
const indexPath =
|
|
3517
|
+
const indexPath = path18.join(wikiRoot, "index.md");
|
|
3452
3518
|
const tmpPath = `${indexPath}.tmp`;
|
|
3453
3519
|
const newRow = `| [[${opts.id}]] | ${opts.type} | ${opts.status} | ${opts.relRawPath} |`;
|
|
3454
3520
|
let content;
|
|
3455
|
-
if (
|
|
3456
|
-
content =
|
|
3521
|
+
if (fs17.existsSync(indexPath)) {
|
|
3522
|
+
content = fs17.readFileSync(indexPath, "utf8");
|
|
3457
3523
|
const idPattern = `[[${opts.id}]]`;
|
|
3458
3524
|
const lines = content.split("\n");
|
|
3459
3525
|
let replaced = false;
|
|
@@ -3472,7 +3538,7 @@ function updateIndex(wikiRoot, opts) {
|
|
|
3472
3538
|
} else {
|
|
3473
3539
|
content = buildMinimalIndex(opts.id, opts.type, opts.status, opts.relRawPath);
|
|
3474
3540
|
}
|
|
3475
|
-
|
|
3541
|
+
fs17.writeFileSync(tmpPath, content, "utf8");
|
|
3476
3542
|
opts.rename(tmpPath, indexPath);
|
|
3477
3543
|
}
|
|
3478
3544
|
function insertIntoSection(content, id, newRow) {
|
|
@@ -3567,41 +3633,41 @@ function buildMinimalIndex(id, type, status, relRawPath) {
|
|
|
3567
3633
|
return lines.join("\n");
|
|
3568
3634
|
}
|
|
3569
3635
|
function recompileSynthesis(wikiRoot, cwd, templateDir) {
|
|
3570
|
-
const deliveryRoot =
|
|
3636
|
+
const deliveryRoot = path18.join(cwd, ".cleargate", "delivery");
|
|
3571
3637
|
let items = [];
|
|
3572
|
-
if (
|
|
3638
|
+
if (fs17.existsSync(deliveryRoot)) {
|
|
3573
3639
|
try {
|
|
3574
3640
|
items = scanRawItems(deliveryRoot, cwd);
|
|
3575
3641
|
} catch {
|
|
3576
3642
|
}
|
|
3577
3643
|
}
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3644
|
+
fs17.writeFileSync(path18.join(wikiRoot, "active-sprint.md"), compile(items, templateDir), "utf8");
|
|
3645
|
+
fs17.writeFileSync(path18.join(wikiRoot, "open-gates.md"), compile2(items, templateDir), "utf8");
|
|
3646
|
+
fs17.writeFileSync(path18.join(wikiRoot, "product-state.md"), compile3(items, templateDir), "utf8");
|
|
3647
|
+
fs17.writeFileSync(path18.join(wikiRoot, "roadmap.md"), compile4(items, templateDir), "utf8");
|
|
3582
3648
|
}
|
|
3583
3649
|
|
|
3584
3650
|
// src/commands/wiki-lint.ts
|
|
3585
3651
|
init_cjs_shims();
|
|
3586
|
-
var
|
|
3652
|
+
var path22 = __toESM(require("path"), 1);
|
|
3587
3653
|
|
|
3588
3654
|
// src/wiki/load-wiki.ts
|
|
3589
3655
|
init_cjs_shims();
|
|
3590
|
-
var
|
|
3591
|
-
var
|
|
3656
|
+
var fs18 = __toESM(require("fs"), 1);
|
|
3657
|
+
var path19 = __toESM(require("path"), 1);
|
|
3592
3658
|
var BUCKET_DIRS = ["epics", "stories", "sprints", "proposals", "crs", "bugs", "topics"];
|
|
3593
3659
|
function loadWikiPages(wikiRoot) {
|
|
3594
3660
|
const results = [];
|
|
3595
3661
|
for (const bucket of BUCKET_DIRS) {
|
|
3596
|
-
const dir =
|
|
3597
|
-
if (!
|
|
3598
|
-
const entries =
|
|
3662
|
+
const dir = path19.join(wikiRoot, bucket);
|
|
3663
|
+
if (!fs18.existsSync(dir)) continue;
|
|
3664
|
+
const entries = fs18.readdirSync(dir, { encoding: "utf8" });
|
|
3599
3665
|
for (const filename of entries) {
|
|
3600
3666
|
if (!filename.endsWith(".md")) continue;
|
|
3601
|
-
const absPath =
|
|
3602
|
-
const stat =
|
|
3667
|
+
const absPath = path19.join(dir, filename);
|
|
3668
|
+
const stat = fs18.statSync(absPath);
|
|
3603
3669
|
if (!stat.isFile()) continue;
|
|
3604
|
-
const raw =
|
|
3670
|
+
const raw = fs18.readFileSync(absPath, "utf8");
|
|
3605
3671
|
let fm;
|
|
3606
3672
|
let body;
|
|
3607
3673
|
try {
|
|
@@ -3631,8 +3697,8 @@ function loadWikiPages(wikiRoot) {
|
|
|
3631
3697
|
|
|
3632
3698
|
// src/wiki/lint-checks.ts
|
|
3633
3699
|
init_cjs_shims();
|
|
3634
|
-
var
|
|
3635
|
-
var
|
|
3700
|
+
var fs19 = __toESM(require("fs"), 1);
|
|
3701
|
+
var path20 = __toESM(require("path"), 1);
|
|
3636
3702
|
var import_node_child_process5 = require("child_process");
|
|
3637
3703
|
var import_js_yaml3 = __toESM(require("js-yaml"), 1);
|
|
3638
3704
|
|
|
@@ -3692,9 +3758,9 @@ function checkOrphan(page, repoRoot) {
|
|
|
3692
3758
|
if (!rawPath) return null;
|
|
3693
3759
|
const isExcluded = EXCLUDED_DIRS.some((excl) => rawPath.startsWith(excl));
|
|
3694
3760
|
if (isExcluded) return null;
|
|
3695
|
-
const absRaw =
|
|
3696
|
-
if (!
|
|
3697
|
-
const relPage =
|
|
3761
|
+
const absRaw = path20.join(repoRoot, rawPath);
|
|
3762
|
+
if (!fs19.existsSync(absRaw)) {
|
|
3763
|
+
const relPage = path20.relative(path20.join(repoRoot, ".cleargate", "wiki"), page.absPath).replace(/\\/g, "/");
|
|
3698
3764
|
return {
|
|
3699
3765
|
category: "orphan",
|
|
3700
3766
|
line: `orphan: ${relPage} -> missing ${rawPath} (raw missing)`
|
|
@@ -3712,7 +3778,7 @@ function checkRepoMismatch(page, repoRoot) {
|
|
|
3712
3778
|
return null;
|
|
3713
3779
|
}
|
|
3714
3780
|
if (page.page.repo !== derivedRepo) {
|
|
3715
|
-
const relPage =
|
|
3781
|
+
const relPage = path20.relative(path20.join(repoRoot, ".cleargate", "wiki"), page.absPath).replace(/\\/g, "/");
|
|
3716
3782
|
return {
|
|
3717
3783
|
category: "repo-mismatch",
|
|
3718
3784
|
line: `repo-mismatch: ${relPage} declares repo:${page.page.repo} but raw_path implies repo:${derivedRepo}`
|
|
@@ -3737,7 +3803,7 @@ function checkStaleCommit(page, repoRoot, gitRunner) {
|
|
|
3737
3803
|
}
|
|
3738
3804
|
if (!currentSha) return null;
|
|
3739
3805
|
if (storedSha !== currentSha) {
|
|
3740
|
-
const relPage =
|
|
3806
|
+
const relPage = path20.relative(path20.join(repoRoot, ".cleargate", "wiki"), page.absPath).replace(/\\/g, "/");
|
|
3741
3807
|
return {
|
|
3742
3808
|
category: "stale-commit",
|
|
3743
3809
|
line: `stale-commit: ${relPage} at ${storedSha}, current ${currentSha}`
|
|
@@ -3748,14 +3814,14 @@ function checkStaleCommit(page, repoRoot, gitRunner) {
|
|
|
3748
3814
|
function checkMissingIngest(page, repoRoot) {
|
|
3749
3815
|
const rawPath = page.page.raw_path;
|
|
3750
3816
|
if (!rawPath) return null;
|
|
3751
|
-
const absRaw =
|
|
3752
|
-
if (!
|
|
3753
|
-
const rawStat =
|
|
3754
|
-
const pageStat =
|
|
3817
|
+
const absRaw = path20.join(repoRoot, rawPath);
|
|
3818
|
+
if (!fs19.existsSync(absRaw)) return null;
|
|
3819
|
+
const rawStat = fs19.statSync(absRaw);
|
|
3820
|
+
const pageStat = fs19.statSync(page.absPath);
|
|
3755
3821
|
const rawMtimeMs = rawStat.mtimeMs;
|
|
3756
3822
|
const pageMtimeMs = pageStat.mtimeMs;
|
|
3757
3823
|
if (rawMtimeMs - pageMtimeMs > 2e3) {
|
|
3758
|
-
const relPage =
|
|
3824
|
+
const relPage = path20.relative(path20.join(repoRoot, ".cleargate", "wiki"), page.absPath).replace(/\\/g, "/");
|
|
3759
3825
|
const rawMtime = rawStat.mtime.toISOString();
|
|
3760
3826
|
const pageMtime = pageStat.mtime.toISOString();
|
|
3761
3827
|
return {
|
|
@@ -3766,7 +3832,7 @@ function checkMissingIngest(page, repoRoot) {
|
|
|
3766
3832
|
return null;
|
|
3767
3833
|
}
|
|
3768
3834
|
function checkBrokenBacklinks(pages, repoRoot) {
|
|
3769
|
-
const wikiRoot =
|
|
3835
|
+
const wikiRoot = path20.join(repoRoot, ".cleargate", "wiki");
|
|
3770
3836
|
const byId = /* @__PURE__ */ new Map();
|
|
3771
3837
|
for (const p of pages) {
|
|
3772
3838
|
if (p.page.id) byId.set(p.page.id, p);
|
|
@@ -3780,7 +3846,7 @@ function checkBrokenBacklinks(pages, repoRoot) {
|
|
|
3780
3846
|
const parentId = match[1];
|
|
3781
3847
|
const parentPage = byId.get(parentId);
|
|
3782
3848
|
if (!parentPage) {
|
|
3783
|
-
const relChild =
|
|
3849
|
+
const relChild = path20.relative(wikiRoot, childPage.absPath).replace(/\\/g, "/");
|
|
3784
3850
|
findings.push({
|
|
3785
3851
|
category: "broken-backlink",
|
|
3786
3852
|
line: `broken-backlink: ${relChild} -> ${parentId} (parent missing child entry)`
|
|
@@ -3793,7 +3859,7 @@ function checkBrokenBacklinks(pages, repoRoot) {
|
|
|
3793
3859
|
(c) => c === childRef || c === childId
|
|
3794
3860
|
);
|
|
3795
3861
|
if (!parentHasChild) {
|
|
3796
|
-
const relChild =
|
|
3862
|
+
const relChild = path20.relative(wikiRoot, childPage.absPath).replace(/\\/g, "/");
|
|
3797
3863
|
findings.push({
|
|
3798
3864
|
category: "broken-backlink",
|
|
3799
3865
|
line: `broken-backlink: ${relChild} -> ${parentId} (parent missing child entry)`
|
|
@@ -3803,7 +3869,7 @@ function checkBrokenBacklinks(pages, repoRoot) {
|
|
|
3803
3869
|
return findings;
|
|
3804
3870
|
}
|
|
3805
3871
|
function checkInvalidatedCitations(pages, repoRoot) {
|
|
3806
|
-
const wikiRoot =
|
|
3872
|
+
const wikiRoot = path20.join(repoRoot, ".cleargate", "wiki");
|
|
3807
3873
|
const byId = /* @__PURE__ */ new Map();
|
|
3808
3874
|
for (const p of pages) {
|
|
3809
3875
|
if (p.page.id) byId.set(p.page.id, p);
|
|
@@ -3811,10 +3877,10 @@ function checkInvalidatedCitations(pages, repoRoot) {
|
|
|
3811
3877
|
const findings = [];
|
|
3812
3878
|
const topicPages = pages.filter((p) => p.page.type === "topic");
|
|
3813
3879
|
for (const topicPage of topicPages) {
|
|
3814
|
-
const relTopic =
|
|
3880
|
+
const relTopic = path20.relative(wikiRoot, topicPage.absPath).replace(/\\/g, "/");
|
|
3815
3881
|
let citesList = [];
|
|
3816
3882
|
try {
|
|
3817
|
-
const raw =
|
|
3883
|
+
const raw = fs19.readFileSync(topicPage.absPath, "utf8");
|
|
3818
3884
|
const { fm } = parseFrontmatter(raw);
|
|
3819
3885
|
const rawCites = fm["cites"];
|
|
3820
3886
|
if (Array.isArray(rawCites)) {
|
|
@@ -3850,7 +3916,7 @@ function checkExcludedPathIngested(page, repoRoot) {
|
|
|
3850
3916
|
if (!rawPath) return null;
|
|
3851
3917
|
const isExcluded = EXCLUDED_DIRS.some((excl) => rawPath.startsWith(excl));
|
|
3852
3918
|
if (isExcluded) {
|
|
3853
|
-
const relPage =
|
|
3919
|
+
const relPage = path20.relative(path20.join(repoRoot, ".cleargate", "wiki"), page.absPath).replace(/\\/g, "/");
|
|
3854
3920
|
return {
|
|
3855
3921
|
category: "excluded-path-ingested",
|
|
3856
3922
|
line: `excluded-path-ingested: ${relPage} (raw_path ${rawPath} is under an excluded directory)`
|
|
@@ -3861,7 +3927,7 @@ function checkExcludedPathIngested(page, repoRoot) {
|
|
|
3861
3927
|
function checkPaginationNeeded(pages) {
|
|
3862
3928
|
const bucketCounts = /* @__PURE__ */ new Map();
|
|
3863
3929
|
for (const p of pages) {
|
|
3864
|
-
const bucket =
|
|
3930
|
+
const bucket = path20.basename(path20.dirname(p.absPath));
|
|
3865
3931
|
bucketCounts.set(bucket, (bucketCounts.get(bucket) ?? 0) + 1);
|
|
3866
3932
|
}
|
|
3867
3933
|
const findings = [];
|
|
@@ -3899,11 +3965,11 @@ function parseCachedGateResult(raw) {
|
|
|
3899
3965
|
function checkGateFailure(page, repoRoot) {
|
|
3900
3966
|
const rawPath = page.page.raw_path;
|
|
3901
3967
|
if (!rawPath) return null;
|
|
3902
|
-
const absRaw =
|
|
3903
|
-
if (!
|
|
3968
|
+
const absRaw = path20.join(repoRoot, rawPath);
|
|
3969
|
+
if (!fs19.existsSync(absRaw)) return null;
|
|
3904
3970
|
let rawFm;
|
|
3905
3971
|
try {
|
|
3906
|
-
const raw =
|
|
3972
|
+
const raw = fs19.readFileSync(absRaw, "utf8");
|
|
3907
3973
|
const { fm } = parseFrontmatter(raw);
|
|
3908
3974
|
rawFm = fm;
|
|
3909
3975
|
} catch {
|
|
@@ -3937,11 +4003,11 @@ function checkGateFailure(page, repoRoot) {
|
|
|
3937
4003
|
function checkGateStaleness(page, repoRoot) {
|
|
3938
4004
|
const rawPath = page.page.raw_path;
|
|
3939
4005
|
if (!rawPath) return null;
|
|
3940
|
-
const absRaw =
|
|
3941
|
-
if (!
|
|
4006
|
+
const absRaw = path20.join(repoRoot, rawPath);
|
|
4007
|
+
if (!fs19.existsSync(absRaw)) return null;
|
|
3942
4008
|
let rawFm;
|
|
3943
4009
|
try {
|
|
3944
|
-
const raw =
|
|
4010
|
+
const raw = fs19.readFileSync(absRaw, "utf8");
|
|
3945
4011
|
const { fm } = parseFrontmatter(raw);
|
|
3946
4012
|
rawFm = fm;
|
|
3947
4013
|
} catch {
|
|
@@ -3964,7 +4030,7 @@ function checkGateStaleness(page, repoRoot) {
|
|
|
3964
4030
|
return null;
|
|
3965
4031
|
}
|
|
3966
4032
|
function discoverPlainTextMentions(pages, repoRoot) {
|
|
3967
|
-
const wikiRoot =
|
|
4033
|
+
const wikiRoot = path20.join(repoRoot, ".cleargate", "wiki");
|
|
3968
4034
|
const byId = /* @__PURE__ */ new Map();
|
|
3969
4035
|
for (const p of pages) {
|
|
3970
4036
|
if (p.page.id) byId.set(p.page.id, true);
|
|
@@ -3973,7 +4039,7 @@ function discoverPlainTextMentions(pages, repoRoot) {
|
|
|
3973
4039
|
const ID_PATTERN = /\b((?:EPIC|STORY|SPRINT|PROPOSAL|CR|BUG)-[\w-]+)\b/g;
|
|
3974
4040
|
const LINK_PATTERN = /\[\[[\w-]+\]\]/g;
|
|
3975
4041
|
for (const page of pages) {
|
|
3976
|
-
const relPage =
|
|
4042
|
+
const relPage = path20.relative(wikiRoot, page.absPath).replace(/\\/g, "/");
|
|
3977
4043
|
const wrappedRefs = /* @__PURE__ */ new Set();
|
|
3978
4044
|
for (const m of page.body.matchAll(LINK_PATTERN)) {
|
|
3979
4045
|
const inner = m[0].slice(2, -2);
|
|
@@ -3990,11 +4056,11 @@ function discoverPlainTextMentions(pages, repoRoot) {
|
|
|
3990
4056
|
return suggestions;
|
|
3991
4057
|
}
|
|
3992
4058
|
function checkIndexBudget(repoRoot, indexTokenCeiling) {
|
|
3993
|
-
const indexPath =
|
|
3994
|
-
if (!
|
|
4059
|
+
const indexPath = path20.join(repoRoot, ".cleargate", "wiki", "index.md");
|
|
4060
|
+
if (!fs19.existsSync(indexPath)) {
|
|
3995
4061
|
return { finding: null };
|
|
3996
4062
|
}
|
|
3997
|
-
const bytes =
|
|
4063
|
+
const bytes = fs19.statSync(indexPath).size;
|
|
3998
4064
|
const tokens = Math.round(bytes / 4);
|
|
3999
4065
|
const ceiling = indexTokenCeiling;
|
|
4000
4066
|
if (tokens > ceiling) {
|
|
@@ -4012,18 +4078,18 @@ function checkIndexBudget(repoRoot, indexTokenCeiling) {
|
|
|
4012
4078
|
|
|
4013
4079
|
// src/lib/wiki-config.ts
|
|
4014
4080
|
init_cjs_shims();
|
|
4015
|
-
var
|
|
4016
|
-
var
|
|
4081
|
+
var fs20 = __toESM(require("fs"), 1);
|
|
4082
|
+
var path21 = __toESM(require("path"), 1);
|
|
4017
4083
|
var import_js_yaml4 = __toESM(require("js-yaml"), 1);
|
|
4018
4084
|
var DEFAULT_INDEX_TOKEN_CEILING = 8e3;
|
|
4019
4085
|
function loadWikiConfig(repoRoot) {
|
|
4020
|
-
const configPath =
|
|
4021
|
-
if (!
|
|
4086
|
+
const configPath = path21.join(repoRoot, ".cleargate", "config.yml");
|
|
4087
|
+
if (!fs20.existsSync(configPath)) {
|
|
4022
4088
|
return { wiki: { index_token_ceiling: DEFAULT_INDEX_TOKEN_CEILING }, gates: {} };
|
|
4023
4089
|
}
|
|
4024
4090
|
let raw;
|
|
4025
4091
|
try {
|
|
4026
|
-
raw =
|
|
4092
|
+
raw = fs20.readFileSync(configPath, "utf8");
|
|
4027
4093
|
} catch (err) {
|
|
4028
4094
|
throw new Error(`Failed to read ${configPath}: ${String(err)}`);
|
|
4029
4095
|
}
|
|
@@ -4084,7 +4150,7 @@ async function wikiLintHandler(opts = {}) {
|
|
|
4084
4150
|
const exit = opts.exit ?? ((c) => process.exit(c));
|
|
4085
4151
|
const gitRunner = opts.gitRunner;
|
|
4086
4152
|
const mode = opts.mode ?? "enforce";
|
|
4087
|
-
const wikiRoot =
|
|
4153
|
+
const wikiRoot = path22.join(cwd, ".cleargate", "wiki");
|
|
4088
4154
|
const repoRoot = cwd;
|
|
4089
4155
|
let pages = loadWikiPages(wikiRoot);
|
|
4090
4156
|
const findings = [];
|
|
@@ -4156,8 +4222,8 @@ async function wikiLintHandler(opts = {}) {
|
|
|
4156
4222
|
|
|
4157
4223
|
// src/commands/wiki-query.ts
|
|
4158
4224
|
init_cjs_shims();
|
|
4159
|
-
var
|
|
4160
|
-
var
|
|
4225
|
+
var fs21 = __toESM(require("fs"), 1);
|
|
4226
|
+
var path23 = __toESM(require("path"), 1);
|
|
4161
4227
|
function computeSlug(query) {
|
|
4162
4228
|
return query.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/-{2,}/g, "-").slice(0, 40).replace(/-+$/, "");
|
|
4163
4229
|
}
|
|
@@ -4187,9 +4253,9 @@ async function wikiQueryHandler(opts) {
|
|
|
4187
4253
|
const query = opts.query;
|
|
4188
4254
|
const persist = opts.persist ?? false;
|
|
4189
4255
|
void stderr;
|
|
4190
|
-
const wikiRoot =
|
|
4191
|
-
const indexPath =
|
|
4192
|
-
if (!
|
|
4256
|
+
const wikiRoot = path23.join(cwd, ".cleargate", "wiki");
|
|
4257
|
+
const indexPath = path23.join(wikiRoot, "index.md");
|
|
4258
|
+
if (!fs21.existsSync(indexPath)) {
|
|
4193
4259
|
stdout(`wiki query: no index.md found at ${indexPath}
|
|
4194
4260
|
`);
|
|
4195
4261
|
stdout(`Run \`cleargate wiki build\` first.
|
|
@@ -4197,7 +4263,7 @@ async function wikiQueryHandler(opts) {
|
|
|
4197
4263
|
exit(1);
|
|
4198
4264
|
return;
|
|
4199
4265
|
}
|
|
4200
|
-
const indexContent =
|
|
4266
|
+
const indexContent = fs21.readFileSync(indexPath, "utf8");
|
|
4201
4267
|
const matches = searchIndex(indexContent, query);
|
|
4202
4268
|
if (matches.length === 0) {
|
|
4203
4269
|
stdout(`wiki query: no matches for "${query}"
|
|
@@ -4222,8 +4288,8 @@ async function wikiQueryHandler(opts) {
|
|
|
4222
4288
|
return;
|
|
4223
4289
|
}
|
|
4224
4290
|
const slug = computeSlug(query);
|
|
4225
|
-
const topicsDir =
|
|
4226
|
-
|
|
4291
|
+
const topicsDir = path23.join(wikiRoot, "topics");
|
|
4292
|
+
fs21.mkdirSync(topicsDir, { recursive: true });
|
|
4227
4293
|
const citesArray = matches.map(({ id }) => `"[[${id}]]"`);
|
|
4228
4294
|
const createdAt = now();
|
|
4229
4295
|
const frontmatter = [
|
|
@@ -4238,13 +4304,13 @@ async function wikiQueryHandler(opts) {
|
|
|
4238
4304
|
const topicContent = `${frontmatter}
|
|
4239
4305
|
|
|
4240
4306
|
${body}`;
|
|
4241
|
-
const topicPath =
|
|
4242
|
-
|
|
4307
|
+
const topicPath = path23.join(topicsDir, `${slug}.md`);
|
|
4308
|
+
fs21.writeFileSync(topicPath, topicContent, "utf8");
|
|
4243
4309
|
updateIndexTopicsSection(indexPath, slug, query, createdAt);
|
|
4244
4310
|
exit(0);
|
|
4245
4311
|
}
|
|
4246
4312
|
function updateIndexTopicsSection(indexPath, slug, query, createdAt) {
|
|
4247
|
-
let content =
|
|
4313
|
+
let content = fs21.readFileSync(indexPath, "utf8");
|
|
4248
4314
|
const row = `| ${slug} | ${query} | ${createdAt} |`;
|
|
4249
4315
|
if (content.includes("## Topics")) {
|
|
4250
4316
|
const topicsIdx = content.indexOf("## Topics");
|
|
@@ -4267,13 +4333,13 @@ ${row}
|
|
|
4267
4333
|
${row}
|
|
4268
4334
|
`;
|
|
4269
4335
|
}
|
|
4270
|
-
|
|
4336
|
+
fs21.writeFileSync(indexPath, content, "utf8");
|
|
4271
4337
|
}
|
|
4272
4338
|
|
|
4273
4339
|
// src/commands/wiki-audit-status.ts
|
|
4274
4340
|
init_cjs_shims();
|
|
4275
|
-
var
|
|
4276
|
-
var
|
|
4341
|
+
var fs22 = __toESM(require("fs"), 1);
|
|
4342
|
+
var path24 = __toESM(require("path"), 1);
|
|
4277
4343
|
var readline4 = __toESM(require("readline"), 1);
|
|
4278
4344
|
var TERMINAL = /* @__PURE__ */ new Set(["Completed", "Done", "Abandoned", "Closed", "Resolved"]);
|
|
4279
4345
|
async function wikiAuditStatusHandler(opts = {}) {
|
|
@@ -4286,8 +4352,8 @@ async function wikiAuditStatusHandler(opts = {}) {
|
|
|
4286
4352
|
});
|
|
4287
4353
|
const exit = opts.exit ?? ((c) => process.exit(c));
|
|
4288
4354
|
const isTTY = opts.isTTY ?? Boolean(process.stdout.isTTY);
|
|
4289
|
-
const deliveryRoot =
|
|
4290
|
-
if (!
|
|
4355
|
+
const deliveryRoot = path24.join(cwd, ".cleargate", "delivery");
|
|
4356
|
+
if (!fs22.existsSync(deliveryRoot)) {
|
|
4291
4357
|
stderr(`audit-status: .cleargate/delivery/ not found at ${deliveryRoot}
|
|
4292
4358
|
`);
|
|
4293
4359
|
exit(1);
|
|
@@ -4427,7 +4493,7 @@ async function wikiAuditStatusHandler(opts = {}) {
|
|
|
4427
4493
|
}
|
|
4428
4494
|
}
|
|
4429
4495
|
for (const d of fixable) {
|
|
4430
|
-
const rawText =
|
|
4496
|
+
const rawText = fs22.readFileSync(d.absPath, "utf8");
|
|
4431
4497
|
const updated = applyStatusFix(rawText, d.suggestedStatus);
|
|
4432
4498
|
if (!opts.quiet) {
|
|
4433
4499
|
stdout(`--- ${d.rawPath}
|
|
@@ -4443,7 +4509,7 @@ async function wikiAuditStatusHandler(opts = {}) {
|
|
|
4443
4509
|
stdout(`+${newLine}
|
|
4444
4510
|
`);
|
|
4445
4511
|
}
|
|
4446
|
-
|
|
4512
|
+
fs22.writeFileSync(d.absPath, updated, "utf8");
|
|
4447
4513
|
}
|
|
4448
4514
|
stdout(`audit-status: applied ${fixable.length} fix(es)
|
|
4449
4515
|
`);
|
|
@@ -4472,8 +4538,8 @@ function applyStatusFix(rawText, newStatus) {
|
|
|
4472
4538
|
|
|
4473
4539
|
// src/commands/doctor.ts
|
|
4474
4540
|
init_cjs_shims();
|
|
4475
|
-
var
|
|
4476
|
-
var
|
|
4541
|
+
var fs23 = __toESM(require("fs"), 1);
|
|
4542
|
+
var path25 = __toESM(require("path"), 1);
|
|
4477
4543
|
var import_node_child_process6 = require("child_process");
|
|
4478
4544
|
|
|
4479
4545
|
// src/lib/pricing.ts
|
|
@@ -4550,24 +4616,24 @@ function parseHookLogLine(line) {
|
|
|
4550
4616
|
};
|
|
4551
4617
|
}
|
|
4552
4618
|
function runHookHealth(stdout, cwd, now, outcome) {
|
|
4553
|
-
const cleargateDir =
|
|
4554
|
-
if (!
|
|
4619
|
+
const cleargateDir = path25.join(cwd, ".cleargate");
|
|
4620
|
+
if (!fs23.existsSync(cleargateDir)) {
|
|
4555
4621
|
stdout("cleargate misconfigured: no .cleargate/ found. Run: cleargate init");
|
|
4556
4622
|
if (outcome) outcome.configError = true;
|
|
4557
4623
|
return;
|
|
4558
4624
|
}
|
|
4559
|
-
const manifestPath =
|
|
4560
|
-
if (!
|
|
4625
|
+
const manifestPath = path25.join(cwd, "cleargate-planning", "MANIFEST.json");
|
|
4626
|
+
if (!fs23.existsSync(manifestPath)) {
|
|
4561
4627
|
stdout(`cleargate misconfigured: cleargate-planning/MANIFEST.json not found. Run: cleargate init`);
|
|
4562
4628
|
if (outcome) outcome.configError = true;
|
|
4563
4629
|
}
|
|
4564
|
-
const settingsPath =
|
|
4565
|
-
if (!
|
|
4630
|
+
const settingsPath = path25.join(cwd, ".claude", "settings.json");
|
|
4631
|
+
if (!fs23.existsSync(settingsPath)) {
|
|
4566
4632
|
stdout("[doctor] No .claude/settings.json found \u2014 hook config unavailable.");
|
|
4567
4633
|
return;
|
|
4568
4634
|
}
|
|
4569
4635
|
try {
|
|
4570
|
-
const raw =
|
|
4636
|
+
const raw = fs23.readFileSync(settingsPath, "utf-8");
|
|
4571
4637
|
const settings = JSON.parse(raw);
|
|
4572
4638
|
const hasHooks = typeof settings === "object" && settings !== null && "hooks" in settings;
|
|
4573
4639
|
if (hasHooks) {
|
|
@@ -4578,13 +4644,13 @@ function runHookHealth(stdout, cwd, now, outcome) {
|
|
|
4578
4644
|
} catch {
|
|
4579
4645
|
stdout("[doctor] .claude/settings.json is not valid JSON \u2014 cannot verify hook config.");
|
|
4580
4646
|
}
|
|
4581
|
-
const logPath =
|
|
4582
|
-
if (!
|
|
4647
|
+
const logPath = path25.join(cwd, ".cleargate", "hook-log", "gate-check.log");
|
|
4648
|
+
if (!fs23.existsSync(logPath)) {
|
|
4583
4649
|
return;
|
|
4584
4650
|
}
|
|
4585
4651
|
let logContent;
|
|
4586
4652
|
try {
|
|
4587
|
-
logContent =
|
|
4653
|
+
logContent = fs23.readFileSync(logPath, "utf-8");
|
|
4588
4654
|
} catch {
|
|
4589
4655
|
return;
|
|
4590
4656
|
}
|
|
@@ -4698,8 +4764,8 @@ function parseCachedGateResult2(raw) {
|
|
|
4698
4764
|
};
|
|
4699
4765
|
}
|
|
4700
4766
|
function emitResolverStatusLine(cwd, stdout) {
|
|
4701
|
-
const distCliPath =
|
|
4702
|
-
if (
|
|
4767
|
+
const distCliPath = path25.join(cwd, "cleargate-cli", "dist", "cli.js");
|
|
4768
|
+
if (fs23.existsSync(distCliPath)) {
|
|
4703
4769
|
stdout(`cleargate CLI: local dist \u2014 ${distCliPath}`);
|
|
4704
4770
|
return;
|
|
4705
4771
|
}
|
|
@@ -4713,10 +4779,10 @@ function emitResolverStatusLine(cwd, stdout) {
|
|
|
4713
4779
|
return;
|
|
4714
4780
|
}
|
|
4715
4781
|
let pinVersion = "unknown";
|
|
4716
|
-
const hookPath =
|
|
4717
|
-
if (
|
|
4782
|
+
const hookPath = path25.join(cwd, ".claude", "hooks", "stamp-and-gate.sh");
|
|
4783
|
+
if (fs23.existsSync(hookPath)) {
|
|
4718
4784
|
try {
|
|
4719
|
-
const hookContent =
|
|
4785
|
+
const hookContent = fs23.readFileSync(hookPath, "utf-8");
|
|
4720
4786
|
const pinMatch = hookContent.match(/^#\s*cleargate-pin:\s*(\S+)\s*$/m);
|
|
4721
4787
|
if (pinMatch?.[1]) {
|
|
4722
4788
|
pinVersion = pinMatch[1];
|
|
@@ -4748,10 +4814,10 @@ async function runSessionStart(cwd, stdout, outcome) {
|
|
|
4748
4814
|
if (outcome && resolverLines.some((l) => l.includes("\u{1F534}"))) {
|
|
4749
4815
|
outcome.configError = true;
|
|
4750
4816
|
}
|
|
4751
|
-
const pendingSyncDir =
|
|
4817
|
+
const pendingSyncDir = path25.join(cwd, ".cleargate", "delivery", "pending-sync");
|
|
4752
4818
|
let files;
|
|
4753
4819
|
try {
|
|
4754
|
-
files =
|
|
4820
|
+
files = fs23.readdirSync(pendingSyncDir).filter((f) => f.endsWith(".md")).map((f) => path25.join(pendingSyncDir, f));
|
|
4755
4821
|
} catch {
|
|
4756
4822
|
return;
|
|
4757
4823
|
}
|
|
@@ -4760,7 +4826,7 @@ async function runSessionStart(cwd, stdout, outcome) {
|
|
|
4760
4826
|
for (const filePath of files) {
|
|
4761
4827
|
let raw;
|
|
4762
4828
|
try {
|
|
4763
|
-
raw =
|
|
4829
|
+
raw = fs23.readFileSync(filePath, "utf-8");
|
|
4764
4830
|
} catch {
|
|
4765
4831
|
continue;
|
|
4766
4832
|
}
|
|
@@ -4786,13 +4852,13 @@ async function runSessionStart(cwd, stdout, outcome) {
|
|
|
4786
4852
|
}
|
|
4787
4853
|
}
|
|
4788
4854
|
if (!itemId) {
|
|
4789
|
-
itemId =
|
|
4855
|
+
itemId = path25.basename(filePath, ".md");
|
|
4790
4856
|
}
|
|
4791
4857
|
const firstCriterionId = gate2.failing_criteria.length > 0 ? gate2.failing_criteria[0]?.id ?? "" : "";
|
|
4792
4858
|
blocked.push({ id: itemId, firstCriterionId });
|
|
4793
4859
|
}
|
|
4794
|
-
const activesentinel =
|
|
4795
|
-
const sprintActive =
|
|
4860
|
+
const activesentinel = path25.join(cwd, ".cleargate", "sprint-runs", ".active");
|
|
4861
|
+
const sprintActive = fs23.existsSync(activesentinel);
|
|
4796
4862
|
const shouldRemind = !hasApprovedStory && !sprintActive;
|
|
4797
4863
|
if (shouldRemind) {
|
|
4798
4864
|
stdout(PLANNING_FIRST_REMINDER);
|
|
@@ -4827,10 +4893,10 @@ async function runPricing(filePath, cwd, stdout, stderr, exit, outcome) {
|
|
|
4827
4893
|
exit(2);
|
|
4828
4894
|
return;
|
|
4829
4895
|
}
|
|
4830
|
-
const absPath =
|
|
4896
|
+
const absPath = path25.isAbsolute(filePath) ? filePath : path25.resolve(cwd, filePath);
|
|
4831
4897
|
let raw;
|
|
4832
4898
|
try {
|
|
4833
|
-
raw =
|
|
4899
|
+
raw = fs23.readFileSync(absPath, "utf-8");
|
|
4834
4900
|
} catch {
|
|
4835
4901
|
stderr(`cleargate doctor --pricing: cannot read file: ${absPath}`);
|
|
4836
4902
|
if (outcome) outcome.configError = true;
|
|
@@ -4892,7 +4958,7 @@ async function runPricing(filePath, cwd, stdout, stderr, exit, outcome) {
|
|
|
4892
4958
|
const output = draftTokens.output ?? 0;
|
|
4893
4959
|
const cacheRead = draftTokens.cache_read ?? 0;
|
|
4894
4960
|
const cacheCreation = draftTokens.cache_creation ?? 0;
|
|
4895
|
-
const fileName =
|
|
4961
|
+
const fileName = path25.basename(absPath);
|
|
4896
4962
|
stdout(
|
|
4897
4963
|
`${fileName}: ${model} \u2014 input:${input} output:${output} cache_read:${cacheRead} cache_creation:${cacheCreation} \u2248 $${usd.toFixed(4)}`
|
|
4898
4964
|
);
|
|
@@ -4905,15 +4971,15 @@ function globMatch(pattern, filePath) {
|
|
|
4905
4971
|
return re.test(normalFile);
|
|
4906
4972
|
}
|
|
4907
4973
|
async function runCanEdit(filePath, cwd, stdout, exit, outcome) {
|
|
4908
|
-
const activeSentinel =
|
|
4909
|
-
if (
|
|
4974
|
+
const activeSentinel = path25.join(cwd, ".cleargate", "sprint-runs", ".active");
|
|
4975
|
+
if (fs23.existsSync(activeSentinel)) {
|
|
4910
4976
|
stdout("allowed: sprint active");
|
|
4911
4977
|
return;
|
|
4912
4978
|
}
|
|
4913
|
-
const pendingSyncDir =
|
|
4979
|
+
const pendingSyncDir = path25.join(cwd, ".cleargate", "delivery", "pending-sync");
|
|
4914
4980
|
let files;
|
|
4915
4981
|
try {
|
|
4916
|
-
files =
|
|
4982
|
+
files = fs23.readdirSync(pendingSyncDir).filter((f) => f.endsWith(".md")).map((f) => path25.join(pendingSyncDir, f));
|
|
4917
4983
|
} catch {
|
|
4918
4984
|
stdout("blocked: no_approved_stories");
|
|
4919
4985
|
if (outcome) outcome.blocker = true;
|
|
@@ -4925,7 +4991,7 @@ async function runCanEdit(filePath, cwd, stdout, exit, outcome) {
|
|
|
4925
4991
|
for (const storyPath of files) {
|
|
4926
4992
|
let raw;
|
|
4927
4993
|
try {
|
|
4928
|
-
raw =
|
|
4994
|
+
raw = fs23.readFileSync(storyPath, "utf-8");
|
|
4929
4995
|
} catch {
|
|
4930
4996
|
continue;
|
|
4931
4997
|
}
|
|
@@ -5023,14 +5089,14 @@ async function doctorHandler(flags, cli) {
|
|
|
5023
5089
|
|
|
5024
5090
|
// src/commands/gate.ts
|
|
5025
5091
|
init_cjs_shims();
|
|
5026
|
-
var
|
|
5027
|
-
var
|
|
5092
|
+
var fs27 = __toESM(require("fs"), 1);
|
|
5093
|
+
var path28 = __toESM(require("path"), 1);
|
|
5028
5094
|
var import_node_child_process7 = require("child_process");
|
|
5029
5095
|
|
|
5030
5096
|
// src/commands/execution-mode.ts
|
|
5031
5097
|
init_cjs_shims();
|
|
5032
|
-
var
|
|
5033
|
-
var
|
|
5098
|
+
var fs24 = __toESM(require("fs"), 1);
|
|
5099
|
+
var path26 = __toESM(require("path"), 1);
|
|
5034
5100
|
var V1_INERT_MESSAGE = "v1 mode active \u2014 command inert. Set execution_mode: v2 in sprint frontmatter to enable.";
|
|
5035
5101
|
function parseFrontmatterSimple(raw) {
|
|
5036
5102
|
const match = /^---\r?\n([\s\S]*?)\r?\n---/.exec(raw);
|
|
@@ -5048,24 +5114,24 @@ function parseFrontmatterSimple(raw) {
|
|
|
5048
5114
|
}
|
|
5049
5115
|
function discoverSprintFile(sprintId, cwd) {
|
|
5050
5116
|
const searchDirs = [
|
|
5051
|
-
|
|
5052
|
-
|
|
5117
|
+
path26.join(cwd, ".cleargate", "delivery", "pending-sync"),
|
|
5118
|
+
path26.join(cwd, ".cleargate", "delivery", "archive")
|
|
5053
5119
|
];
|
|
5054
5120
|
for (const dir of searchDirs) {
|
|
5055
|
-
if (!
|
|
5121
|
+
if (!fs24.existsSync(dir)) continue;
|
|
5056
5122
|
let entries;
|
|
5057
5123
|
try {
|
|
5058
|
-
entries =
|
|
5124
|
+
entries = fs24.readdirSync(dir);
|
|
5059
5125
|
} catch {
|
|
5060
5126
|
continue;
|
|
5061
5127
|
}
|
|
5062
5128
|
const prefix = `${sprintId}_`;
|
|
5063
5129
|
for (const entry of entries) {
|
|
5064
5130
|
if (entry.startsWith(prefix) && entry.endsWith(".md")) {
|
|
5065
|
-
return
|
|
5131
|
+
return path26.join(dir, entry);
|
|
5066
5132
|
}
|
|
5067
5133
|
if (entry === `${sprintId}.md`) {
|
|
5068
|
-
return
|
|
5134
|
+
return path26.join(dir, entry);
|
|
5069
5135
|
}
|
|
5070
5136
|
}
|
|
5071
5137
|
}
|
|
@@ -5073,9 +5139,9 @@ function discoverSprintFile(sprintId, cwd) {
|
|
|
5073
5139
|
}
|
|
5074
5140
|
function resolveSprintIdFromSentinel(cwd) {
|
|
5075
5141
|
const resolvedCwd = cwd ?? process.cwd();
|
|
5076
|
-
const sentinelPath =
|
|
5142
|
+
const sentinelPath = path26.join(resolvedCwd, ".cleargate", "sprint-runs", ".active");
|
|
5077
5143
|
try {
|
|
5078
|
-
const content =
|
|
5144
|
+
const content = fs24.readFileSync(sentinelPath, "utf8").trim();
|
|
5079
5145
|
return content.length > 0 ? content : null;
|
|
5080
5146
|
} catch {
|
|
5081
5147
|
return null;
|
|
@@ -5094,12 +5160,12 @@ function readSprintExecutionMode(sprintId, opts = {}) {
|
|
|
5094
5160
|
if (!filePath) {
|
|
5095
5161
|
filePath = discoverSprintFile(resolvedSprintId, cwd);
|
|
5096
5162
|
}
|
|
5097
|
-
if (!filePath || !
|
|
5163
|
+
if (!filePath || !fs24.existsSync(filePath)) {
|
|
5098
5164
|
return "v1";
|
|
5099
5165
|
}
|
|
5100
5166
|
let raw;
|
|
5101
5167
|
try {
|
|
5102
|
-
raw =
|
|
5168
|
+
raw = fs24.readFileSync(filePath, "utf8");
|
|
5103
5169
|
} catch {
|
|
5104
5170
|
return "v1";
|
|
5105
5171
|
}
|
|
@@ -5118,8 +5184,8 @@ var import_js_yaml6 = __toESM(require("js-yaml"), 1);
|
|
|
5118
5184
|
|
|
5119
5185
|
// src/lib/readiness-predicates.ts
|
|
5120
5186
|
init_cjs_shims();
|
|
5121
|
-
var
|
|
5122
|
-
var
|
|
5187
|
+
var fs25 = __toESM(require("fs"), 1);
|
|
5188
|
+
var path27 = __toESM(require("path"), 1);
|
|
5123
5189
|
function parsePredicate(src) {
|
|
5124
5190
|
const s = src.trim();
|
|
5125
5191
|
const fmMatch = s.match(
|
|
@@ -5283,18 +5349,18 @@ function compareValues(actual, op, expected) {
|
|
|
5283
5349
|
}
|
|
5284
5350
|
function resolveLinkedPath(ref, docAbsPath, projectRoot) {
|
|
5285
5351
|
const candidates = [
|
|
5286
|
-
|
|
5287
|
-
|
|
5352
|
+
path27.resolve(path27.dirname(docAbsPath), ref),
|
|
5353
|
+
path27.resolve(projectRoot, ref)
|
|
5288
5354
|
];
|
|
5289
5355
|
for (const candidate of candidates) {
|
|
5290
5356
|
if (!candidate.startsWith(projectRoot)) continue;
|
|
5291
|
-
if (
|
|
5357
|
+
if (fs25.existsSync(candidate)) return candidate;
|
|
5292
5358
|
}
|
|
5293
5359
|
return null;
|
|
5294
5360
|
}
|
|
5295
5361
|
function readFrontmatterFromFile(absPath) {
|
|
5296
5362
|
try {
|
|
5297
|
-
const raw =
|
|
5363
|
+
const raw = fs25.readFileSync(absPath, "utf8");
|
|
5298
5364
|
const lines = raw.split("\n");
|
|
5299
5365
|
if (lines[0] !== "---") return {};
|
|
5300
5366
|
let closeIdx = -1;
|
|
@@ -5438,14 +5504,14 @@ function applyCountOp(actual, op, n) {
|
|
|
5438
5504
|
}
|
|
5439
5505
|
}
|
|
5440
5506
|
function evalFileExists(parsed, projectRoot) {
|
|
5441
|
-
const resolved =
|
|
5442
|
-
if (!resolved.startsWith(projectRoot +
|
|
5507
|
+
const resolved = path27.resolve(projectRoot, parsed.path);
|
|
5508
|
+
if (!resolved.startsWith(projectRoot + path27.sep) && resolved !== projectRoot) {
|
|
5443
5509
|
return {
|
|
5444
5510
|
pass: false,
|
|
5445
5511
|
detail: `path '${parsed.path}' resolves outside project root (sandbox violation)`
|
|
5446
5512
|
};
|
|
5447
5513
|
}
|
|
5448
|
-
const exists =
|
|
5514
|
+
const exists = fs25.existsSync(resolved);
|
|
5449
5515
|
return {
|
|
5450
5516
|
pass: exists,
|
|
5451
5517
|
detail: exists ? `${parsed.path} exists` : `${parsed.path} not found`
|
|
@@ -5453,13 +5519,13 @@ function evalFileExists(parsed, projectRoot) {
|
|
|
5453
5519
|
}
|
|
5454
5520
|
function evalLinkTargetExists(parsed, opts) {
|
|
5455
5521
|
const projectRoot = opts?.projectRoot ?? process.cwd();
|
|
5456
|
-
const wikiIndexPath = opts?.wikiIndexPath ??
|
|
5522
|
+
const wikiIndexPath = opts?.wikiIndexPath ?? path27.join(projectRoot, ".cleargate", "wiki", "index.md");
|
|
5457
5523
|
if (!wikiIndexPath.startsWith(projectRoot)) {
|
|
5458
5524
|
return { pass: false, detail: "wikiIndexPath resolves outside project root" };
|
|
5459
5525
|
}
|
|
5460
5526
|
let indexContent;
|
|
5461
5527
|
try {
|
|
5462
|
-
indexContent =
|
|
5528
|
+
indexContent = fs25.readFileSync(wikiIndexPath, "utf8");
|
|
5463
5529
|
} catch {
|
|
5464
5530
|
return { pass: false, detail: `wiki index not found at ${wikiIndexPath}` };
|
|
5465
5531
|
}
|
|
@@ -5470,13 +5536,13 @@ function evalLinkTargetExists(parsed, opts) {
|
|
|
5470
5536
|
};
|
|
5471
5537
|
}
|
|
5472
5538
|
function evalStatusOf(parsed, opts, projectRoot) {
|
|
5473
|
-
const wikiIndexPath = opts?.wikiIndexPath ??
|
|
5539
|
+
const wikiIndexPath = opts?.wikiIndexPath ?? path27.join(projectRoot, ".cleargate", "wiki", "index.md");
|
|
5474
5540
|
if (!wikiIndexPath.startsWith(projectRoot)) {
|
|
5475
5541
|
return { pass: false, detail: "wikiIndexPath resolves outside project root" };
|
|
5476
5542
|
}
|
|
5477
5543
|
let indexContent;
|
|
5478
5544
|
try {
|
|
5479
|
-
indexContent =
|
|
5545
|
+
indexContent = fs25.readFileSync(wikiIndexPath, "utf8");
|
|
5480
5546
|
} catch {
|
|
5481
5547
|
return { pass: false, detail: `wiki index not found at ${wikiIndexPath}` };
|
|
5482
5548
|
}
|
|
@@ -5487,7 +5553,7 @@ function evalStatusOf(parsed, opts, projectRoot) {
|
|
|
5487
5553
|
return { pass: false, detail: `[[${parsed.id}]] not found in wiki index` };
|
|
5488
5554
|
}
|
|
5489
5555
|
const rawPath = rowMatch[1].trim();
|
|
5490
|
-
const fullPath =
|
|
5556
|
+
const fullPath = path27.resolve(projectRoot, rawPath);
|
|
5491
5557
|
if (!fullPath.startsWith(projectRoot)) {
|
|
5492
5558
|
return { pass: false, detail: `wiki path for ${parsed.id} resolves outside project root` };
|
|
5493
5559
|
}
|
|
@@ -5505,12 +5571,12 @@ function evalStatusOf(parsed, opts, projectRoot) {
|
|
|
5505
5571
|
|
|
5506
5572
|
// src/lib/frontmatter-cache.ts
|
|
5507
5573
|
init_cjs_shims();
|
|
5508
|
-
var
|
|
5574
|
+
var fs26 = __toESM(require("fs/promises"), 1);
|
|
5509
5575
|
var import_js_yaml5 = __toESM(require("js-yaml"), 1);
|
|
5510
5576
|
async function readCachedGate(absPath) {
|
|
5511
5577
|
let raw;
|
|
5512
5578
|
try {
|
|
5513
|
-
raw = await
|
|
5579
|
+
raw = await fs26.readFile(absPath, "utf8");
|
|
5514
5580
|
} catch {
|
|
5515
5581
|
return null;
|
|
5516
5582
|
}
|
|
@@ -5530,7 +5596,7 @@ async function writeCachedGate(absPath, result, opts) {
|
|
|
5530
5596
|
failing_criteria: result.failing_criteria,
|
|
5531
5597
|
last_gate_check: lastGateCheck
|
|
5532
5598
|
};
|
|
5533
|
-
const raw = await
|
|
5599
|
+
const raw = await fs26.readFile(absPath, "utf8");
|
|
5534
5600
|
let fm;
|
|
5535
5601
|
let body;
|
|
5536
5602
|
try {
|
|
@@ -5560,7 +5626,7 @@ async function writeCachedGate(absPath, result, opts) {
|
|
|
5560
5626
|
|
|
5561
5627
|
${body}` : `${fmBlock}
|
|
5562
5628
|
`;
|
|
5563
|
-
await
|
|
5629
|
+
await fs26.writeFile(absPath, newContent, "utf8");
|
|
5564
5630
|
}
|
|
5565
5631
|
function coerceCachedGate(val) {
|
|
5566
5632
|
if (val === void 0 || val === null) return null;
|
|
@@ -5591,7 +5657,7 @@ function coerceCachedGate(val) {
|
|
|
5591
5657
|
|
|
5592
5658
|
// src/commands/gate.ts
|
|
5593
5659
|
function loadGateBlocks(gatesDocPath) {
|
|
5594
|
-
const raw =
|
|
5660
|
+
const raw = fs27.readFileSync(gatesDocPath, "utf8");
|
|
5595
5661
|
const blocks = [];
|
|
5596
5662
|
const fenceRe = /^```yaml\n([\s\S]*?)^```/gm;
|
|
5597
5663
|
let match;
|
|
@@ -5626,14 +5692,14 @@ async function gateCheckHandler(file, opts, cli) {
|
|
|
5626
5692
|
const exitFn = cli?.exit ?? ((code) => process.exit(code));
|
|
5627
5693
|
const cwd = cli?.cwd ?? process.cwd();
|
|
5628
5694
|
const nowFn = cli?.now ?? (() => /* @__PURE__ */ new Date());
|
|
5629
|
-
const absPath =
|
|
5630
|
-
if (!
|
|
5695
|
+
const absPath = path28.isAbsolute(file) ? file : path28.resolve(cwd, file);
|
|
5696
|
+
if (!fs27.existsSync(absPath)) {
|
|
5631
5697
|
stderrFn(`[cleargate gate] error: file not found: ${absPath}`);
|
|
5632
5698
|
return exitFn(1);
|
|
5633
5699
|
}
|
|
5634
5700
|
let raw;
|
|
5635
5701
|
try {
|
|
5636
|
-
raw =
|
|
5702
|
+
raw = fs27.readFileSync(absPath, "utf8");
|
|
5637
5703
|
} catch (err) {
|
|
5638
5704
|
stderrFn(`[cleargate gate] error: cannot read file: ${absPath}`);
|
|
5639
5705
|
return exitFn(1);
|
|
@@ -5652,8 +5718,8 @@ async function gateCheckHandler(file, opts, cli) {
|
|
|
5652
5718
|
return exitFn(1);
|
|
5653
5719
|
}
|
|
5654
5720
|
const projectRoot = cwd;
|
|
5655
|
-
const gatesDocPath = cli?.gatesDocPath ??
|
|
5656
|
-
if (!
|
|
5721
|
+
const gatesDocPath = cli?.gatesDocPath ?? path28.join(projectRoot, ".cleargate", "knowledge", "readiness-gates.md");
|
|
5722
|
+
if (!fs27.existsSync(gatesDocPath)) {
|
|
5657
5723
|
stderrFn(`[cleargate gate] error: readiness-gates.md not found at: ${gatesDocPath}`);
|
|
5658
5724
|
return exitFn(1);
|
|
5659
5725
|
}
|
|
@@ -5726,8 +5792,8 @@ async function gateExplainHandler(file, cli) {
|
|
|
5726
5792
|
const stderrFn = cli?.stderr ?? ((s) => process.stderr.write(s + "\n"));
|
|
5727
5793
|
const exitFn = cli?.exit ?? ((code) => process.exit(code));
|
|
5728
5794
|
const cwd = cli?.cwd ?? process.cwd();
|
|
5729
|
-
const absPath =
|
|
5730
|
-
if (!
|
|
5795
|
+
const absPath = path28.isAbsolute(file) ? file : path28.resolve(cwd, file);
|
|
5796
|
+
if (!fs27.existsSync(absPath)) {
|
|
5731
5797
|
stderrFn(`[cleargate gate] error: file not found: ${absPath}`);
|
|
5732
5798
|
return exitFn(1);
|
|
5733
5799
|
}
|
|
@@ -5738,7 +5804,7 @@ async function gateExplainHandler(file, cli) {
|
|
|
5738
5804
|
}
|
|
5739
5805
|
let raw;
|
|
5740
5806
|
try {
|
|
5741
|
-
raw =
|
|
5807
|
+
raw = fs27.readFileSync(absPath, "utf8");
|
|
5742
5808
|
} catch {
|
|
5743
5809
|
stderrFn(`[cleargate gate] error: cannot read file: ${absPath}`);
|
|
5744
5810
|
return exitFn(1);
|
|
@@ -5759,7 +5825,7 @@ async function gateExplainHandler(file, cli) {
|
|
|
5759
5825
|
function resolveRunScriptForGate(opts) {
|
|
5760
5826
|
if (opts.runScriptPath) return opts.runScriptPath;
|
|
5761
5827
|
const cwd = opts.cwd ?? process.cwd();
|
|
5762
|
-
return
|
|
5828
|
+
return path28.join(cwd, ".cleargate", "scripts", "run_script.sh");
|
|
5763
5829
|
}
|
|
5764
5830
|
function gateQaHandler(opts, cli) {
|
|
5765
5831
|
const stdoutFn = cli?.stdout ?? ((s) => process.stdout.write(s + "\n"));
|
|
@@ -5853,15 +5919,15 @@ function gateRunHandler(name, opts, cli) {
|
|
|
5853
5919
|
|
|
5854
5920
|
// src/commands/sprint.ts
|
|
5855
5921
|
init_cjs_shims();
|
|
5856
|
-
var
|
|
5857
|
-
var
|
|
5922
|
+
var fs28 = __toESM(require("fs"), 1);
|
|
5923
|
+
var path29 = __toESM(require("path"), 1);
|
|
5858
5924
|
var import_node_child_process9 = require("child_process");
|
|
5859
5925
|
var import_js_yaml7 = __toESM(require("js-yaml"), 1);
|
|
5860
5926
|
var TERMINAL_STATUSES2 = /* @__PURE__ */ new Set(["Completed", "Done", "Abandoned", "Closed", "Resolved"]);
|
|
5861
5927
|
function resolveRunScript(opts) {
|
|
5862
5928
|
if (opts.runScriptPath) return opts.runScriptPath;
|
|
5863
5929
|
const cwd = opts.cwd ?? process.cwd();
|
|
5864
|
-
return
|
|
5930
|
+
return path29.join(cwd, ".cleargate", "scripts", "run_script.sh");
|
|
5865
5931
|
}
|
|
5866
5932
|
function defaultExit(code) {
|
|
5867
5933
|
return process.exit(code);
|
|
@@ -5955,8 +6021,8 @@ ${body}`;
|
|
|
5955
6021
|
}
|
|
5956
6022
|
function atomicWriteStr(filePath, content) {
|
|
5957
6023
|
const tmp = `${filePath}.tmp.${process.pid}`;
|
|
5958
|
-
|
|
5959
|
-
|
|
6024
|
+
fs28.writeFileSync(tmp, content, "utf8");
|
|
6025
|
+
fs28.renameSync(tmp, filePath);
|
|
5960
6026
|
}
|
|
5961
6027
|
function deriveSprintBranchForArchive(sprintId) {
|
|
5962
6028
|
const match = /^SPRINT-(\d+)/.exec(sprintId);
|
|
@@ -5970,7 +6036,7 @@ function stampFile(raw, status, completedAt) {
|
|
|
5970
6036
|
return serializeFileContent(fm, body);
|
|
5971
6037
|
}
|
|
5972
6038
|
function stampSprintClose(sprintPath, now) {
|
|
5973
|
-
const previousContent =
|
|
6039
|
+
const previousContent = fs28.readFileSync(sprintPath, "utf8");
|
|
5974
6040
|
const { fm, body } = parseFileFrontmatter(previousContent);
|
|
5975
6041
|
const currentStatus = typeof fm["status"] === "string" ? fm["status"] : "";
|
|
5976
6042
|
const alreadyTerminal = TERMINAL_STATUSES2.has(currentStatus);
|
|
@@ -6030,14 +6096,14 @@ async function sprintArchiveHandler(opts, cli) {
|
|
|
6030
6096
|
if (mode === "v1") {
|
|
6031
6097
|
return printInertAndExit(stdoutFn, exitFn);
|
|
6032
6098
|
}
|
|
6033
|
-
const stateFile =
|
|
6034
|
-
if (!
|
|
6099
|
+
const stateFile = path29.join(cwd, ".cleargate", "sprint-runs", opts.sprintId, "state.json");
|
|
6100
|
+
if (!fs28.existsSync(stateFile)) {
|
|
6035
6101
|
stderrFn(`[cleargate sprint archive] state.json not found at ${stateFile}`);
|
|
6036
6102
|
return exitFn(1);
|
|
6037
6103
|
}
|
|
6038
6104
|
let state2;
|
|
6039
6105
|
try {
|
|
6040
|
-
state2 = JSON.parse(
|
|
6106
|
+
state2 = JSON.parse(fs28.readFileSync(stateFile, "utf8"));
|
|
6041
6107
|
} catch (err) {
|
|
6042
6108
|
stderrFn(`[cleargate sprint archive] failed to parse state.json: ${err.message}`);
|
|
6043
6109
|
return exitFn(1);
|
|
@@ -6049,18 +6115,18 @@ async function sprintArchiveHandler(opts, cli) {
|
|
|
6049
6115
|
return exitFn(1);
|
|
6050
6116
|
}
|
|
6051
6117
|
const stateStories = state2.stories ?? {};
|
|
6052
|
-
const pendingDir =
|
|
6053
|
-
const archiveDir =
|
|
6118
|
+
const pendingDir = path29.join(cwd, ".cleargate", "delivery", "pending-sync");
|
|
6119
|
+
const archiveDir = path29.join(cwd, ".cleargate", "delivery", "archive");
|
|
6054
6120
|
let sprintFile = null;
|
|
6055
|
-
for (const entry of
|
|
6121
|
+
for (const entry of fs28.readdirSync(pendingDir)) {
|
|
6056
6122
|
if ((entry.startsWith(`${opts.sprintId}_`) || entry === `${opts.sprintId}.md`) && entry.endsWith(".md")) {
|
|
6057
|
-
sprintFile =
|
|
6123
|
+
sprintFile = path29.join(pendingDir, entry);
|
|
6058
6124
|
break;
|
|
6059
6125
|
}
|
|
6060
6126
|
}
|
|
6061
6127
|
let epicIds = [];
|
|
6062
|
-
if (sprintFile &&
|
|
6063
|
-
const { fm } = parseFileFrontmatter(
|
|
6128
|
+
if (sprintFile && fs28.existsSync(sprintFile)) {
|
|
6129
|
+
const { fm } = parseFileFrontmatter(fs28.readFileSync(sprintFile, "utf8"));
|
|
6064
6130
|
const epics = fm["epics"];
|
|
6065
6131
|
if (Array.isArray(epics)) {
|
|
6066
6132
|
epicIds = epics.map(String);
|
|
@@ -6070,15 +6136,15 @@ async function sprintArchiveHandler(opts, cli) {
|
|
|
6070
6136
|
if (sprintFile) {
|
|
6071
6137
|
plan.push({
|
|
6072
6138
|
src: sprintFile,
|
|
6073
|
-
destName:
|
|
6139
|
+
destName: path29.basename(sprintFile),
|
|
6074
6140
|
status: "Completed"
|
|
6075
6141
|
});
|
|
6076
6142
|
}
|
|
6077
6143
|
for (const epicId of epicIds) {
|
|
6078
|
-
for (const entry of
|
|
6144
|
+
for (const entry of fs28.readdirSync(pendingDir)) {
|
|
6079
6145
|
if ((entry.startsWith(`${epicId}_`) || entry === `${epicId}.md`) && entry.endsWith(".md")) {
|
|
6080
6146
|
plan.push({
|
|
6081
|
-
src:
|
|
6147
|
+
src: path29.join(pendingDir, entry),
|
|
6082
6148
|
destName: entry,
|
|
6083
6149
|
status: "Approved"
|
|
6084
6150
|
});
|
|
@@ -6087,10 +6153,10 @@ async function sprintArchiveHandler(opts, cli) {
|
|
|
6087
6153
|
}
|
|
6088
6154
|
const storyKeys = storyKeysForEpic(stateStories, epicId);
|
|
6089
6155
|
for (const storyId of storyKeys) {
|
|
6090
|
-
for (const entry of
|
|
6156
|
+
for (const entry of fs28.readdirSync(pendingDir)) {
|
|
6091
6157
|
if ((entry.startsWith(`${storyId}_`) || entry === `${storyId}.md`) && entry.endsWith(".md")) {
|
|
6092
6158
|
plan.push({
|
|
6093
|
-
src:
|
|
6159
|
+
src: path29.join(pendingDir, entry),
|
|
6094
6160
|
destName: entry,
|
|
6095
6161
|
status: "Done"
|
|
6096
6162
|
});
|
|
@@ -6102,13 +6168,13 @@ async function sprintArchiveHandler(opts, cli) {
|
|
|
6102
6168
|
const storyIdsInState = new Set(Object.keys(stateStories));
|
|
6103
6169
|
const planSrcs = new Set(plan.map((p) => p.src));
|
|
6104
6170
|
const orphans = [];
|
|
6105
|
-
for (const entry of
|
|
6171
|
+
for (const entry of fs28.readdirSync(pendingDir)) {
|
|
6106
6172
|
if (!entry.startsWith("STORY-") || !entry.endsWith(".md")) continue;
|
|
6107
|
-
const candidate =
|
|
6173
|
+
const candidate = path29.join(pendingDir, entry);
|
|
6108
6174
|
if (planSrcs.has(candidate)) continue;
|
|
6109
6175
|
let raw;
|
|
6110
6176
|
try {
|
|
6111
|
-
raw =
|
|
6177
|
+
raw = fs28.readFileSync(candidate, "utf8");
|
|
6112
6178
|
} catch {
|
|
6113
6179
|
continue;
|
|
6114
6180
|
}
|
|
@@ -6125,18 +6191,18 @@ async function sprintArchiveHandler(opts, cli) {
|
|
|
6125
6191
|
}
|
|
6126
6192
|
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6127
6193
|
const sprintBranch = deriveSprintBranchForArchive(opts.sprintId);
|
|
6128
|
-
const activePath =
|
|
6194
|
+
const activePath = path29.join(cwd, ".cleargate", "sprint-runs", ".active");
|
|
6129
6195
|
if (opts.dryRun) {
|
|
6130
6196
|
stdoutFn(`[dry-run] Sprint archive plan for ${opts.sprintId}:`);
|
|
6131
6197
|
stdoutFn(` Sprint branch: ${sprintBranch}`);
|
|
6132
6198
|
stdoutFn(` Files to archive (${plan.length}):`);
|
|
6133
6199
|
for (const entry of plan) {
|
|
6134
6200
|
stdoutFn(
|
|
6135
|
-
` ${
|
|
6201
|
+
` ${path29.basename(entry.src)} \u2192 archive/${entry.destName} [stamp: status=${entry.status}, completed_at=<now>]`
|
|
6136
6202
|
);
|
|
6137
6203
|
}
|
|
6138
6204
|
if (orphans.length > 0) {
|
|
6139
|
-
stdoutFn(` Orphan files (${orphans.length}): ${orphans.map((o) =>
|
|
6205
|
+
stdoutFn(` Orphan files (${orphans.length}): ${orphans.map((o) => path29.basename(o)).join(", ")}`);
|
|
6140
6206
|
}
|
|
6141
6207
|
stdoutFn(` .active \u2192 "" (truncate)`);
|
|
6142
6208
|
stdoutFn(` git checkout main`);
|
|
@@ -6145,9 +6211,9 @@ async function sprintArchiveHandler(opts, cli) {
|
|
|
6145
6211
|
return exitFn(0);
|
|
6146
6212
|
}
|
|
6147
6213
|
let sprintFileSnapshot = null;
|
|
6148
|
-
const wikiRoot =
|
|
6149
|
-
const wikiInitialised =
|
|
6150
|
-
if (sprintFile &&
|
|
6214
|
+
const wikiRoot = path29.join(cwd, ".cleargate", "wiki");
|
|
6215
|
+
const wikiInitialised = fs28.existsSync(wikiRoot);
|
|
6216
|
+
if (sprintFile && fs28.existsSync(sprintFile)) {
|
|
6151
6217
|
const { previousContent } = stampSprintClose(sprintFile, () => completedAt);
|
|
6152
6218
|
sprintFileSnapshot = previousContent;
|
|
6153
6219
|
if (wikiInitialised) {
|
|
@@ -6169,15 +6235,15 @@ async function sprintArchiveHandler(opts, cli) {
|
|
|
6169
6235
|
}
|
|
6170
6236
|
}
|
|
6171
6237
|
for (const entry of plan) {
|
|
6172
|
-
if (!
|
|
6238
|
+
if (!fs28.existsSync(entry.src)) {
|
|
6173
6239
|
stderrFn(`[cleargate sprint archive] source not found: ${entry.src} \u2014 skipping`);
|
|
6174
6240
|
continue;
|
|
6175
6241
|
}
|
|
6176
|
-
const raw =
|
|
6242
|
+
const raw = fs28.readFileSync(entry.src, "utf8");
|
|
6177
6243
|
const stamped = stampFile(raw, entry.status, completedAt);
|
|
6178
|
-
const dest =
|
|
6244
|
+
const dest = path29.join(archiveDir, entry.destName);
|
|
6179
6245
|
atomicWriteStr(entry.src, stamped);
|
|
6180
|
-
|
|
6246
|
+
fs28.renameSync(entry.src, dest);
|
|
6181
6247
|
stdoutFn(`archived: ${entry.destName}`);
|
|
6182
6248
|
}
|
|
6183
6249
|
try {
|
|
@@ -6220,8 +6286,8 @@ async function sprintArchiveHandler(opts, cli) {
|
|
|
6220
6286
|
|
|
6221
6287
|
// src/commands/story.ts
|
|
6222
6288
|
init_cjs_shims();
|
|
6223
|
-
var
|
|
6224
|
-
var
|
|
6289
|
+
var fs29 = __toESM(require("fs"), 1);
|
|
6290
|
+
var path30 = __toESM(require("path"), 1);
|
|
6225
6291
|
var import_node_child_process10 = require("child_process");
|
|
6226
6292
|
function defaultExit2(code) {
|
|
6227
6293
|
return process.exit(code);
|
|
@@ -6229,7 +6295,7 @@ function defaultExit2(code) {
|
|
|
6229
6295
|
function resolveRunScript2(opts) {
|
|
6230
6296
|
if (opts.runScriptPath) return opts.runScriptPath;
|
|
6231
6297
|
const cwd = opts.cwd ?? process.cwd();
|
|
6232
|
-
return
|
|
6298
|
+
return path30.join(cwd, ".cleargate", "scripts", "run_script.sh");
|
|
6233
6299
|
}
|
|
6234
6300
|
function deriveSprintBranch(sprintId) {
|
|
6235
6301
|
const match = /^SPRINT-(\d+)/.exec(sprintId);
|
|
@@ -6238,11 +6304,11 @@ function deriveSprintBranch(sprintId) {
|
|
|
6238
6304
|
}
|
|
6239
6305
|
function atomicWriteString(filePath, text) {
|
|
6240
6306
|
const tmpFile = `${filePath}.tmp.${process.pid}`;
|
|
6241
|
-
|
|
6242
|
-
|
|
6307
|
+
fs29.writeFileSync(tmpFile, text, "utf8");
|
|
6308
|
+
fs29.renameSync(tmpFile, filePath);
|
|
6243
6309
|
}
|
|
6244
6310
|
function stateJsonPath(cwd, sprintId) {
|
|
6245
|
-
return
|
|
6311
|
+
return path30.join(cwd, ".cleargate", "sprint-runs", sprintId, "state.json");
|
|
6246
6312
|
}
|
|
6247
6313
|
function storyStartHandler(opts, cli) {
|
|
6248
6314
|
const stdoutFn = cli?.stdout ?? ((s) => process.stdout.write(s + "\n"));
|
|
@@ -6259,7 +6325,7 @@ function storyStartHandler(opts, cli) {
|
|
|
6259
6325
|
return printInertAndExit(stdoutFn, exitFn);
|
|
6260
6326
|
}
|
|
6261
6327
|
const sprintBranch = deriveSprintBranch(sprintId);
|
|
6262
|
-
const worktreePath =
|
|
6328
|
+
const worktreePath = path30.join(cwd, ".worktrees", opts.storyId);
|
|
6263
6329
|
const storyBranch = `story/${opts.storyId}`;
|
|
6264
6330
|
const step1 = spawnFn(
|
|
6265
6331
|
"git",
|
|
@@ -6291,13 +6357,13 @@ function storyStartHandler(opts, cli) {
|
|
|
6291
6357
|
return exitFn(step2.status ?? 1);
|
|
6292
6358
|
}
|
|
6293
6359
|
const stateFile = stateJsonPath(cwd, sprintId);
|
|
6294
|
-
if (!
|
|
6360
|
+
if (!fs29.existsSync(stateFile)) {
|
|
6295
6361
|
stderrFn(`[cleargate story start] step 3: state.json not found at ${stateFile}`);
|
|
6296
6362
|
return exitFn(1);
|
|
6297
6363
|
}
|
|
6298
6364
|
let state2;
|
|
6299
6365
|
try {
|
|
6300
|
-
state2 = JSON.parse(
|
|
6366
|
+
state2 = JSON.parse(fs29.readFileSync(stateFile, "utf8"));
|
|
6301
6367
|
} catch (err) {
|
|
6302
6368
|
stderrFn(`[cleargate story start] step 3: failed to parse state.json: ${err.message}`);
|
|
6303
6369
|
return exitFn(1);
|
|
@@ -6338,7 +6404,7 @@ function storyCompleteHandler(opts, cli) {
|
|
|
6338
6404
|
}
|
|
6339
6405
|
const sprintBranch = deriveSprintBranch(sprintId);
|
|
6340
6406
|
const storyBranch = `story/${opts.storyId}`;
|
|
6341
|
-
const worktreeRel =
|
|
6407
|
+
const worktreeRel = path30.join(".worktrees", opts.storyId);
|
|
6342
6408
|
const step1 = spawnFn(
|
|
6343
6409
|
"git",
|
|
6344
6410
|
["rev-list", "--count", `${sprintBranch}..${storyBranch}`],
|
|
@@ -6436,7 +6502,7 @@ function storyCompleteHandler(opts, cli) {
|
|
|
6436
6502
|
|
|
6437
6503
|
// src/commands/state.ts
|
|
6438
6504
|
init_cjs_shims();
|
|
6439
|
-
var
|
|
6505
|
+
var path31 = __toESM(require("path"), 1);
|
|
6440
6506
|
var import_node_child_process11 = require("child_process");
|
|
6441
6507
|
function defaultExit3(code) {
|
|
6442
6508
|
return process.exit(code);
|
|
@@ -6444,7 +6510,7 @@ function defaultExit3(code) {
|
|
|
6444
6510
|
function resolveRunScript3(opts) {
|
|
6445
6511
|
if (opts.runScriptPath) return opts.runScriptPath;
|
|
6446
6512
|
const cwd = opts.cwd ?? process.cwd();
|
|
6447
|
-
return
|
|
6513
|
+
return path31.join(cwd, ".cleargate", "scripts", "run_script.sh");
|
|
6448
6514
|
}
|
|
6449
6515
|
function stateUpdateHandler(opts, cli) {
|
|
6450
6516
|
const stdoutFn = cli?.stdout ?? ((s) => process.stdout.write(s + "\n"));
|
|
@@ -6501,21 +6567,21 @@ function stateValidateHandler(opts, cli) {
|
|
|
6501
6567
|
|
|
6502
6568
|
// src/commands/stamp-tokens.ts
|
|
6503
6569
|
init_cjs_shims();
|
|
6504
|
-
var
|
|
6505
|
-
var
|
|
6570
|
+
var fs31 = __toESM(require("fs"), 1);
|
|
6571
|
+
var path33 = __toESM(require("path"), 1);
|
|
6506
6572
|
|
|
6507
6573
|
// src/lib/ledger-reader.ts
|
|
6508
6574
|
init_cjs_shims();
|
|
6509
|
-
var
|
|
6510
|
-
var
|
|
6575
|
+
var fs30 = __toESM(require("fs"), 1);
|
|
6576
|
+
var path32 = __toESM(require("path"), 1);
|
|
6511
6577
|
function findSprintRunsRoot(startDir) {
|
|
6512
6578
|
let dir = startDir;
|
|
6513
6579
|
while (true) {
|
|
6514
|
-
const candidate =
|
|
6515
|
-
if (
|
|
6580
|
+
const candidate = path32.join(dir, ".cleargate", "sprint-runs");
|
|
6581
|
+
if (fs30.existsSync(candidate)) {
|
|
6516
6582
|
return candidate;
|
|
6517
6583
|
}
|
|
6518
|
-
const parent =
|
|
6584
|
+
const parent = path32.dirname(dir);
|
|
6519
6585
|
if (parent === dir) {
|
|
6520
6586
|
return null;
|
|
6521
6587
|
}
|
|
@@ -6568,13 +6634,13 @@ function readLedgerForWorkItem(workItemId, opts = {}) {
|
|
|
6568
6634
|
}
|
|
6569
6635
|
sprintRunsRoot = found;
|
|
6570
6636
|
}
|
|
6571
|
-
if (!
|
|
6637
|
+
if (!fs30.existsSync(sprintRunsRoot)) {
|
|
6572
6638
|
return [];
|
|
6573
6639
|
}
|
|
6574
6640
|
let ledgerFiles;
|
|
6575
6641
|
try {
|
|
6576
|
-
const entries =
|
|
6577
|
-
ledgerFiles = entries.filter((e) => e.isDirectory()).map((e) =>
|
|
6642
|
+
const entries = fs30.readdirSync(sprintRunsRoot, { withFileTypes: true });
|
|
6643
|
+
ledgerFiles = entries.filter((e) => e.isDirectory()).map((e) => path32.join(sprintRunsRoot, e.name, "token-ledger.jsonl")).filter((f) => fs30.existsSync(f));
|
|
6578
6644
|
} catch {
|
|
6579
6645
|
return [];
|
|
6580
6646
|
}
|
|
@@ -6582,7 +6648,7 @@ function readLedgerForWorkItem(workItemId, opts = {}) {
|
|
|
6582
6648
|
for (const ledgerFile of ledgerFiles) {
|
|
6583
6649
|
let content;
|
|
6584
6650
|
try {
|
|
6585
|
-
content =
|
|
6651
|
+
content = fs30.readFileSync(ledgerFile, "utf-8");
|
|
6586
6652
|
} catch {
|
|
6587
6653
|
continue;
|
|
6588
6654
|
}
|
|
@@ -6633,7 +6699,7 @@ async function stampTokensHandler(file, opts, cli) {
|
|
|
6633
6699
|
});
|
|
6634
6700
|
const nowFn = cli?.now ?? (() => /* @__PURE__ */ new Date());
|
|
6635
6701
|
const cwd = cli?.cwd ?? process.cwd();
|
|
6636
|
-
const absPath =
|
|
6702
|
+
const absPath = path33.isAbsolute(file) ? file : path33.resolve(cwd, file);
|
|
6637
6703
|
if (/\/\.cleargate\/delivery\/archive\//.test(absPath)) {
|
|
6638
6704
|
stdoutFn(`[frozen] ${absPath}`);
|
|
6639
6705
|
exitFn(0);
|
|
@@ -6641,7 +6707,7 @@ async function stampTokensHandler(file, opts, cli) {
|
|
|
6641
6707
|
}
|
|
6642
6708
|
let rawContent;
|
|
6643
6709
|
try {
|
|
6644
|
-
rawContent =
|
|
6710
|
+
rawContent = fs31.readFileSync(absPath, "utf-8");
|
|
6645
6711
|
} catch {
|
|
6646
6712
|
stdoutFn(`[stamp-tokens] error: cannot read file: ${absPath}`);
|
|
6647
6713
|
exitFn(1);
|
|
@@ -6713,7 +6779,7 @@ async function stampTokensHandler(file, opts, cli) {
|
|
|
6713
6779
|
return;
|
|
6714
6780
|
}
|
|
6715
6781
|
try {
|
|
6716
|
-
|
|
6782
|
+
fs31.writeFileSync(absPath, serialized, "utf-8");
|
|
6717
6783
|
} catch {
|
|
6718
6784
|
stdoutFn(`[stamp-tokens] error: cannot write file: ${absPath}`);
|
|
6719
6785
|
exitFn(1);
|
|
@@ -6730,7 +6796,7 @@ function extractWorkItemId(fm, absPath) {
|
|
|
6730
6796
|
return val.trim();
|
|
6731
6797
|
}
|
|
6732
6798
|
}
|
|
6733
|
-
const basename12 =
|
|
6799
|
+
const basename12 = path33.basename(absPath);
|
|
6734
6800
|
const match = basename12.match(/^(STORY|EPIC|PROPOSAL|CR|BUG)-\d+(-\d+)?/i);
|
|
6735
6801
|
if (match) {
|
|
6736
6802
|
return match[0].toUpperCase();
|
|
@@ -6836,7 +6902,7 @@ ${body}`;
|
|
|
6836
6902
|
// src/commands/upgrade.ts
|
|
6837
6903
|
init_cjs_shims();
|
|
6838
6904
|
var fsp = __toESM(require("fs/promises"), 1);
|
|
6839
|
-
var
|
|
6905
|
+
var path34 = __toESM(require("path"), 1);
|
|
6840
6906
|
|
|
6841
6907
|
// src/lib/claude-md-surgery.ts
|
|
6842
6908
|
init_cjs_shims();
|
|
@@ -6986,7 +7052,7 @@ async function writeAtomic2(filePath, content) {
|
|
|
6986
7052
|
await fsp.rename(tmpPath, filePath);
|
|
6987
7053
|
}
|
|
6988
7054
|
async function updateSnapshotEntry(projectRoot, filePath, newSha) {
|
|
6989
|
-
const snapshotPath =
|
|
7055
|
+
const snapshotPath = path34.join(projectRoot, ".cleargate", ".install-manifest.json");
|
|
6990
7056
|
let snapshot;
|
|
6991
7057
|
try {
|
|
6992
7058
|
const raw = await fsp.readFile(snapshotPath, "utf-8");
|
|
@@ -7003,17 +7069,17 @@ async function updateSnapshotEntry(projectRoot, filePath, newSha) {
|
|
|
7003
7069
|
await writeAtomic2(snapshotPath, JSON.stringify(updated, null, 2) + "\n");
|
|
7004
7070
|
}
|
|
7005
7071
|
function isClaudeMd(filePath) {
|
|
7006
|
-
return
|
|
7072
|
+
return path34.basename(filePath) === "CLAUDE.md";
|
|
7007
7073
|
}
|
|
7008
7074
|
function isSettingsJson(filePath) {
|
|
7009
|
-
return
|
|
7075
|
+
return path34.basename(filePath) === "settings.json" && filePath.includes(".claude");
|
|
7010
7076
|
}
|
|
7011
7077
|
async function applyAlwaysOverwrite(entry, projectRoot, packageRoot, stdout) {
|
|
7012
|
-
const targetPath =
|
|
7013
|
-
const sourcePath =
|
|
7078
|
+
const targetPath = path34.join(projectRoot, entry.path);
|
|
7079
|
+
const sourcePath = path34.join(packageRoot, entry.path);
|
|
7014
7080
|
try {
|
|
7015
7081
|
const pkgContent = await fsp.readFile(sourcePath, "utf-8");
|
|
7016
|
-
await fsp.mkdir(
|
|
7082
|
+
await fsp.mkdir(path34.dirname(targetPath), { recursive: true });
|
|
7017
7083
|
await writeAtomic2(targetPath, pkgContent);
|
|
7018
7084
|
await updateSnapshotEntry(projectRoot, entry.path, entry.sha256);
|
|
7019
7085
|
stdout(`[always] overwritten: ${entry.path}`);
|
|
@@ -7023,8 +7089,8 @@ async function applyAlwaysOverwrite(entry, projectRoot, packageRoot, stdout) {
|
|
|
7023
7089
|
}
|
|
7024
7090
|
async function applyMerge3Way(entry, projectRoot, packageRoot, installSha, currentSha, flags, opts) {
|
|
7025
7091
|
const { stdout, stderr, promptMergeChoiceFn, openInEditorFn, stdin } = opts;
|
|
7026
|
-
const targetPath =
|
|
7027
|
-
const sourcePath =
|
|
7092
|
+
const targetPath = path34.join(projectRoot, entry.path);
|
|
7093
|
+
const sourcePath = path34.join(packageRoot, entry.path);
|
|
7028
7094
|
let ours = "";
|
|
7029
7095
|
let theirs = "";
|
|
7030
7096
|
try {
|
|
@@ -7087,7 +7153,7 @@ async function applyMerge3Way(entry, projectRoot, packageRoot, installSha, curre
|
|
|
7087
7153
|
mergedContent = theirs;
|
|
7088
7154
|
}
|
|
7089
7155
|
}
|
|
7090
|
-
await fsp.mkdir(
|
|
7156
|
+
await fsp.mkdir(path34.dirname(targetPath), { recursive: true });
|
|
7091
7157
|
await writeAtomic2(targetPath, mergedContent);
|
|
7092
7158
|
const newSha2 = hashNormalized(mergedContent);
|
|
7093
7159
|
await updateSnapshotEntry(projectRoot, entry.path, newSha2);
|
|
@@ -7099,7 +7165,7 @@ async function applyMerge3Way(entry, projectRoot, packageRoot, installSha, curre
|
|
|
7099
7165
|
${ours}=======
|
|
7100
7166
|
${theirs}>>>>>>> theirs (upstream)
|
|
7101
7167
|
`;
|
|
7102
|
-
await fsp.mkdir(
|
|
7168
|
+
await fsp.mkdir(path34.dirname(mergeFilePath), { recursive: true });
|
|
7103
7169
|
await writeAtomic2(mergeFilePath, conflictContent);
|
|
7104
7170
|
try {
|
|
7105
7171
|
const result = await openInEditorFn(mergeFilePath);
|
|
@@ -7234,9 +7300,9 @@ async function upgradeHandler(flags, cli) {
|
|
|
7234
7300
|
|
|
7235
7301
|
// src/commands/uninstall.ts
|
|
7236
7302
|
init_cjs_shims();
|
|
7237
|
-
var
|
|
7303
|
+
var fs32 = __toESM(require("fs"), 1);
|
|
7238
7304
|
var fsp2 = __toESM(require("fs/promises"), 1);
|
|
7239
|
-
var
|
|
7305
|
+
var path35 = __toESM(require("path"), 1);
|
|
7240
7306
|
var import_node_child_process13 = require("child_process");
|
|
7241
7307
|
var USER_ARTIFACT_TIERS = ["user-artifact"];
|
|
7242
7308
|
var FRAMEWORK_TIERS = ["protocol", "template", "agent", "hook", "skill", "cli-config", "derived"];
|
|
@@ -7262,10 +7328,10 @@ function shouldPreserve(entry, preserveSet, removeSet) {
|
|
|
7262
7328
|
return false;
|
|
7263
7329
|
}
|
|
7264
7330
|
function resolveProjectName(target) {
|
|
7265
|
-
const pkgPath =
|
|
7266
|
-
if (
|
|
7331
|
+
const pkgPath = path35.join(target, "package.json");
|
|
7332
|
+
if (fs32.existsSync(pkgPath)) {
|
|
7267
7333
|
try {
|
|
7268
|
-
const raw =
|
|
7334
|
+
const raw = fs32.readFileSync(pkgPath, "utf-8");
|
|
7269
7335
|
const parsed = JSON.parse(raw);
|
|
7270
7336
|
if (parsed.name && typeof parsed.name === "string") {
|
|
7271
7337
|
return parsed.name;
|
|
@@ -7273,7 +7339,7 @@ function resolveProjectName(target) {
|
|
|
7273
7339
|
} catch {
|
|
7274
7340
|
}
|
|
7275
7341
|
}
|
|
7276
|
-
return
|
|
7342
|
+
return path35.basename(target);
|
|
7277
7343
|
}
|
|
7278
7344
|
function detectUncommittedChanges(target, manifestPaths, gitRunner) {
|
|
7279
7345
|
const run = gitRunner ?? ((args) => {
|
|
@@ -7302,8 +7368,8 @@ function detectUncommittedChanges(target, manifestPaths, gitRunner) {
|
|
|
7302
7368
|
return changedFiles.filter((f) => manifestSet.has(f));
|
|
7303
7369
|
}
|
|
7304
7370
|
async function removeFromPackageJson(target, dryRun) {
|
|
7305
|
-
const pkgPath =
|
|
7306
|
-
if (!
|
|
7371
|
+
const pkgPath = path35.join(target, "package.json");
|
|
7372
|
+
if (!fs32.existsSync(pkgPath)) return false;
|
|
7307
7373
|
let raw;
|
|
7308
7374
|
try {
|
|
7309
7375
|
raw = await fsp2.readFile(pkgPath, "utf-8");
|
|
@@ -7344,7 +7410,7 @@ async function removeFile(filePath) {
|
|
|
7344
7410
|
}
|
|
7345
7411
|
async function removeDir(dirPath) {
|
|
7346
7412
|
try {
|
|
7347
|
-
|
|
7413
|
+
fs32.rmSync(dirPath, { recursive: true, force: true });
|
|
7348
7414
|
} catch {
|
|
7349
7415
|
}
|
|
7350
7416
|
}
|
|
@@ -7364,12 +7430,12 @@ async function uninstallHandler(opts) {
|
|
|
7364
7430
|
for (const t of FRAMEWORK_TIERS) removeSet.add(t);
|
|
7365
7431
|
for (const u of USER_ARTIFACT_TIERS) removeSet.add(u);
|
|
7366
7432
|
}
|
|
7367
|
-
const target = opts.path ?
|
|
7368
|
-
const cleargateDir =
|
|
7369
|
-
const manifestPath =
|
|
7370
|
-
const uninstalledPath =
|
|
7371
|
-
if (!
|
|
7372
|
-
if (
|
|
7433
|
+
const target = opts.path ? path35.resolve(opts.path) : cwd;
|
|
7434
|
+
const cleargateDir = path35.join(target, ".cleargate");
|
|
7435
|
+
const manifestPath = path35.join(cleargateDir, ".install-manifest.json");
|
|
7436
|
+
const uninstalledPath = path35.join(cleargateDir, ".uninstalled");
|
|
7437
|
+
if (!fs32.existsSync(manifestPath)) {
|
|
7438
|
+
if (fs32.existsSync(uninstalledPath)) {
|
|
7373
7439
|
stdout("already uninstalled");
|
|
7374
7440
|
exit(0);
|
|
7375
7441
|
return;
|
|
@@ -7378,7 +7444,7 @@ async function uninstallHandler(opts) {
|
|
|
7378
7444
|
exit(0);
|
|
7379
7445
|
return;
|
|
7380
7446
|
}
|
|
7381
|
-
if (
|
|
7447
|
+
if (fs32.existsSync(uninstalledPath) && !fs32.existsSync(manifestPath)) {
|
|
7382
7448
|
stdout("already uninstalled");
|
|
7383
7449
|
exit(0);
|
|
7384
7450
|
return;
|
|
@@ -7400,10 +7466,10 @@ async function uninstallHandler(opts) {
|
|
|
7400
7466
|
return;
|
|
7401
7467
|
}
|
|
7402
7468
|
}
|
|
7403
|
-
const claudeMdPath =
|
|
7469
|
+
const claudeMdPath = path35.join(target, "CLAUDE.md");
|
|
7404
7470
|
let claudeMdContent = null;
|
|
7405
|
-
if (
|
|
7406
|
-
claudeMdContent =
|
|
7471
|
+
if (fs32.existsSync(claudeMdPath)) {
|
|
7472
|
+
claudeMdContent = fs32.readFileSync(claudeMdPath, "utf-8");
|
|
7407
7473
|
if (!claudeMdContent.includes(CLEARGATE_START)) {
|
|
7408
7474
|
stderr("CLAUDE.md is missing <!-- CLEARGATE:START --> marker");
|
|
7409
7475
|
exit(1);
|
|
@@ -7419,8 +7485,8 @@ async function uninstallHandler(opts) {
|
|
|
7419
7485
|
const toPreserve = [];
|
|
7420
7486
|
const toSkip = [];
|
|
7421
7487
|
for (const entry of snapshot.files) {
|
|
7422
|
-
const filePath =
|
|
7423
|
-
if (!
|
|
7488
|
+
const filePath = path35.join(target, entry.path);
|
|
7489
|
+
if (!fs32.existsSync(filePath)) {
|
|
7424
7490
|
toSkip.push(entry);
|
|
7425
7491
|
continue;
|
|
7426
7492
|
}
|
|
@@ -7477,7 +7543,7 @@ async function uninstallHandler(opts) {
|
|
|
7477
7543
|
const removedPaths = [];
|
|
7478
7544
|
const preservedPaths = [];
|
|
7479
7545
|
for (const entry of toRemove) {
|
|
7480
|
-
const filePath =
|
|
7546
|
+
const filePath = path35.join(target, entry.path);
|
|
7481
7547
|
await removeFile(filePath);
|
|
7482
7548
|
removedPaths.push(entry.path);
|
|
7483
7549
|
}
|
|
@@ -7493,10 +7559,10 @@ async function uninstallHandler(opts) {
|
|
|
7493
7559
|
stderr(`Warning: could not strip CLAUDE.md block: ${err.message}`);
|
|
7494
7560
|
}
|
|
7495
7561
|
}
|
|
7496
|
-
const settingsPath =
|
|
7497
|
-
if (
|
|
7562
|
+
const settingsPath = path35.join(target, ".claude", "settings.json");
|
|
7563
|
+
if (fs32.existsSync(settingsPath)) {
|
|
7498
7564
|
try {
|
|
7499
|
-
const raw =
|
|
7565
|
+
const raw = fs32.readFileSync(settingsPath, "utf-8");
|
|
7500
7566
|
const settings = JSON.parse(raw);
|
|
7501
7567
|
const cleaned = removeClearGateHooks(settings);
|
|
7502
7568
|
await writeAtomic3(settingsPath, JSON.stringify(cleaned, null, 2) + "\n");
|
|
@@ -7511,7 +7577,7 @@ async function uninstallHandler(opts) {
|
|
|
7511
7577
|
stdout("Removed @cleargate/cli from package.json. Run `npm install` to update package-lock.json.");
|
|
7512
7578
|
}
|
|
7513
7579
|
await removeFile(manifestPath);
|
|
7514
|
-
await removeFile(
|
|
7580
|
+
await removeFile(path35.join(cleargateDir, ".drift-state.json"));
|
|
7515
7581
|
const marker = {
|
|
7516
7582
|
uninstalled_at: now().toISOString(),
|
|
7517
7583
|
prior_version: snapshot.cleargate_version,
|
|
@@ -7536,30 +7602,30 @@ async function uninstallHandler(opts) {
|
|
|
7536
7602
|
// src/commands/sync.ts
|
|
7537
7603
|
init_cjs_shims();
|
|
7538
7604
|
var fsPromises8 = __toESM(require("fs/promises"), 1);
|
|
7539
|
-
var
|
|
7605
|
+
var path43 = __toESM(require("path"), 1);
|
|
7540
7606
|
|
|
7541
7607
|
// src/lib/sync-log.ts
|
|
7542
7608
|
init_cjs_shims();
|
|
7543
|
-
var
|
|
7609
|
+
var fs33 = __toESM(require("fs"), 1);
|
|
7544
7610
|
var fsPromises2 = __toESM(require("fs/promises"), 1);
|
|
7545
|
-
var
|
|
7611
|
+
var path36 = __toESM(require("path"), 1);
|
|
7546
7612
|
function resolveActiveSprintDir(projectRoot, _opts) {
|
|
7547
|
-
const sprintRunsRoot =
|
|
7548
|
-
const offSprint =
|
|
7549
|
-
if (!
|
|
7550
|
-
|
|
7551
|
-
|
|
7613
|
+
const sprintRunsRoot = path36.join(projectRoot, ".cleargate", "sprint-runs");
|
|
7614
|
+
const offSprint = path36.join(sprintRunsRoot, "_off-sprint");
|
|
7615
|
+
if (!fs33.existsSync(sprintRunsRoot)) {
|
|
7616
|
+
fs33.mkdirSync(sprintRunsRoot, { recursive: true });
|
|
7617
|
+
fs33.mkdirSync(offSprint, { recursive: true });
|
|
7552
7618
|
return offSprint;
|
|
7553
7619
|
}
|
|
7554
|
-
const entries =
|
|
7620
|
+
const entries = fs33.readdirSync(sprintRunsRoot, { withFileTypes: true });
|
|
7555
7621
|
const sprintDirs = entries.filter((e) => e.isDirectory() && e.name !== "_off-sprint").map((e) => {
|
|
7556
|
-
const fullPath =
|
|
7557
|
-
const stat =
|
|
7622
|
+
const fullPath = path36.join(sprintRunsRoot, e.name);
|
|
7623
|
+
const stat = fs33.statSync(fullPath);
|
|
7558
7624
|
return { name: e.name, fullPath, mtimeMs: stat.mtimeMs };
|
|
7559
7625
|
}).sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
7560
7626
|
if (sprintDirs.length === 0) {
|
|
7561
|
-
if (!
|
|
7562
|
-
|
|
7627
|
+
if (!fs33.existsSync(offSprint)) {
|
|
7628
|
+
fs33.mkdirSync(offSprint, { recursive: true });
|
|
7563
7629
|
}
|
|
7564
7630
|
return offSprint;
|
|
7565
7631
|
}
|
|
@@ -7570,7 +7636,7 @@ function redactDetail(detail) {
|
|
|
7570
7636
|
return detail.replace(/eyJ[A-Za-z0-9._-]+/g, "[REDACTED]");
|
|
7571
7637
|
}
|
|
7572
7638
|
async function appendSyncLog(sprintRoot, entry) {
|
|
7573
|
-
const logPath =
|
|
7639
|
+
const logPath = path36.join(sprintRoot, "sync-log.jsonl");
|
|
7574
7640
|
await fsPromises2.mkdir(sprintRoot, { recursive: true });
|
|
7575
7641
|
const safeEntry = {
|
|
7576
7642
|
...entry,
|
|
@@ -7580,7 +7646,7 @@ async function appendSyncLog(sprintRoot, entry) {
|
|
|
7580
7646
|
await fsPromises2.appendFile(logPath, line, { encoding: "utf8" });
|
|
7581
7647
|
}
|
|
7582
7648
|
async function readSyncLog(sprintRoot, filters) {
|
|
7583
|
-
const logPath =
|
|
7649
|
+
const logPath = path36.join(sprintRoot, "sync-log.jsonl");
|
|
7584
7650
|
let raw;
|
|
7585
7651
|
try {
|
|
7586
7652
|
raw = await fsPromises2.readFile(logPath, "utf8");
|
|
@@ -7686,7 +7752,7 @@ function classify2(local, remote, since) {
|
|
|
7686
7752
|
init_cjs_shims();
|
|
7687
7753
|
var import_node_fs2 = require("fs");
|
|
7688
7754
|
var os6 = __toESM(require("os"), 1);
|
|
7689
|
-
var
|
|
7755
|
+
var path37 = __toESM(require("path"), 1);
|
|
7690
7756
|
function promptFourChoice(opts) {
|
|
7691
7757
|
const { stdin, stdout } = opts;
|
|
7692
7758
|
stdout("[k]eep mine / [t]ake theirs / [e]dit in $EDITOR / [a]bort: ");
|
|
@@ -7756,7 +7822,7 @@ async function promptThreeWayMerge(opts) {
|
|
|
7756
7822
|
case "a":
|
|
7757
7823
|
return { resolution: "aborted", body: local };
|
|
7758
7824
|
case "e": {
|
|
7759
|
-
const tmpFile =
|
|
7825
|
+
const tmpFile = path37.join(os6.tmpdir(), `cleargate-merge-${itemId}-${now()}.md`);
|
|
7760
7826
|
const markerContent = `<<<<<<< local
|
|
7761
7827
|
${local}
|
|
7762
7828
|
=======
|
|
@@ -7856,12 +7922,12 @@ init_config();
|
|
|
7856
7922
|
// src/lib/intake.ts
|
|
7857
7923
|
init_cjs_shims();
|
|
7858
7924
|
var fsPromises4 = __toESM(require("fs/promises"), 1);
|
|
7859
|
-
var
|
|
7925
|
+
var path39 = __toESM(require("path"), 1);
|
|
7860
7926
|
|
|
7861
7927
|
// src/lib/slug.ts
|
|
7862
7928
|
init_cjs_shims();
|
|
7863
7929
|
var fsPromises3 = __toESM(require("fs/promises"), 1);
|
|
7864
|
-
var
|
|
7930
|
+
var path38 = __toESM(require("path"), 1);
|
|
7865
7931
|
function slugify(title, max = 40) {
|
|
7866
7932
|
const normalized = title.normalize("NFKD").replace(new RegExp("\\p{M}", "gu"), "");
|
|
7867
7933
|
const lowered = normalized.toLowerCase();
|
|
@@ -7876,8 +7942,8 @@ function slugify(title, max = 40) {
|
|
|
7876
7942
|
var PROPOSAL_ID_RE = /^proposal_id:\s*"?PROP-(\d+)"?/m;
|
|
7877
7943
|
async function nextProposalId(projectRoot) {
|
|
7878
7944
|
const dirs = [
|
|
7879
|
-
|
|
7880
|
-
|
|
7945
|
+
path38.join(projectRoot, ".cleargate", "delivery", "pending-sync"),
|
|
7946
|
+
path38.join(projectRoot, ".cleargate", "delivery", "archive")
|
|
7881
7947
|
];
|
|
7882
7948
|
let maxN = 0;
|
|
7883
7949
|
for (const dir of dirs) {
|
|
@@ -7889,7 +7955,7 @@ async function nextProposalId(projectRoot) {
|
|
|
7889
7955
|
}
|
|
7890
7956
|
for (const entry of entries) {
|
|
7891
7957
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
7892
|
-
const fullPath =
|
|
7958
|
+
const fullPath = path38.join(dir, entry.name);
|
|
7893
7959
|
try {
|
|
7894
7960
|
const raw = await fsPromises3.readFile(fullPath, "utf8");
|
|
7895
7961
|
const fmEnd = extractFrontmatterBlock(raw);
|
|
@@ -7907,8 +7973,8 @@ async function nextProposalId(projectRoot) {
|
|
|
7907
7973
|
}
|
|
7908
7974
|
async function findByRemoteId(projectRoot, remoteId) {
|
|
7909
7975
|
const dirs = [
|
|
7910
|
-
|
|
7911
|
-
|
|
7976
|
+
path38.join(projectRoot, ".cleargate", "delivery", "pending-sync"),
|
|
7977
|
+
path38.join(projectRoot, ".cleargate", "delivery", "archive")
|
|
7912
7978
|
];
|
|
7913
7979
|
const escaped = remoteId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
7914
7980
|
const re = new RegExp(`^remote_id:\\s*"?${escaped}"?\\s*$`, "m");
|
|
@@ -7921,7 +7987,7 @@ async function findByRemoteId(projectRoot, remoteId) {
|
|
|
7921
7987
|
}
|
|
7922
7988
|
for (const entry of entries) {
|
|
7923
7989
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
7924
|
-
const fullPath =
|
|
7990
|
+
const fullPath = path38.join(dir, entry.name);
|
|
7925
7991
|
try {
|
|
7926
7992
|
const raw = await fsPromises3.readFile(fullPath, "utf8");
|
|
7927
7993
|
const fm = extractFrontmatterBlock(raw);
|
|
@@ -7958,7 +8024,7 @@ async function runIntakeBranch(opts) {
|
|
|
7958
8024
|
labelFilter = "cleargate:proposal",
|
|
7959
8025
|
now = () => (/* @__PURE__ */ new Date()).toISOString()
|
|
7960
8026
|
} = opts;
|
|
7961
|
-
const pendingSyncDir =
|
|
8027
|
+
const pendingSyncDir = path39.join(projectRoot, ".cleargate", "delivery", "pending-sync");
|
|
7962
8028
|
let remoteItems = [];
|
|
7963
8029
|
try {
|
|
7964
8030
|
remoteItems = await mcp.call(
|
|
@@ -7989,7 +8055,7 @@ async function runIntakeBranch(opts) {
|
|
|
7989
8055
|
const slug2 = slugify(item.title ?? "untitled");
|
|
7990
8056
|
const num2 = proposalId2.replace("PROP-", "");
|
|
7991
8057
|
const filename2 = `PROPOSAL-${num2}-remote-${slug2}.md`;
|
|
7992
|
-
const targetPath2 =
|
|
8058
|
+
const targetPath2 = path39.join(pendingSyncDir, filename2);
|
|
7993
8059
|
createdItems.push({
|
|
7994
8060
|
proposalId: proposalId2,
|
|
7995
8061
|
remoteId: item.remote_id,
|
|
@@ -8002,7 +8068,7 @@ async function runIntakeBranch(opts) {
|
|
|
8002
8068
|
const num = proposalId.replace("PROP-", "");
|
|
8003
8069
|
const slug = slugify(item.title ?? "untitled");
|
|
8004
8070
|
const filename = `PROPOSAL-${num}-remote-${slug}.md`;
|
|
8005
|
-
const targetPath =
|
|
8071
|
+
const targetPath = path39.join(pendingSyncDir, filename);
|
|
8006
8072
|
const nowTs = now();
|
|
8007
8073
|
const fm = {
|
|
8008
8074
|
proposal_id: proposalId,
|
|
@@ -8094,8 +8160,8 @@ path/to/new/file.ext - {Explanation of purpose}
|
|
|
8094
8160
|
}
|
|
8095
8161
|
async function hasAnyRemoteAuthored(projectRoot) {
|
|
8096
8162
|
const dirs = [
|
|
8097
|
-
|
|
8098
|
-
|
|
8163
|
+
path39.join(projectRoot, ".cleargate", "delivery", "pending-sync"),
|
|
8164
|
+
path39.join(projectRoot, ".cleargate", "delivery", "archive")
|
|
8099
8165
|
];
|
|
8100
8166
|
for (const dir of dirs) {
|
|
8101
8167
|
let entries;
|
|
@@ -8106,7 +8172,7 @@ async function hasAnyRemoteAuthored(projectRoot) {
|
|
|
8106
8172
|
}
|
|
8107
8173
|
for (const entry of entries) {
|
|
8108
8174
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
8109
|
-
const fullPath =
|
|
8175
|
+
const fullPath = path39.join(dir, entry.name);
|
|
8110
8176
|
try {
|
|
8111
8177
|
const raw = await fsPromises4.readFile(fullPath, "utf8");
|
|
8112
8178
|
const fmEnd = raw.indexOf("\n---", 4);
|
|
@@ -8124,9 +8190,9 @@ async function hasAnyRemoteAuthored(projectRoot) {
|
|
|
8124
8190
|
|
|
8125
8191
|
// src/lib/active-criteria.ts
|
|
8126
8192
|
init_cjs_shims();
|
|
8127
|
-
var
|
|
8193
|
+
var fs35 = __toESM(require("fs"), 1);
|
|
8128
8194
|
var fsPromises5 = __toESM(require("fs/promises"), 1);
|
|
8129
|
-
var
|
|
8195
|
+
var path40 = __toESM(require("path"), 1);
|
|
8130
8196
|
async function resolveActiveItems(projectRoot, localItems, nowFn = () => (/* @__PURE__ */ new Date()).toISOString()) {
|
|
8131
8197
|
const active = /* @__PURE__ */ new Set();
|
|
8132
8198
|
const now = Date.parse(nowFn());
|
|
@@ -8151,7 +8217,7 @@ async function resolveInSprintIds(projectRoot) {
|
|
|
8151
8217
|
const ids = /* @__PURE__ */ new Set();
|
|
8152
8218
|
try {
|
|
8153
8219
|
const sprintDir = resolveActiveSprintDir(projectRoot);
|
|
8154
|
-
const sprintId =
|
|
8220
|
+
const sprintId = path40.basename(sprintDir);
|
|
8155
8221
|
if (sprintId === "_off-sprint") return ids;
|
|
8156
8222
|
const sprintFile = await findSprintFile(projectRoot, sprintId);
|
|
8157
8223
|
if (!sprintFile) return ids;
|
|
@@ -8166,14 +8232,14 @@ async function resolveInSprintIds(projectRoot) {
|
|
|
8166
8232
|
return ids;
|
|
8167
8233
|
}
|
|
8168
8234
|
async function findSprintFile(projectRoot, sprintId) {
|
|
8169
|
-
const pendingSync =
|
|
8170
|
-
const archive =
|
|
8235
|
+
const pendingSync = path40.join(projectRoot, ".cleargate", "delivery", "pending-sync");
|
|
8236
|
+
const archive = path40.join(projectRoot, ".cleargate", "delivery", "archive");
|
|
8171
8237
|
for (const dir of [pendingSync, archive]) {
|
|
8172
8238
|
try {
|
|
8173
|
-
const entries =
|
|
8239
|
+
const entries = fs35.readdirSync(dir, { withFileTypes: true });
|
|
8174
8240
|
for (const entry of entries) {
|
|
8175
8241
|
if (entry.isFile() && entry.name.startsWith(sprintId) && entry.name.endsWith(".md")) {
|
|
8176
|
-
return
|
|
8242
|
+
return path40.join(dir, entry.name);
|
|
8177
8243
|
}
|
|
8178
8244
|
}
|
|
8179
8245
|
} catch {
|
|
@@ -8185,12 +8251,12 @@ async function findSprintFile(projectRoot, sprintId) {
|
|
|
8185
8251
|
// src/lib/comments-cache.ts
|
|
8186
8252
|
init_cjs_shims();
|
|
8187
8253
|
var fsPromises6 = __toESM(require("fs/promises"), 1);
|
|
8188
|
-
var
|
|
8254
|
+
var path41 = __toESM(require("path"), 1);
|
|
8189
8255
|
function cacheDir(projectRoot) {
|
|
8190
|
-
return
|
|
8256
|
+
return path41.join(projectRoot, ".cleargate", ".comments-cache");
|
|
8191
8257
|
}
|
|
8192
8258
|
function cachePath(projectRoot, remoteId) {
|
|
8193
|
-
return
|
|
8259
|
+
return path41.join(cacheDir(projectRoot), `${remoteId}.json`);
|
|
8194
8260
|
}
|
|
8195
8261
|
async function writeCommentCache(projectRoot, remoteId, comments) {
|
|
8196
8262
|
const dir = cacheDir(projectRoot);
|
|
@@ -8205,7 +8271,7 @@ async function writeCommentCache(projectRoot, remoteId, comments) {
|
|
|
8205
8271
|
// src/lib/wiki-comments-render.ts
|
|
8206
8272
|
init_cjs_shims();
|
|
8207
8273
|
var fsPromises7 = __toESM(require("fs/promises"), 1);
|
|
8208
|
-
var
|
|
8274
|
+
var path42 = __toESM(require("path"), 1);
|
|
8209
8275
|
var START = "<!-- cleargate:comments:start -->";
|
|
8210
8276
|
var END = "<!-- cleargate:comments:end -->";
|
|
8211
8277
|
function resolveBucket(fm) {
|
|
@@ -8250,7 +8316,7 @@ async function renderCommentsSection(opts) {
|
|
|
8250
8316
|
const bucket = resolveBucket(localItem.fm);
|
|
8251
8317
|
const primaryId = getPrimaryId(localItem.fm);
|
|
8252
8318
|
if (!bucket || !primaryId) return;
|
|
8253
|
-
const wikiPath =
|
|
8319
|
+
const wikiPath = path42.join(
|
|
8254
8320
|
projectRoot,
|
|
8255
8321
|
".cleargate",
|
|
8256
8322
|
"wiki",
|
|
@@ -8286,7 +8352,7 @@ async function renderCommentsSection(opts) {
|
|
|
8286
8352
|
await writeAtomic4(wikiPath, updated);
|
|
8287
8353
|
}
|
|
8288
8354
|
async function writeAtomic4(filePath, content) {
|
|
8289
|
-
await fsPromises7.mkdir(
|
|
8355
|
+
await fsPromises7.mkdir(path42.dirname(filePath), { recursive: true });
|
|
8290
8356
|
const tmpPath = `${filePath}.tmp.${Date.now()}`;
|
|
8291
8357
|
await fsPromises7.writeFile(tmpPath, content, "utf8");
|
|
8292
8358
|
await fsPromises7.rename(tmpPath, filePath);
|
|
@@ -8298,11 +8364,11 @@ async function syncCheckHandler(opts = {}) {
|
|
|
8298
8364
|
const env = opts.env ?? process.env;
|
|
8299
8365
|
const stdout = opts.stdout ?? ((s) => process.stdout.write(s));
|
|
8300
8366
|
const nowFn = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
8301
|
-
const markerPath =
|
|
8367
|
+
const markerPath = path43.join(projectRoot, ".cleargate", ".sync-marker.json");
|
|
8302
8368
|
const updateMarker = async (nowIso2) => {
|
|
8303
8369
|
try {
|
|
8304
8370
|
const content = JSON.stringify({ last_check: nowIso2 });
|
|
8305
|
-
await fsPromises8.mkdir(
|
|
8371
|
+
await fsPromises8.mkdir(path43.dirname(markerPath), { recursive: true });
|
|
8306
8372
|
const tmpPath = `${markerPath}.tmp.${Date.now()}`;
|
|
8307
8373
|
await fsPromises8.writeFile(tmpPath, content, "utf8");
|
|
8308
8374
|
await fsPromises8.rename(tmpPath, markerPath);
|
|
@@ -8385,7 +8451,7 @@ async function syncHandler(opts = {}) {
|
|
|
8385
8451
|
const nowFn = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
8386
8452
|
const identity = resolveIdentity(projectRoot);
|
|
8387
8453
|
const sprintRoot = resolveActiveSprintDir(projectRoot);
|
|
8388
|
-
const sprintId =
|
|
8454
|
+
const sprintId = path43.basename(sprintRoot);
|
|
8389
8455
|
let mcp;
|
|
8390
8456
|
if (opts.mcp) {
|
|
8391
8457
|
mcp = opts.mcp;
|
|
@@ -8446,7 +8512,7 @@ async function syncHandler(opts = {}) {
|
|
|
8446
8512
|
exit(2);
|
|
8447
8513
|
return;
|
|
8448
8514
|
}
|
|
8449
|
-
const wikiMetaPath =
|
|
8515
|
+
const wikiMetaPath = path43.join(projectRoot, ".cleargate", "wiki", "meta.json");
|
|
8450
8516
|
let lastRemoteSync = "1970-01-01T00:00:00.000Z";
|
|
8451
8517
|
try {
|
|
8452
8518
|
const metaRaw = await fsPromises8.readFile(wikiMetaPath, "utf8");
|
|
@@ -8687,7 +8753,7 @@ async function syncHandler(opts = {}) {
|
|
|
8687
8753
|
};
|
|
8688
8754
|
await appendSyncLog(sprintRoot, entry);
|
|
8689
8755
|
}
|
|
8690
|
-
const conflictsFile =
|
|
8756
|
+
const conflictsFile = path43.join(projectRoot, ".cleargate", ".conflicts.json");
|
|
8691
8757
|
const conflictsContent = {
|
|
8692
8758
|
generated_at: nowFn(),
|
|
8693
8759
|
sprint_id: sprintId,
|
|
@@ -8695,7 +8761,7 @@ async function syncHandler(opts = {}) {
|
|
|
8695
8761
|
};
|
|
8696
8762
|
await writeAtomic5(conflictsFile, JSON.stringify(conflictsContent, null, 2) + "\n");
|
|
8697
8763
|
try {
|
|
8698
|
-
await fsPromises8.mkdir(
|
|
8764
|
+
await fsPromises8.mkdir(path43.dirname(wikiMetaPath), { recursive: true });
|
|
8699
8765
|
let meta = {};
|
|
8700
8766
|
try {
|
|
8701
8767
|
const raw = await fsPromises8.readFile(wikiMetaPath, "utf8");
|
|
@@ -8736,13 +8802,13 @@ async function applyPull(item, localPath, fm, actorEmail, nowFn) {
|
|
|
8736
8802
|
await writeAtomic5(localPath, newContent);
|
|
8737
8803
|
}
|
|
8738
8804
|
async function writeAtomic5(filePath, content) {
|
|
8739
|
-
await fsPromises8.mkdir(
|
|
8805
|
+
await fsPromises8.mkdir(path43.dirname(filePath), { recursive: true });
|
|
8740
8806
|
const tmpPath = `${filePath}.tmp.${Date.now()}`;
|
|
8741
8807
|
await fsPromises8.writeFile(tmpPath, content, "utf8");
|
|
8742
8808
|
await fsPromises8.rename(tmpPath, filePath);
|
|
8743
8809
|
}
|
|
8744
8810
|
async function scanLocalItems(projectRoot) {
|
|
8745
|
-
const pendingSync =
|
|
8811
|
+
const pendingSync = path43.join(projectRoot, ".cleargate", "delivery", "pending-sync");
|
|
8746
8812
|
const results = [];
|
|
8747
8813
|
let entries;
|
|
8748
8814
|
try {
|
|
@@ -8752,7 +8818,7 @@ async function scanLocalItems(projectRoot) {
|
|
|
8752
8818
|
}
|
|
8753
8819
|
for (const entry of entries) {
|
|
8754
8820
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
8755
|
-
const fullPath =
|
|
8821
|
+
const fullPath = path43.join(pendingSync, entry.name);
|
|
8756
8822
|
try {
|
|
8757
8823
|
const raw = await fsPromises8.readFile(fullPath, "utf8");
|
|
8758
8824
|
const { fm, body } = parseFrontmatter(raw);
|
|
@@ -8775,7 +8841,7 @@ function getItemId(fm) {
|
|
|
8775
8841
|
// src/commands/pull.ts
|
|
8776
8842
|
init_cjs_shims();
|
|
8777
8843
|
var fsPromises9 = __toESM(require("fs/promises"), 1);
|
|
8778
|
-
var
|
|
8844
|
+
var path44 = __toESM(require("path"), 1);
|
|
8779
8845
|
init_acquire();
|
|
8780
8846
|
init_config();
|
|
8781
8847
|
async function pullHandler(idOrRemoteId, opts = {}) {
|
|
@@ -8890,7 +8956,7 @@ async function pullHandler(idOrRemoteId, opts = {}) {
|
|
|
8890
8956
|
result: "ok"
|
|
8891
8957
|
};
|
|
8892
8958
|
await appendSyncLog(sprintRoot, entry);
|
|
8893
|
-
stdout(`pull: ${remoteId} applied to ${
|
|
8959
|
+
stdout(`pull: ${remoteId} applied to ${path44.relative(projectRoot, localPath)}
|
|
8894
8960
|
`);
|
|
8895
8961
|
if (opts.comments) {
|
|
8896
8962
|
const comments = await mcp.call(
|
|
@@ -8913,7 +8979,7 @@ async function resolveRemoteId(idOrRemoteId, projectRoot) {
|
|
|
8913
8979
|
if (/^[A-Z]+-\d+/.test(idOrRemoteId)) {
|
|
8914
8980
|
return idOrRemoteId;
|
|
8915
8981
|
}
|
|
8916
|
-
const pendingSync =
|
|
8982
|
+
const pendingSync = path44.join(projectRoot, ".cleargate", "delivery", "pending-sync");
|
|
8917
8983
|
let entries;
|
|
8918
8984
|
try {
|
|
8919
8985
|
entries = await fsPromises9.readdir(pendingSync, { withFileTypes: true });
|
|
@@ -8923,7 +8989,7 @@ async function resolveRemoteId(idOrRemoteId, projectRoot) {
|
|
|
8923
8989
|
for (const entry of entries) {
|
|
8924
8990
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
8925
8991
|
try {
|
|
8926
|
-
const raw = await fsPromises9.readFile(
|
|
8992
|
+
const raw = await fsPromises9.readFile(path44.join(pendingSync, entry.name), "utf8");
|
|
8927
8993
|
const { fm } = parseFrontmatter(raw);
|
|
8928
8994
|
for (const key of ["story_id", "epic_id", "proposal_id", "cr_id", "bug_id"]) {
|
|
8929
8995
|
if (fm[key] === idOrRemoteId && typeof fm["remote_id"] === "string") {
|
|
@@ -8936,7 +9002,7 @@ async function resolveRemoteId(idOrRemoteId, projectRoot) {
|
|
|
8936
9002
|
return null;
|
|
8937
9003
|
}
|
|
8938
9004
|
async function findLocalFile(remoteId, projectRoot) {
|
|
8939
|
-
const pendingSync =
|
|
9005
|
+
const pendingSync = path44.join(projectRoot, ".cleargate", "delivery", "pending-sync");
|
|
8940
9006
|
let entries;
|
|
8941
9007
|
try {
|
|
8942
9008
|
entries = await fsPromises9.readdir(pendingSync, { withFileTypes: true });
|
|
@@ -8945,7 +9011,7 @@ async function findLocalFile(remoteId, projectRoot) {
|
|
|
8945
9011
|
}
|
|
8946
9012
|
for (const entry of entries) {
|
|
8947
9013
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
8948
|
-
const fullPath =
|
|
9014
|
+
const fullPath = path44.join(pendingSync, entry.name);
|
|
8949
9015
|
try {
|
|
8950
9016
|
const raw = await fsPromises9.readFile(fullPath, "utf8");
|
|
8951
9017
|
const { fm } = parseFrontmatter(raw);
|
|
@@ -8956,7 +9022,7 @@ async function findLocalFile(remoteId, projectRoot) {
|
|
|
8956
9022
|
return null;
|
|
8957
9023
|
}
|
|
8958
9024
|
async function writeAtomic6(filePath, content) {
|
|
8959
|
-
await fsPromises9.mkdir(
|
|
9025
|
+
await fsPromises9.mkdir(path44.dirname(filePath), { recursive: true });
|
|
8960
9026
|
const tmpPath = `${filePath}.tmp.${Date.now()}`;
|
|
8961
9027
|
await fsPromises9.writeFile(tmpPath, content, "utf8");
|
|
8962
9028
|
await fsPromises9.rename(tmpPath, filePath);
|
|
@@ -8972,7 +9038,7 @@ function getItemId2(fm) {
|
|
|
8972
9038
|
// src/commands/push.ts
|
|
8973
9039
|
init_cjs_shims();
|
|
8974
9040
|
var fsPromises10 = __toESM(require("fs/promises"), 1);
|
|
8975
|
-
var
|
|
9041
|
+
var path45 = __toESM(require("path"), 1);
|
|
8976
9042
|
init_acquire();
|
|
8977
9043
|
init_config();
|
|
8978
9044
|
async function pushHandler(fileOrId, opts = {}) {
|
|
@@ -9048,7 +9114,7 @@ async function pushHandler(fileOrId, opts = {}) {
|
|
|
9048
9114
|
}
|
|
9049
9115
|
async function handlePush(filePath, ctx) {
|
|
9050
9116
|
const { projectRoot, identity, sprintRoot, nowFn, resolveMcp, stdout, stderr, exit } = ctx;
|
|
9051
|
-
const resolvedPath =
|
|
9117
|
+
const resolvedPath = path45.isAbsolute(filePath) ? filePath : path45.resolve(projectRoot, filePath);
|
|
9052
9118
|
let rawContent;
|
|
9053
9119
|
try {
|
|
9054
9120
|
rawContent = await fsPromises10.readFile(resolvedPath, "utf8");
|
|
@@ -9174,8 +9240,8 @@ async function handleRevert(idOrRemoteId, ctx) {
|
|
|
9174
9240
|
void localPath;
|
|
9175
9241
|
}
|
|
9176
9242
|
async function resolveLocalItem(idOrRemoteId, projectRoot) {
|
|
9177
|
-
const pendingSync =
|
|
9178
|
-
const archive =
|
|
9243
|
+
const pendingSync = path45.join(projectRoot, ".cleargate", "delivery", "pending-sync");
|
|
9244
|
+
const archive = path45.join(projectRoot, ".cleargate", "delivery", "archive");
|
|
9179
9245
|
for (const dir of [pendingSync, archive]) {
|
|
9180
9246
|
let entries;
|
|
9181
9247
|
try {
|
|
@@ -9185,7 +9251,7 @@ async function resolveLocalItem(idOrRemoteId, projectRoot) {
|
|
|
9185
9251
|
}
|
|
9186
9252
|
for (const entry of entries) {
|
|
9187
9253
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
9188
|
-
const fullPath =
|
|
9254
|
+
const fullPath = path45.join(dir, entry.name);
|
|
9189
9255
|
try {
|
|
9190
9256
|
const raw = await fsPromises10.readFile(fullPath, "utf8");
|
|
9191
9257
|
const { fm } = parseFrontmatter(raw);
|
|
@@ -9204,7 +9270,7 @@ async function resolveLocalItem(idOrRemoteId, projectRoot) {
|
|
|
9204
9270
|
return null;
|
|
9205
9271
|
}
|
|
9206
9272
|
async function writeAtomic7(filePath, content) {
|
|
9207
|
-
await fsPromises10.mkdir(
|
|
9273
|
+
await fsPromises10.mkdir(path45.dirname(filePath), { recursive: true });
|
|
9208
9274
|
const tmpPath = `${filePath}.tmp.${Date.now()}`;
|
|
9209
9275
|
await fsPromises10.writeFile(tmpPath, content, "utf8");
|
|
9210
9276
|
await fsPromises10.rename(tmpPath, filePath);
|
|
@@ -9233,7 +9299,7 @@ function getItemType(fm) {
|
|
|
9233
9299
|
// src/commands/conflicts.ts
|
|
9234
9300
|
init_cjs_shims();
|
|
9235
9301
|
var fsPromises11 = __toESM(require("fs/promises"), 1);
|
|
9236
|
-
var
|
|
9302
|
+
var path46 = __toESM(require("path"), 1);
|
|
9237
9303
|
init_acquire();
|
|
9238
9304
|
init_config();
|
|
9239
9305
|
var RESOLUTION_HINTS = {
|
|
@@ -9271,7 +9337,7 @@ async function conflictsHandler(opts = {}) {
|
|
|
9271
9337
|
}
|
|
9272
9338
|
}
|
|
9273
9339
|
}
|
|
9274
|
-
const conflictsFile =
|
|
9340
|
+
const conflictsFile = path46.join(projectRoot, ".cleargate", ".conflicts.json");
|
|
9275
9341
|
let data;
|
|
9276
9342
|
try {
|
|
9277
9343
|
const raw = await fsPromises11.readFile(conflictsFile, "utf8");
|
|
@@ -9359,8 +9425,8 @@ function formatEntry(entry) {
|
|
|
9359
9425
|
|
|
9360
9426
|
// src/commands/admin-login.ts
|
|
9361
9427
|
init_cjs_shims();
|
|
9362
|
-
var
|
|
9363
|
-
var
|
|
9428
|
+
var fs36 = __toESM(require("fs"), 1);
|
|
9429
|
+
var path47 = __toESM(require("path"), 1);
|
|
9364
9430
|
var os7 = __toESM(require("os"), 1);
|
|
9365
9431
|
var DEFAULT_MCP_URL = "http://localhost:3000";
|
|
9366
9432
|
function resolveMcpUrl(mcpUrlFlag, env) {
|
|
@@ -9369,14 +9435,14 @@ function resolveMcpUrl(mcpUrlFlag, env) {
|
|
|
9369
9435
|
function resolveAuthFilePath(opts) {
|
|
9370
9436
|
if (opts.authFilePath) return opts.authFilePath;
|
|
9371
9437
|
const homedirFn = opts.homedir ?? os7.homedir;
|
|
9372
|
-
return
|
|
9438
|
+
return path47.join(homedirFn(), ".cleargate", "admin-auth.json");
|
|
9373
9439
|
}
|
|
9374
9440
|
function writeAdminAuth(filePath, token) {
|
|
9375
|
-
const dir =
|
|
9376
|
-
|
|
9441
|
+
const dir = path47.dirname(filePath);
|
|
9442
|
+
fs36.mkdirSync(dir, { recursive: true });
|
|
9377
9443
|
const payload = JSON.stringify({ version: 1, token }, null, 2);
|
|
9378
|
-
|
|
9379
|
-
|
|
9444
|
+
fs36.writeFileSync(filePath, payload, { encoding: "utf8", mode: 384 });
|
|
9445
|
+
fs36.chmodSync(filePath, 384);
|
|
9380
9446
|
}
|
|
9381
9447
|
async function adminLoginHandler(opts = {}) {
|
|
9382
9448
|
const fetchFn = opts.fetch ?? globalThis.fetch;
|
|
@@ -9486,8 +9552,8 @@ async function adminLoginHandler(opts = {}) {
|
|
|
9486
9552
|
|
|
9487
9553
|
// src/commands/hotfix.ts
|
|
9488
9554
|
init_cjs_shims();
|
|
9489
|
-
var
|
|
9490
|
-
var
|
|
9555
|
+
var fs37 = __toESM(require("fs"), 1);
|
|
9556
|
+
var path48 = __toESM(require("path"), 1);
|
|
9491
9557
|
function defaultExit4(code) {
|
|
9492
9558
|
return process.exit(code);
|
|
9493
9559
|
}
|
|
@@ -9497,7 +9563,7 @@ function maxHotfixId(pendingDir) {
|
|
|
9497
9563
|
let max = 0;
|
|
9498
9564
|
let entries;
|
|
9499
9565
|
try {
|
|
9500
|
-
entries =
|
|
9566
|
+
entries = fs37.readdirSync(pendingDir);
|
|
9501
9567
|
} catch {
|
|
9502
9568
|
return 0;
|
|
9503
9569
|
}
|
|
@@ -9511,13 +9577,13 @@ function maxHotfixId(pendingDir) {
|
|
|
9511
9577
|
return max;
|
|
9512
9578
|
}
|
|
9513
9579
|
function countActiveHotfixes(repoRoot) {
|
|
9514
|
-
const pendingDir =
|
|
9515
|
-
const archiveDir =
|
|
9580
|
+
const pendingDir = path48.join(repoRoot, ".cleargate", "delivery", "pending-sync");
|
|
9581
|
+
const archiveDir = path48.join(repoRoot, ".cleargate", "delivery", "archive");
|
|
9516
9582
|
const sevenDaysAgo = Date.now() - 7 * 24 * 60 * 60 * 1e3;
|
|
9517
9583
|
let count = 0;
|
|
9518
9584
|
let pendingEntries = [];
|
|
9519
9585
|
try {
|
|
9520
|
-
pendingEntries =
|
|
9586
|
+
pendingEntries = fs37.readdirSync(pendingDir);
|
|
9521
9587
|
} catch {
|
|
9522
9588
|
}
|
|
9523
9589
|
for (const entry of pendingEntries) {
|
|
@@ -9525,13 +9591,13 @@ function countActiveHotfixes(repoRoot) {
|
|
|
9525
9591
|
}
|
|
9526
9592
|
let archiveEntries = [];
|
|
9527
9593
|
try {
|
|
9528
|
-
archiveEntries =
|
|
9594
|
+
archiveEntries = fs37.readdirSync(archiveDir);
|
|
9529
9595
|
} catch {
|
|
9530
9596
|
}
|
|
9531
9597
|
for (const entry of archiveEntries) {
|
|
9532
9598
|
if (entry.startsWith("HOTFIX-") && entry.endsWith(".md")) {
|
|
9533
9599
|
try {
|
|
9534
|
-
const stat =
|
|
9600
|
+
const stat = fs37.statSync(path48.join(archiveDir, entry));
|
|
9535
9601
|
if (stat.mtimeMs >= sevenDaysAgo) count++;
|
|
9536
9602
|
} catch {
|
|
9537
9603
|
}
|
|
@@ -9540,7 +9606,7 @@ function countActiveHotfixes(repoRoot) {
|
|
|
9540
9606
|
return count;
|
|
9541
9607
|
}
|
|
9542
9608
|
function resolveTemplatePath(repoRoot) {
|
|
9543
|
-
return
|
|
9609
|
+
return path48.join(repoRoot, ".cleargate", "templates", "hotfix.md");
|
|
9544
9610
|
}
|
|
9545
9611
|
function hotfixNewHandler(opts, cli) {
|
|
9546
9612
|
const stdoutFn = cli?.stdout ?? ((s) => process.stdout.write(s + "\n"));
|
|
@@ -9559,14 +9625,14 @@ function hotfixNewHandler(opts, cli) {
|
|
|
9559
9625
|
);
|
|
9560
9626
|
return exitFn(1);
|
|
9561
9627
|
}
|
|
9562
|
-
const pendingDir =
|
|
9628
|
+
const pendingDir = path48.join(repoRoot, ".cleargate", "delivery", "pending-sync");
|
|
9563
9629
|
const maxId = maxHotfixId(pendingDir);
|
|
9564
9630
|
const nextId = maxId + 1;
|
|
9565
9631
|
const idStr = `HOTFIX-${String(nextId).padStart(3, "0")}`;
|
|
9566
9632
|
const templatePath = resolveTemplatePath(repoRoot);
|
|
9567
9633
|
let templateContent;
|
|
9568
9634
|
try {
|
|
9569
|
-
templateContent =
|
|
9635
|
+
templateContent = fs37.readFileSync(templatePath, "utf8");
|
|
9570
9636
|
} catch {
|
|
9571
9637
|
stderrFn(`[cleargate hotfix new] template not found: ${templatePath}`);
|
|
9572
9638
|
return exitFn(2);
|
|
@@ -9574,10 +9640,10 @@ function hotfixNewHandler(opts, cli) {
|
|
|
9574
9640
|
const content = templateContent.replace(/\{ID\}/g, idStr).replace(/\{SLUG\}/g, opts.slug).replace(/\{ISO\}/g, now);
|
|
9575
9641
|
const fileSlug = opts.slug.replace(/-/g, "_");
|
|
9576
9642
|
const fileName = `${idStr}_${fileSlug}.md`;
|
|
9577
|
-
const outPath =
|
|
9643
|
+
const outPath = path48.join(pendingDir, fileName);
|
|
9578
9644
|
try {
|
|
9579
|
-
|
|
9580
|
-
|
|
9645
|
+
fs37.mkdirSync(pendingDir, { recursive: true });
|
|
9646
|
+
fs37.writeFileSync(outPath, content, "utf8");
|
|
9581
9647
|
} catch (err) {
|
|
9582
9648
|
const msg = err instanceof Error ? err.message : String(err);
|
|
9583
9649
|
stderrFn(`[cleargate hotfix new] write failed: ${msg}`);
|