codebase-snapshot 0.9.5 → 0.9.7
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/CHANGELOG.md +80 -0
- package/README.md +4 -1
- package/dist/bin.js +471 -205
- package/dist/{chunk-4AHHSNGG.js → chunk-DPK6ZTOG.js} +33 -7
- package/dist/{chunk-B336MMNC.js → chunk-QABL7NRC.js} +445 -224
- package/dist/cli.cjs +574 -312
- package/dist/cli.d.cts +7 -1
- package/dist/cli.d.ts +7 -1
- package/dist/cli.js +16 -2
- package/dist/index.cjs +453 -217
- package/dist/index.d.cts +68 -2
- package/dist/index.d.ts +68 -2
- package/dist/index.js +1 -1
- package/dist/mcp.cjs +31 -4
- package/dist/mcp.d.cts +14 -1
- package/dist/mcp.d.ts +14 -1
- package/dist/mcp.js +2 -2
- package/docs/cli.md +19 -2
- package/docs/mcp.md +3 -3
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { spawnSync } from "child_process";
|
|
5
|
-
import * as
|
|
6
|
-
import * as
|
|
5
|
+
import * as fs17 from "fs";
|
|
6
|
+
import * as path18 from "path";
|
|
7
7
|
import { parseArgs } from "util";
|
|
8
8
|
|
|
9
9
|
// src/diff.ts
|
|
@@ -4346,7 +4346,7 @@ function gitignorePath(cwd, filePath) {
|
|
|
4346
4346
|
|
|
4347
4347
|
// src/version.ts
|
|
4348
4348
|
var GENERATOR_NAME = "codebase-snapshot";
|
|
4349
|
-
var GENERATOR_VERSION = "0.9.
|
|
4349
|
+
var GENERATOR_VERSION = "0.9.7";
|
|
4350
4350
|
var LEGACY_GENERATOR_NAMES = ["@ankurhalder/codebase-snapshot"];
|
|
4351
4351
|
function isOurGeneratorName(name) {
|
|
4352
4352
|
if (typeof name !== "string") return false;
|
|
@@ -4509,6 +4509,57 @@ function toggleIndex(selected, index) {
|
|
|
4509
4509
|
else next.add(index);
|
|
4510
4510
|
return next;
|
|
4511
4511
|
}
|
|
4512
|
+
function isWideCodePoint(code) {
|
|
4513
|
+
return code >= 4352 && code <= 4447 || // Hangul Jamo
|
|
4514
|
+
code >= 11904 && code <= 12350 || // CJK radicals, Kangxi, CJK symbols
|
|
4515
|
+
code >= 12353 && code <= 13311 || // kana through CJK compatibility
|
|
4516
|
+
code >= 13312 && code <= 19903 || // CJK extension A
|
|
4517
|
+
code >= 19968 && code <= 40959 || // CJK unified ideographs
|
|
4518
|
+
code >= 40960 && code <= 42191 || // Yi
|
|
4519
|
+
code >= 44032 && code <= 55203 || // Hangul syllables
|
|
4520
|
+
code >= 63744 && code <= 64255 || // CJK compatibility ideographs
|
|
4521
|
+
code >= 65072 && code <= 65135 || // CJK compatibility forms
|
|
4522
|
+
code >= 65280 && code <= 65376 || // fullwidth forms
|
|
4523
|
+
code >= 65504 && code <= 65510 || code >= 127744 && code <= 129535 || // emoji and pictographs
|
|
4524
|
+
code >= 131072 && code <= 262141;
|
|
4525
|
+
}
|
|
4526
|
+
var ANSI_PATTERN = /\u001b\[[0-9;?]*[A-Za-z]/g;
|
|
4527
|
+
function displayWidth(line) {
|
|
4528
|
+
let width = 0;
|
|
4529
|
+
for (const character of line.replace(ANSI_PATTERN, "")) {
|
|
4530
|
+
width += isWideCodePoint(character.codePointAt(0) ?? 0) ? 2 : 1;
|
|
4531
|
+
}
|
|
4532
|
+
return width;
|
|
4533
|
+
}
|
|
4534
|
+
function wrapOneLine(line, limit) {
|
|
4535
|
+
if (displayWidth(line) <= limit) return [line];
|
|
4536
|
+
const indent = /^[ \t]*/.exec(line)?.[0] ?? "";
|
|
4537
|
+
const continuation = `${indent} `;
|
|
4538
|
+
const rows = [];
|
|
4539
|
+
let prefix = indent;
|
|
4540
|
+
let rest = line.slice(indent.length).trimEnd();
|
|
4541
|
+
while (rest.length > 0) {
|
|
4542
|
+
const room = limit - displayWidth(prefix);
|
|
4543
|
+
if (room <= 0 || displayWidth(rest) <= room) {
|
|
4544
|
+
rows.push(prefix + rest);
|
|
4545
|
+
break;
|
|
4546
|
+
}
|
|
4547
|
+
let cut = room;
|
|
4548
|
+
const space = rest.slice(0, room).lastIndexOf(" ");
|
|
4549
|
+
if (space > 0) cut = space;
|
|
4550
|
+
const taken = rest.slice(0, cut).trimEnd();
|
|
4551
|
+
rows.push(`${prefix}${taken.length > 0 ? taken : rest.slice(0, cut)}`);
|
|
4552
|
+
rest = rest.slice(cut).trimStart();
|
|
4553
|
+
prefix = continuation;
|
|
4554
|
+
}
|
|
4555
|
+
return rows;
|
|
4556
|
+
}
|
|
4557
|
+
function wrapLines(text2, limit) {
|
|
4558
|
+
const width = Math.max(8, Math.floor(limit));
|
|
4559
|
+
const rows = [];
|
|
4560
|
+
for (const line of text2.split("\n")) rows.push(...wrapOneLine(line, width));
|
|
4561
|
+
return rows;
|
|
4562
|
+
}
|
|
4512
4563
|
function marker(active) {
|
|
4513
4564
|
return active ? "\u276F" : " ";
|
|
4514
4565
|
}
|
|
@@ -4586,14 +4637,27 @@ var Frame = class {
|
|
|
4586
4637
|
drawn = 0;
|
|
4587
4638
|
draw(text2) {
|
|
4588
4639
|
this.erase();
|
|
4589
|
-
this.
|
|
4640
|
+
const rows = wrapLines(text2, this.width());
|
|
4641
|
+
this.output.write(`${rows.join("\n")}
|
|
4590
4642
|
`);
|
|
4591
|
-
this.drawn =
|
|
4643
|
+
this.drawn = rows.length;
|
|
4592
4644
|
}
|
|
4593
4645
|
erase() {
|
|
4594
4646
|
if (this.drawn > 0) this.output.write(`\x1B[${this.drawn}A\x1B[0J`);
|
|
4595
4647
|
this.drawn = 0;
|
|
4596
4648
|
}
|
|
4649
|
+
/**
|
|
4650
|
+
* The usable width, in cells.
|
|
4651
|
+
*
|
|
4652
|
+
* Two cells of slack: an ambiguous-width glyph (`◆`, `❯`, `←`) renders two cells in some fonts
|
|
4653
|
+
* and one in others, and a row that lands exactly on the last column is the one case a terminal
|
|
4654
|
+
* may or may not wrap. Below that, a line cannot reach the edge to begin with.
|
|
4655
|
+
*/
|
|
4656
|
+
width() {
|
|
4657
|
+
const columns = this.output.columns;
|
|
4658
|
+
if (typeof columns !== "number" || !Number.isFinite(columns) || columns <= 0) return 80;
|
|
4659
|
+
return Math.max(8, Math.floor(columns) - 2);
|
|
4660
|
+
}
|
|
4597
4661
|
};
|
|
4598
4662
|
function isCancel(key) {
|
|
4599
4663
|
return key.ctrl === true && key.name === "c" || key.ctrl === true && key.name === "d";
|
|
@@ -5289,9 +5353,9 @@ function hasPriorSnapshot(cwd, outDir) {
|
|
|
5289
5353
|
// src/index.ts
|
|
5290
5354
|
import * as crypto3 from "crypto";
|
|
5291
5355
|
import { AsyncLocalStorage } from "async_hooks";
|
|
5292
|
-
import * as
|
|
5293
|
-
import * as
|
|
5294
|
-
import * as
|
|
5356
|
+
import * as fs12 from "fs";
|
|
5357
|
+
import * as os2 from "os";
|
|
5358
|
+
import * as path13 from "path";
|
|
5295
5359
|
|
|
5296
5360
|
// src/ast/profiles/shared.ts
|
|
5297
5361
|
var MAX_SUMMARY_CHARS = 1e6;
|
|
@@ -5515,9 +5579,9 @@ var JAVA_METHOD = /^\s+(?:public|private|protected)\s+(?:static\s+)?(?:final\s+)
|
|
|
5515
5579
|
var jvmProfile = {
|
|
5516
5580
|
id: "jvm",
|
|
5517
5581
|
extensions: [".java", ".kt", ".kts", ".scala"],
|
|
5518
|
-
summarize(
|
|
5582
|
+
summarize(path19, content) {
|
|
5519
5583
|
const summary = emptySummary();
|
|
5520
|
-
const lower =
|
|
5584
|
+
const lower = path19.toLowerCase();
|
|
5521
5585
|
const isJava = lower.endsWith(".java");
|
|
5522
5586
|
const isKotlin = lower.endsWith(".kt") || lower.endsWith(".kts");
|
|
5523
5587
|
eachLine(content, (line) => {
|
|
@@ -5973,16 +6037,16 @@ function summarizeTypeScriptSource(relativePath, content) {
|
|
|
5973
6037
|
// src/ast/profiles/sfc.ts
|
|
5974
6038
|
var SCRIPT_BLOCK = /<script[^>]*>([\s\S]*?)<\/script>/gi;
|
|
5975
6039
|
var ASTRO_FRONTMATTER = /^\uFEFF?---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*(?=\r?\n|$)/;
|
|
5976
|
-
function componentName(
|
|
5977
|
-
const base =
|
|
6040
|
+
function componentName(path19) {
|
|
6041
|
+
const base = path19.split("/").pop() ?? path19;
|
|
5978
6042
|
return base.replace(/\.[^.]+$/, "");
|
|
5979
6043
|
}
|
|
5980
6044
|
var sfcProfile = {
|
|
5981
6045
|
id: "sfc",
|
|
5982
6046
|
extensions: [".vue", ".svelte", ".astro"],
|
|
5983
|
-
summarize(
|
|
6047
|
+
summarize(path19, content) {
|
|
5984
6048
|
let script = "";
|
|
5985
|
-
if (
|
|
6049
|
+
if (path19.toLowerCase().endsWith(".astro")) {
|
|
5986
6050
|
const frontmatter = ASTRO_FRONTMATTER.exec(content);
|
|
5987
6051
|
if (frontmatter) script += `${frontmatter[1]}
|
|
5988
6052
|
`;
|
|
@@ -5991,7 +6055,7 @@ var sfcProfile = {
|
|
|
5991
6055
|
script += `${match[1]}
|
|
5992
6056
|
`;
|
|
5993
6057
|
}
|
|
5994
|
-
const name = componentName(
|
|
6058
|
+
const name = componentName(path19);
|
|
5995
6059
|
if (!script.trim()) {
|
|
5996
6060
|
const summary2 = emptySummary();
|
|
5997
6061
|
if (name) {
|
|
@@ -6025,15 +6089,15 @@ for (const profile of LANGUAGE_PROFILES) {
|
|
|
6025
6089
|
BY_EXTENSION.set(extension.toLowerCase(), profile);
|
|
6026
6090
|
}
|
|
6027
6091
|
}
|
|
6028
|
-
function profileForPath(
|
|
6029
|
-
const lower =
|
|
6092
|
+
function profileForPath(path19) {
|
|
6093
|
+
const lower = path19.toLowerCase();
|
|
6030
6094
|
const dot = lower.lastIndexOf(".");
|
|
6031
6095
|
if (dot === -1) return null;
|
|
6032
6096
|
return BY_EXTENSION.get(lower.slice(dot)) ?? null;
|
|
6033
6097
|
}
|
|
6034
|
-
function summarizeWithProfile(
|
|
6035
|
-
const profile = profileForPath(
|
|
6036
|
-
return profile ? profile.summarize(
|
|
6098
|
+
function summarizeWithProfile(path19, content) {
|
|
6099
|
+
const profile = profileForPath(path19);
|
|
6100
|
+
return profile ? profile.summarize(path19, content) : null;
|
|
6037
6101
|
}
|
|
6038
6102
|
|
|
6039
6103
|
// src/ast/structured.ts
|
|
@@ -6882,6 +6946,168 @@ function generateProjectTree(filePaths) {
|
|
|
6882
6946
|
return ["# Project Tree", "", `${fence}text`, ...lines, fence, ""].join("\n");
|
|
6883
6947
|
}
|
|
6884
6948
|
|
|
6949
|
+
// src/update-check.ts
|
|
6950
|
+
import * as fs11 from "fs";
|
|
6951
|
+
import * as https from "https";
|
|
6952
|
+
import * as os from "os";
|
|
6953
|
+
import * as path12 from "path";
|
|
6954
|
+
var PACKAGE_LATEST_URL = "https://registry.npmjs.org/codebase-snapshot/latest";
|
|
6955
|
+
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
6956
|
+
var FAILURE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
6957
|
+
var REQUEST_TIMEOUT_MS = 1200;
|
|
6958
|
+
function versionParts(value) {
|
|
6959
|
+
const core = value.trim().replace(/^v/i, "").split("-")[0];
|
|
6960
|
+
return core.split(".").map((part) => {
|
|
6961
|
+
const parsed = Number(part);
|
|
6962
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
6963
|
+
});
|
|
6964
|
+
}
|
|
6965
|
+
function isNewer(candidate, current) {
|
|
6966
|
+
const left = versionParts(candidate);
|
|
6967
|
+
const right = versionParts(current);
|
|
6968
|
+
const length = Math.max(left.length, right.length);
|
|
6969
|
+
for (let index = 0; index < length; index += 1) {
|
|
6970
|
+
const a = left[index] ?? 0;
|
|
6971
|
+
const b = right[index] ?? 0;
|
|
6972
|
+
if (a !== b) return a > b;
|
|
6973
|
+
}
|
|
6974
|
+
return false;
|
|
6975
|
+
}
|
|
6976
|
+
function cacheDirectory(env = process.env, platform = process.platform) {
|
|
6977
|
+
if (platform === "win32") {
|
|
6978
|
+
const base2 = env.LOCALAPPDATA ?? path12.join(os.homedir(), "AppData", "Local");
|
|
6979
|
+
return path12.join(base2, "codebase-snapshot");
|
|
6980
|
+
}
|
|
6981
|
+
if (platform === "darwin") {
|
|
6982
|
+
return path12.join(os.homedir(), "Library", "Caches", "codebase-snapshot");
|
|
6983
|
+
}
|
|
6984
|
+
const base = env.XDG_CACHE_HOME ?? path12.join(os.homedir(), ".cache");
|
|
6985
|
+
return path12.join(base, "codebase-snapshot");
|
|
6986
|
+
}
|
|
6987
|
+
function cacheFilePath(env = process.env) {
|
|
6988
|
+
return path12.join(cacheDirectory(env), "update-check.json");
|
|
6989
|
+
}
|
|
6990
|
+
function shouldCheck(argv, env) {
|
|
6991
|
+
const disabled = ["1", "true", "yes"].includes(
|
|
6992
|
+
String(env.CODEBASE_SNAPSHOT_NO_UPDATE_CHECK ?? "").toLowerCase()
|
|
6993
|
+
);
|
|
6994
|
+
if (disabled) return false;
|
|
6995
|
+
if (env.CI !== void 0 && env.CI !== "" && env.CI !== "false" && env.CI !== "0") return false;
|
|
6996
|
+
const args = argv.map((argument) => argument.toLowerCase());
|
|
6997
|
+
if (args.includes("--no-update-check")) return false;
|
|
6998
|
+
for (const skip of ["mcp", "describe", "skill", "--version", "-v", "--help", "-h"]) {
|
|
6999
|
+
if (args.includes(skip)) return false;
|
|
7000
|
+
}
|
|
7001
|
+
if (args.some((argument) => argument.includes("json"))) return false;
|
|
7002
|
+
return true;
|
|
7003
|
+
}
|
|
7004
|
+
function readCache(file) {
|
|
7005
|
+
let raw;
|
|
7006
|
+
try {
|
|
7007
|
+
raw = fs11.readFileSync(file, "utf8");
|
|
7008
|
+
} catch {
|
|
7009
|
+
return null;
|
|
7010
|
+
}
|
|
7011
|
+
try {
|
|
7012
|
+
const parsed = JSON.parse(raw);
|
|
7013
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
7014
|
+
const candidate = parsed;
|
|
7015
|
+
if (typeof candidate.checkedAt !== "number") return null;
|
|
7016
|
+
const latest = typeof candidate.latest === "string" ? candidate.latest : null;
|
|
7017
|
+
return { checkedAt: candidate.checkedAt, latest };
|
|
7018
|
+
} catch {
|
|
7019
|
+
return null;
|
|
7020
|
+
}
|
|
7021
|
+
}
|
|
7022
|
+
function writeCache(file, entry) {
|
|
7023
|
+
try {
|
|
7024
|
+
fs11.mkdirSync(path12.dirname(file), { recursive: true });
|
|
7025
|
+
fs11.writeFileSync(file, `${JSON.stringify(entry)}
|
|
7026
|
+
`, "utf8");
|
|
7027
|
+
} catch {
|
|
7028
|
+
}
|
|
7029
|
+
}
|
|
7030
|
+
function fetchLatestVersion(url = PACKAGE_LATEST_URL, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
7031
|
+
return new Promise((resolve9) => {
|
|
7032
|
+
let settled = false;
|
|
7033
|
+
const finish = (value) => {
|
|
7034
|
+
if (settled) return;
|
|
7035
|
+
settled = true;
|
|
7036
|
+
resolve9(value);
|
|
7037
|
+
};
|
|
7038
|
+
const request = https.get(
|
|
7039
|
+
url,
|
|
7040
|
+
{
|
|
7041
|
+
headers: {
|
|
7042
|
+
accept: "application/json",
|
|
7043
|
+
"user-agent": "codebase-snapshot (update check)"
|
|
7044
|
+
}
|
|
7045
|
+
},
|
|
7046
|
+
(response) => {
|
|
7047
|
+
if (response.statusCode !== 200) {
|
|
7048
|
+
response.resume();
|
|
7049
|
+
finish(null);
|
|
7050
|
+
return;
|
|
7051
|
+
}
|
|
7052
|
+
let body = "";
|
|
7053
|
+
response.setEncoding("utf8");
|
|
7054
|
+
response.on("data", (chunk) => {
|
|
7055
|
+
body += chunk;
|
|
7056
|
+
if (body.length > 64 * 1024) {
|
|
7057
|
+
request.destroy();
|
|
7058
|
+
finish(null);
|
|
7059
|
+
}
|
|
7060
|
+
});
|
|
7061
|
+
response.on("end", () => {
|
|
7062
|
+
try {
|
|
7063
|
+
const parsed = JSON.parse(body);
|
|
7064
|
+
finish(typeof parsed.version === "string" ? parsed.version : null);
|
|
7065
|
+
} catch {
|
|
7066
|
+
finish(null);
|
|
7067
|
+
}
|
|
7068
|
+
});
|
|
7069
|
+
}
|
|
7070
|
+
);
|
|
7071
|
+
request.on("error", () => finish(null));
|
|
7072
|
+
request.setTimeout(timeoutMs, () => {
|
|
7073
|
+
request.destroy();
|
|
7074
|
+
finish(null);
|
|
7075
|
+
});
|
|
7076
|
+
});
|
|
7077
|
+
}
|
|
7078
|
+
async function checkForUpdate(options) {
|
|
7079
|
+
if (options.enabled === false) return null;
|
|
7080
|
+
const now = options.now ?? (() => Date.now());
|
|
7081
|
+
const fetchLatest = options.fetchLatest ?? fetchLatestVersion;
|
|
7082
|
+
let latest = null;
|
|
7083
|
+
try {
|
|
7084
|
+
const cached = readCache(options.cacheFile);
|
|
7085
|
+
let needsFetch = cached === null;
|
|
7086
|
+
if (cached) {
|
|
7087
|
+
const age = now() - cached.checkedAt;
|
|
7088
|
+
if (cached.latest === null) {
|
|
7089
|
+
needsFetch = age >= FAILURE_INTERVAL_MS;
|
|
7090
|
+
} else {
|
|
7091
|
+
latest = cached.latest;
|
|
7092
|
+
needsFetch = age >= CHECK_INTERVAL_MS;
|
|
7093
|
+
}
|
|
7094
|
+
}
|
|
7095
|
+
if (needsFetch) {
|
|
7096
|
+
const fetched = await fetchLatest(PACKAGE_LATEST_URL, REQUEST_TIMEOUT_MS);
|
|
7097
|
+
writeCache(options.cacheFile, { checkedAt: now(), latest: fetched });
|
|
7098
|
+
latest = fetched;
|
|
7099
|
+
}
|
|
7100
|
+
} catch {
|
|
7101
|
+
return null;
|
|
7102
|
+
}
|
|
7103
|
+
if (!latest) return null;
|
|
7104
|
+
return isNewer(latest, options.current) ? { current: options.current, latest } : null;
|
|
7105
|
+
}
|
|
7106
|
+
function formatUpdateNotice(notice) {
|
|
7107
|
+
return `\u2191 codebase-snapshot ${notice.latest} is available \u2014 you have ${notice.current}.
|
|
7108
|
+
npm install -g codebase-snapshot`;
|
|
7109
|
+
}
|
|
7110
|
+
|
|
6885
7111
|
// src/index.ts
|
|
6886
7112
|
var SCHEMA_VERSION = "3.0.0";
|
|
6887
7113
|
var SUMMARY_MAX_SOURCE_BYTES = 2 * 1024 * 1024;
|
|
@@ -6963,13 +7189,13 @@ function buildImportResolver(files) {
|
|
|
6963
7189
|
const relativePath = file.relativePath;
|
|
6964
7190
|
remember(byPath, relativePath, relativePath);
|
|
6965
7191
|
remember(byPathFolded, relativePath.toLowerCase(), relativePath);
|
|
6966
|
-
const extension =
|
|
7192
|
+
const extension = path13.posix.extname(relativePath);
|
|
6967
7193
|
if (!SOURCE_EXTENSIONS.has(extension)) continue;
|
|
6968
7194
|
const stem = relativePath.slice(0, -extension.length);
|
|
6969
7195
|
remember(byStem, stem, relativePath);
|
|
6970
7196
|
remember(byStemFolded, stem.toLowerCase(), relativePath);
|
|
6971
|
-
if (
|
|
6972
|
-
const directory =
|
|
7197
|
+
if (path13.posix.basename(stem) === "index") {
|
|
7198
|
+
const directory = path13.posix.dirname(stem);
|
|
6973
7199
|
remember(byStem, directory, relativePath);
|
|
6974
7200
|
remember(byStemFolded, directory.toLowerCase(), relativePath);
|
|
6975
7201
|
}
|
|
@@ -6978,8 +7204,8 @@ function buildImportResolver(files) {
|
|
|
6978
7204
|
}
|
|
6979
7205
|
function resolveSpecifier(fromFile, specifier, resolver) {
|
|
6980
7206
|
if (!RELATIVE_SPECIFIER.test(specifier)) return null;
|
|
6981
|
-
const joined =
|
|
6982
|
-
|
|
7207
|
+
const joined = path13.posix.normalize(
|
|
7208
|
+
path13.posix.join(path13.posix.dirname(fromFile), specifier)
|
|
6983
7209
|
);
|
|
6984
7210
|
const exact = (key) => resolver.byPath.get(key) ?? resolver.byPathFolded.get(key.toLowerCase()) ?? null;
|
|
6985
7211
|
const direct = exact(joined);
|
|
@@ -7072,11 +7298,11 @@ function verifyInMemory(files, parts, maxPartBytes) {
|
|
|
7072
7298
|
return errors;
|
|
7073
7299
|
}
|
|
7074
7300
|
function resolveSafeOutputDirs(config) {
|
|
7075
|
-
const cwd =
|
|
7076
|
-
const outDir =
|
|
7077
|
-
const tmpDir =
|
|
7078
|
-
const root =
|
|
7079
|
-
const contains = (parent, child) => child === parent || child.startsWith(`${parent}${
|
|
7301
|
+
const cwd = path13.resolve(config.cwd);
|
|
7302
|
+
const outDir = path13.resolve(cwd, config.outDir);
|
|
7303
|
+
const tmpDir = path13.resolve(cwd, config.tmpDir);
|
|
7304
|
+
const root = path13.parse(cwd).root;
|
|
7305
|
+
const contains = (parent, child) => child === parent || child.startsWith(`${parent}${path13.sep}`);
|
|
7080
7306
|
const reject = (reason) => {
|
|
7081
7307
|
throw new Error(
|
|
7082
7308
|
`Refusing to generate: ${reason}. outDir=${outDir} tmpDir=${tmpDir} cwd=${cwd}`
|
|
@@ -7132,14 +7358,14 @@ function listEntries(dir) {
|
|
|
7132
7358
|
const walk = (absolute, prefix) => {
|
|
7133
7359
|
let children;
|
|
7134
7360
|
try {
|
|
7135
|
-
children =
|
|
7361
|
+
children = fs12.readdirSync(absolute, { withFileTypes: true });
|
|
7136
7362
|
} catch {
|
|
7137
7363
|
return;
|
|
7138
7364
|
}
|
|
7139
7365
|
for (const child of children) {
|
|
7140
7366
|
const relative11 = prefix === "" ? child.name : `${prefix}/${child.name}`;
|
|
7141
7367
|
entries.push(relative11);
|
|
7142
|
-
if (child.isDirectory()) walk(
|
|
7368
|
+
if (child.isDirectory()) walk(path13.join(absolute, child.name), relative11);
|
|
7143
7369
|
}
|
|
7144
7370
|
};
|
|
7145
7371
|
walk(dir, "");
|
|
@@ -7156,28 +7382,28 @@ function markerPayload(dir, state, id, createdAt) {
|
|
|
7156
7382
|
pid: process.pid,
|
|
7157
7383
|
entries: listEntries(dir)
|
|
7158
7384
|
};
|
|
7159
|
-
const manifestPath =
|
|
7160
|
-
if (
|
|
7161
|
-
marker2.manifestSha256 = sha256Hex2(
|
|
7385
|
+
const manifestPath = path13.join(dir, "manifest.json");
|
|
7386
|
+
if (fs12.existsSync(manifestPath)) {
|
|
7387
|
+
marker2.manifestSha256 = sha256Hex2(fs12.readFileSync(manifestPath));
|
|
7162
7388
|
}
|
|
7163
7389
|
return marker2;
|
|
7164
7390
|
}
|
|
7165
7391
|
function writeOwnershipMarker(dir, marker2) {
|
|
7166
|
-
|
|
7167
|
-
|
|
7392
|
+
fs12.writeFileSync(
|
|
7393
|
+
path13.join(dir, OWNERSHIP_MARKER),
|
|
7168
7394
|
`${JSON.stringify(marker2, null, 2)}
|
|
7169
7395
|
`,
|
|
7170
7396
|
"utf8"
|
|
7171
7397
|
);
|
|
7172
7398
|
}
|
|
7173
7399
|
function readOwnershipMarker(dir) {
|
|
7174
|
-
const markerPath =
|
|
7175
|
-
if (!
|
|
7400
|
+
const markerPath = path13.join(dir, OWNERSHIP_MARKER);
|
|
7401
|
+
if (!fs12.existsSync(markerPath)) {
|
|
7176
7402
|
return { marker: null, problem: "it holds no .codebase-snapshot marker" };
|
|
7177
7403
|
}
|
|
7178
7404
|
let raw;
|
|
7179
7405
|
try {
|
|
7180
|
-
raw =
|
|
7406
|
+
raw = fs12.readFileSync(markerPath, "utf8");
|
|
7181
7407
|
} catch {
|
|
7182
7408
|
return { marker: null, problem: "its .codebase-snapshot marker could not be read" };
|
|
7183
7409
|
}
|
|
@@ -7245,11 +7471,11 @@ function assessSnapshotDirectory(dir) {
|
|
|
7245
7471
|
marker: marker2
|
|
7246
7472
|
};
|
|
7247
7473
|
}
|
|
7248
|
-
const manifestPath =
|
|
7249
|
-
if (
|
|
7474
|
+
const manifestPath = path13.join(dir, "manifest.json");
|
|
7475
|
+
if (fs12.existsSync(manifestPath)) {
|
|
7250
7476
|
let raw;
|
|
7251
7477
|
try {
|
|
7252
|
-
raw =
|
|
7478
|
+
raw = fs12.readFileSync(manifestPath, "utf8");
|
|
7253
7479
|
} catch {
|
|
7254
7480
|
return {
|
|
7255
7481
|
owned: false,
|
|
@@ -7331,13 +7557,13 @@ function containsNoFiles(dir) {
|
|
|
7331
7557
|
const current = pending.pop();
|
|
7332
7558
|
let entries;
|
|
7333
7559
|
try {
|
|
7334
|
-
entries =
|
|
7560
|
+
entries = fs12.readdirSync(current, { withFileTypes: true });
|
|
7335
7561
|
} catch {
|
|
7336
7562
|
return false;
|
|
7337
7563
|
}
|
|
7338
7564
|
for (const entry of entries) {
|
|
7339
7565
|
if (entry.isDirectory()) {
|
|
7340
|
-
pending.push(
|
|
7566
|
+
pending.push(path13.join(current, entry.name));
|
|
7341
7567
|
} else if (!IGNORABLE_ENTRIES.has(entry.name)) {
|
|
7342
7568
|
return false;
|
|
7343
7569
|
}
|
|
@@ -7346,13 +7572,13 @@ function containsNoFiles(dir) {
|
|
|
7346
7572
|
return true;
|
|
7347
7573
|
}
|
|
7348
7574
|
function assertReplaceable(target, label, secretRules) {
|
|
7349
|
-
if (!
|
|
7350
|
-
if (!
|
|
7575
|
+
if (!fs12.existsSync(target)) return;
|
|
7576
|
+
if (!fs12.statSync(target).isDirectory()) {
|
|
7351
7577
|
throw new Error(
|
|
7352
7578
|
`Refusing to write into ${label} ${target}: it exists but is not a directory.`
|
|
7353
7579
|
);
|
|
7354
7580
|
}
|
|
7355
|
-
const entries =
|
|
7581
|
+
const entries = fs12.readdirSync(target);
|
|
7356
7582
|
if (entries.length === 0) return;
|
|
7357
7583
|
if (entries.every((name) => IGNORABLE_ENTRIES.has(name))) return;
|
|
7358
7584
|
if (containsNoFiles(target)) return;
|
|
@@ -7372,18 +7598,18 @@ function assertReplaceable(target, label, secretRules) {
|
|
|
7372
7598
|
);
|
|
7373
7599
|
}
|
|
7374
7600
|
function assertStagingRootUsable(tmpDir, secretRules) {
|
|
7375
|
-
if (!
|
|
7376
|
-
if (!
|
|
7601
|
+
if (!fs12.existsSync(tmpDir)) return;
|
|
7602
|
+
if (!fs12.statSync(tmpDir).isDirectory()) {
|
|
7377
7603
|
throw new Error(`Refusing to write into tmpDir ${tmpDir}: it exists but is not a directory.`);
|
|
7378
7604
|
}
|
|
7379
|
-
const entries =
|
|
7605
|
+
const entries = fs12.readdirSync(tmpDir);
|
|
7380
7606
|
if (entries.length === 0) return;
|
|
7381
7607
|
const foreign = entries.filter((name) => {
|
|
7382
|
-
const full =
|
|
7608
|
+
const full = path13.join(tmpDir, name);
|
|
7383
7609
|
if (!name.startsWith(RUN_DIR_PREFIX)) return true;
|
|
7384
7610
|
let stats;
|
|
7385
7611
|
try {
|
|
7386
|
-
stats =
|
|
7612
|
+
stats = fs12.statSync(full);
|
|
7387
7613
|
} catch {
|
|
7388
7614
|
return true;
|
|
7389
7615
|
}
|
|
@@ -7391,7 +7617,7 @@ function assertStagingRootUsable(tmpDir, secretRules) {
|
|
|
7391
7617
|
const { marker: marker2 } = readOwnershipMarker(full);
|
|
7392
7618
|
if (marker2) return false;
|
|
7393
7619
|
try {
|
|
7394
|
-
return
|
|
7620
|
+
return fs12.readdirSync(full).length > 0;
|
|
7395
7621
|
} catch {
|
|
7396
7622
|
return true;
|
|
7397
7623
|
}
|
|
@@ -7406,13 +7632,13 @@ function assertStagingRootUsable(tmpDir, secretRules) {
|
|
|
7406
7632
|
function cleanStaleRunDirs(tmpDir) {
|
|
7407
7633
|
let entries;
|
|
7408
7634
|
try {
|
|
7409
|
-
entries =
|
|
7635
|
+
entries = fs12.readdirSync(tmpDir);
|
|
7410
7636
|
} catch {
|
|
7411
7637
|
return;
|
|
7412
7638
|
}
|
|
7413
7639
|
for (const name of entries) {
|
|
7414
7640
|
if (!name.startsWith(RUN_DIR_PREFIX)) continue;
|
|
7415
|
-
const full =
|
|
7641
|
+
const full = path13.join(tmpDir, name);
|
|
7416
7642
|
const { marker: marker2 } = readOwnershipMarker(full);
|
|
7417
7643
|
if (marker2) {
|
|
7418
7644
|
const created = Date.parse(marker2.createdAt);
|
|
@@ -7420,33 +7646,33 @@ function cleanStaleRunDirs(tmpDir) {
|
|
|
7420
7646
|
const abandoned = typeof marker2.pid === "number" && !isProcessAlive(marker2.pid);
|
|
7421
7647
|
if (aged || abandoned) {
|
|
7422
7648
|
try {
|
|
7423
|
-
|
|
7649
|
+
fs12.rmSync(full, { recursive: true, force: true });
|
|
7424
7650
|
} catch {
|
|
7425
7651
|
}
|
|
7426
7652
|
}
|
|
7427
7653
|
continue;
|
|
7428
7654
|
}
|
|
7429
7655
|
try {
|
|
7430
|
-
if (
|
|
7656
|
+
if (fs12.readdirSync(full).length === 0) fs12.rmSync(full, { recursive: true, force: true });
|
|
7431
7657
|
} catch {
|
|
7432
7658
|
}
|
|
7433
7659
|
}
|
|
7434
7660
|
}
|
|
7435
7661
|
function removeIfEmpty(dir) {
|
|
7436
7662
|
try {
|
|
7437
|
-
if (
|
|
7663
|
+
if (fs12.readdirSync(dir).length === 0) fs12.rmSync(dir, { recursive: true, force: true });
|
|
7438
7664
|
} catch {
|
|
7439
7665
|
}
|
|
7440
7666
|
}
|
|
7441
7667
|
function ensureParentDirectory(target, label) {
|
|
7442
|
-
const parent =
|
|
7443
|
-
if (
|
|
7668
|
+
const parent = path13.dirname(target);
|
|
7669
|
+
if (fs12.existsSync(parent) && !fs12.statSync(parent).isDirectory()) {
|
|
7444
7670
|
throw new Error(
|
|
7445
7671
|
`Refusing to create ${label} ${target}: ${parent} exists but is not a directory.`
|
|
7446
7672
|
);
|
|
7447
7673
|
}
|
|
7448
7674
|
try {
|
|
7449
|
-
|
|
7675
|
+
fs12.mkdirSync(parent, { recursive: true });
|
|
7450
7676
|
} catch (err) {
|
|
7451
7677
|
throw new Error(
|
|
7452
7678
|
`Refusing to create ${label} ${target}: its parent ${parent} could not be created (${err.message}).`
|
|
@@ -7455,7 +7681,7 @@ function ensureParentDirectory(target, label) {
|
|
|
7455
7681
|
}
|
|
7456
7682
|
function acquireRunLock(stagingRoot) {
|
|
7457
7683
|
const key = lockKeyFor(stagingRoot);
|
|
7458
|
-
const lockPath =
|
|
7684
|
+
const lockPath = path13.join(os2.tmpdir(), `codebase-snapshot-${key}.lock`);
|
|
7459
7685
|
const payload = `${JSON.stringify({
|
|
7460
7686
|
pid: process.pid,
|
|
7461
7687
|
stagingRoot,
|
|
@@ -7464,22 +7690,22 @@ function acquireRunLock(stagingRoot) {
|
|
|
7464
7690
|
`;
|
|
7465
7691
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
7466
7692
|
try {
|
|
7467
|
-
const handle =
|
|
7693
|
+
const handle = fs12.openSync(lockPath, "wx");
|
|
7468
7694
|
try {
|
|
7469
|
-
|
|
7695
|
+
fs12.writeSync(handle, payload);
|
|
7470
7696
|
} finally {
|
|
7471
|
-
|
|
7697
|
+
fs12.closeSync(handle);
|
|
7472
7698
|
}
|
|
7473
7699
|
return () => {
|
|
7474
7700
|
try {
|
|
7475
|
-
|
|
7701
|
+
fs12.rmSync(lockPath, { force: true });
|
|
7476
7702
|
} catch {
|
|
7477
7703
|
}
|
|
7478
7704
|
};
|
|
7479
7705
|
} catch (err) {
|
|
7480
7706
|
if (err.code !== "EEXIST") throw err;
|
|
7481
7707
|
if (attempt === 0 && lockOwnerIsGone(lockPath)) {
|
|
7482
|
-
|
|
7708
|
+
fs12.rmSync(lockPath, { force: true });
|
|
7483
7709
|
continue;
|
|
7484
7710
|
}
|
|
7485
7711
|
throw new Error(
|
|
@@ -7492,7 +7718,7 @@ function acquireRunLock(stagingRoot) {
|
|
|
7492
7718
|
function lockKeyFor(stagingRoot) {
|
|
7493
7719
|
let key = stagingRoot;
|
|
7494
7720
|
try {
|
|
7495
|
-
key =
|
|
7721
|
+
key = fs12.realpathSync(stagingRoot);
|
|
7496
7722
|
} catch {
|
|
7497
7723
|
}
|
|
7498
7724
|
if (process.platform === "win32" || process.platform === "darwin") key = key.toLowerCase();
|
|
@@ -7501,7 +7727,7 @@ function lockKeyFor(stagingRoot) {
|
|
|
7501
7727
|
function lockOwnerIsGone(lockPath) {
|
|
7502
7728
|
let raw;
|
|
7503
7729
|
try {
|
|
7504
|
-
raw =
|
|
7730
|
+
raw = fs12.readFileSync(lockPath, "utf8");
|
|
7505
7731
|
} catch {
|
|
7506
7732
|
return false;
|
|
7507
7733
|
}
|
|
@@ -7521,12 +7747,12 @@ function lockOwnerIsGone(lockPath) {
|
|
|
7521
7747
|
}
|
|
7522
7748
|
function writeManifestAtomically(manifestPath, manifest) {
|
|
7523
7749
|
const tempPath = `${manifestPath}.${process.pid}.tmp`;
|
|
7524
|
-
|
|
7750
|
+
fs12.writeFileSync(tempPath, JSON.stringify(manifest, null, 2), "utf8");
|
|
7525
7751
|
try {
|
|
7526
|
-
|
|
7752
|
+
fs12.renameSync(tempPath, manifestPath);
|
|
7527
7753
|
} catch (err) {
|
|
7528
7754
|
try {
|
|
7529
|
-
|
|
7755
|
+
fs12.rmSync(tempPath, { force: true });
|
|
7530
7756
|
} catch {
|
|
7531
7757
|
}
|
|
7532
7758
|
throw err;
|
|
@@ -7549,31 +7775,31 @@ function swapIntoPlace(stagingDir, outDir) {
|
|
|
7549
7775
|
}
|
|
7550
7776
|
function swapOnce(stagingDir, outDir) {
|
|
7551
7777
|
const backupDir = `${outDir}.backup-${process.pid}-${Date.now()}`;
|
|
7552
|
-
const hadPrevious =
|
|
7553
|
-
if (hadPrevious)
|
|
7778
|
+
const hadPrevious = fs12.existsSync(outDir);
|
|
7779
|
+
if (hadPrevious) fs12.renameSync(outDir, backupDir);
|
|
7554
7780
|
try {
|
|
7555
7781
|
try {
|
|
7556
|
-
|
|
7782
|
+
fs12.renameSync(stagingDir, outDir);
|
|
7557
7783
|
} catch (err) {
|
|
7558
7784
|
if (err.code !== "EXDEV") throw err;
|
|
7559
7785
|
const partialDir = `${outDir}.partial-${process.pid}-${Date.now()}`;
|
|
7560
7786
|
try {
|
|
7561
|
-
|
|
7562
|
-
|
|
7563
|
-
|
|
7787
|
+
fs12.cpSync(stagingDir, partialDir, { recursive: true });
|
|
7788
|
+
fs12.renameSync(partialDir, outDir);
|
|
7789
|
+
fs12.rmSync(stagingDir, { recursive: true, force: true });
|
|
7564
7790
|
} catch (copyErr) {
|
|
7565
7791
|
try {
|
|
7566
|
-
|
|
7792
|
+
fs12.rmSync(partialDir, { recursive: true, force: true });
|
|
7567
7793
|
} catch {
|
|
7568
7794
|
}
|
|
7569
7795
|
throw copyErr;
|
|
7570
7796
|
}
|
|
7571
7797
|
}
|
|
7572
|
-
if (hadPrevious)
|
|
7798
|
+
if (hadPrevious) fs12.rmSync(backupDir, { recursive: true, force: true });
|
|
7573
7799
|
} catch (err) {
|
|
7574
|
-
if (hadPrevious &&
|
|
7575
|
-
if (
|
|
7576
|
-
|
|
7800
|
+
if (hadPrevious && fs12.existsSync(backupDir)) {
|
|
7801
|
+
if (fs12.existsSync(outDir)) fs12.rmSync(outDir, { recursive: true, force: true });
|
|
7802
|
+
fs12.renameSync(backupDir, outDir);
|
|
7577
7803
|
}
|
|
7578
7804
|
throw err;
|
|
7579
7805
|
}
|
|
@@ -7668,7 +7894,7 @@ function generateSnapshot(configInput, options) {
|
|
|
7668
7894
|
// first run cannot have marked them yet, so the name alone keeps a run from recording its own
|
|
7669
7895
|
// output — but only at these exact paths, not at every directory that shares the name.
|
|
7670
7896
|
toolOwnedPaths: [config.outDir, config.tmpDir].map(
|
|
7671
|
-
(directory) =>
|
|
7897
|
+
(directory) => path13.relative(config.cwd, path13.resolve(config.cwd, directory)).replace(/\\/g, "/")
|
|
7672
7898
|
).filter((relative11) => relative11.length > 0 && !relative11.startsWith(".."))
|
|
7673
7899
|
});
|
|
7674
7900
|
const files = [];
|
|
@@ -7689,12 +7915,12 @@ function generateSnapshot(configInput, options) {
|
|
|
7689
7915
|
let totalSizeBytes = 0;
|
|
7690
7916
|
let largeFileCount = 0;
|
|
7691
7917
|
for (const relativePath of collection.files) {
|
|
7692
|
-
const absolutePath =
|
|
7918
|
+
const absolutePath = path13.join(config.cwd, relativePath);
|
|
7693
7919
|
let stats;
|
|
7694
7920
|
try {
|
|
7695
|
-
stats =
|
|
7921
|
+
stats = fs12.statSync(absolutePath);
|
|
7696
7922
|
if (!stats.isFile()) {
|
|
7697
|
-
const nestedRepository = stats.isDirectory() &&
|
|
7923
|
+
const nestedRepository = stats.isDirectory() && fs12.existsSync(path13.join(absolutePath, ".git"));
|
|
7698
7924
|
skipped.push(
|
|
7699
7925
|
nestedRepository ? {
|
|
7700
7926
|
path: relativePath,
|
|
@@ -7714,7 +7940,7 @@ function generateSnapshot(configInput, options) {
|
|
|
7714
7940
|
}
|
|
7715
7941
|
let buffer;
|
|
7716
7942
|
try {
|
|
7717
|
-
buffer =
|
|
7943
|
+
buffer = fs12.readFileSync(absolutePath);
|
|
7718
7944
|
} catch {
|
|
7719
7945
|
skipped.push({ path: relativePath, reason: "read-error" });
|
|
7720
7946
|
continue;
|
|
@@ -8044,12 +8270,12 @@ function generateSnapshot(configInput, options) {
|
|
|
8044
8270
|
let stagingDir = null;
|
|
8045
8271
|
try {
|
|
8046
8272
|
ensureParentDirectory(outDir, "outDir");
|
|
8047
|
-
|
|
8273
|
+
fs12.mkdirSync(tmpDir, { recursive: true });
|
|
8048
8274
|
cleanStaleRunDirs(tmpDir);
|
|
8049
8275
|
const snapshotId = crypto3.randomUUID();
|
|
8050
8276
|
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8051
|
-
stagingDir =
|
|
8052
|
-
|
|
8277
|
+
stagingDir = path13.join(tmpDir, `${RUN_DIR_PREFIX}${process.pid}-${snapshotId.slice(0, 8)}`);
|
|
8278
|
+
fs12.mkdirSync(stagingDir);
|
|
8053
8279
|
writeOwnershipMarker(stagingDir, {
|
|
8054
8280
|
generator: GENERATOR_NAME,
|
|
8055
8281
|
version: GENERATOR_VERSION,
|
|
@@ -8059,41 +8285,41 @@ function generateSnapshot(configInput, options) {
|
|
|
8059
8285
|
id: snapshotId,
|
|
8060
8286
|
pid: process.pid
|
|
8061
8287
|
});
|
|
8062
|
-
const partsDir =
|
|
8063
|
-
const reportsDir =
|
|
8064
|
-
if (artifacts.parts)
|
|
8288
|
+
const partsDir = path13.join(stagingDir, "parts");
|
|
8289
|
+
const reportsDir = path13.join(stagingDir, "reports");
|
|
8290
|
+
if (artifacts.parts) fs12.mkdirSync(partsDir, { recursive: true });
|
|
8065
8291
|
if (artifacts.navigation || artifacts.projectTree || artifacts.audit) {
|
|
8066
|
-
|
|
8292
|
+
fs12.mkdirSync(reportsDir, { recursive: true });
|
|
8067
8293
|
}
|
|
8068
8294
|
if (artifacts.parts) {
|
|
8069
8295
|
for (const part of parts) {
|
|
8070
|
-
|
|
8296
|
+
fs12.writeFileSync(path13.join(partsDir, part.filename), part.markdown, "utf8");
|
|
8071
8297
|
}
|
|
8072
8298
|
}
|
|
8073
8299
|
if (projectTree !== null) {
|
|
8074
|
-
|
|
8300
|
+
fs12.writeFileSync(path13.join(reportsDir, "project-tree.md"), projectTree, "utf8");
|
|
8075
8301
|
}
|
|
8076
8302
|
if (artifacts.navigation) {
|
|
8077
|
-
|
|
8303
|
+
fs12.writeFileSync(path13.join(reportsDir, "navigation.md"), generateNavigation(manifest), "utf8");
|
|
8078
8304
|
}
|
|
8079
8305
|
if (artifacts.audit) {
|
|
8080
|
-
|
|
8081
|
-
|
|
8306
|
+
fs12.writeFileSync(
|
|
8307
|
+
path13.join(reportsDir, "audit.md"),
|
|
8082
8308
|
generateAuditReport(manifest, secretFindings, config.secretRules),
|
|
8083
8309
|
"utf8"
|
|
8084
8310
|
);
|
|
8085
8311
|
}
|
|
8086
8312
|
if (artifacts.readme) {
|
|
8087
|
-
|
|
8313
|
+
fs12.writeFileSync(path13.join(stagingDir, "README.md"), generateReadme(manifest, artifacts), "utf8");
|
|
8088
8314
|
}
|
|
8089
8315
|
if (artifacts.manifest) {
|
|
8090
|
-
|
|
8316
|
+
fs12.writeFileSync(path13.join(stagingDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf8");
|
|
8091
8317
|
}
|
|
8092
8318
|
if (monolithJson !== null) {
|
|
8093
|
-
|
|
8319
|
+
fs12.writeFileSync(path13.join(stagingDir, "full-codebase.json"), monolithJson, "utf8");
|
|
8094
8320
|
}
|
|
8095
8321
|
if (monolithMarkdown !== null) {
|
|
8096
|
-
|
|
8322
|
+
fs12.writeFileSync(path13.join(stagingDir, "full-codebase.md"), monolithMarkdown, "utf8");
|
|
8097
8323
|
}
|
|
8098
8324
|
writeOwnershipMarker(stagingDir, markerPayload(stagingDir, "complete", snapshotId, createdAt));
|
|
8099
8325
|
swapIntoPlace(stagingDir, outDir);
|
|
@@ -8102,7 +8328,7 @@ function generateSnapshot(configInput, options) {
|
|
|
8102
8328
|
const pendingHook = runAfterGenerate(config, manifest);
|
|
8103
8329
|
const shipHookState = () => {
|
|
8104
8330
|
if (artifacts.manifest && manifest.warnings.length !== warningsBeforeHook) {
|
|
8105
|
-
writeManifestAtomically(
|
|
8331
|
+
writeManifestAtomically(path13.join(outDir, "manifest.json"), manifest);
|
|
8106
8332
|
}
|
|
8107
8333
|
writeOwnershipMarker(outDir, markerPayload(outDir, "complete", snapshotId, createdAt));
|
|
8108
8334
|
};
|
|
@@ -8125,7 +8351,7 @@ function generateSnapshot(configInput, options) {
|
|
|
8125
8351
|
} catch (err) {
|
|
8126
8352
|
if (stagingDir !== null) {
|
|
8127
8353
|
try {
|
|
8128
|
-
|
|
8354
|
+
fs12.rmSync(stagingDir, { recursive: true, force: true });
|
|
8129
8355
|
} catch {
|
|
8130
8356
|
}
|
|
8131
8357
|
}
|
|
@@ -8137,8 +8363,8 @@ function generateSnapshot(configInput, options) {
|
|
|
8137
8363
|
}
|
|
8138
8364
|
|
|
8139
8365
|
// src/mcp/tools.ts
|
|
8140
|
-
import * as
|
|
8141
|
-
import * as
|
|
8366
|
+
import * as fs14 from "fs";
|
|
8367
|
+
import * as path15 from "path";
|
|
8142
8368
|
|
|
8143
8369
|
// src/mcp/part.ts
|
|
8144
8370
|
var FILE_HEADING = "### FILE: ";
|
|
@@ -8224,15 +8450,15 @@ function windowText(body, fromLine, lineCount) {
|
|
|
8224
8450
|
}
|
|
8225
8451
|
|
|
8226
8452
|
// src/mcp/workspace.ts
|
|
8227
|
-
import * as
|
|
8228
|
-
import * as
|
|
8453
|
+
import * as fs13 from "fs";
|
|
8454
|
+
import * as path14 from "path";
|
|
8229
8455
|
function foldForComparison(value) {
|
|
8230
8456
|
return process.platform === "win32" || process.platform === "darwin" ? value.toLowerCase() : value;
|
|
8231
8457
|
}
|
|
8232
8458
|
function canonicalPath(value) {
|
|
8233
|
-
const absolute =
|
|
8459
|
+
const absolute = path14.resolve(value);
|
|
8234
8460
|
try {
|
|
8235
|
-
return
|
|
8461
|
+
return fs13.realpathSync(absolute);
|
|
8236
8462
|
} catch {
|
|
8237
8463
|
return absolute;
|
|
8238
8464
|
}
|
|
@@ -8240,17 +8466,17 @@ function canonicalPath(value) {
|
|
|
8240
8466
|
function isInside2(parent, child) {
|
|
8241
8467
|
const foldedParent = foldForComparison(parent);
|
|
8242
8468
|
const foldedChild = foldForComparison(child);
|
|
8243
|
-
return foldedChild === foldedParent || foldedChild.startsWith(`${foldedParent}${
|
|
8469
|
+
return foldedChild === foldedParent || foldedChild.startsWith(`${foldedParent}${path14.sep}`);
|
|
8244
8470
|
}
|
|
8245
8471
|
function resolveRoot(requested, context) {
|
|
8246
8472
|
if (requested !== void 0 && (typeof requested !== "string" || requested.trim().length === 0)) {
|
|
8247
8473
|
return { ok: false, message: '"root" must be a non-empty string when provided.' };
|
|
8248
8474
|
}
|
|
8249
8475
|
const wanted = typeof requested === "string" ? requested.trim() : context.cwd;
|
|
8250
|
-
const absolute =
|
|
8476
|
+
const absolute = path14.resolve(context.cwd, wanted);
|
|
8251
8477
|
let stats;
|
|
8252
8478
|
try {
|
|
8253
|
-
stats =
|
|
8479
|
+
stats = fs13.statSync(absolute);
|
|
8254
8480
|
} catch {
|
|
8255
8481
|
return { ok: false, message: `"${wanted}" does not exist, so it cannot be a repository root.` };
|
|
8256
8482
|
}
|
|
@@ -8301,14 +8527,14 @@ async function openWorkspace(root, overrides = {}) {
|
|
|
8301
8527
|
}
|
|
8302
8528
|
var manifestCache = /* @__PURE__ */ new Map();
|
|
8303
8529
|
function manifestPathFor(workspace) {
|
|
8304
|
-
return
|
|
8530
|
+
return path14.resolve(workspace.root, workspace.config.outDir, "manifest.json");
|
|
8305
8531
|
}
|
|
8306
8532
|
function readManifest3(workspace) {
|
|
8307
8533
|
const file = manifestPathFor(workspace);
|
|
8308
|
-
const shown =
|
|
8534
|
+
const shown = path14.relative(workspace.root, file).split(path14.sep).join("/") || file;
|
|
8309
8535
|
let stats;
|
|
8310
8536
|
try {
|
|
8311
|
-
stats =
|
|
8537
|
+
stats = fs13.statSync(file);
|
|
8312
8538
|
} catch {
|
|
8313
8539
|
return {
|
|
8314
8540
|
ok: false,
|
|
@@ -8322,7 +8548,7 @@ function readManifest3(workspace) {
|
|
|
8322
8548
|
}
|
|
8323
8549
|
let raw;
|
|
8324
8550
|
try {
|
|
8325
|
-
raw =
|
|
8551
|
+
raw = fs13.readFileSync(file, "utf8");
|
|
8326
8552
|
} catch (error) {
|
|
8327
8553
|
return {
|
|
8328
8554
|
ok: false,
|
|
@@ -8380,7 +8606,7 @@ function formatBytes(bytes) {
|
|
|
8380
8606
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
8381
8607
|
}
|
|
8382
8608
|
function relativeFor(workspace, absolute) {
|
|
8383
|
-
return
|
|
8609
|
+
return path15.relative(workspace.root, absolute).split(path15.sep).join("/");
|
|
8384
8610
|
}
|
|
8385
8611
|
function capped(values, limit = 20) {
|
|
8386
8612
|
return { count: values.length, sample: values.slice(0, limit) };
|
|
@@ -8399,12 +8625,12 @@ function summarizeDrift(drift) {
|
|
|
8399
8625
|
function toRelativePath(root, value) {
|
|
8400
8626
|
const cleaned = value.trim();
|
|
8401
8627
|
if (cleaned.length === 0) return null;
|
|
8402
|
-
if (!
|
|
8628
|
+
if (!path15.isAbsolute(cleaned)) {
|
|
8403
8629
|
return cleaned.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
8404
8630
|
}
|
|
8405
|
-
const relative11 =
|
|
8406
|
-
if (relative11.startsWith("..") ||
|
|
8407
|
-
return relative11.split(
|
|
8631
|
+
const relative11 = path15.relative(root, cleaned);
|
|
8632
|
+
if (relative11.startsWith("..") || path15.isAbsolute(relative11)) return null;
|
|
8633
|
+
return relative11.split(path15.sep).join("/");
|
|
8408
8634
|
}
|
|
8409
8635
|
function symbolsOf(entry) {
|
|
8410
8636
|
const summary = entry.summary;
|
|
@@ -8615,7 +8841,7 @@ var generateTool = {
|
|
|
8615
8841
|
return failure(error instanceof Error ? error.message : String(error));
|
|
8616
8842
|
}
|
|
8617
8843
|
forgetManifest(manifestPathFor(workspace));
|
|
8618
|
-
const written = relativeFor(workspace,
|
|
8844
|
+
const written = relativeFor(workspace, path15.resolve(workspace.root, workspace.config.outDir));
|
|
8619
8845
|
const lines = [
|
|
8620
8846
|
`Wrote ${written}/`,
|
|
8621
8847
|
`Files: ${manifest.counts.totalFiles} included, ${manifest.counts.skippedCount} skipped, ${plural(manifest.counts.partsCount, "part")}`,
|
|
@@ -8880,10 +9106,10 @@ var readTool = {
|
|
|
8880
9106
|
`${relativePath} is not in the snapshot. Call snapshot_locate to find the path, or snapshot_explain to learn why it was excluded.`
|
|
8881
9107
|
);
|
|
8882
9108
|
}
|
|
8883
|
-
const partPath =
|
|
9109
|
+
const partPath = path15.resolve(workspace.root, workspace.config.outDir, "parts", entry.part);
|
|
8884
9110
|
let partText;
|
|
8885
9111
|
try {
|
|
8886
|
-
partText =
|
|
9112
|
+
partText = fs14.readFileSync(partPath, "utf8");
|
|
8887
9113
|
} catch {
|
|
8888
9114
|
return failure(
|
|
8889
9115
|
`${entry.part} could not be read, so the snapshot is damaged. Run snapshot_generate to rebuild it.`
|
|
@@ -9145,6 +9371,7 @@ var VALIDATE_OPTIONS = [
|
|
|
9145
9371
|
"no-config",
|
|
9146
9372
|
"discovery",
|
|
9147
9373
|
"format",
|
|
9374
|
+
"no-update-check",
|
|
9148
9375
|
"mode",
|
|
9149
9376
|
"help",
|
|
9150
9377
|
"version"
|
|
@@ -9168,6 +9395,7 @@ var COMMAND_OPTION_SCOPE = {
|
|
|
9168
9395
|
"watch",
|
|
9169
9396
|
"autopilot",
|
|
9170
9397
|
"no-install",
|
|
9398
|
+
"no-update-check",
|
|
9171
9399
|
"help",
|
|
9172
9400
|
"version"
|
|
9173
9401
|
],
|
|
@@ -9183,6 +9411,7 @@ var COMMAND_OPTION_SCOPE = {
|
|
|
9183
9411
|
"skip-artifacts",
|
|
9184
9412
|
"only-artifacts",
|
|
9185
9413
|
"no-config",
|
|
9414
|
+
"no-update-check",
|
|
9186
9415
|
"format",
|
|
9187
9416
|
"mode",
|
|
9188
9417
|
"help",
|
|
@@ -9200,12 +9429,13 @@ var COMMAND_OPTION_SCOPE = {
|
|
|
9200
9429
|
"only-artifacts",
|
|
9201
9430
|
"no-config",
|
|
9202
9431
|
"discovery",
|
|
9432
|
+
"no-update-check",
|
|
9203
9433
|
"format",
|
|
9204
9434
|
"mode",
|
|
9205
9435
|
"help",
|
|
9206
9436
|
"version"
|
|
9207
9437
|
],
|
|
9208
|
-
diff: ["format", "mode", "no-config", "help", "version"],
|
|
9438
|
+
diff: ["format", "mode", "no-config", "no-update-check", "help", "version"],
|
|
9209
9439
|
describe: ["format", "mode", "help", "version"],
|
|
9210
9440
|
init: [
|
|
9211
9441
|
"cwd",
|
|
@@ -9220,6 +9450,7 @@ var COMMAND_OPTION_SCOPE = {
|
|
|
9220
9450
|
"yes",
|
|
9221
9451
|
"force",
|
|
9222
9452
|
"dry-run",
|
|
9453
|
+
"no-update-check",
|
|
9223
9454
|
"format",
|
|
9224
9455
|
"mode",
|
|
9225
9456
|
"help",
|
|
@@ -9243,6 +9474,7 @@ var COMMAND_OPTION_SCOPE = {
|
|
|
9243
9474
|
"force",
|
|
9244
9475
|
"no-install",
|
|
9245
9476
|
"dry-run",
|
|
9477
|
+
"no-update-check",
|
|
9246
9478
|
"format",
|
|
9247
9479
|
"mode",
|
|
9248
9480
|
"help",
|
|
@@ -9675,6 +9907,13 @@ function buildAgentContract() {
|
|
|
9675
9907
|
default: false,
|
|
9676
9908
|
appliesTo: optionAppliesTo("no-install"),
|
|
9677
9909
|
summary: "Skip installing the dependency before generating: `setup`, or `generate --autopilot` (CI, offline, or a lockfile you do not want a package manager to touch)."
|
|
9910
|
+
},
|
|
9911
|
+
{
|
|
9912
|
+
flag: "--no-update-check",
|
|
9913
|
+
type: "boolean",
|
|
9914
|
+
default: false,
|
|
9915
|
+
appliesTo: optionAppliesTo("no-update-check"),
|
|
9916
|
+
summary: "Skip the once-a-day check for a newer published version. The check is already skipped when stdout is not a terminal, in CI, in a JSON invocation, and in `mcp`, which makes no network requests."
|
|
9678
9917
|
}
|
|
9679
9918
|
],
|
|
9680
9919
|
presets: PRESETS.map((preset) => ({
|
|
@@ -9807,8 +10046,8 @@ function buildAgentContract() {
|
|
|
9807
10046
|
}
|
|
9808
10047
|
|
|
9809
10048
|
// src/agent/install-skill.ts
|
|
9810
|
-
import * as
|
|
9811
|
-
import * as
|
|
10049
|
+
import * as fs15 from "fs";
|
|
10050
|
+
import * as path16 from "path";
|
|
9812
10051
|
|
|
9813
10052
|
// src/agent/skill.ts
|
|
9814
10053
|
var PACKAGE_NAME2 = "codebase-snapshot";
|
|
@@ -9966,17 +10205,17 @@ var LEGACY_AGENTS_MARKER = "<!-- codebase-snapshot:skill:start -->";
|
|
|
9966
10205
|
function targetPath(cwd, target) {
|
|
9967
10206
|
switch (target) {
|
|
9968
10207
|
case "claude":
|
|
9969
|
-
return
|
|
10208
|
+
return path16.join(cwd, ".claude", "skills", SKILL_NAME, "SKILL.md");
|
|
9970
10209
|
case "cursor":
|
|
9971
|
-
return
|
|
10210
|
+
return path16.join(cwd, ".cursor", "rules", `${SKILL_NAME}.mdc`);
|
|
9972
10211
|
}
|
|
9973
10212
|
}
|
|
9974
10213
|
function assertWritableInsideRepository(cwd, filePath) {
|
|
9975
|
-
const root =
|
|
9976
|
-
const shown =
|
|
10214
|
+
const root = fs15.realpathSync(cwd);
|
|
10215
|
+
const shown = path16.relative(root, filePath) || filePath;
|
|
9977
10216
|
let stat = null;
|
|
9978
10217
|
try {
|
|
9979
|
-
stat =
|
|
10218
|
+
stat = fs15.lstatSync(filePath);
|
|
9980
10219
|
} catch {
|
|
9981
10220
|
stat = null;
|
|
9982
10221
|
}
|
|
@@ -9992,32 +10231,32 @@ function assertWritableInsideRepository(cwd, filePath) {
|
|
|
9992
10231
|
}
|
|
9993
10232
|
let existing = filePath;
|
|
9994
10233
|
const missing = [];
|
|
9995
|
-
while (!
|
|
9996
|
-
missing.unshift(
|
|
9997
|
-
const parent =
|
|
10234
|
+
while (!fs15.existsSync(existing)) {
|
|
10235
|
+
missing.unshift(path16.basename(existing));
|
|
10236
|
+
const parent = path16.dirname(existing);
|
|
9998
10237
|
if (parent === existing) break;
|
|
9999
10238
|
existing = parent;
|
|
10000
10239
|
}
|
|
10001
|
-
const realAncestor =
|
|
10002
|
-
const resolved = missing.length === 0 ? realAncestor :
|
|
10003
|
-
if (resolved !== root && !resolved.startsWith(`${root}${
|
|
10240
|
+
const realAncestor = fs15.realpathSync(existing);
|
|
10241
|
+
const resolved = missing.length === 0 ? realAncestor : path16.join(realAncestor, ...missing);
|
|
10242
|
+
if (resolved !== root && !resolved.startsWith(`${root}${path16.sep}`)) {
|
|
10004
10243
|
throw new Error(
|
|
10005
10244
|
`${REFUSED}: ${shown} resolves outside the repository (${resolved}). Move it back inside ${root}, or remove the link, and run again.`
|
|
10006
10245
|
);
|
|
10007
10246
|
}
|
|
10008
10247
|
}
|
|
10009
10248
|
function writeIfChanged(target, filePath, content) {
|
|
10010
|
-
const existing =
|
|
10249
|
+
const existing = fs15.existsSync(filePath) ? fs15.readFileSync(filePath, "utf8") : null;
|
|
10011
10250
|
if (existing === content) return { target, path: filePath, action: "unchanged" };
|
|
10012
|
-
|
|
10013
|
-
|
|
10251
|
+
fs15.mkdirSync(path16.dirname(filePath), { recursive: true });
|
|
10252
|
+
fs15.writeFileSync(filePath, content, "utf8");
|
|
10014
10253
|
return { target, path: filePath, action: existing === null ? "created" : "updated" };
|
|
10015
10254
|
}
|
|
10016
10255
|
function legacyAgentsNote(cwd) {
|
|
10017
|
-
const filePath =
|
|
10256
|
+
const filePath = path16.join(cwd, "AGENTS.md");
|
|
10018
10257
|
try {
|
|
10019
|
-
if (!
|
|
10020
|
-
if (!
|
|
10258
|
+
if (!fs15.existsSync(filePath)) return null;
|
|
10259
|
+
if (!fs15.readFileSync(filePath, "utf8").includes(LEGACY_AGENTS_MARKER)) return null;
|
|
10021
10260
|
} catch {
|
|
10022
10261
|
return null;
|
|
10023
10262
|
}
|
|
@@ -10029,9 +10268,9 @@ function installSkill(cwd, target = "all") {
|
|
|
10029
10268
|
const notes = [];
|
|
10030
10269
|
const wanted = target === "all" ? ["claude", "cursor"] : [target];
|
|
10031
10270
|
for (const item of wanted) assertWritableInsideRepository(cwd, targetPath(cwd, item));
|
|
10032
|
-
const createsSomething = wanted.some((item) => !
|
|
10271
|
+
const createsSomething = wanted.some((item) => !fs15.existsSync(targetPath(cwd, item)));
|
|
10033
10272
|
if (createsSomething) {
|
|
10034
|
-
assertWritableInsideRepository(cwd,
|
|
10273
|
+
assertWritableInsideRepository(cwd, path16.join(cwd, ".gitignore"));
|
|
10035
10274
|
}
|
|
10036
10275
|
for (const item of wanted) {
|
|
10037
10276
|
written.push(writeIfChanged(item, targetPath(cwd, item), skill));
|
|
@@ -10166,52 +10405,52 @@ function validateArguments(schema, value) {
|
|
|
10166
10405
|
}
|
|
10167
10406
|
return problems;
|
|
10168
10407
|
}
|
|
10169
|
-
function checkValue(
|
|
10408
|
+
function checkValue(path19, schema, value) {
|
|
10170
10409
|
if (schema.enum && !schema.enum.includes(value)) {
|
|
10171
|
-
return [`"${
|
|
10410
|
+
return [`"${path19}" must be one of: ${schema.enum.join(", ")}`];
|
|
10172
10411
|
}
|
|
10173
10412
|
switch (schema.type) {
|
|
10174
10413
|
case "string": {
|
|
10175
|
-
if (typeof value !== "string") return [`"${
|
|
10414
|
+
if (typeof value !== "string") return [`"${path19}" must be a string`];
|
|
10176
10415
|
const problems = [];
|
|
10177
10416
|
if (schema.minLength !== void 0 && value.length < schema.minLength) {
|
|
10178
|
-
problems.push(`"${
|
|
10417
|
+
problems.push(`"${path19}" must be at least ${schema.minLength} character(s)`);
|
|
10179
10418
|
}
|
|
10180
10419
|
if (schema.maxLength !== void 0 && value.length > schema.maxLength) {
|
|
10181
|
-
problems.push(`"${
|
|
10420
|
+
problems.push(`"${path19}" must be at most ${schema.maxLength} character(s)`);
|
|
10182
10421
|
}
|
|
10183
10422
|
return problems;
|
|
10184
10423
|
}
|
|
10185
10424
|
case "number":
|
|
10186
10425
|
case "integer": {
|
|
10187
10426
|
if (typeof value !== "number" || Number.isNaN(value)) {
|
|
10188
|
-
return [`"${
|
|
10427
|
+
return [`"${path19}" must be a number`];
|
|
10189
10428
|
}
|
|
10190
10429
|
if (schema.type === "integer" && !Number.isInteger(value)) {
|
|
10191
|
-
return [`"${
|
|
10430
|
+
return [`"${path19}" must be an integer`];
|
|
10192
10431
|
}
|
|
10193
10432
|
const problems = [];
|
|
10194
10433
|
if (schema.minimum !== void 0 && value < schema.minimum) {
|
|
10195
|
-
problems.push(`"${
|
|
10434
|
+
problems.push(`"${path19}" must be >= ${schema.minimum}`);
|
|
10196
10435
|
}
|
|
10197
10436
|
if (schema.maximum !== void 0 && value > schema.maximum) {
|
|
10198
|
-
problems.push(`"${
|
|
10437
|
+
problems.push(`"${path19}" must be <= ${schema.maximum}`);
|
|
10199
10438
|
}
|
|
10200
10439
|
return problems;
|
|
10201
10440
|
}
|
|
10202
10441
|
case "boolean":
|
|
10203
|
-
return typeof value === "boolean" ? [] : [`"${
|
|
10442
|
+
return typeof value === "boolean" ? [] : [`"${path19}" must be a boolean`];
|
|
10204
10443
|
case "array": {
|
|
10205
|
-
if (!Array.isArray(value)) return [`"${
|
|
10444
|
+
if (!Array.isArray(value)) return [`"${path19}" must be an array`];
|
|
10206
10445
|
if (!schema.items) return [];
|
|
10207
10446
|
const problems = [];
|
|
10208
10447
|
value.forEach((item, index) => {
|
|
10209
|
-
problems.push(...checkValue(`${
|
|
10448
|
+
problems.push(...checkValue(`${path19}[${index}]`, schema.items, item));
|
|
10210
10449
|
});
|
|
10211
10450
|
return problems;
|
|
10212
10451
|
}
|
|
10213
10452
|
case "object":
|
|
10214
|
-
return isJsonObject(value) ? [] : [`"${
|
|
10453
|
+
return isJsonObject(value) ? [] : [`"${path19}" must be an object`];
|
|
10215
10454
|
default:
|
|
10216
10455
|
return [];
|
|
10217
10456
|
}
|
|
@@ -10225,6 +10464,18 @@ function applyDefaults(schema, value) {
|
|
|
10225
10464
|
}
|
|
10226
10465
|
return out;
|
|
10227
10466
|
}
|
|
10467
|
+
function coerceNumericStrings(schema, value) {
|
|
10468
|
+
const out = { ...value };
|
|
10469
|
+
for (const [key, propertySchema] of Object.entries(schema.properties)) {
|
|
10470
|
+
const raw = out[key];
|
|
10471
|
+
if (typeof raw !== "string") continue;
|
|
10472
|
+
if (propertySchema.type !== "number" && propertySchema.type !== "integer") continue;
|
|
10473
|
+
if (raw.trim().length === 0) continue;
|
|
10474
|
+
const parsed = Number(raw);
|
|
10475
|
+
if (Number.isFinite(parsed)) out[key] = parsed;
|
|
10476
|
+
}
|
|
10477
|
+
return out;
|
|
10478
|
+
}
|
|
10228
10479
|
|
|
10229
10480
|
// src/mcp/server.ts
|
|
10230
10481
|
function capabilities() {
|
|
@@ -10313,7 +10564,8 @@ function createMcpServer(options) {
|
|
|
10313
10564
|
if (!isJsonObject(rawArguments)) {
|
|
10314
10565
|
return errorResponse(id, ERROR_CODES.invalidParams, '"arguments" must be a JSON object.');
|
|
10315
10566
|
}
|
|
10316
|
-
const
|
|
10567
|
+
const supplied = coerceNumericStrings(tool.inputSchema, rawArguments);
|
|
10568
|
+
const problems = validateArguments(tool.inputSchema, supplied);
|
|
10317
10569
|
if (problems.length > 0) {
|
|
10318
10570
|
return ok(
|
|
10319
10571
|
id,
|
|
@@ -10325,7 +10577,7 @@ function createMcpServer(options) {
|
|
|
10325
10577
|
modern
|
|
10326
10578
|
);
|
|
10327
10579
|
}
|
|
10328
|
-
const args = applyDefaults(tool.inputSchema,
|
|
10580
|
+
const args = applyDefaults(tool.inputSchema, supplied);
|
|
10329
10581
|
let outcome;
|
|
10330
10582
|
try {
|
|
10331
10583
|
outcome = await tool.handler(args, options.context);
|
|
@@ -10494,8 +10746,8 @@ function createMcpServer(options) {
|
|
|
10494
10746
|
}
|
|
10495
10747
|
|
|
10496
10748
|
// src/mcp/resources.ts
|
|
10497
|
-
import * as
|
|
10498
|
-
import * as
|
|
10749
|
+
import * as fs16 from "fs";
|
|
10750
|
+
import * as path17 from "path";
|
|
10499
10751
|
var MAX_RESOURCE_BYTES = 512 * 1024;
|
|
10500
10752
|
var PART_URI = /^snapshot:\/\/parts\/(\d+)$/;
|
|
10501
10753
|
var REPORT_URIS = /* @__PURE__ */ new Map([
|
|
@@ -10529,7 +10781,7 @@ async function workspaceFor(context) {
|
|
|
10529
10781
|
}
|
|
10530
10782
|
function readFileIfPresent(file) {
|
|
10531
10783
|
try {
|
|
10532
|
-
return
|
|
10784
|
+
return fs16.readFileSync(file, "utf8");
|
|
10533
10785
|
} catch {
|
|
10534
10786
|
return null;
|
|
10535
10787
|
}
|
|
@@ -10608,7 +10860,7 @@ function buildResourceProvider() {
|
|
|
10608
10860
|
const report = REPORT_URIS.get(uri);
|
|
10609
10861
|
if (report) {
|
|
10610
10862
|
const text2 = readFileIfPresent(
|
|
10611
|
-
|
|
10863
|
+
path17.resolve(workspace.root, workspace.config.outDir, "reports", report.file)
|
|
10612
10864
|
);
|
|
10613
10865
|
if (text2 === null) return null;
|
|
10614
10866
|
return { uri, mimeType: "text/markdown", text: capUtf8(text2, MAX_RESOURCE_BYTES).text };
|
|
@@ -10627,7 +10879,7 @@ function buildResourceProvider() {
|
|
|
10627
10879
|
const part = read.manifest.parts.find((candidate) => candidate.index === index);
|
|
10628
10880
|
if (!part) return null;
|
|
10629
10881
|
const text2 = readFileIfPresent(
|
|
10630
|
-
|
|
10882
|
+
path17.resolve(workspace.root, workspace.config.outDir, "parts", part.filename)
|
|
10631
10883
|
);
|
|
10632
10884
|
if (text2 === null) return null;
|
|
10633
10885
|
return { uri, mimeType: "text/markdown", text: capUtf8(text2, MAX_RESOURCE_BYTES).text };
|
|
@@ -10865,6 +11117,7 @@ var CLI_OPTIONS = {
|
|
|
10865
11117
|
install: { type: "boolean" },
|
|
10866
11118
|
"no-install": { type: "boolean" },
|
|
10867
11119
|
target: { type: "string" },
|
|
11120
|
+
"no-update-check": { type: "boolean" },
|
|
10868
11121
|
help: { type: "boolean", short: "h" },
|
|
10869
11122
|
version: { type: "boolean", short: "v" }
|
|
10870
11123
|
};
|
|
@@ -10939,6 +11192,8 @@ Options:
|
|
|
10939
11192
|
--install Write the skill into the repository instead of printing
|
|
10940
11193
|
--target <name> claude | cursor | all (default: all)
|
|
10941
11194
|
|
|
11195
|
+
--no-update-check Skip the daily check for a newer published version
|
|
11196
|
+
|
|
10942
11197
|
-h, --help Display this help message
|
|
10943
11198
|
-v, --version Display version
|
|
10944
11199
|
|
|
@@ -11003,10 +11258,10 @@ function parseCliArgs(argv) {
|
|
|
11003
11258
|
}
|
|
11004
11259
|
if (values.cwd !== void 0) {
|
|
11005
11260
|
const target = String(values.cwd);
|
|
11006
|
-
if (!
|
|
11261
|
+
if (!fs17.existsSync(target)) {
|
|
11007
11262
|
throw new UsageError(`--cwd directory does not exist: ${target}`);
|
|
11008
11263
|
}
|
|
11009
|
-
if (!
|
|
11264
|
+
if (!fs17.statSync(target).isDirectory()) {
|
|
11010
11265
|
throw new UsageError(`--cwd is not a directory: ${target}`);
|
|
11011
11266
|
}
|
|
11012
11267
|
}
|
|
@@ -11152,7 +11407,7 @@ function parseCliArgs(argv) {
|
|
|
11152
11407
|
install: values.install === true,
|
|
11153
11408
|
noInstall: values["no-install"] === true,
|
|
11154
11409
|
target: values.target === void 0 ? void 0 : String(values.target),
|
|
11155
|
-
cwd: values.cwd === void 0 ? process.cwd() :
|
|
11410
|
+
cwd: values.cwd === void 0 ? process.cwd() : path18.resolve(String(values.cwd)),
|
|
11156
11411
|
outDir: values["out-dir"] === void 0 ? void 0 : String(values["out-dir"]),
|
|
11157
11412
|
includeMonolith: values["include-monolith"] === true || onlyNamesMonolith === true ? true : void 0,
|
|
11158
11413
|
includeSensitiveFiles: values["include-sensitive-files"] === true ? true : void 0,
|
|
@@ -11209,24 +11464,24 @@ function splitList(value) {
|
|
|
11209
11464
|
return value.split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
11210
11465
|
}
|
|
11211
11466
|
function displayOutPath(cwd, outDir, ...segments) {
|
|
11212
|
-
const absolute =
|
|
11213
|
-
const relative11 =
|
|
11214
|
-
if (relative11 && !relative11.startsWith("..") && !
|
|
11467
|
+
const absolute = path18.resolve(cwd, outDir, ...segments);
|
|
11468
|
+
const relative11 = path18.relative(cwd, absolute);
|
|
11469
|
+
if (relative11 && !relative11.startsWith("..") && !path18.isAbsolute(relative11)) return relative11;
|
|
11215
11470
|
return absolute;
|
|
11216
11471
|
}
|
|
11217
11472
|
function nextStepHint(cwd, scriptsAddedNow) {
|
|
11218
11473
|
let hasScript = scriptsAddedNow;
|
|
11219
11474
|
if (!hasScript) {
|
|
11220
11475
|
try {
|
|
11221
|
-
const parsed = JSON.parse(
|
|
11476
|
+
const parsed = JSON.parse(fs17.readFileSync(path18.join(cwd, "package.json"), "utf8"));
|
|
11222
11477
|
hasScript = typeof parsed.scripts?.snapshot === "string";
|
|
11223
11478
|
} catch {
|
|
11224
11479
|
hasScript = false;
|
|
11225
11480
|
}
|
|
11226
11481
|
}
|
|
11227
11482
|
if (!hasScript) return `npx ${GENERATOR_NAME} generate`;
|
|
11228
|
-
const bin =
|
|
11229
|
-
const installed =
|
|
11483
|
+
const bin = path18.join(cwd, "node_modules", ".bin", "codebase-snapshot");
|
|
11484
|
+
const installed = fs17.existsSync(bin) || fs17.existsSync(`${bin}.cmd`);
|
|
11230
11485
|
return installed ? "npm run snapshot" : "npm install && npm run snapshot";
|
|
11231
11486
|
}
|
|
11232
11487
|
function presetSummary(init) {
|
|
@@ -11256,7 +11511,7 @@ async function runInitCommand(invocation) {
|
|
|
11256
11511
|
info(result.configText);
|
|
11257
11512
|
return 0;
|
|
11258
11513
|
}
|
|
11259
|
-
info(`\u2705 Created ${
|
|
11514
|
+
info(`\u2705 Created ${path18.basename(result.configPath)} (${presetSummary(result)})`);
|
|
11260
11515
|
if (result.scripts.length > 0) info(` Added npm scripts: ${result.scripts.join(", ")}`);
|
|
11261
11516
|
if (result.dependency.added && result.dependency.range !== null) {
|
|
11262
11517
|
info(` Added devDependency: ${result.dependency.name} ${result.dependency.range}`);
|
|
@@ -11276,7 +11531,7 @@ async function runInitCommand(invocation) {
|
|
|
11276
11531
|
}
|
|
11277
11532
|
}
|
|
11278
11533
|
function detectPackageManager(cwd) {
|
|
11279
|
-
const has = (name) =>
|
|
11534
|
+
const has = (name) => fs17.existsSync(path18.join(cwd, name));
|
|
11280
11535
|
if (has("bun.lockb") || has("bun.lock")) return { name: "bun", command: "bun", args: ["install"] };
|
|
11281
11536
|
if (has("pnpm-lock.yaml")) return { name: "pnpm", command: "pnpm", args: ["install"] };
|
|
11282
11537
|
if (has("yarn.lock")) return { name: "yarn", command: "yarn", args: ["install"] };
|
|
@@ -11365,7 +11620,7 @@ async function runSetupCommand(invocation, options = {}) {
|
|
|
11365
11620
|
}
|
|
11366
11621
|
info("\n\u{1F9EA} Dry run \u2014 setup would:");
|
|
11367
11622
|
info(
|
|
11368
|
-
` 1. ${init.reused ? `keep ${
|
|
11623
|
+
` 1. ${init.reused ? `keep ${path18.basename(init.configPath)}` : `write ${path18.basename(init.configPath)} (preset: ${init.preset})`}`
|
|
11369
11624
|
);
|
|
11370
11625
|
info(" 2. add the npm scripts, the devDependency and the .gitignore entries");
|
|
11371
11626
|
if (manager !== null) info(` 3. install with ${manager}`);
|
|
@@ -11377,9 +11632,9 @@ async function runSetupCommand(invocation, options = {}) {
|
|
|
11377
11632
|
if (invocation.format !== "json") {
|
|
11378
11633
|
if (!interactive) info(`\u{1F680} Setup for: ${invocation.cwd}`);
|
|
11379
11634
|
if (init.reused) {
|
|
11380
|
-
info(`\u267B Keeping ${
|
|
11635
|
+
info(`\u267B Keeping ${path18.basename(init.configPath)} (${presetSummary(init)})`);
|
|
11381
11636
|
} else {
|
|
11382
|
-
info(`\u2705 Created ${
|
|
11637
|
+
info(`\u2705 Created ${path18.basename(init.configPath)} (${presetSummary(init)})`);
|
|
11383
11638
|
}
|
|
11384
11639
|
if (init.scripts.length > 0) info(` Added npm scripts: ${init.scripts.join(", ")}`);
|
|
11385
11640
|
if (init.dependency.added && init.dependency.range !== null) {
|
|
@@ -11487,7 +11742,7 @@ function runSkillCommand(invocation) {
|
|
|
11487
11742
|
}
|
|
11488
11743
|
info(`\u2705 Agent skill installed (${SKILL_NAME}):`);
|
|
11489
11744
|
for (const item of result.written) {
|
|
11490
|
-
const shown =
|
|
11745
|
+
const shown = path18.relative(invocation.cwd, item.path) || item.path;
|
|
11491
11746
|
info(` - ${item.action.padEnd(9)} ${shown}`);
|
|
11492
11747
|
}
|
|
11493
11748
|
if (result.gitignored.length > 0) {
|
|
@@ -11551,6 +11806,17 @@ function requestsJsonFormat(argv) {
|
|
|
11551
11806
|
return format === "json";
|
|
11552
11807
|
}
|
|
11553
11808
|
async function main(argv = process.argv.slice(2)) {
|
|
11809
|
+
const pending = process.stdout.isTTY === true && shouldCheck(argv, process.env) ? checkForUpdate({ current: GENERATOR_VERSION, cacheFile: cacheFilePath() }) : null;
|
|
11810
|
+
const code = await runCommand(argv);
|
|
11811
|
+
if (pending) {
|
|
11812
|
+
const notice = await pending;
|
|
11813
|
+
if (notice) process.stderr.write(`
|
|
11814
|
+
${formatUpdateNotice(notice)}
|
|
11815
|
+
`);
|
|
11816
|
+
}
|
|
11817
|
+
return code;
|
|
11818
|
+
}
|
|
11819
|
+
async function runCommand(argv) {
|
|
11554
11820
|
let invocation;
|
|
11555
11821
|
try {
|
|
11556
11822
|
invocation = parseCliArgs(argv);
|
|
@@ -11583,7 +11849,7 @@ ${USAGE}
|
|
|
11583
11849
|
return runSkillCommand(invocation);
|
|
11584
11850
|
}
|
|
11585
11851
|
if (invocation.command === "mcp") {
|
|
11586
|
-
return await runStdioServer({ cwd:
|
|
11852
|
+
return await runStdioServer({ cwd: path18.resolve(invocation.cwd) });
|
|
11587
11853
|
}
|
|
11588
11854
|
if (invocation.command === "setup") {
|
|
11589
11855
|
return runSetupCommand(invocation);
|
|
@@ -11600,7 +11866,7 @@ ${USAGE}
|
|
|
11600
11866
|
warn("\u274C The config file could not be read. Fix it, or delete it to use the defaults.");
|
|
11601
11867
|
return 1;
|
|
11602
11868
|
}
|
|
11603
|
-
if (loaded.file) info(`\u2699 Using config ${
|
|
11869
|
+
if (loaded.file) info(`\u2699 Using config ${path18.relative(process.cwd(), loaded.file) || loaded.file}`);
|
|
11604
11870
|
const resolved = resolveConfig(
|
|
11605
11871
|
loaded.config,
|
|
11606
11872
|
{
|
|
@@ -11622,10 +11888,10 @@ ${USAGE}
|
|
|
11622
11888
|
if (invocation.command === "validate") {
|
|
11623
11889
|
const result = validateSnapshot(resolved);
|
|
11624
11890
|
if (result.valid) {
|
|
11625
|
-
const manifestPath =
|
|
11891
|
+
const manifestPath = path18.resolve(resolved.cwd, resolved.outDir, "manifest.json");
|
|
11626
11892
|
let manifest;
|
|
11627
11893
|
try {
|
|
11628
|
-
manifest = JSON.parse(
|
|
11894
|
+
manifest = JSON.parse(fs17.readFileSync(manifestPath, "utf8"));
|
|
11629
11895
|
} catch {
|
|
11630
11896
|
manifest = void 0;
|
|
11631
11897
|
}
|
|
@@ -11638,13 +11904,13 @@ ${USAGE}
|
|
|
11638
11904
|
return result.valid ? 0 : 1;
|
|
11639
11905
|
}
|
|
11640
11906
|
if (invocation.command === "report") {
|
|
11641
|
-
const manifestPath =
|
|
11642
|
-
if (!
|
|
11907
|
+
const manifestPath = path18.resolve(resolved.cwd, resolved.outDir, "manifest.json");
|
|
11908
|
+
if (!fs17.existsSync(manifestPath)) {
|
|
11643
11909
|
return fail(invocation.format, `Manifest not found at ${manifestPath}. Run generate first.`);
|
|
11644
11910
|
}
|
|
11645
11911
|
let manifest;
|
|
11646
11912
|
try {
|
|
11647
|
-
manifest = JSON.parse(
|
|
11913
|
+
manifest = JSON.parse(fs17.readFileSync(manifestPath, "utf8"));
|
|
11648
11914
|
} catch (err) {
|
|
11649
11915
|
return fail(invocation.format, `Could not read ${manifestPath}: ${err.message}`);
|
|
11650
11916
|
}
|
|
@@ -11808,20 +12074,20 @@ function finishWatchRun(state, now) {
|
|
|
11808
12074
|
};
|
|
11809
12075
|
}
|
|
11810
12076
|
function comparablePath(value) {
|
|
11811
|
-
const absolute =
|
|
12077
|
+
const absolute = path18.resolve(value);
|
|
11812
12078
|
return process.platform === "win32" ? absolute.toLowerCase() : absolute;
|
|
11813
12079
|
}
|
|
11814
12080
|
function isSameOrInside(parent, candidate) {
|
|
11815
|
-
const relative11 =
|
|
11816
|
-
return relative11 === "" || relative11 !== ".." && !relative11.startsWith(`..${
|
|
12081
|
+
const relative11 = path18.relative(parent, candidate);
|
|
12082
|
+
return relative11 === "" || relative11 !== ".." && !relative11.startsWith(`..${path18.sep}`) && !path18.isAbsolute(relative11);
|
|
11817
12083
|
}
|
|
11818
12084
|
function isGeneratedWatchPath(candidate, paths) {
|
|
11819
|
-
const absolute = comparablePath(
|
|
11820
|
-
const outDir = comparablePath(
|
|
11821
|
-
const tmpDir = comparablePath(
|
|
12085
|
+
const absolute = comparablePath(path18.resolve(paths.cwd, candidate));
|
|
12086
|
+
const outDir = comparablePath(path18.resolve(paths.cwd, paths.outDir));
|
|
12087
|
+
const tmpDir = comparablePath(path18.resolve(paths.cwd, paths.tmpDir));
|
|
11822
12088
|
if (isSameOrInside(outDir, absolute) || isSameOrInside(tmpDir, absolute)) return true;
|
|
11823
|
-
const parent =
|
|
11824
|
-
if (parent ===
|
|
12089
|
+
const parent = path18.dirname(absolute);
|
|
12090
|
+
if (parent === path18.dirname(outDir) && path18.basename(absolute).startsWith(`${path18.basename(outDir)}.backup-`)) {
|
|
11825
12091
|
return true;
|
|
11826
12092
|
}
|
|
11827
12093
|
return absolute.split(/[\\/]+/).some((segment) => VCS_SEGMENTS.has(segment));
|
|
@@ -11838,8 +12104,8 @@ function decideWatchEvent(state, event, paths) {
|
|
|
11838
12104
|
return { state, action: "schedule" };
|
|
11839
12105
|
}
|
|
11840
12106
|
async function runWatchLoop(invocation, resolved) {
|
|
11841
|
-
const outDir =
|
|
11842
|
-
const tmpDir =
|
|
12107
|
+
const outDir = path18.resolve(resolved.cwd, resolved.outDir);
|
|
12108
|
+
const tmpDir = path18.resolve(resolved.cwd, resolved.tmpDir);
|
|
11843
12109
|
const paths = { cwd: resolved.cwd, outDir, tmpDir };
|
|
11844
12110
|
let state = initialWatchState();
|
|
11845
12111
|
let timer = null;
|
|
@@ -11904,11 +12170,11 @@ async function runWatchLoop(invocation, resolved) {
|
|
|
11904
12170
|
};
|
|
11905
12171
|
info("\u{1F440} Watching for changes. Press Ctrl+C to stop.");
|
|
11906
12172
|
void regenerate();
|
|
11907
|
-
const watcher =
|
|
12173
|
+
const watcher = fs17.watch(resolved.cwd, { recursive: true }, (_event, filename) => {
|
|
11908
12174
|
const decision = decideWatchEvent(
|
|
11909
12175
|
state,
|
|
11910
12176
|
{
|
|
11911
|
-
path: filename ?
|
|
12177
|
+
path: filename ? path18.resolve(resolved.cwd, filename.toString()) : null,
|
|
11912
12178
|
now: performance.now()
|
|
11913
12179
|
},
|
|
11914
12180
|
paths
|
|
@@ -11936,7 +12202,7 @@ async function runWatchLoop(invocation, resolved) {
|
|
|
11936
12202
|
return 0;
|
|
11937
12203
|
}
|
|
11938
12204
|
var entryFile = process.argv[1] ?? "";
|
|
11939
|
-
var entryName =
|
|
12205
|
+
var entryName = path18.basename(entryFile).toLowerCase();
|
|
11940
12206
|
var invokedDirectly = entryName === "cli.js" || entryName === "cli.cjs";
|
|
11941
12207
|
if (invokedDirectly) {
|
|
11942
12208
|
void main().then(
|