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