codebase-snapshot 0.9.4 → 0.9.6
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 +84 -0
- package/README.md +4 -1
- package/dist/bin.js +594 -304
- package/dist/{chunk-FCWD4LY7.js → chunk-3KUL3QRH.js} +564 -318
- package/dist/{chunk-EWZYK3VX.js → chunk-ZKZOGGM3.js} +33 -7
- package/dist/cli.cjs +697 -411
- package/dist/cli.d.cts +7 -1
- package/dist/cli.d.ts +7 -1
- package/dist/cli.js +19 -6
- package/dist/index.cjs +570 -312
- package/dist/index.d.cts +54 -2
- package/dist/index.d.ts +54 -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 +29 -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.6";
|
|
4350
4350
|
var LEGACY_GENERATOR_NAMES = ["@ankurhalder/codebase-snapshot"];
|
|
4351
4351
|
function isOurGeneratorName(name) {
|
|
4352
4352
|
if (typeof name !== "string") return false;
|
|
@@ -4487,6 +4487,12 @@ var PromptCancelled = class extends Error {
|
|
|
4487
4487
|
this.name = "PromptCancelled";
|
|
4488
4488
|
}
|
|
4489
4489
|
};
|
|
4490
|
+
var PromptBack = class extends Error {
|
|
4491
|
+
constructor() {
|
|
4492
|
+
super("Prompt back");
|
|
4493
|
+
this.name = "PromptBack";
|
|
4494
|
+
}
|
|
4495
|
+
};
|
|
4490
4496
|
function defaultIO() {
|
|
4491
4497
|
return { input: process.stdin, output: process.stdout };
|
|
4492
4498
|
}
|
|
@@ -4510,35 +4516,40 @@ function choiceLine(choice, active) {
|
|
|
4510
4516
|
const hint = choice.hint ? ` ${choice.hint}` : "";
|
|
4511
4517
|
return ` ${marker(active)} ${choice.label}${hint}`;
|
|
4512
4518
|
}
|
|
4513
|
-
function
|
|
4519
|
+
function listFooter(base, allowBack) {
|
|
4520
|
+
return allowBack ? `${base} \xB7 \u2190 back` : base;
|
|
4521
|
+
}
|
|
4522
|
+
function renderSelect(message, choices, index, allowBack = false) {
|
|
4514
4523
|
const lines = [`\u25C6 ${message}`, ""];
|
|
4515
4524
|
choices.forEach((choice, position) => lines.push(choiceLine(choice, position === index)));
|
|
4516
|
-
lines.push("", "
|
|
4525
|
+
lines.push("", ` ${listFooter("\u2191\u2193 move \xB7 enter select", allowBack)}`);
|
|
4517
4526
|
return lines.join("\n");
|
|
4518
4527
|
}
|
|
4519
|
-
function renderMultiSelect(message, choices, index, selected) {
|
|
4528
|
+
function renderMultiSelect(message, choices, index, selected, allowBack = false) {
|
|
4520
4529
|
const lines = [`\u25C6 ${message}`, ""];
|
|
4521
4530
|
choices.forEach((choice, position) => {
|
|
4522
4531
|
const box = selected.has(position) ? "\u25FC" : "\u25FB";
|
|
4523
4532
|
const hint = choice.hint ? ` ${choice.hint}` : "";
|
|
4524
4533
|
lines.push(` ${marker(position === index)} ${box} ${choice.label}${hint}`);
|
|
4525
4534
|
});
|
|
4526
|
-
lines.push("", "
|
|
4535
|
+
lines.push("", ` ${listFooter("\u2191\u2193 move \xB7 space toggle \xB7 enter confirm", allowBack)}`);
|
|
4527
4536
|
return lines.join("\n");
|
|
4528
4537
|
}
|
|
4529
|
-
function renderConfirm(message, value) {
|
|
4538
|
+
function renderConfirm(message, value, allowBack = false) {
|
|
4539
|
+
const footer = allowBack ? "\u2190\u2192 or y/n \xB7 enter confirm \xB7 b back" : "\u2190\u2192 or y/n \xB7 enter confirm";
|
|
4530
4540
|
return [
|
|
4531
4541
|
`\u25C6 ${message}`,
|
|
4532
4542
|
"",
|
|
4533
4543
|
` ${marker(value)} Yes`,
|
|
4534
4544
|
` ${marker(!value)} No`,
|
|
4535
4545
|
"",
|
|
4536
|
-
|
|
4546
|
+
` ${footer}`
|
|
4537
4547
|
].join("\n");
|
|
4538
4548
|
}
|
|
4539
|
-
function renderText(message, value, error) {
|
|
4549
|
+
function renderText(message, value, error, allowBack = false) {
|
|
4540
4550
|
const lines = [`\u25C6 ${message}`, "", ` ${value || "\u258F"}`];
|
|
4541
4551
|
if (error) lines.push("", ` \u2716 ${error}`);
|
|
4552
|
+
if (allowBack) lines.push("", " enter confirm \xB7 \u2190 back");
|
|
4542
4553
|
return lines.join("\n");
|
|
4543
4554
|
}
|
|
4544
4555
|
function keyReader(input) {
|
|
@@ -4587,14 +4598,18 @@ var Frame = class {
|
|
|
4587
4598
|
function isCancel(key) {
|
|
4588
4599
|
return key.ctrl === true && key.name === "c" || key.ctrl === true && key.name === "d";
|
|
4589
4600
|
}
|
|
4601
|
+
function isListBack(key, allowBack) {
|
|
4602
|
+
return allowBack && (key.name === "left" || key.name === "b");
|
|
4603
|
+
}
|
|
4590
4604
|
async function select(io, message, choices, options = {}) {
|
|
4591
4605
|
if (choices.length === 0) throw new Error("select() requires at least one choice.");
|
|
4606
|
+
const allowBack = options.allowBack === true;
|
|
4592
4607
|
const fallbackIndex = Math.max(
|
|
4593
4608
|
0,
|
|
4594
4609
|
choices.findIndex((choice) => choice.value === options.defaultValue)
|
|
4595
4610
|
);
|
|
4596
4611
|
if (!isInteractive(io)) {
|
|
4597
|
-
io.output.write(`${renderSelect(message, choices, fallbackIndex)}
|
|
4612
|
+
io.output.write(`${renderSelect(message, choices, fallbackIndex, allowBack)}
|
|
4598
4613
|
`);
|
|
4599
4614
|
return choices[fallbackIndex].value;
|
|
4600
4615
|
}
|
|
@@ -4603,9 +4618,10 @@ async function select(io, message, choices, options = {}) {
|
|
|
4603
4618
|
let index = fallbackIndex;
|
|
4604
4619
|
try {
|
|
4605
4620
|
for (; ; ) {
|
|
4606
|
-
frame.draw(renderSelect(message, choices, index));
|
|
4621
|
+
frame.draw(renderSelect(message, choices, index, allowBack));
|
|
4607
4622
|
const key = await reader.next();
|
|
4608
4623
|
if (isCancel(key)) throw new PromptCancelled();
|
|
4624
|
+
if (isListBack(key, allowBack)) throw new PromptBack();
|
|
4609
4625
|
if (key.name === "up") index = moveSelection(index, -1, choices.length);
|
|
4610
4626
|
else if (key.name === "down") index = moveSelection(index, 1, choices.length);
|
|
4611
4627
|
else if (key.name === "return" || key.name === "enter") {
|
|
@@ -4614,18 +4630,22 @@ async function select(io, message, choices, options = {}) {
|
|
|
4614
4630
|
} else if (key.name === "j") index = moveSelection(index, 1, choices.length);
|
|
4615
4631
|
else if (key.name === "k") index = moveSelection(index, -1, choices.length);
|
|
4616
4632
|
}
|
|
4633
|
+
} catch (error) {
|
|
4634
|
+
frame.erase();
|
|
4635
|
+
throw error;
|
|
4617
4636
|
} finally {
|
|
4618
4637
|
reader.close();
|
|
4619
4638
|
}
|
|
4620
4639
|
}
|
|
4621
4640
|
async function multiSelect(io, message, choices, options = {}) {
|
|
4641
|
+
const allowBack = options.allowBack === true;
|
|
4622
4642
|
const selected = /* @__PURE__ */ new Set();
|
|
4623
4643
|
choices.forEach((choice, position) => {
|
|
4624
4644
|
if (options.defaults?.includes(choice.value)) selected.add(position);
|
|
4625
4645
|
});
|
|
4626
4646
|
const result = () => choices.filter((_, position) => selected.has(position)).map((choice) => choice.value);
|
|
4627
4647
|
if (!isInteractive(io)) {
|
|
4628
|
-
io.output.write(`${renderMultiSelect(message, choices, 0, selected)}
|
|
4648
|
+
io.output.write(`${renderMultiSelect(message, choices, 0, selected, allowBack)}
|
|
4629
4649
|
`);
|
|
4630
4650
|
return result();
|
|
4631
4651
|
}
|
|
@@ -4634,9 +4654,10 @@ async function multiSelect(io, message, choices, options = {}) {
|
|
|
4634
4654
|
let index = 0;
|
|
4635
4655
|
try {
|
|
4636
4656
|
for (; ; ) {
|
|
4637
|
-
frame.draw(renderMultiSelect(message, choices, index, selected));
|
|
4657
|
+
frame.draw(renderMultiSelect(message, choices, index, selected, allowBack));
|
|
4638
4658
|
const key = await reader.next();
|
|
4639
4659
|
if (isCancel(key)) throw new PromptCancelled();
|
|
4660
|
+
if (isListBack(key, allowBack)) throw new PromptBack();
|
|
4640
4661
|
if (key.name === "up") index = moveSelection(index, -1, choices.length);
|
|
4641
4662
|
else if (key.name === "down") index = moveSelection(index, 1, choices.length);
|
|
4642
4663
|
else if (key.name === "space") {
|
|
@@ -4651,14 +4672,18 @@ async function multiSelect(io, message, choices, options = {}) {
|
|
|
4651
4672
|
return result();
|
|
4652
4673
|
}
|
|
4653
4674
|
}
|
|
4675
|
+
} catch (error) {
|
|
4676
|
+
frame.erase();
|
|
4677
|
+
throw error;
|
|
4654
4678
|
} finally {
|
|
4655
4679
|
reader.close();
|
|
4656
4680
|
}
|
|
4657
4681
|
}
|
|
4658
4682
|
async function confirm(io, message, options = {}) {
|
|
4683
|
+
const allowBack = options.allowBack === true;
|
|
4659
4684
|
let value = options.defaultValue ?? true;
|
|
4660
4685
|
if (!isInteractive(io)) {
|
|
4661
|
-
io.output.write(`${renderConfirm(message, value)}
|
|
4686
|
+
io.output.write(`${renderConfirm(message, value, allowBack)}
|
|
4662
4687
|
`);
|
|
4663
4688
|
return value;
|
|
4664
4689
|
}
|
|
@@ -4666,9 +4691,10 @@ async function confirm(io, message, options = {}) {
|
|
|
4666
4691
|
const reader = keyReader(io.input);
|
|
4667
4692
|
try {
|
|
4668
4693
|
for (; ; ) {
|
|
4669
|
-
frame.draw(renderConfirm(message, value));
|
|
4694
|
+
frame.draw(renderConfirm(message, value, allowBack));
|
|
4670
4695
|
const key = await reader.next();
|
|
4671
4696
|
if (isCancel(key)) throw new PromptCancelled();
|
|
4697
|
+
if (allowBack && key.name === "b") throw new PromptBack();
|
|
4672
4698
|
if (key.name === "up" || key.name === "left") value = true;
|
|
4673
4699
|
else if (key.name === "down" || key.name === "right") value = false;
|
|
4674
4700
|
else if (key.name === "y") value = true;
|
|
@@ -4678,14 +4704,18 @@ async function confirm(io, message, options = {}) {
|
|
|
4678
4704
|
return value;
|
|
4679
4705
|
}
|
|
4680
4706
|
}
|
|
4707
|
+
} catch (error) {
|
|
4708
|
+
frame.erase();
|
|
4709
|
+
throw error;
|
|
4681
4710
|
} finally {
|
|
4682
4711
|
reader.close();
|
|
4683
4712
|
}
|
|
4684
4713
|
}
|
|
4685
4714
|
async function text(io, message, options = {}) {
|
|
4715
|
+
const allowBack = options.allowBack === true;
|
|
4686
4716
|
const fallback = options.defaultValue ?? "";
|
|
4687
4717
|
if (!isInteractive(io)) {
|
|
4688
|
-
io.output.write(`${renderText(message, fallback)}
|
|
4718
|
+
io.output.write(`${renderText(message, fallback, void 0, allowBack)}
|
|
4689
4719
|
`);
|
|
4690
4720
|
return fallback;
|
|
4691
4721
|
}
|
|
@@ -4695,9 +4725,10 @@ async function text(io, message, options = {}) {
|
|
|
4695
4725
|
let error;
|
|
4696
4726
|
try {
|
|
4697
4727
|
for (; ; ) {
|
|
4698
|
-
frame.draw(renderText(message, value, error));
|
|
4728
|
+
frame.draw(renderText(message, value, error, allowBack));
|
|
4699
4729
|
const key = await reader.next();
|
|
4700
4730
|
if (isCancel(key)) throw new PromptCancelled();
|
|
4731
|
+
if (allowBack && key.name === "left") throw new PromptBack();
|
|
4701
4732
|
error = void 0;
|
|
4702
4733
|
if (key.name === "return" || key.name === "enter") {
|
|
4703
4734
|
const problem = options.validate ? options.validate(value) : null;
|
|
@@ -4718,6 +4749,9 @@ async function text(io, message, options = {}) {
|
|
|
4718
4749
|
}
|
|
4719
4750
|
if (_isPrintable(key)) value += _keyText(key);
|
|
4720
4751
|
}
|
|
4752
|
+
} catch (error_) {
|
|
4753
|
+
frame.erase();
|
|
4754
|
+
throw error_;
|
|
4721
4755
|
} finally {
|
|
4722
4756
|
reader.close();
|
|
4723
4757
|
}
|
|
@@ -4974,98 +5008,108 @@ async function runInit(options) {
|
|
|
4974
5008
|
{ value: "walk", label: "Go through each setting", hint: "every question starts on its default" },
|
|
4975
5009
|
{ value: "choose", label: "Pick a different preset", hint: "everything else stays at the defaults" }
|
|
4976
5010
|
];
|
|
4977
|
-
const
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
{
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
]
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5011
|
+
const baseInclude = [...answers.include];
|
|
5012
|
+
const baseExclude = [...answers.excludePatterns];
|
|
5013
|
+
let lastScope = "all";
|
|
5014
|
+
const artifactChoices = [
|
|
5015
|
+
{ value: "readme", label: "README.md", hint: "orientation page" },
|
|
5016
|
+
{ value: "manifest", label: "manifest.json", hint: "required by validate and report" },
|
|
5017
|
+
{ value: "parts", label: "parts/", hint: "the code payload" },
|
|
5018
|
+
{ value: "navigation", label: "reports/navigation.md", hint: "file \u2192 part index" },
|
|
5019
|
+
{ value: "projectTree", label: "reports/project-tree.md", hint: "ASCII tree" },
|
|
5020
|
+
{ value: "audit", label: "reports/audit.md", hint: "security and health findings" },
|
|
5021
|
+
{ value: "monolith", label: "full-codebase.json + .md", hint: "roughly doubles output" }
|
|
5022
|
+
];
|
|
5023
|
+
const scopeStep = async () => {
|
|
5024
|
+
for (; ; ) {
|
|
5025
|
+
const scope = await select(
|
|
5026
|
+
io,
|
|
5027
|
+
"What should go into the snapshot?",
|
|
5028
|
+
[
|
|
5029
|
+
{ value: "all", label: "Everything the scanner finds", hint: "recommended \xB7 respects .gitignore" },
|
|
5030
|
+
{ value: "folders", label: "Only specific folders\u2026" },
|
|
5031
|
+
{ value: "no-tests", label: "Everything, but skip tests" },
|
|
5032
|
+
{ value: "custom", label: "Let me write glob patterns" }
|
|
5033
|
+
],
|
|
5034
|
+
{ defaultValue: lastScope, allowBack: true }
|
|
5035
|
+
);
|
|
5036
|
+
lastScope = scope;
|
|
5037
|
+
answers.include = [...baseInclude];
|
|
5038
|
+
answers.excludePatterns = [...baseExclude];
|
|
5039
|
+
try {
|
|
5040
|
+
if (scope === "folders") {
|
|
5041
|
+
const dirs = topLevelDirs(options.cwd).slice(0, 40);
|
|
5042
|
+
if (dirs.length === 0) {
|
|
5043
|
+
notes.push("No top-level directories found; the snapshot will include everything.");
|
|
5044
|
+
} else {
|
|
5045
|
+
const chosen = await multiSelect(
|
|
5046
|
+
io,
|
|
5047
|
+
"Select folders",
|
|
5048
|
+
dirs.map((name) => ({ value: name, label: `${name}/` })),
|
|
5049
|
+
{ defaults: dirs, allowBack: true }
|
|
5050
|
+
);
|
|
5051
|
+
answers.include = chosen.map((name) => `${name}/**`);
|
|
5052
|
+
}
|
|
5053
|
+
} else if (scope === "no-tests") {
|
|
5054
|
+
answers.excludePatterns = [...baseExclude, "**/*.test.*", "**/*.spec.*", "tests", "test"];
|
|
5055
|
+
} else if (scope === "custom") {
|
|
5056
|
+
const raw = await text(io, "Glob patterns to include (comma separated)", {
|
|
5057
|
+
defaultValue: baseInclude.join(", "),
|
|
5058
|
+
allowBack: true
|
|
5059
|
+
});
|
|
5060
|
+
answers.include = raw.split(",").map((value) => value.trim()).filter(Boolean);
|
|
5061
|
+
}
|
|
5062
|
+
return;
|
|
5063
|
+
} catch (error) {
|
|
5064
|
+
if (error instanceof PromptBack) continue;
|
|
5065
|
+
throw error;
|
|
5018
5066
|
}
|
|
5019
|
-
} else if (scope === "no-tests") {
|
|
5020
|
-
answers.excludePatterns = [
|
|
5021
|
-
...answers.excludePatterns,
|
|
5022
|
-
"**/*.test.*",
|
|
5023
|
-
"**/*.spec.*",
|
|
5024
|
-
"tests",
|
|
5025
|
-
"test"
|
|
5026
|
-
];
|
|
5027
|
-
} else if (scope === "custom") {
|
|
5028
|
-
const raw = await text(io, "Glob patterns to include (comma separated)", {
|
|
5029
|
-
defaultValue: ""
|
|
5030
|
-
});
|
|
5031
|
-
answers.include = raw.split(",").map((value) => value.trim()).filter(Boolean);
|
|
5032
5067
|
}
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
validate: (value) => parseByteSize(value) === void 0 ? "Not a valid size." : null
|
|
5068
|
+
};
|
|
5069
|
+
const sizeStep = async () => {
|
|
5070
|
+
for (; ; ) {
|
|
5071
|
+
const currentBudget = String(answers.maxPartBytes);
|
|
5072
|
+
const sizeChoice = await select(io, "How large should each part be?", SIZE_CHOICES, {
|
|
5073
|
+
defaultValue: SIZE_CHOICES.some((choice) => choice.value === currentBudget) ? currentBudget : "custom",
|
|
5074
|
+
allowBack: true
|
|
5041
5075
|
});
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5076
|
+
if (sizeChoice !== "custom") {
|
|
5077
|
+
answers.maxPartBytes = sizeChoice;
|
|
5078
|
+
return;
|
|
5079
|
+
}
|
|
5080
|
+
try {
|
|
5081
|
+
const raw = await text(io, "Part size (e.g. 150k, 1.5mb, 900000)", {
|
|
5082
|
+
defaultValue: currentBudget,
|
|
5083
|
+
validate: (value) => parseByteSize(value) === void 0 ? "Not a valid size." : null,
|
|
5084
|
+
allowBack: true
|
|
5085
|
+
});
|
|
5086
|
+
answers.maxPartBytes = parseByteSize(raw) ?? answers.maxPartBytes;
|
|
5087
|
+
return;
|
|
5088
|
+
} catch (error) {
|
|
5089
|
+
if (error instanceof PromptBack) continue;
|
|
5090
|
+
throw error;
|
|
5091
|
+
}
|
|
5092
|
+
}
|
|
5093
|
+
};
|
|
5094
|
+
const artifactsStep = async () => {
|
|
5095
|
+
const enabled = ARTIFACT_NAMES.filter((name) => answers.artifacts[name]);
|
|
5056
5096
|
const selectedArtifacts = await multiSelect(
|
|
5057
5097
|
io,
|
|
5058
5098
|
"Which artifacts should be generated?",
|
|
5059
5099
|
artifactChoices,
|
|
5060
|
-
{ defaults:
|
|
5100
|
+
{ defaults: enabled, allowBack: true }
|
|
5061
5101
|
);
|
|
5062
5102
|
for (const name of ARTIFACT_NAMES) answers.artifacts[name] = selectedArtifacts.includes(name);
|
|
5063
|
-
|
|
5103
|
+
};
|
|
5104
|
+
const credentialsStep = async () => {
|
|
5064
5105
|
answers.includeSensitiveFiles = await confirm(
|
|
5065
5106
|
io,
|
|
5066
|
-
"Include credential files (.env, *.pem,
|
|
5067
|
-
{ defaultValue:
|
|
5107
|
+
"Include credential files (.env, *.pem, *.npmrc, *.ssh/)?",
|
|
5108
|
+
{ defaultValue: answers.includeSensitiveFiles, allowBack: true }
|
|
5068
5109
|
);
|
|
5110
|
+
};
|
|
5111
|
+
const reviewStep = async () => {
|
|
5112
|
+
const enabledCount = ARTIFACT_NAMES.filter((name) => answers.artifacts[name]).length;
|
|
5069
5113
|
io.output.write("\n Review\n\n");
|
|
5070
5114
|
io.output.write(` project ${answers.preset}
|
|
5071
5115
|
`);
|
|
@@ -5080,8 +5124,53 @@ async function runInit(options) {
|
|
|
5080
5124
|
`);
|
|
5081
5125
|
io.output.write(" Will write snapshot.config with your answers, plus every other setting\n");
|
|
5082
5126
|
io.output.write(" at its default \u2014 so nothing is hidden behind the docs.\n\n");
|
|
5083
|
-
const proceed = await confirm(io, "Write this and continue?", {
|
|
5084
|
-
|
|
5127
|
+
const proceed = await confirm(io, "Write this and continue?", {
|
|
5128
|
+
defaultValue: true,
|
|
5129
|
+
allowBack: true
|
|
5130
|
+
});
|
|
5131
|
+
if (!proceed) throw new PromptBack();
|
|
5132
|
+
};
|
|
5133
|
+
const walk = async () => {
|
|
5134
|
+
const steps = [scopeStep, sizeStep, artifactsStep, credentialsStep, reviewStep];
|
|
5135
|
+
let index = 0;
|
|
5136
|
+
while (index < steps.length) {
|
|
5137
|
+
try {
|
|
5138
|
+
await steps[index]();
|
|
5139
|
+
index += 1;
|
|
5140
|
+
} catch (error) {
|
|
5141
|
+
if (!(error instanceof PromptBack)) throw error;
|
|
5142
|
+
if (index === 0) return false;
|
|
5143
|
+
index -= 1;
|
|
5144
|
+
io.output.write("\n");
|
|
5145
|
+
}
|
|
5146
|
+
}
|
|
5147
|
+
return true;
|
|
5148
|
+
};
|
|
5149
|
+
for (; ; ) {
|
|
5150
|
+
const pace = await select(io, "How should I set this up?", paceChoices, {
|
|
5151
|
+
defaultValue: fallback ? "choose" : "recommended"
|
|
5152
|
+
});
|
|
5153
|
+
if (pace === "choose") {
|
|
5154
|
+
try {
|
|
5155
|
+
answers.preset = await select(
|
|
5156
|
+
io,
|
|
5157
|
+
"What kind of project is this?",
|
|
5158
|
+
listPresetNames().map(
|
|
5159
|
+
(name) => resolvePresetChoice(fallback ? null : detected.presetName, name)
|
|
5160
|
+
),
|
|
5161
|
+
{ defaultValue: answers.preset, allowBack: true }
|
|
5162
|
+
);
|
|
5163
|
+
} catch (error) {
|
|
5164
|
+
if (error instanceof PromptBack) continue;
|
|
5165
|
+
throw error;
|
|
5166
|
+
}
|
|
5167
|
+
const chosen = detectPreset(options.cwd, answers.preset);
|
|
5168
|
+
answers.preset = chosen.presetName;
|
|
5169
|
+
reason = null;
|
|
5170
|
+
fallback = false;
|
|
5171
|
+
}
|
|
5172
|
+
if (pace === "walk" && !await walk()) continue;
|
|
5173
|
+
break;
|
|
5085
5174
|
}
|
|
5086
5175
|
}
|
|
5087
5176
|
const configText = renderConfig(answers);
|
|
@@ -5200,9 +5289,9 @@ function hasPriorSnapshot(cwd, outDir) {
|
|
|
5200
5289
|
// src/index.ts
|
|
5201
5290
|
import * as crypto3 from "crypto";
|
|
5202
5291
|
import { AsyncLocalStorage } from "async_hooks";
|
|
5203
|
-
import * as
|
|
5204
|
-
import * as
|
|
5205
|
-
import * as
|
|
5292
|
+
import * as fs12 from "fs";
|
|
5293
|
+
import * as os2 from "os";
|
|
5294
|
+
import * as path13 from "path";
|
|
5206
5295
|
|
|
5207
5296
|
// src/ast/profiles/shared.ts
|
|
5208
5297
|
var MAX_SUMMARY_CHARS = 1e6;
|
|
@@ -5426,9 +5515,9 @@ var JAVA_METHOD = /^\s+(?:public|private|protected)\s+(?:static\s+)?(?:final\s+)
|
|
|
5426
5515
|
var jvmProfile = {
|
|
5427
5516
|
id: "jvm",
|
|
5428
5517
|
extensions: [".java", ".kt", ".kts", ".scala"],
|
|
5429
|
-
summarize(
|
|
5518
|
+
summarize(path19, content) {
|
|
5430
5519
|
const summary = emptySummary();
|
|
5431
|
-
const lower =
|
|
5520
|
+
const lower = path19.toLowerCase();
|
|
5432
5521
|
const isJava = lower.endsWith(".java");
|
|
5433
5522
|
const isKotlin = lower.endsWith(".kt") || lower.endsWith(".kts");
|
|
5434
5523
|
eachLine(content, (line) => {
|
|
@@ -5884,16 +5973,16 @@ function summarizeTypeScriptSource(relativePath, content) {
|
|
|
5884
5973
|
// src/ast/profiles/sfc.ts
|
|
5885
5974
|
var SCRIPT_BLOCK = /<script[^>]*>([\s\S]*?)<\/script>/gi;
|
|
5886
5975
|
var ASTRO_FRONTMATTER = /^\uFEFF?---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*(?=\r?\n|$)/;
|
|
5887
|
-
function componentName(
|
|
5888
|
-
const base =
|
|
5976
|
+
function componentName(path19) {
|
|
5977
|
+
const base = path19.split("/").pop() ?? path19;
|
|
5889
5978
|
return base.replace(/\.[^.]+$/, "");
|
|
5890
5979
|
}
|
|
5891
5980
|
var sfcProfile = {
|
|
5892
5981
|
id: "sfc",
|
|
5893
5982
|
extensions: [".vue", ".svelte", ".astro"],
|
|
5894
|
-
summarize(
|
|
5983
|
+
summarize(path19, content) {
|
|
5895
5984
|
let script = "";
|
|
5896
|
-
if (
|
|
5985
|
+
if (path19.toLowerCase().endsWith(".astro")) {
|
|
5897
5986
|
const frontmatter = ASTRO_FRONTMATTER.exec(content);
|
|
5898
5987
|
if (frontmatter) script += `${frontmatter[1]}
|
|
5899
5988
|
`;
|
|
@@ -5902,7 +5991,7 @@ var sfcProfile = {
|
|
|
5902
5991
|
script += `${match[1]}
|
|
5903
5992
|
`;
|
|
5904
5993
|
}
|
|
5905
|
-
const name = componentName(
|
|
5994
|
+
const name = componentName(path19);
|
|
5906
5995
|
if (!script.trim()) {
|
|
5907
5996
|
const summary2 = emptySummary();
|
|
5908
5997
|
if (name) {
|
|
@@ -5936,15 +6025,15 @@ for (const profile of LANGUAGE_PROFILES) {
|
|
|
5936
6025
|
BY_EXTENSION.set(extension.toLowerCase(), profile);
|
|
5937
6026
|
}
|
|
5938
6027
|
}
|
|
5939
|
-
function profileForPath(
|
|
5940
|
-
const lower =
|
|
6028
|
+
function profileForPath(path19) {
|
|
6029
|
+
const lower = path19.toLowerCase();
|
|
5941
6030
|
const dot = lower.lastIndexOf(".");
|
|
5942
6031
|
if (dot === -1) return null;
|
|
5943
6032
|
return BY_EXTENSION.get(lower.slice(dot)) ?? null;
|
|
5944
6033
|
}
|
|
5945
|
-
function summarizeWithProfile(
|
|
5946
|
-
const profile = profileForPath(
|
|
5947
|
-
return profile ? profile.summarize(
|
|
6034
|
+
function summarizeWithProfile(path19, content) {
|
|
6035
|
+
const profile = profileForPath(path19);
|
|
6036
|
+
return profile ? profile.summarize(path19, content) : null;
|
|
5948
6037
|
}
|
|
5949
6038
|
|
|
5950
6039
|
// src/ast/structured.ts
|
|
@@ -6793,6 +6882,168 @@ function generateProjectTree(filePaths) {
|
|
|
6793
6882
|
return ["# Project Tree", "", `${fence}text`, ...lines, fence, ""].join("\n");
|
|
6794
6883
|
}
|
|
6795
6884
|
|
|
6885
|
+
// src/update-check.ts
|
|
6886
|
+
import * as fs11 from "fs";
|
|
6887
|
+
import * as https from "https";
|
|
6888
|
+
import * as os from "os";
|
|
6889
|
+
import * as path12 from "path";
|
|
6890
|
+
var PACKAGE_LATEST_URL = "https://registry.npmjs.org/codebase-snapshot/latest";
|
|
6891
|
+
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
6892
|
+
var FAILURE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
6893
|
+
var REQUEST_TIMEOUT_MS = 1200;
|
|
6894
|
+
function versionParts(value) {
|
|
6895
|
+
const core = value.trim().replace(/^v/i, "").split("-")[0];
|
|
6896
|
+
return core.split(".").map((part) => {
|
|
6897
|
+
const parsed = Number(part);
|
|
6898
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
6899
|
+
});
|
|
6900
|
+
}
|
|
6901
|
+
function isNewer(candidate, current) {
|
|
6902
|
+
const left = versionParts(candidate);
|
|
6903
|
+
const right = versionParts(current);
|
|
6904
|
+
const length = Math.max(left.length, right.length);
|
|
6905
|
+
for (let index = 0; index < length; index += 1) {
|
|
6906
|
+
const a = left[index] ?? 0;
|
|
6907
|
+
const b = right[index] ?? 0;
|
|
6908
|
+
if (a !== b) return a > b;
|
|
6909
|
+
}
|
|
6910
|
+
return false;
|
|
6911
|
+
}
|
|
6912
|
+
function cacheDirectory(env = process.env, platform = process.platform) {
|
|
6913
|
+
if (platform === "win32") {
|
|
6914
|
+
const base2 = env.LOCALAPPDATA ?? path12.join(os.homedir(), "AppData", "Local");
|
|
6915
|
+
return path12.join(base2, "codebase-snapshot");
|
|
6916
|
+
}
|
|
6917
|
+
if (platform === "darwin") {
|
|
6918
|
+
return path12.join(os.homedir(), "Library", "Caches", "codebase-snapshot");
|
|
6919
|
+
}
|
|
6920
|
+
const base = env.XDG_CACHE_HOME ?? path12.join(os.homedir(), ".cache");
|
|
6921
|
+
return path12.join(base, "codebase-snapshot");
|
|
6922
|
+
}
|
|
6923
|
+
function cacheFilePath(env = process.env) {
|
|
6924
|
+
return path12.join(cacheDirectory(env), "update-check.json");
|
|
6925
|
+
}
|
|
6926
|
+
function shouldCheck(argv, env) {
|
|
6927
|
+
const disabled = ["1", "true", "yes"].includes(
|
|
6928
|
+
String(env.CODEBASE_SNAPSHOT_NO_UPDATE_CHECK ?? "").toLowerCase()
|
|
6929
|
+
);
|
|
6930
|
+
if (disabled) return false;
|
|
6931
|
+
if (env.CI !== void 0 && env.CI !== "" && env.CI !== "false" && env.CI !== "0") return false;
|
|
6932
|
+
const args = argv.map((argument) => argument.toLowerCase());
|
|
6933
|
+
if (args.includes("--no-update-check")) return false;
|
|
6934
|
+
for (const skip of ["mcp", "describe", "skill", "--version", "-v", "--help", "-h"]) {
|
|
6935
|
+
if (args.includes(skip)) return false;
|
|
6936
|
+
}
|
|
6937
|
+
if (args.some((argument) => argument.includes("json"))) return false;
|
|
6938
|
+
return true;
|
|
6939
|
+
}
|
|
6940
|
+
function readCache(file) {
|
|
6941
|
+
let raw;
|
|
6942
|
+
try {
|
|
6943
|
+
raw = fs11.readFileSync(file, "utf8");
|
|
6944
|
+
} catch {
|
|
6945
|
+
return null;
|
|
6946
|
+
}
|
|
6947
|
+
try {
|
|
6948
|
+
const parsed = JSON.parse(raw);
|
|
6949
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
6950
|
+
const candidate = parsed;
|
|
6951
|
+
if (typeof candidate.checkedAt !== "number") return null;
|
|
6952
|
+
const latest = typeof candidate.latest === "string" ? candidate.latest : null;
|
|
6953
|
+
return { checkedAt: candidate.checkedAt, latest };
|
|
6954
|
+
} catch {
|
|
6955
|
+
return null;
|
|
6956
|
+
}
|
|
6957
|
+
}
|
|
6958
|
+
function writeCache(file, entry) {
|
|
6959
|
+
try {
|
|
6960
|
+
fs11.mkdirSync(path12.dirname(file), { recursive: true });
|
|
6961
|
+
fs11.writeFileSync(file, `${JSON.stringify(entry)}
|
|
6962
|
+
`, "utf8");
|
|
6963
|
+
} catch {
|
|
6964
|
+
}
|
|
6965
|
+
}
|
|
6966
|
+
function fetchLatestVersion(url = PACKAGE_LATEST_URL, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
6967
|
+
return new Promise((resolve9) => {
|
|
6968
|
+
let settled = false;
|
|
6969
|
+
const finish = (value) => {
|
|
6970
|
+
if (settled) return;
|
|
6971
|
+
settled = true;
|
|
6972
|
+
resolve9(value);
|
|
6973
|
+
};
|
|
6974
|
+
const request = https.get(
|
|
6975
|
+
url,
|
|
6976
|
+
{
|
|
6977
|
+
headers: {
|
|
6978
|
+
accept: "application/json",
|
|
6979
|
+
"user-agent": "codebase-snapshot (update check)"
|
|
6980
|
+
}
|
|
6981
|
+
},
|
|
6982
|
+
(response) => {
|
|
6983
|
+
if (response.statusCode !== 200) {
|
|
6984
|
+
response.resume();
|
|
6985
|
+
finish(null);
|
|
6986
|
+
return;
|
|
6987
|
+
}
|
|
6988
|
+
let body = "";
|
|
6989
|
+
response.setEncoding("utf8");
|
|
6990
|
+
response.on("data", (chunk) => {
|
|
6991
|
+
body += chunk;
|
|
6992
|
+
if (body.length > 64 * 1024) {
|
|
6993
|
+
request.destroy();
|
|
6994
|
+
finish(null);
|
|
6995
|
+
}
|
|
6996
|
+
});
|
|
6997
|
+
response.on("end", () => {
|
|
6998
|
+
try {
|
|
6999
|
+
const parsed = JSON.parse(body);
|
|
7000
|
+
finish(typeof parsed.version === "string" ? parsed.version : null);
|
|
7001
|
+
} catch {
|
|
7002
|
+
finish(null);
|
|
7003
|
+
}
|
|
7004
|
+
});
|
|
7005
|
+
}
|
|
7006
|
+
);
|
|
7007
|
+
request.on("error", () => finish(null));
|
|
7008
|
+
request.setTimeout(timeoutMs, () => {
|
|
7009
|
+
request.destroy();
|
|
7010
|
+
finish(null);
|
|
7011
|
+
});
|
|
7012
|
+
});
|
|
7013
|
+
}
|
|
7014
|
+
async function checkForUpdate(options) {
|
|
7015
|
+
if (options.enabled === false) return null;
|
|
7016
|
+
const now = options.now ?? (() => Date.now());
|
|
7017
|
+
const fetchLatest = options.fetchLatest ?? fetchLatestVersion;
|
|
7018
|
+
let latest = null;
|
|
7019
|
+
try {
|
|
7020
|
+
const cached = readCache(options.cacheFile);
|
|
7021
|
+
let needsFetch = cached === null;
|
|
7022
|
+
if (cached) {
|
|
7023
|
+
const age = now() - cached.checkedAt;
|
|
7024
|
+
if (cached.latest === null) {
|
|
7025
|
+
needsFetch = age >= FAILURE_INTERVAL_MS;
|
|
7026
|
+
} else {
|
|
7027
|
+
latest = cached.latest;
|
|
7028
|
+
needsFetch = age >= CHECK_INTERVAL_MS;
|
|
7029
|
+
}
|
|
7030
|
+
}
|
|
7031
|
+
if (needsFetch) {
|
|
7032
|
+
const fetched = await fetchLatest(PACKAGE_LATEST_URL, REQUEST_TIMEOUT_MS);
|
|
7033
|
+
writeCache(options.cacheFile, { checkedAt: now(), latest: fetched });
|
|
7034
|
+
latest = fetched;
|
|
7035
|
+
}
|
|
7036
|
+
} catch {
|
|
7037
|
+
return null;
|
|
7038
|
+
}
|
|
7039
|
+
if (!latest) return null;
|
|
7040
|
+
return isNewer(latest, options.current) ? { current: options.current, latest } : null;
|
|
7041
|
+
}
|
|
7042
|
+
function formatUpdateNotice(notice) {
|
|
7043
|
+
return `\u2191 codebase-snapshot ${notice.latest} is available \u2014 you have ${notice.current}.
|
|
7044
|
+
npm install -g codebase-snapshot`;
|
|
7045
|
+
}
|
|
7046
|
+
|
|
6796
7047
|
// src/index.ts
|
|
6797
7048
|
var SCHEMA_VERSION = "3.0.0";
|
|
6798
7049
|
var SUMMARY_MAX_SOURCE_BYTES = 2 * 1024 * 1024;
|
|
@@ -6874,13 +7125,13 @@ function buildImportResolver(files) {
|
|
|
6874
7125
|
const relativePath = file.relativePath;
|
|
6875
7126
|
remember(byPath, relativePath, relativePath);
|
|
6876
7127
|
remember(byPathFolded, relativePath.toLowerCase(), relativePath);
|
|
6877
|
-
const extension =
|
|
7128
|
+
const extension = path13.posix.extname(relativePath);
|
|
6878
7129
|
if (!SOURCE_EXTENSIONS.has(extension)) continue;
|
|
6879
7130
|
const stem = relativePath.slice(0, -extension.length);
|
|
6880
7131
|
remember(byStem, stem, relativePath);
|
|
6881
7132
|
remember(byStemFolded, stem.toLowerCase(), relativePath);
|
|
6882
|
-
if (
|
|
6883
|
-
const directory =
|
|
7133
|
+
if (path13.posix.basename(stem) === "index") {
|
|
7134
|
+
const directory = path13.posix.dirname(stem);
|
|
6884
7135
|
remember(byStem, directory, relativePath);
|
|
6885
7136
|
remember(byStemFolded, directory.toLowerCase(), relativePath);
|
|
6886
7137
|
}
|
|
@@ -6889,8 +7140,8 @@ function buildImportResolver(files) {
|
|
|
6889
7140
|
}
|
|
6890
7141
|
function resolveSpecifier(fromFile, specifier, resolver) {
|
|
6891
7142
|
if (!RELATIVE_SPECIFIER.test(specifier)) return null;
|
|
6892
|
-
const joined =
|
|
6893
|
-
|
|
7143
|
+
const joined = path13.posix.normalize(
|
|
7144
|
+
path13.posix.join(path13.posix.dirname(fromFile), specifier)
|
|
6894
7145
|
);
|
|
6895
7146
|
const exact = (key) => resolver.byPath.get(key) ?? resolver.byPathFolded.get(key.toLowerCase()) ?? null;
|
|
6896
7147
|
const direct = exact(joined);
|
|
@@ -6983,11 +7234,11 @@ function verifyInMemory(files, parts, maxPartBytes) {
|
|
|
6983
7234
|
return errors;
|
|
6984
7235
|
}
|
|
6985
7236
|
function resolveSafeOutputDirs(config) {
|
|
6986
|
-
const cwd =
|
|
6987
|
-
const outDir =
|
|
6988
|
-
const tmpDir =
|
|
6989
|
-
const root =
|
|
6990
|
-
const contains = (parent, child) => child === parent || child.startsWith(`${parent}${
|
|
7237
|
+
const cwd = path13.resolve(config.cwd);
|
|
7238
|
+
const outDir = path13.resolve(cwd, config.outDir);
|
|
7239
|
+
const tmpDir = path13.resolve(cwd, config.tmpDir);
|
|
7240
|
+
const root = path13.parse(cwd).root;
|
|
7241
|
+
const contains = (parent, child) => child === parent || child.startsWith(`${parent}${path13.sep}`);
|
|
6991
7242
|
const reject = (reason) => {
|
|
6992
7243
|
throw new Error(
|
|
6993
7244
|
`Refusing to generate: ${reason}. outDir=${outDir} tmpDir=${tmpDir} cwd=${cwd}`
|
|
@@ -7043,14 +7294,14 @@ function listEntries(dir) {
|
|
|
7043
7294
|
const walk = (absolute, prefix) => {
|
|
7044
7295
|
let children;
|
|
7045
7296
|
try {
|
|
7046
|
-
children =
|
|
7297
|
+
children = fs12.readdirSync(absolute, { withFileTypes: true });
|
|
7047
7298
|
} catch {
|
|
7048
7299
|
return;
|
|
7049
7300
|
}
|
|
7050
7301
|
for (const child of children) {
|
|
7051
7302
|
const relative11 = prefix === "" ? child.name : `${prefix}/${child.name}`;
|
|
7052
7303
|
entries.push(relative11);
|
|
7053
|
-
if (child.isDirectory()) walk(
|
|
7304
|
+
if (child.isDirectory()) walk(path13.join(absolute, child.name), relative11);
|
|
7054
7305
|
}
|
|
7055
7306
|
};
|
|
7056
7307
|
walk(dir, "");
|
|
@@ -7067,28 +7318,28 @@ function markerPayload(dir, state, id, createdAt) {
|
|
|
7067
7318
|
pid: process.pid,
|
|
7068
7319
|
entries: listEntries(dir)
|
|
7069
7320
|
};
|
|
7070
|
-
const manifestPath =
|
|
7071
|
-
if (
|
|
7072
|
-
marker2.manifestSha256 = sha256Hex2(
|
|
7321
|
+
const manifestPath = path13.join(dir, "manifest.json");
|
|
7322
|
+
if (fs12.existsSync(manifestPath)) {
|
|
7323
|
+
marker2.manifestSha256 = sha256Hex2(fs12.readFileSync(manifestPath));
|
|
7073
7324
|
}
|
|
7074
7325
|
return marker2;
|
|
7075
7326
|
}
|
|
7076
7327
|
function writeOwnershipMarker(dir, marker2) {
|
|
7077
|
-
|
|
7078
|
-
|
|
7328
|
+
fs12.writeFileSync(
|
|
7329
|
+
path13.join(dir, OWNERSHIP_MARKER),
|
|
7079
7330
|
`${JSON.stringify(marker2, null, 2)}
|
|
7080
7331
|
`,
|
|
7081
7332
|
"utf8"
|
|
7082
7333
|
);
|
|
7083
7334
|
}
|
|
7084
7335
|
function readOwnershipMarker(dir) {
|
|
7085
|
-
const markerPath =
|
|
7086
|
-
if (!
|
|
7336
|
+
const markerPath = path13.join(dir, OWNERSHIP_MARKER);
|
|
7337
|
+
if (!fs12.existsSync(markerPath)) {
|
|
7087
7338
|
return { marker: null, problem: "it holds no .codebase-snapshot marker" };
|
|
7088
7339
|
}
|
|
7089
7340
|
let raw;
|
|
7090
7341
|
try {
|
|
7091
|
-
raw =
|
|
7342
|
+
raw = fs12.readFileSync(markerPath, "utf8");
|
|
7092
7343
|
} catch {
|
|
7093
7344
|
return { marker: null, problem: "its .codebase-snapshot marker could not be read" };
|
|
7094
7345
|
}
|
|
@@ -7156,11 +7407,11 @@ function assessSnapshotDirectory(dir) {
|
|
|
7156
7407
|
marker: marker2
|
|
7157
7408
|
};
|
|
7158
7409
|
}
|
|
7159
|
-
const manifestPath =
|
|
7160
|
-
if (
|
|
7410
|
+
const manifestPath = path13.join(dir, "manifest.json");
|
|
7411
|
+
if (fs12.existsSync(manifestPath)) {
|
|
7161
7412
|
let raw;
|
|
7162
7413
|
try {
|
|
7163
|
-
raw =
|
|
7414
|
+
raw = fs12.readFileSync(manifestPath, "utf8");
|
|
7164
7415
|
} catch {
|
|
7165
7416
|
return {
|
|
7166
7417
|
owned: false,
|
|
@@ -7242,13 +7493,13 @@ function containsNoFiles(dir) {
|
|
|
7242
7493
|
const current = pending.pop();
|
|
7243
7494
|
let entries;
|
|
7244
7495
|
try {
|
|
7245
|
-
entries =
|
|
7496
|
+
entries = fs12.readdirSync(current, { withFileTypes: true });
|
|
7246
7497
|
} catch {
|
|
7247
7498
|
return false;
|
|
7248
7499
|
}
|
|
7249
7500
|
for (const entry of entries) {
|
|
7250
7501
|
if (entry.isDirectory()) {
|
|
7251
|
-
pending.push(
|
|
7502
|
+
pending.push(path13.join(current, entry.name));
|
|
7252
7503
|
} else if (!IGNORABLE_ENTRIES.has(entry.name)) {
|
|
7253
7504
|
return false;
|
|
7254
7505
|
}
|
|
@@ -7257,13 +7508,13 @@ function containsNoFiles(dir) {
|
|
|
7257
7508
|
return true;
|
|
7258
7509
|
}
|
|
7259
7510
|
function assertReplaceable(target, label, secretRules) {
|
|
7260
|
-
if (!
|
|
7261
|
-
if (!
|
|
7511
|
+
if (!fs12.existsSync(target)) return;
|
|
7512
|
+
if (!fs12.statSync(target).isDirectory()) {
|
|
7262
7513
|
throw new Error(
|
|
7263
7514
|
`Refusing to write into ${label} ${target}: it exists but is not a directory.`
|
|
7264
7515
|
);
|
|
7265
7516
|
}
|
|
7266
|
-
const entries =
|
|
7517
|
+
const entries = fs12.readdirSync(target);
|
|
7267
7518
|
if (entries.length === 0) return;
|
|
7268
7519
|
if (entries.every((name) => IGNORABLE_ENTRIES.has(name))) return;
|
|
7269
7520
|
if (containsNoFiles(target)) return;
|
|
@@ -7283,18 +7534,18 @@ function assertReplaceable(target, label, secretRules) {
|
|
|
7283
7534
|
);
|
|
7284
7535
|
}
|
|
7285
7536
|
function assertStagingRootUsable(tmpDir, secretRules) {
|
|
7286
|
-
if (!
|
|
7287
|
-
if (!
|
|
7537
|
+
if (!fs12.existsSync(tmpDir)) return;
|
|
7538
|
+
if (!fs12.statSync(tmpDir).isDirectory()) {
|
|
7288
7539
|
throw new Error(`Refusing to write into tmpDir ${tmpDir}: it exists but is not a directory.`);
|
|
7289
7540
|
}
|
|
7290
|
-
const entries =
|
|
7541
|
+
const entries = fs12.readdirSync(tmpDir);
|
|
7291
7542
|
if (entries.length === 0) return;
|
|
7292
7543
|
const foreign = entries.filter((name) => {
|
|
7293
|
-
const full =
|
|
7544
|
+
const full = path13.join(tmpDir, name);
|
|
7294
7545
|
if (!name.startsWith(RUN_DIR_PREFIX)) return true;
|
|
7295
7546
|
let stats;
|
|
7296
7547
|
try {
|
|
7297
|
-
stats =
|
|
7548
|
+
stats = fs12.statSync(full);
|
|
7298
7549
|
} catch {
|
|
7299
7550
|
return true;
|
|
7300
7551
|
}
|
|
@@ -7302,7 +7553,7 @@ function assertStagingRootUsable(tmpDir, secretRules) {
|
|
|
7302
7553
|
const { marker: marker2 } = readOwnershipMarker(full);
|
|
7303
7554
|
if (marker2) return false;
|
|
7304
7555
|
try {
|
|
7305
|
-
return
|
|
7556
|
+
return fs12.readdirSync(full).length > 0;
|
|
7306
7557
|
} catch {
|
|
7307
7558
|
return true;
|
|
7308
7559
|
}
|
|
@@ -7317,13 +7568,13 @@ function assertStagingRootUsable(tmpDir, secretRules) {
|
|
|
7317
7568
|
function cleanStaleRunDirs(tmpDir) {
|
|
7318
7569
|
let entries;
|
|
7319
7570
|
try {
|
|
7320
|
-
entries =
|
|
7571
|
+
entries = fs12.readdirSync(tmpDir);
|
|
7321
7572
|
} catch {
|
|
7322
7573
|
return;
|
|
7323
7574
|
}
|
|
7324
7575
|
for (const name of entries) {
|
|
7325
7576
|
if (!name.startsWith(RUN_DIR_PREFIX)) continue;
|
|
7326
|
-
const full =
|
|
7577
|
+
const full = path13.join(tmpDir, name);
|
|
7327
7578
|
const { marker: marker2 } = readOwnershipMarker(full);
|
|
7328
7579
|
if (marker2) {
|
|
7329
7580
|
const created = Date.parse(marker2.createdAt);
|
|
@@ -7331,33 +7582,33 @@ function cleanStaleRunDirs(tmpDir) {
|
|
|
7331
7582
|
const abandoned = typeof marker2.pid === "number" && !isProcessAlive(marker2.pid);
|
|
7332
7583
|
if (aged || abandoned) {
|
|
7333
7584
|
try {
|
|
7334
|
-
|
|
7585
|
+
fs12.rmSync(full, { recursive: true, force: true });
|
|
7335
7586
|
} catch {
|
|
7336
7587
|
}
|
|
7337
7588
|
}
|
|
7338
7589
|
continue;
|
|
7339
7590
|
}
|
|
7340
7591
|
try {
|
|
7341
|
-
if (
|
|
7592
|
+
if (fs12.readdirSync(full).length === 0) fs12.rmSync(full, { recursive: true, force: true });
|
|
7342
7593
|
} catch {
|
|
7343
7594
|
}
|
|
7344
7595
|
}
|
|
7345
7596
|
}
|
|
7346
7597
|
function removeIfEmpty(dir) {
|
|
7347
7598
|
try {
|
|
7348
|
-
if (
|
|
7599
|
+
if (fs12.readdirSync(dir).length === 0) fs12.rmSync(dir, { recursive: true, force: true });
|
|
7349
7600
|
} catch {
|
|
7350
7601
|
}
|
|
7351
7602
|
}
|
|
7352
7603
|
function ensureParentDirectory(target, label) {
|
|
7353
|
-
const parent =
|
|
7354
|
-
if (
|
|
7604
|
+
const parent = path13.dirname(target);
|
|
7605
|
+
if (fs12.existsSync(parent) && !fs12.statSync(parent).isDirectory()) {
|
|
7355
7606
|
throw new Error(
|
|
7356
7607
|
`Refusing to create ${label} ${target}: ${parent} exists but is not a directory.`
|
|
7357
7608
|
);
|
|
7358
7609
|
}
|
|
7359
7610
|
try {
|
|
7360
|
-
|
|
7611
|
+
fs12.mkdirSync(parent, { recursive: true });
|
|
7361
7612
|
} catch (err) {
|
|
7362
7613
|
throw new Error(
|
|
7363
7614
|
`Refusing to create ${label} ${target}: its parent ${parent} could not be created (${err.message}).`
|
|
@@ -7366,7 +7617,7 @@ function ensureParentDirectory(target, label) {
|
|
|
7366
7617
|
}
|
|
7367
7618
|
function acquireRunLock(stagingRoot) {
|
|
7368
7619
|
const key = lockKeyFor(stagingRoot);
|
|
7369
|
-
const lockPath =
|
|
7620
|
+
const lockPath = path13.join(os2.tmpdir(), `codebase-snapshot-${key}.lock`);
|
|
7370
7621
|
const payload = `${JSON.stringify({
|
|
7371
7622
|
pid: process.pid,
|
|
7372
7623
|
stagingRoot,
|
|
@@ -7375,22 +7626,22 @@ function acquireRunLock(stagingRoot) {
|
|
|
7375
7626
|
`;
|
|
7376
7627
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
7377
7628
|
try {
|
|
7378
|
-
const handle =
|
|
7629
|
+
const handle = fs12.openSync(lockPath, "wx");
|
|
7379
7630
|
try {
|
|
7380
|
-
|
|
7631
|
+
fs12.writeSync(handle, payload);
|
|
7381
7632
|
} finally {
|
|
7382
|
-
|
|
7633
|
+
fs12.closeSync(handle);
|
|
7383
7634
|
}
|
|
7384
7635
|
return () => {
|
|
7385
7636
|
try {
|
|
7386
|
-
|
|
7637
|
+
fs12.rmSync(lockPath, { force: true });
|
|
7387
7638
|
} catch {
|
|
7388
7639
|
}
|
|
7389
7640
|
};
|
|
7390
7641
|
} catch (err) {
|
|
7391
7642
|
if (err.code !== "EEXIST") throw err;
|
|
7392
7643
|
if (attempt === 0 && lockOwnerIsGone(lockPath)) {
|
|
7393
|
-
|
|
7644
|
+
fs12.rmSync(lockPath, { force: true });
|
|
7394
7645
|
continue;
|
|
7395
7646
|
}
|
|
7396
7647
|
throw new Error(
|
|
@@ -7403,7 +7654,7 @@ function acquireRunLock(stagingRoot) {
|
|
|
7403
7654
|
function lockKeyFor(stagingRoot) {
|
|
7404
7655
|
let key = stagingRoot;
|
|
7405
7656
|
try {
|
|
7406
|
-
key =
|
|
7657
|
+
key = fs12.realpathSync(stagingRoot);
|
|
7407
7658
|
} catch {
|
|
7408
7659
|
}
|
|
7409
7660
|
if (process.platform === "win32" || process.platform === "darwin") key = key.toLowerCase();
|
|
@@ -7412,7 +7663,7 @@ function lockKeyFor(stagingRoot) {
|
|
|
7412
7663
|
function lockOwnerIsGone(lockPath) {
|
|
7413
7664
|
let raw;
|
|
7414
7665
|
try {
|
|
7415
|
-
raw =
|
|
7666
|
+
raw = fs12.readFileSync(lockPath, "utf8");
|
|
7416
7667
|
} catch {
|
|
7417
7668
|
return false;
|
|
7418
7669
|
}
|
|
@@ -7432,12 +7683,12 @@ function lockOwnerIsGone(lockPath) {
|
|
|
7432
7683
|
}
|
|
7433
7684
|
function writeManifestAtomically(manifestPath, manifest) {
|
|
7434
7685
|
const tempPath = `${manifestPath}.${process.pid}.tmp`;
|
|
7435
|
-
|
|
7686
|
+
fs12.writeFileSync(tempPath, JSON.stringify(manifest, null, 2), "utf8");
|
|
7436
7687
|
try {
|
|
7437
|
-
|
|
7688
|
+
fs12.renameSync(tempPath, manifestPath);
|
|
7438
7689
|
} catch (err) {
|
|
7439
7690
|
try {
|
|
7440
|
-
|
|
7691
|
+
fs12.rmSync(tempPath, { force: true });
|
|
7441
7692
|
} catch {
|
|
7442
7693
|
}
|
|
7443
7694
|
throw err;
|
|
@@ -7460,31 +7711,31 @@ function swapIntoPlace(stagingDir, outDir) {
|
|
|
7460
7711
|
}
|
|
7461
7712
|
function swapOnce(stagingDir, outDir) {
|
|
7462
7713
|
const backupDir = `${outDir}.backup-${process.pid}-${Date.now()}`;
|
|
7463
|
-
const hadPrevious =
|
|
7464
|
-
if (hadPrevious)
|
|
7714
|
+
const hadPrevious = fs12.existsSync(outDir);
|
|
7715
|
+
if (hadPrevious) fs12.renameSync(outDir, backupDir);
|
|
7465
7716
|
try {
|
|
7466
7717
|
try {
|
|
7467
|
-
|
|
7718
|
+
fs12.renameSync(stagingDir, outDir);
|
|
7468
7719
|
} catch (err) {
|
|
7469
7720
|
if (err.code !== "EXDEV") throw err;
|
|
7470
7721
|
const partialDir = `${outDir}.partial-${process.pid}-${Date.now()}`;
|
|
7471
7722
|
try {
|
|
7472
|
-
|
|
7473
|
-
|
|
7474
|
-
|
|
7723
|
+
fs12.cpSync(stagingDir, partialDir, { recursive: true });
|
|
7724
|
+
fs12.renameSync(partialDir, outDir);
|
|
7725
|
+
fs12.rmSync(stagingDir, { recursive: true, force: true });
|
|
7475
7726
|
} catch (copyErr) {
|
|
7476
7727
|
try {
|
|
7477
|
-
|
|
7728
|
+
fs12.rmSync(partialDir, { recursive: true, force: true });
|
|
7478
7729
|
} catch {
|
|
7479
7730
|
}
|
|
7480
7731
|
throw copyErr;
|
|
7481
7732
|
}
|
|
7482
7733
|
}
|
|
7483
|
-
if (hadPrevious)
|
|
7734
|
+
if (hadPrevious) fs12.rmSync(backupDir, { recursive: true, force: true });
|
|
7484
7735
|
} catch (err) {
|
|
7485
|
-
if (hadPrevious &&
|
|
7486
|
-
if (
|
|
7487
|
-
|
|
7736
|
+
if (hadPrevious && fs12.existsSync(backupDir)) {
|
|
7737
|
+
if (fs12.existsSync(outDir)) fs12.rmSync(outDir, { recursive: true, force: true });
|
|
7738
|
+
fs12.renameSync(backupDir, outDir);
|
|
7488
7739
|
}
|
|
7489
7740
|
throw err;
|
|
7490
7741
|
}
|
|
@@ -7579,7 +7830,7 @@ function generateSnapshot(configInput, options) {
|
|
|
7579
7830
|
// first run cannot have marked them yet, so the name alone keeps a run from recording its own
|
|
7580
7831
|
// output — but only at these exact paths, not at every directory that shares the name.
|
|
7581
7832
|
toolOwnedPaths: [config.outDir, config.tmpDir].map(
|
|
7582
|
-
(directory) =>
|
|
7833
|
+
(directory) => path13.relative(config.cwd, path13.resolve(config.cwd, directory)).replace(/\\/g, "/")
|
|
7583
7834
|
).filter((relative11) => relative11.length > 0 && !relative11.startsWith(".."))
|
|
7584
7835
|
});
|
|
7585
7836
|
const files = [];
|
|
@@ -7600,12 +7851,12 @@ function generateSnapshot(configInput, options) {
|
|
|
7600
7851
|
let totalSizeBytes = 0;
|
|
7601
7852
|
let largeFileCount = 0;
|
|
7602
7853
|
for (const relativePath of collection.files) {
|
|
7603
|
-
const absolutePath =
|
|
7854
|
+
const absolutePath = path13.join(config.cwd, relativePath);
|
|
7604
7855
|
let stats;
|
|
7605
7856
|
try {
|
|
7606
|
-
stats =
|
|
7857
|
+
stats = fs12.statSync(absolutePath);
|
|
7607
7858
|
if (!stats.isFile()) {
|
|
7608
|
-
const nestedRepository = stats.isDirectory() &&
|
|
7859
|
+
const nestedRepository = stats.isDirectory() && fs12.existsSync(path13.join(absolutePath, ".git"));
|
|
7609
7860
|
skipped.push(
|
|
7610
7861
|
nestedRepository ? {
|
|
7611
7862
|
path: relativePath,
|
|
@@ -7625,7 +7876,7 @@ function generateSnapshot(configInput, options) {
|
|
|
7625
7876
|
}
|
|
7626
7877
|
let buffer;
|
|
7627
7878
|
try {
|
|
7628
|
-
buffer =
|
|
7879
|
+
buffer = fs12.readFileSync(absolutePath);
|
|
7629
7880
|
} catch {
|
|
7630
7881
|
skipped.push({ path: relativePath, reason: "read-error" });
|
|
7631
7882
|
continue;
|
|
@@ -7955,12 +8206,12 @@ function generateSnapshot(configInput, options) {
|
|
|
7955
8206
|
let stagingDir = null;
|
|
7956
8207
|
try {
|
|
7957
8208
|
ensureParentDirectory(outDir, "outDir");
|
|
7958
|
-
|
|
8209
|
+
fs12.mkdirSync(tmpDir, { recursive: true });
|
|
7959
8210
|
cleanStaleRunDirs(tmpDir);
|
|
7960
8211
|
const snapshotId = crypto3.randomUUID();
|
|
7961
8212
|
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7962
|
-
stagingDir =
|
|
7963
|
-
|
|
8213
|
+
stagingDir = path13.join(tmpDir, `${RUN_DIR_PREFIX}${process.pid}-${snapshotId.slice(0, 8)}`);
|
|
8214
|
+
fs12.mkdirSync(stagingDir);
|
|
7964
8215
|
writeOwnershipMarker(stagingDir, {
|
|
7965
8216
|
generator: GENERATOR_NAME,
|
|
7966
8217
|
version: GENERATOR_VERSION,
|
|
@@ -7970,41 +8221,41 @@ function generateSnapshot(configInput, options) {
|
|
|
7970
8221
|
id: snapshotId,
|
|
7971
8222
|
pid: process.pid
|
|
7972
8223
|
});
|
|
7973
|
-
const partsDir =
|
|
7974
|
-
const reportsDir =
|
|
7975
|
-
if (artifacts.parts)
|
|
8224
|
+
const partsDir = path13.join(stagingDir, "parts");
|
|
8225
|
+
const reportsDir = path13.join(stagingDir, "reports");
|
|
8226
|
+
if (artifacts.parts) fs12.mkdirSync(partsDir, { recursive: true });
|
|
7976
8227
|
if (artifacts.navigation || artifacts.projectTree || artifacts.audit) {
|
|
7977
|
-
|
|
8228
|
+
fs12.mkdirSync(reportsDir, { recursive: true });
|
|
7978
8229
|
}
|
|
7979
8230
|
if (artifacts.parts) {
|
|
7980
8231
|
for (const part of parts) {
|
|
7981
|
-
|
|
8232
|
+
fs12.writeFileSync(path13.join(partsDir, part.filename), part.markdown, "utf8");
|
|
7982
8233
|
}
|
|
7983
8234
|
}
|
|
7984
8235
|
if (projectTree !== null) {
|
|
7985
|
-
|
|
8236
|
+
fs12.writeFileSync(path13.join(reportsDir, "project-tree.md"), projectTree, "utf8");
|
|
7986
8237
|
}
|
|
7987
8238
|
if (artifacts.navigation) {
|
|
7988
|
-
|
|
8239
|
+
fs12.writeFileSync(path13.join(reportsDir, "navigation.md"), generateNavigation(manifest), "utf8");
|
|
7989
8240
|
}
|
|
7990
8241
|
if (artifacts.audit) {
|
|
7991
|
-
|
|
7992
|
-
|
|
8242
|
+
fs12.writeFileSync(
|
|
8243
|
+
path13.join(reportsDir, "audit.md"),
|
|
7993
8244
|
generateAuditReport(manifest, secretFindings, config.secretRules),
|
|
7994
8245
|
"utf8"
|
|
7995
8246
|
);
|
|
7996
8247
|
}
|
|
7997
8248
|
if (artifacts.readme) {
|
|
7998
|
-
|
|
8249
|
+
fs12.writeFileSync(path13.join(stagingDir, "README.md"), generateReadme(manifest, artifacts), "utf8");
|
|
7999
8250
|
}
|
|
8000
8251
|
if (artifacts.manifest) {
|
|
8001
|
-
|
|
8252
|
+
fs12.writeFileSync(path13.join(stagingDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf8");
|
|
8002
8253
|
}
|
|
8003
8254
|
if (monolithJson !== null) {
|
|
8004
|
-
|
|
8255
|
+
fs12.writeFileSync(path13.join(stagingDir, "full-codebase.json"), monolithJson, "utf8");
|
|
8005
8256
|
}
|
|
8006
8257
|
if (monolithMarkdown !== null) {
|
|
8007
|
-
|
|
8258
|
+
fs12.writeFileSync(path13.join(stagingDir, "full-codebase.md"), monolithMarkdown, "utf8");
|
|
8008
8259
|
}
|
|
8009
8260
|
writeOwnershipMarker(stagingDir, markerPayload(stagingDir, "complete", snapshotId, createdAt));
|
|
8010
8261
|
swapIntoPlace(stagingDir, outDir);
|
|
@@ -8013,7 +8264,7 @@ function generateSnapshot(configInput, options) {
|
|
|
8013
8264
|
const pendingHook = runAfterGenerate(config, manifest);
|
|
8014
8265
|
const shipHookState = () => {
|
|
8015
8266
|
if (artifacts.manifest && manifest.warnings.length !== warningsBeforeHook) {
|
|
8016
|
-
writeManifestAtomically(
|
|
8267
|
+
writeManifestAtomically(path13.join(outDir, "manifest.json"), manifest);
|
|
8017
8268
|
}
|
|
8018
8269
|
writeOwnershipMarker(outDir, markerPayload(outDir, "complete", snapshotId, createdAt));
|
|
8019
8270
|
};
|
|
@@ -8036,7 +8287,7 @@ function generateSnapshot(configInput, options) {
|
|
|
8036
8287
|
} catch (err) {
|
|
8037
8288
|
if (stagingDir !== null) {
|
|
8038
8289
|
try {
|
|
8039
|
-
|
|
8290
|
+
fs12.rmSync(stagingDir, { recursive: true, force: true });
|
|
8040
8291
|
} catch {
|
|
8041
8292
|
}
|
|
8042
8293
|
}
|
|
@@ -8048,8 +8299,8 @@ function generateSnapshot(configInput, options) {
|
|
|
8048
8299
|
}
|
|
8049
8300
|
|
|
8050
8301
|
// src/mcp/tools.ts
|
|
8051
|
-
import * as
|
|
8052
|
-
import * as
|
|
8302
|
+
import * as fs14 from "fs";
|
|
8303
|
+
import * as path15 from "path";
|
|
8053
8304
|
|
|
8054
8305
|
// src/mcp/part.ts
|
|
8055
8306
|
var FILE_HEADING = "### FILE: ";
|
|
@@ -8135,15 +8386,15 @@ function windowText(body, fromLine, lineCount) {
|
|
|
8135
8386
|
}
|
|
8136
8387
|
|
|
8137
8388
|
// src/mcp/workspace.ts
|
|
8138
|
-
import * as
|
|
8139
|
-
import * as
|
|
8389
|
+
import * as fs13 from "fs";
|
|
8390
|
+
import * as path14 from "path";
|
|
8140
8391
|
function foldForComparison(value) {
|
|
8141
8392
|
return process.platform === "win32" || process.platform === "darwin" ? value.toLowerCase() : value;
|
|
8142
8393
|
}
|
|
8143
8394
|
function canonicalPath(value) {
|
|
8144
|
-
const absolute =
|
|
8395
|
+
const absolute = path14.resolve(value);
|
|
8145
8396
|
try {
|
|
8146
|
-
return
|
|
8397
|
+
return fs13.realpathSync(absolute);
|
|
8147
8398
|
} catch {
|
|
8148
8399
|
return absolute;
|
|
8149
8400
|
}
|
|
@@ -8151,17 +8402,17 @@ function canonicalPath(value) {
|
|
|
8151
8402
|
function isInside2(parent, child) {
|
|
8152
8403
|
const foldedParent = foldForComparison(parent);
|
|
8153
8404
|
const foldedChild = foldForComparison(child);
|
|
8154
|
-
return foldedChild === foldedParent || foldedChild.startsWith(`${foldedParent}${
|
|
8405
|
+
return foldedChild === foldedParent || foldedChild.startsWith(`${foldedParent}${path14.sep}`);
|
|
8155
8406
|
}
|
|
8156
8407
|
function resolveRoot(requested, context) {
|
|
8157
8408
|
if (requested !== void 0 && (typeof requested !== "string" || requested.trim().length === 0)) {
|
|
8158
8409
|
return { ok: false, message: '"root" must be a non-empty string when provided.' };
|
|
8159
8410
|
}
|
|
8160
8411
|
const wanted = typeof requested === "string" ? requested.trim() : context.cwd;
|
|
8161
|
-
const absolute =
|
|
8412
|
+
const absolute = path14.resolve(context.cwd, wanted);
|
|
8162
8413
|
let stats;
|
|
8163
8414
|
try {
|
|
8164
|
-
stats =
|
|
8415
|
+
stats = fs13.statSync(absolute);
|
|
8165
8416
|
} catch {
|
|
8166
8417
|
return { ok: false, message: `"${wanted}" does not exist, so it cannot be a repository root.` };
|
|
8167
8418
|
}
|
|
@@ -8212,14 +8463,14 @@ async function openWorkspace(root, overrides = {}) {
|
|
|
8212
8463
|
}
|
|
8213
8464
|
var manifestCache = /* @__PURE__ */ new Map();
|
|
8214
8465
|
function manifestPathFor(workspace) {
|
|
8215
|
-
return
|
|
8466
|
+
return path14.resolve(workspace.root, workspace.config.outDir, "manifest.json");
|
|
8216
8467
|
}
|
|
8217
8468
|
function readManifest3(workspace) {
|
|
8218
8469
|
const file = manifestPathFor(workspace);
|
|
8219
|
-
const shown =
|
|
8470
|
+
const shown = path14.relative(workspace.root, file).split(path14.sep).join("/") || file;
|
|
8220
8471
|
let stats;
|
|
8221
8472
|
try {
|
|
8222
|
-
stats =
|
|
8473
|
+
stats = fs13.statSync(file);
|
|
8223
8474
|
} catch {
|
|
8224
8475
|
return {
|
|
8225
8476
|
ok: false,
|
|
@@ -8233,7 +8484,7 @@ function readManifest3(workspace) {
|
|
|
8233
8484
|
}
|
|
8234
8485
|
let raw;
|
|
8235
8486
|
try {
|
|
8236
|
-
raw =
|
|
8487
|
+
raw = fs13.readFileSync(file, "utf8");
|
|
8237
8488
|
} catch (error) {
|
|
8238
8489
|
return {
|
|
8239
8490
|
ok: false,
|
|
@@ -8291,7 +8542,7 @@ function formatBytes(bytes) {
|
|
|
8291
8542
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
8292
8543
|
}
|
|
8293
8544
|
function relativeFor(workspace, absolute) {
|
|
8294
|
-
return
|
|
8545
|
+
return path15.relative(workspace.root, absolute).split(path15.sep).join("/");
|
|
8295
8546
|
}
|
|
8296
8547
|
function capped(values, limit = 20) {
|
|
8297
8548
|
return { count: values.length, sample: values.slice(0, limit) };
|
|
@@ -8310,12 +8561,12 @@ function summarizeDrift(drift) {
|
|
|
8310
8561
|
function toRelativePath(root, value) {
|
|
8311
8562
|
const cleaned = value.trim();
|
|
8312
8563
|
if (cleaned.length === 0) return null;
|
|
8313
|
-
if (!
|
|
8564
|
+
if (!path15.isAbsolute(cleaned)) {
|
|
8314
8565
|
return cleaned.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
8315
8566
|
}
|
|
8316
|
-
const relative11 =
|
|
8317
|
-
if (relative11.startsWith("..") ||
|
|
8318
|
-
return relative11.split(
|
|
8567
|
+
const relative11 = path15.relative(root, cleaned);
|
|
8568
|
+
if (relative11.startsWith("..") || path15.isAbsolute(relative11)) return null;
|
|
8569
|
+
return relative11.split(path15.sep).join("/");
|
|
8319
8570
|
}
|
|
8320
8571
|
function symbolsOf(entry) {
|
|
8321
8572
|
const summary = entry.summary;
|
|
@@ -8526,7 +8777,7 @@ var generateTool = {
|
|
|
8526
8777
|
return failure(error instanceof Error ? error.message : String(error));
|
|
8527
8778
|
}
|
|
8528
8779
|
forgetManifest(manifestPathFor(workspace));
|
|
8529
|
-
const written = relativeFor(workspace,
|
|
8780
|
+
const written = relativeFor(workspace, path15.resolve(workspace.root, workspace.config.outDir));
|
|
8530
8781
|
const lines = [
|
|
8531
8782
|
`Wrote ${written}/`,
|
|
8532
8783
|
`Files: ${manifest.counts.totalFiles} included, ${manifest.counts.skippedCount} skipped, ${plural(manifest.counts.partsCount, "part")}`,
|
|
@@ -8791,10 +9042,10 @@ var readTool = {
|
|
|
8791
9042
|
`${relativePath} is not in the snapshot. Call snapshot_locate to find the path, or snapshot_explain to learn why it was excluded.`
|
|
8792
9043
|
);
|
|
8793
9044
|
}
|
|
8794
|
-
const partPath =
|
|
9045
|
+
const partPath = path15.resolve(workspace.root, workspace.config.outDir, "parts", entry.part);
|
|
8795
9046
|
let partText;
|
|
8796
9047
|
try {
|
|
8797
|
-
partText =
|
|
9048
|
+
partText = fs14.readFileSync(partPath, "utf8");
|
|
8798
9049
|
} catch {
|
|
8799
9050
|
return failure(
|
|
8800
9051
|
`${entry.part} could not be read, so the snapshot is damaged. Run snapshot_generate to rebuild it.`
|
|
@@ -9056,6 +9307,7 @@ var VALIDATE_OPTIONS = [
|
|
|
9056
9307
|
"no-config",
|
|
9057
9308
|
"discovery",
|
|
9058
9309
|
"format",
|
|
9310
|
+
"no-update-check",
|
|
9059
9311
|
"mode",
|
|
9060
9312
|
"help",
|
|
9061
9313
|
"version"
|
|
@@ -9079,6 +9331,7 @@ var COMMAND_OPTION_SCOPE = {
|
|
|
9079
9331
|
"watch",
|
|
9080
9332
|
"autopilot",
|
|
9081
9333
|
"no-install",
|
|
9334
|
+
"no-update-check",
|
|
9082
9335
|
"help",
|
|
9083
9336
|
"version"
|
|
9084
9337
|
],
|
|
@@ -9094,6 +9347,7 @@ var COMMAND_OPTION_SCOPE = {
|
|
|
9094
9347
|
"skip-artifacts",
|
|
9095
9348
|
"only-artifacts",
|
|
9096
9349
|
"no-config",
|
|
9350
|
+
"no-update-check",
|
|
9097
9351
|
"format",
|
|
9098
9352
|
"mode",
|
|
9099
9353
|
"help",
|
|
@@ -9111,12 +9365,13 @@ var COMMAND_OPTION_SCOPE = {
|
|
|
9111
9365
|
"only-artifacts",
|
|
9112
9366
|
"no-config",
|
|
9113
9367
|
"discovery",
|
|
9368
|
+
"no-update-check",
|
|
9114
9369
|
"format",
|
|
9115
9370
|
"mode",
|
|
9116
9371
|
"help",
|
|
9117
9372
|
"version"
|
|
9118
9373
|
],
|
|
9119
|
-
diff: ["format", "mode", "no-config", "help", "version"],
|
|
9374
|
+
diff: ["format", "mode", "no-config", "no-update-check", "help", "version"],
|
|
9120
9375
|
describe: ["format", "mode", "help", "version"],
|
|
9121
9376
|
init: [
|
|
9122
9377
|
"cwd",
|
|
@@ -9131,6 +9386,7 @@ var COMMAND_OPTION_SCOPE = {
|
|
|
9131
9386
|
"yes",
|
|
9132
9387
|
"force",
|
|
9133
9388
|
"dry-run",
|
|
9389
|
+
"no-update-check",
|
|
9134
9390
|
"format",
|
|
9135
9391
|
"mode",
|
|
9136
9392
|
"help",
|
|
@@ -9154,6 +9410,7 @@ var COMMAND_OPTION_SCOPE = {
|
|
|
9154
9410
|
"force",
|
|
9155
9411
|
"no-install",
|
|
9156
9412
|
"dry-run",
|
|
9413
|
+
"no-update-check",
|
|
9157
9414
|
"format",
|
|
9158
9415
|
"mode",
|
|
9159
9416
|
"help",
|
|
@@ -9586,6 +9843,13 @@ function buildAgentContract() {
|
|
|
9586
9843
|
default: false,
|
|
9587
9844
|
appliesTo: optionAppliesTo("no-install"),
|
|
9588
9845
|
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)."
|
|
9846
|
+
},
|
|
9847
|
+
{
|
|
9848
|
+
flag: "--no-update-check",
|
|
9849
|
+
type: "boolean",
|
|
9850
|
+
default: false,
|
|
9851
|
+
appliesTo: optionAppliesTo("no-update-check"),
|
|
9852
|
+
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."
|
|
9589
9853
|
}
|
|
9590
9854
|
],
|
|
9591
9855
|
presets: PRESETS.map((preset) => ({
|
|
@@ -9718,8 +9982,8 @@ function buildAgentContract() {
|
|
|
9718
9982
|
}
|
|
9719
9983
|
|
|
9720
9984
|
// src/agent/install-skill.ts
|
|
9721
|
-
import * as
|
|
9722
|
-
import * as
|
|
9985
|
+
import * as fs15 from "fs";
|
|
9986
|
+
import * as path16 from "path";
|
|
9723
9987
|
|
|
9724
9988
|
// src/agent/skill.ts
|
|
9725
9989
|
var PACKAGE_NAME2 = "codebase-snapshot";
|
|
@@ -9877,17 +10141,17 @@ var LEGACY_AGENTS_MARKER = "<!-- codebase-snapshot:skill:start -->";
|
|
|
9877
10141
|
function targetPath(cwd, target) {
|
|
9878
10142
|
switch (target) {
|
|
9879
10143
|
case "claude":
|
|
9880
|
-
return
|
|
10144
|
+
return path16.join(cwd, ".claude", "skills", SKILL_NAME, "SKILL.md");
|
|
9881
10145
|
case "cursor":
|
|
9882
|
-
return
|
|
10146
|
+
return path16.join(cwd, ".cursor", "rules", `${SKILL_NAME}.mdc`);
|
|
9883
10147
|
}
|
|
9884
10148
|
}
|
|
9885
10149
|
function assertWritableInsideRepository(cwd, filePath) {
|
|
9886
|
-
const root =
|
|
9887
|
-
const shown =
|
|
10150
|
+
const root = fs15.realpathSync(cwd);
|
|
10151
|
+
const shown = path16.relative(root, filePath) || filePath;
|
|
9888
10152
|
let stat = null;
|
|
9889
10153
|
try {
|
|
9890
|
-
stat =
|
|
10154
|
+
stat = fs15.lstatSync(filePath);
|
|
9891
10155
|
} catch {
|
|
9892
10156
|
stat = null;
|
|
9893
10157
|
}
|
|
@@ -9903,32 +10167,32 @@ function assertWritableInsideRepository(cwd, filePath) {
|
|
|
9903
10167
|
}
|
|
9904
10168
|
let existing = filePath;
|
|
9905
10169
|
const missing = [];
|
|
9906
|
-
while (!
|
|
9907
|
-
missing.unshift(
|
|
9908
|
-
const parent =
|
|
10170
|
+
while (!fs15.existsSync(existing)) {
|
|
10171
|
+
missing.unshift(path16.basename(existing));
|
|
10172
|
+
const parent = path16.dirname(existing);
|
|
9909
10173
|
if (parent === existing) break;
|
|
9910
10174
|
existing = parent;
|
|
9911
10175
|
}
|
|
9912
|
-
const realAncestor =
|
|
9913
|
-
const resolved = missing.length === 0 ? realAncestor :
|
|
9914
|
-
if (resolved !== root && !resolved.startsWith(`${root}${
|
|
10176
|
+
const realAncestor = fs15.realpathSync(existing);
|
|
10177
|
+
const resolved = missing.length === 0 ? realAncestor : path16.join(realAncestor, ...missing);
|
|
10178
|
+
if (resolved !== root && !resolved.startsWith(`${root}${path16.sep}`)) {
|
|
9915
10179
|
throw new Error(
|
|
9916
10180
|
`${REFUSED}: ${shown} resolves outside the repository (${resolved}). Move it back inside ${root}, or remove the link, and run again.`
|
|
9917
10181
|
);
|
|
9918
10182
|
}
|
|
9919
10183
|
}
|
|
9920
10184
|
function writeIfChanged(target, filePath, content) {
|
|
9921
|
-
const existing =
|
|
10185
|
+
const existing = fs15.existsSync(filePath) ? fs15.readFileSync(filePath, "utf8") : null;
|
|
9922
10186
|
if (existing === content) return { target, path: filePath, action: "unchanged" };
|
|
9923
|
-
|
|
9924
|
-
|
|
10187
|
+
fs15.mkdirSync(path16.dirname(filePath), { recursive: true });
|
|
10188
|
+
fs15.writeFileSync(filePath, content, "utf8");
|
|
9925
10189
|
return { target, path: filePath, action: existing === null ? "created" : "updated" };
|
|
9926
10190
|
}
|
|
9927
10191
|
function legacyAgentsNote(cwd) {
|
|
9928
|
-
const filePath =
|
|
10192
|
+
const filePath = path16.join(cwd, "AGENTS.md");
|
|
9929
10193
|
try {
|
|
9930
|
-
if (!
|
|
9931
|
-
if (!
|
|
10194
|
+
if (!fs15.existsSync(filePath)) return null;
|
|
10195
|
+
if (!fs15.readFileSync(filePath, "utf8").includes(LEGACY_AGENTS_MARKER)) return null;
|
|
9932
10196
|
} catch {
|
|
9933
10197
|
return null;
|
|
9934
10198
|
}
|
|
@@ -9940,9 +10204,9 @@ function installSkill(cwd, target = "all") {
|
|
|
9940
10204
|
const notes = [];
|
|
9941
10205
|
const wanted = target === "all" ? ["claude", "cursor"] : [target];
|
|
9942
10206
|
for (const item of wanted) assertWritableInsideRepository(cwd, targetPath(cwd, item));
|
|
9943
|
-
const createsSomething = wanted.some((item) => !
|
|
10207
|
+
const createsSomething = wanted.some((item) => !fs15.existsSync(targetPath(cwd, item)));
|
|
9944
10208
|
if (createsSomething) {
|
|
9945
|
-
assertWritableInsideRepository(cwd,
|
|
10209
|
+
assertWritableInsideRepository(cwd, path16.join(cwd, ".gitignore"));
|
|
9946
10210
|
}
|
|
9947
10211
|
for (const item of wanted) {
|
|
9948
10212
|
written.push(writeIfChanged(item, targetPath(cwd, item), skill));
|
|
@@ -10077,52 +10341,52 @@ function validateArguments(schema, value) {
|
|
|
10077
10341
|
}
|
|
10078
10342
|
return problems;
|
|
10079
10343
|
}
|
|
10080
|
-
function checkValue(
|
|
10344
|
+
function checkValue(path19, schema, value) {
|
|
10081
10345
|
if (schema.enum && !schema.enum.includes(value)) {
|
|
10082
|
-
return [`"${
|
|
10346
|
+
return [`"${path19}" must be one of: ${schema.enum.join(", ")}`];
|
|
10083
10347
|
}
|
|
10084
10348
|
switch (schema.type) {
|
|
10085
10349
|
case "string": {
|
|
10086
|
-
if (typeof value !== "string") return [`"${
|
|
10350
|
+
if (typeof value !== "string") return [`"${path19}" must be a string`];
|
|
10087
10351
|
const problems = [];
|
|
10088
10352
|
if (schema.minLength !== void 0 && value.length < schema.minLength) {
|
|
10089
|
-
problems.push(`"${
|
|
10353
|
+
problems.push(`"${path19}" must be at least ${schema.minLength} character(s)`);
|
|
10090
10354
|
}
|
|
10091
10355
|
if (schema.maxLength !== void 0 && value.length > schema.maxLength) {
|
|
10092
|
-
problems.push(`"${
|
|
10356
|
+
problems.push(`"${path19}" must be at most ${schema.maxLength} character(s)`);
|
|
10093
10357
|
}
|
|
10094
10358
|
return problems;
|
|
10095
10359
|
}
|
|
10096
10360
|
case "number":
|
|
10097
10361
|
case "integer": {
|
|
10098
10362
|
if (typeof value !== "number" || Number.isNaN(value)) {
|
|
10099
|
-
return [`"${
|
|
10363
|
+
return [`"${path19}" must be a number`];
|
|
10100
10364
|
}
|
|
10101
10365
|
if (schema.type === "integer" && !Number.isInteger(value)) {
|
|
10102
|
-
return [`"${
|
|
10366
|
+
return [`"${path19}" must be an integer`];
|
|
10103
10367
|
}
|
|
10104
10368
|
const problems = [];
|
|
10105
10369
|
if (schema.minimum !== void 0 && value < schema.minimum) {
|
|
10106
|
-
problems.push(`"${
|
|
10370
|
+
problems.push(`"${path19}" must be >= ${schema.minimum}`);
|
|
10107
10371
|
}
|
|
10108
10372
|
if (schema.maximum !== void 0 && value > schema.maximum) {
|
|
10109
|
-
problems.push(`"${
|
|
10373
|
+
problems.push(`"${path19}" must be <= ${schema.maximum}`);
|
|
10110
10374
|
}
|
|
10111
10375
|
return problems;
|
|
10112
10376
|
}
|
|
10113
10377
|
case "boolean":
|
|
10114
|
-
return typeof value === "boolean" ? [] : [`"${
|
|
10378
|
+
return typeof value === "boolean" ? [] : [`"${path19}" must be a boolean`];
|
|
10115
10379
|
case "array": {
|
|
10116
|
-
if (!Array.isArray(value)) return [`"${
|
|
10380
|
+
if (!Array.isArray(value)) return [`"${path19}" must be an array`];
|
|
10117
10381
|
if (!schema.items) return [];
|
|
10118
10382
|
const problems = [];
|
|
10119
10383
|
value.forEach((item, index) => {
|
|
10120
|
-
problems.push(...checkValue(`${
|
|
10384
|
+
problems.push(...checkValue(`${path19}[${index}]`, schema.items, item));
|
|
10121
10385
|
});
|
|
10122
10386
|
return problems;
|
|
10123
10387
|
}
|
|
10124
10388
|
case "object":
|
|
10125
|
-
return isJsonObject(value) ? [] : [`"${
|
|
10389
|
+
return isJsonObject(value) ? [] : [`"${path19}" must be an object`];
|
|
10126
10390
|
default:
|
|
10127
10391
|
return [];
|
|
10128
10392
|
}
|
|
@@ -10136,6 +10400,18 @@ function applyDefaults(schema, value) {
|
|
|
10136
10400
|
}
|
|
10137
10401
|
return out;
|
|
10138
10402
|
}
|
|
10403
|
+
function coerceNumericStrings(schema, value) {
|
|
10404
|
+
const out = { ...value };
|
|
10405
|
+
for (const [key, propertySchema] of Object.entries(schema.properties)) {
|
|
10406
|
+
const raw = out[key];
|
|
10407
|
+
if (typeof raw !== "string") continue;
|
|
10408
|
+
if (propertySchema.type !== "number" && propertySchema.type !== "integer") continue;
|
|
10409
|
+
if (raw.trim().length === 0) continue;
|
|
10410
|
+
const parsed = Number(raw);
|
|
10411
|
+
if (Number.isFinite(parsed)) out[key] = parsed;
|
|
10412
|
+
}
|
|
10413
|
+
return out;
|
|
10414
|
+
}
|
|
10139
10415
|
|
|
10140
10416
|
// src/mcp/server.ts
|
|
10141
10417
|
function capabilities() {
|
|
@@ -10224,7 +10500,8 @@ function createMcpServer(options) {
|
|
|
10224
10500
|
if (!isJsonObject(rawArguments)) {
|
|
10225
10501
|
return errorResponse(id, ERROR_CODES.invalidParams, '"arguments" must be a JSON object.');
|
|
10226
10502
|
}
|
|
10227
|
-
const
|
|
10503
|
+
const supplied = coerceNumericStrings(tool.inputSchema, rawArguments);
|
|
10504
|
+
const problems = validateArguments(tool.inputSchema, supplied);
|
|
10228
10505
|
if (problems.length > 0) {
|
|
10229
10506
|
return ok(
|
|
10230
10507
|
id,
|
|
@@ -10236,7 +10513,7 @@ function createMcpServer(options) {
|
|
|
10236
10513
|
modern
|
|
10237
10514
|
);
|
|
10238
10515
|
}
|
|
10239
|
-
const args = applyDefaults(tool.inputSchema,
|
|
10516
|
+
const args = applyDefaults(tool.inputSchema, supplied);
|
|
10240
10517
|
let outcome;
|
|
10241
10518
|
try {
|
|
10242
10519
|
outcome = await tool.handler(args, options.context);
|
|
@@ -10405,8 +10682,8 @@ function createMcpServer(options) {
|
|
|
10405
10682
|
}
|
|
10406
10683
|
|
|
10407
10684
|
// src/mcp/resources.ts
|
|
10408
|
-
import * as
|
|
10409
|
-
import * as
|
|
10685
|
+
import * as fs16 from "fs";
|
|
10686
|
+
import * as path17 from "path";
|
|
10410
10687
|
var MAX_RESOURCE_BYTES = 512 * 1024;
|
|
10411
10688
|
var PART_URI = /^snapshot:\/\/parts\/(\d+)$/;
|
|
10412
10689
|
var REPORT_URIS = /* @__PURE__ */ new Map([
|
|
@@ -10440,7 +10717,7 @@ async function workspaceFor(context) {
|
|
|
10440
10717
|
}
|
|
10441
10718
|
function readFileIfPresent(file) {
|
|
10442
10719
|
try {
|
|
10443
|
-
return
|
|
10720
|
+
return fs16.readFileSync(file, "utf8");
|
|
10444
10721
|
} catch {
|
|
10445
10722
|
return null;
|
|
10446
10723
|
}
|
|
@@ -10519,7 +10796,7 @@ function buildResourceProvider() {
|
|
|
10519
10796
|
const report = REPORT_URIS.get(uri);
|
|
10520
10797
|
if (report) {
|
|
10521
10798
|
const text2 = readFileIfPresent(
|
|
10522
|
-
|
|
10799
|
+
path17.resolve(workspace.root, workspace.config.outDir, "reports", report.file)
|
|
10523
10800
|
);
|
|
10524
10801
|
if (text2 === null) return null;
|
|
10525
10802
|
return { uri, mimeType: "text/markdown", text: capUtf8(text2, MAX_RESOURCE_BYTES).text };
|
|
@@ -10538,7 +10815,7 @@ function buildResourceProvider() {
|
|
|
10538
10815
|
const part = read.manifest.parts.find((candidate) => candidate.index === index);
|
|
10539
10816
|
if (!part) return null;
|
|
10540
10817
|
const text2 = readFileIfPresent(
|
|
10541
|
-
|
|
10818
|
+
path17.resolve(workspace.root, workspace.config.outDir, "parts", part.filename)
|
|
10542
10819
|
);
|
|
10543
10820
|
if (text2 === null) return null;
|
|
10544
10821
|
return { uri, mimeType: "text/markdown", text: capUtf8(text2, MAX_RESOURCE_BYTES).text };
|
|
@@ -10776,6 +11053,7 @@ var CLI_OPTIONS = {
|
|
|
10776
11053
|
install: { type: "boolean" },
|
|
10777
11054
|
"no-install": { type: "boolean" },
|
|
10778
11055
|
target: { type: "string" },
|
|
11056
|
+
"no-update-check": { type: "boolean" },
|
|
10779
11057
|
help: { type: "boolean", short: "h" },
|
|
10780
11058
|
version: { type: "boolean", short: "v" }
|
|
10781
11059
|
};
|
|
@@ -10850,6 +11128,8 @@ Options:
|
|
|
10850
11128
|
--install Write the skill into the repository instead of printing
|
|
10851
11129
|
--target <name> claude | cursor | all (default: all)
|
|
10852
11130
|
|
|
11131
|
+
--no-update-check Skip the daily check for a newer published version
|
|
11132
|
+
|
|
10853
11133
|
-h, --help Display this help message
|
|
10854
11134
|
-v, --version Display version
|
|
10855
11135
|
|
|
@@ -10914,10 +11194,10 @@ function parseCliArgs(argv) {
|
|
|
10914
11194
|
}
|
|
10915
11195
|
if (values.cwd !== void 0) {
|
|
10916
11196
|
const target = String(values.cwd);
|
|
10917
|
-
if (!
|
|
11197
|
+
if (!fs17.existsSync(target)) {
|
|
10918
11198
|
throw new UsageError(`--cwd directory does not exist: ${target}`);
|
|
10919
11199
|
}
|
|
10920
|
-
if (!
|
|
11200
|
+
if (!fs17.statSync(target).isDirectory()) {
|
|
10921
11201
|
throw new UsageError(`--cwd is not a directory: ${target}`);
|
|
10922
11202
|
}
|
|
10923
11203
|
}
|
|
@@ -11063,7 +11343,7 @@ function parseCliArgs(argv) {
|
|
|
11063
11343
|
install: values.install === true,
|
|
11064
11344
|
noInstall: values["no-install"] === true,
|
|
11065
11345
|
target: values.target === void 0 ? void 0 : String(values.target),
|
|
11066
|
-
cwd: values.cwd === void 0 ? process.cwd() :
|
|
11346
|
+
cwd: values.cwd === void 0 ? process.cwd() : path18.resolve(String(values.cwd)),
|
|
11067
11347
|
outDir: values["out-dir"] === void 0 ? void 0 : String(values["out-dir"]),
|
|
11068
11348
|
includeMonolith: values["include-monolith"] === true || onlyNamesMonolith === true ? true : void 0,
|
|
11069
11349
|
includeSensitiveFiles: values["include-sensitive-files"] === true ? true : void 0,
|
|
@@ -11120,24 +11400,24 @@ function splitList(value) {
|
|
|
11120
11400
|
return value.split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
11121
11401
|
}
|
|
11122
11402
|
function displayOutPath(cwd, outDir, ...segments) {
|
|
11123
|
-
const absolute =
|
|
11124
|
-
const relative11 =
|
|
11125
|
-
if (relative11 && !relative11.startsWith("..") && !
|
|
11403
|
+
const absolute = path18.resolve(cwd, outDir, ...segments);
|
|
11404
|
+
const relative11 = path18.relative(cwd, absolute);
|
|
11405
|
+
if (relative11 && !relative11.startsWith("..") && !path18.isAbsolute(relative11)) return relative11;
|
|
11126
11406
|
return absolute;
|
|
11127
11407
|
}
|
|
11128
11408
|
function nextStepHint(cwd, scriptsAddedNow) {
|
|
11129
11409
|
let hasScript = scriptsAddedNow;
|
|
11130
11410
|
if (!hasScript) {
|
|
11131
11411
|
try {
|
|
11132
|
-
const parsed = JSON.parse(
|
|
11412
|
+
const parsed = JSON.parse(fs17.readFileSync(path18.join(cwd, "package.json"), "utf8"));
|
|
11133
11413
|
hasScript = typeof parsed.scripts?.snapshot === "string";
|
|
11134
11414
|
} catch {
|
|
11135
11415
|
hasScript = false;
|
|
11136
11416
|
}
|
|
11137
11417
|
}
|
|
11138
11418
|
if (!hasScript) return `npx ${GENERATOR_NAME} generate`;
|
|
11139
|
-
const bin =
|
|
11140
|
-
const installed =
|
|
11419
|
+
const bin = path18.join(cwd, "node_modules", ".bin", "codebase-snapshot");
|
|
11420
|
+
const installed = fs17.existsSync(bin) || fs17.existsSync(`${bin}.cmd`);
|
|
11141
11421
|
return installed ? "npm run snapshot" : "npm install && npm run snapshot";
|
|
11142
11422
|
}
|
|
11143
11423
|
function presetSummary(init) {
|
|
@@ -11167,7 +11447,7 @@ async function runInitCommand(invocation) {
|
|
|
11167
11447
|
info(result.configText);
|
|
11168
11448
|
return 0;
|
|
11169
11449
|
}
|
|
11170
|
-
info(`\u2705 Created ${
|
|
11450
|
+
info(`\u2705 Created ${path18.basename(result.configPath)} (${presetSummary(result)})`);
|
|
11171
11451
|
if (result.scripts.length > 0) info(` Added npm scripts: ${result.scripts.join(", ")}`);
|
|
11172
11452
|
if (result.dependency.added && result.dependency.range !== null) {
|
|
11173
11453
|
info(` Added devDependency: ${result.dependency.name} ${result.dependency.range}`);
|
|
@@ -11187,7 +11467,7 @@ async function runInitCommand(invocation) {
|
|
|
11187
11467
|
}
|
|
11188
11468
|
}
|
|
11189
11469
|
function detectPackageManager(cwd) {
|
|
11190
|
-
const has = (name) =>
|
|
11470
|
+
const has = (name) => fs17.existsSync(path18.join(cwd, name));
|
|
11191
11471
|
if (has("bun.lockb") || has("bun.lock")) return { name: "bun", command: "bun", args: ["install"] };
|
|
11192
11472
|
if (has("pnpm-lock.yaml")) return { name: "pnpm", command: "pnpm", args: ["install"] };
|
|
11193
11473
|
if (has("yarn.lock")) return { name: "yarn", command: "yarn", args: ["install"] };
|
|
@@ -11206,14 +11486,13 @@ function installDependencies(cwd) {
|
|
|
11206
11486
|
const manager = detectPackageManager(cwd);
|
|
11207
11487
|
const command = `${manager.command} ${manager.args.join(" ")}`;
|
|
11208
11488
|
const started = performance.now();
|
|
11209
|
-
const
|
|
11489
|
+
const spawnOptions = {
|
|
11210
11490
|
cwd,
|
|
11211
11491
|
encoding: "utf8",
|
|
11212
|
-
// npm, pnpm and yarn ship as `.cmd` shims on Windows, which spawnSync cannot exec directly.
|
|
11213
|
-
shell: process.platform === "win32",
|
|
11214
11492
|
timeout: INSTALL_TIMEOUT_MS,
|
|
11215
11493
|
env: cleanInstallEnv()
|
|
11216
|
-
}
|
|
11494
|
+
};
|
|
11495
|
+
const result = process.platform === "win32" ? spawnSync(command, { ...spawnOptions, shell: true }) : spawnSync(manager.command, manager.args, spawnOptions);
|
|
11217
11496
|
const seconds = `${((performance.now() - started) / 1e3).toFixed(1)}s`;
|
|
11218
11497
|
if (result.error) {
|
|
11219
11498
|
return { manager: manager.name, command, ok: false, detail: `${result.error.message} (${seconds})` };
|
|
@@ -11277,7 +11556,7 @@ async function runSetupCommand(invocation, options = {}) {
|
|
|
11277
11556
|
}
|
|
11278
11557
|
info("\n\u{1F9EA} Dry run \u2014 setup would:");
|
|
11279
11558
|
info(
|
|
11280
|
-
` 1. ${init.reused ? `keep ${
|
|
11559
|
+
` 1. ${init.reused ? `keep ${path18.basename(init.configPath)}` : `write ${path18.basename(init.configPath)} (preset: ${init.preset})`}`
|
|
11281
11560
|
);
|
|
11282
11561
|
info(" 2. add the npm scripts, the devDependency and the .gitignore entries");
|
|
11283
11562
|
if (manager !== null) info(` 3. install with ${manager}`);
|
|
@@ -11289,9 +11568,9 @@ async function runSetupCommand(invocation, options = {}) {
|
|
|
11289
11568
|
if (invocation.format !== "json") {
|
|
11290
11569
|
if (!interactive) info(`\u{1F680} Setup for: ${invocation.cwd}`);
|
|
11291
11570
|
if (init.reused) {
|
|
11292
|
-
info(`\u267B Keeping ${
|
|
11571
|
+
info(`\u267B Keeping ${path18.basename(init.configPath)} (${presetSummary(init)})`);
|
|
11293
11572
|
} else {
|
|
11294
|
-
info(`\u2705 Created ${
|
|
11573
|
+
info(`\u2705 Created ${path18.basename(init.configPath)} (${presetSummary(init)})`);
|
|
11295
11574
|
}
|
|
11296
11575
|
if (init.scripts.length > 0) info(` Added npm scripts: ${init.scripts.join(", ")}`);
|
|
11297
11576
|
if (init.dependency.added && init.dependency.range !== null) {
|
|
@@ -11399,7 +11678,7 @@ function runSkillCommand(invocation) {
|
|
|
11399
11678
|
}
|
|
11400
11679
|
info(`\u2705 Agent skill installed (${SKILL_NAME}):`);
|
|
11401
11680
|
for (const item of result.written) {
|
|
11402
|
-
const shown =
|
|
11681
|
+
const shown = path18.relative(invocation.cwd, item.path) || item.path;
|
|
11403
11682
|
info(` - ${item.action.padEnd(9)} ${shown}`);
|
|
11404
11683
|
}
|
|
11405
11684
|
if (result.gitignored.length > 0) {
|
|
@@ -11463,6 +11742,17 @@ function requestsJsonFormat(argv) {
|
|
|
11463
11742
|
return format === "json";
|
|
11464
11743
|
}
|
|
11465
11744
|
async function main(argv = process.argv.slice(2)) {
|
|
11745
|
+
const pending = process.stdout.isTTY === true && shouldCheck(argv, process.env) ? checkForUpdate({ current: GENERATOR_VERSION, cacheFile: cacheFilePath() }) : null;
|
|
11746
|
+
const code = await runCommand(argv);
|
|
11747
|
+
if (pending) {
|
|
11748
|
+
const notice = await pending;
|
|
11749
|
+
if (notice) process.stderr.write(`
|
|
11750
|
+
${formatUpdateNotice(notice)}
|
|
11751
|
+
`);
|
|
11752
|
+
}
|
|
11753
|
+
return code;
|
|
11754
|
+
}
|
|
11755
|
+
async function runCommand(argv) {
|
|
11466
11756
|
let invocation;
|
|
11467
11757
|
try {
|
|
11468
11758
|
invocation = parseCliArgs(argv);
|
|
@@ -11495,7 +11785,7 @@ ${USAGE}
|
|
|
11495
11785
|
return runSkillCommand(invocation);
|
|
11496
11786
|
}
|
|
11497
11787
|
if (invocation.command === "mcp") {
|
|
11498
|
-
return await runStdioServer({ cwd:
|
|
11788
|
+
return await runStdioServer({ cwd: path18.resolve(invocation.cwd) });
|
|
11499
11789
|
}
|
|
11500
11790
|
if (invocation.command === "setup") {
|
|
11501
11791
|
return runSetupCommand(invocation);
|
|
@@ -11512,7 +11802,7 @@ ${USAGE}
|
|
|
11512
11802
|
warn("\u274C The config file could not be read. Fix it, or delete it to use the defaults.");
|
|
11513
11803
|
return 1;
|
|
11514
11804
|
}
|
|
11515
|
-
if (loaded.file) info(`\u2699 Using config ${
|
|
11805
|
+
if (loaded.file) info(`\u2699 Using config ${path18.relative(process.cwd(), loaded.file) || loaded.file}`);
|
|
11516
11806
|
const resolved = resolveConfig(
|
|
11517
11807
|
loaded.config,
|
|
11518
11808
|
{
|
|
@@ -11534,10 +11824,10 @@ ${USAGE}
|
|
|
11534
11824
|
if (invocation.command === "validate") {
|
|
11535
11825
|
const result = validateSnapshot(resolved);
|
|
11536
11826
|
if (result.valid) {
|
|
11537
|
-
const manifestPath =
|
|
11827
|
+
const manifestPath = path18.resolve(resolved.cwd, resolved.outDir, "manifest.json");
|
|
11538
11828
|
let manifest;
|
|
11539
11829
|
try {
|
|
11540
|
-
manifest = JSON.parse(
|
|
11830
|
+
manifest = JSON.parse(fs17.readFileSync(manifestPath, "utf8"));
|
|
11541
11831
|
} catch {
|
|
11542
11832
|
manifest = void 0;
|
|
11543
11833
|
}
|
|
@@ -11550,13 +11840,13 @@ ${USAGE}
|
|
|
11550
11840
|
return result.valid ? 0 : 1;
|
|
11551
11841
|
}
|
|
11552
11842
|
if (invocation.command === "report") {
|
|
11553
|
-
const manifestPath =
|
|
11554
|
-
if (!
|
|
11843
|
+
const manifestPath = path18.resolve(resolved.cwd, resolved.outDir, "manifest.json");
|
|
11844
|
+
if (!fs17.existsSync(manifestPath)) {
|
|
11555
11845
|
return fail(invocation.format, `Manifest not found at ${manifestPath}. Run generate first.`);
|
|
11556
11846
|
}
|
|
11557
11847
|
let manifest;
|
|
11558
11848
|
try {
|
|
11559
|
-
manifest = JSON.parse(
|
|
11849
|
+
manifest = JSON.parse(fs17.readFileSync(manifestPath, "utf8"));
|
|
11560
11850
|
} catch (err) {
|
|
11561
11851
|
return fail(invocation.format, `Could not read ${manifestPath}: ${err.message}`);
|
|
11562
11852
|
}
|
|
@@ -11720,20 +12010,20 @@ function finishWatchRun(state, now) {
|
|
|
11720
12010
|
};
|
|
11721
12011
|
}
|
|
11722
12012
|
function comparablePath(value) {
|
|
11723
|
-
const absolute =
|
|
12013
|
+
const absolute = path18.resolve(value);
|
|
11724
12014
|
return process.platform === "win32" ? absolute.toLowerCase() : absolute;
|
|
11725
12015
|
}
|
|
11726
12016
|
function isSameOrInside(parent, candidate) {
|
|
11727
|
-
const relative11 =
|
|
11728
|
-
return relative11 === "" || relative11 !== ".." && !relative11.startsWith(`..${
|
|
12017
|
+
const relative11 = path18.relative(parent, candidate);
|
|
12018
|
+
return relative11 === "" || relative11 !== ".." && !relative11.startsWith(`..${path18.sep}`) && !path18.isAbsolute(relative11);
|
|
11729
12019
|
}
|
|
11730
12020
|
function isGeneratedWatchPath(candidate, paths) {
|
|
11731
|
-
const absolute = comparablePath(
|
|
11732
|
-
const outDir = comparablePath(
|
|
11733
|
-
const tmpDir = comparablePath(
|
|
12021
|
+
const absolute = comparablePath(path18.resolve(paths.cwd, candidate));
|
|
12022
|
+
const outDir = comparablePath(path18.resolve(paths.cwd, paths.outDir));
|
|
12023
|
+
const tmpDir = comparablePath(path18.resolve(paths.cwd, paths.tmpDir));
|
|
11734
12024
|
if (isSameOrInside(outDir, absolute) || isSameOrInside(tmpDir, absolute)) return true;
|
|
11735
|
-
const parent =
|
|
11736
|
-
if (parent ===
|
|
12025
|
+
const parent = path18.dirname(absolute);
|
|
12026
|
+
if (parent === path18.dirname(outDir) && path18.basename(absolute).startsWith(`${path18.basename(outDir)}.backup-`)) {
|
|
11737
12027
|
return true;
|
|
11738
12028
|
}
|
|
11739
12029
|
return absolute.split(/[\\/]+/).some((segment) => VCS_SEGMENTS.has(segment));
|
|
@@ -11750,8 +12040,8 @@ function decideWatchEvent(state, event, paths) {
|
|
|
11750
12040
|
return { state, action: "schedule" };
|
|
11751
12041
|
}
|
|
11752
12042
|
async function runWatchLoop(invocation, resolved) {
|
|
11753
|
-
const outDir =
|
|
11754
|
-
const tmpDir =
|
|
12043
|
+
const outDir = path18.resolve(resolved.cwd, resolved.outDir);
|
|
12044
|
+
const tmpDir = path18.resolve(resolved.cwd, resolved.tmpDir);
|
|
11755
12045
|
const paths = { cwd: resolved.cwd, outDir, tmpDir };
|
|
11756
12046
|
let state = initialWatchState();
|
|
11757
12047
|
let timer = null;
|
|
@@ -11816,11 +12106,11 @@ async function runWatchLoop(invocation, resolved) {
|
|
|
11816
12106
|
};
|
|
11817
12107
|
info("\u{1F440} Watching for changes. Press Ctrl+C to stop.");
|
|
11818
12108
|
void regenerate();
|
|
11819
|
-
const watcher =
|
|
12109
|
+
const watcher = fs17.watch(resolved.cwd, { recursive: true }, (_event, filename) => {
|
|
11820
12110
|
const decision = decideWatchEvent(
|
|
11821
12111
|
state,
|
|
11822
12112
|
{
|
|
11823
|
-
path: filename ?
|
|
12113
|
+
path: filename ? path18.resolve(resolved.cwd, filename.toString()) : null,
|
|
11824
12114
|
now: performance.now()
|
|
11825
12115
|
},
|
|
11826
12116
|
paths
|
|
@@ -11848,7 +12138,7 @@ async function runWatchLoop(invocation, resolved) {
|
|
|
11848
12138
|
return 0;
|
|
11849
12139
|
}
|
|
11850
12140
|
var entryFile = process.argv[1] ?? "";
|
|
11851
|
-
var entryName =
|
|
12141
|
+
var entryName = path18.basename(entryFile).toLowerCase();
|
|
11852
12142
|
var invokedDirectly = entryName === "cli.js" || entryName === "cli.cjs";
|
|
11853
12143
|
if (invokedDirectly) {
|
|
11854
12144
|
void main().then(
|