@ankurhalder/codebase-snapshot 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -0
- package/dist/bin.js +809 -49
- package/dist/{chunk-QEIOXPMU.js → chunk-5I564BGX.js} +43 -19
- package/dist/cli.cjs +791 -35
- package/dist/cli.d.cts +5 -1
- package/dist/cli.d.ts +5 -1
- package/dist/cli.js +750 -17
- package/dist/index.cjs +41 -17
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/docs/agent.md +80 -0
- package/docs/install.md +15 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1707,11 +1707,12 @@ function detectPreset(cwd, forced) {
|
|
|
1707
1707
|
}
|
|
1708
1708
|
function getGitInfo(cwd, fallbackBranch = "main") {
|
|
1709
1709
|
try {
|
|
1710
|
-
const
|
|
1710
|
+
const reported = childProcess__namespace.execSync("git rev-parse --abbrev-ref HEAD", {
|
|
1711
1711
|
cwd,
|
|
1712
1712
|
stdio: ["ignore", "pipe", "ignore"],
|
|
1713
1713
|
encoding: "utf8"
|
|
1714
|
-
}).trim()
|
|
1714
|
+
}).trim();
|
|
1715
|
+
const branch = !reported || reported === "HEAD" ? fallbackBranch : reported;
|
|
1715
1716
|
const commit = childProcess__namespace.execSync("git rev-parse HEAD", {
|
|
1716
1717
|
cwd,
|
|
1717
1718
|
stdio: ["ignore", "pipe", "ignore"],
|
|
@@ -2332,6 +2333,9 @@ async function loadConfigFileDetailed(cwd) {
|
|
|
2332
2333
|
if (snapshotKey && typeof snapshotKey === "object") {
|
|
2333
2334
|
return { config: snapshotKey, file: pkgJsonPath, warnings, errors };
|
|
2334
2335
|
}
|
|
2336
|
+
if (snapshotKey !== void 0) {
|
|
2337
|
+
warnings.push(`${pkgJsonPath}: the "snapshot" key must be an object; it was ignored.`);
|
|
2338
|
+
}
|
|
2335
2339
|
} catch (err) {
|
|
2336
2340
|
const message = err.message;
|
|
2337
2341
|
warnings.push(message);
|
|
@@ -3446,6 +3450,7 @@ function validateSnapshot(configInput) {
|
|
|
3446
3450
|
drift.missingOnDisk.push(snapshotFile);
|
|
3447
3451
|
}
|
|
3448
3452
|
}
|
|
3453
|
+
drift.missingOnDisk = [...new Set(drift.missingOnDisk)];
|
|
3449
3454
|
const maxEffectiveBudget = Math.max(
|
|
3450
3455
|
config.maxPartBytes,
|
|
3451
3456
|
config.budgets.default,
|
|
@@ -3857,7 +3862,13 @@ function compareManifests(before, after) {
|
|
|
3857
3862
|
};
|
|
3858
3863
|
}
|
|
3859
3864
|
function loadManifest(target) {
|
|
3860
|
-
|
|
3865
|
+
let isDirectory;
|
|
3866
|
+
try {
|
|
3867
|
+
isDirectory = fs9__namespace.statSync(target).isDirectory();
|
|
3868
|
+
} catch {
|
|
3869
|
+
throw new Error(`No snapshot found at ${target}: the path does not exist.`);
|
|
3870
|
+
}
|
|
3871
|
+
const manifestPath = isDirectory ? path10__namespace.join(target, "manifest.json") : target;
|
|
3861
3872
|
let raw;
|
|
3862
3873
|
try {
|
|
3863
3874
|
raw = fs9__namespace.readFileSync(manifestPath, "utf8");
|
|
@@ -3873,6 +3884,9 @@ function loadManifest(target) {
|
|
|
3873
3884
|
if (!parsed || !Array.isArray(parsed.files)) {
|
|
3874
3885
|
throw new Error(`${manifestPath} is not a snapshot manifest (no "files" array).`);
|
|
3875
3886
|
}
|
|
3887
|
+
if (!parsed.counts || typeof parsed.counts !== "object") {
|
|
3888
|
+
throw new Error(`${manifestPath} is not a snapshot manifest (no "counts" object).`);
|
|
3889
|
+
}
|
|
3876
3890
|
return parsed;
|
|
3877
3891
|
}
|
|
3878
3892
|
function renderDiffText(diff) {
|
|
@@ -3929,11 +3943,12 @@ var PRESET_NAMES = [
|
|
|
3929
3943
|
"java-spring \xB7 dotnet \xB7 php-laravel \xB7 ruby-rails",
|
|
3930
3944
|
"monorepo \xB7 docs \xB7 infra \xB7 generic"
|
|
3931
3945
|
];
|
|
3946
|
+
var CONFIG_HEADER = `// Generated by codebase-snapshot. A plain object on purpose: this file must load
|
|
3947
|
+
// even when the package itself is only run through npx.
|
|
3948
|
+
`;
|
|
3932
3949
|
function renderConfig(answers) {
|
|
3933
3950
|
const { artifacts } = answers;
|
|
3934
|
-
return
|
|
3935
|
-
|
|
3936
|
-
export default defineConfig({
|
|
3951
|
+
return `${CONFIG_HEADER}export default {
|
|
3937
3952
|
// \u2500\u2500 Project \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3938
3953
|
// Which project shape to use. Detected from the repo when omitted.
|
|
3939
3954
|
// ${PRESET_NAMES.join("\n // ")}
|
|
@@ -4002,7 +4017,7 @@ export default defineConfig({
|
|
|
4002
4017
|
// \u2500\u2500 Advanced \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
4003
4018
|
// afterGenerate: (manifest) => { /* write your own artifact */ }
|
|
4004
4019
|
hooks: {},
|
|
4005
|
-
}
|
|
4020
|
+
};
|
|
4006
4021
|
`;
|
|
4007
4022
|
}
|
|
4008
4023
|
function renderMinimalConfig(answers) {
|
|
@@ -4030,16 +4045,12 @@ ${entries},
|
|
|
4030
4045
|
},`);
|
|
4031
4046
|
}
|
|
4032
4047
|
if (lines.length === 0) {
|
|
4033
|
-
return
|
|
4034
|
-
|
|
4035
|
-
export default defineConfig({});
|
|
4048
|
+
return `${CONFIG_HEADER}export default {};
|
|
4036
4049
|
`;
|
|
4037
4050
|
}
|
|
4038
|
-
return
|
|
4039
|
-
|
|
4040
|
-
export default defineConfig({
|
|
4051
|
+
return `${CONFIG_HEADER}export default {
|
|
4041
4052
|
${lines.join("\n")}
|
|
4042
|
-
}
|
|
4053
|
+
};
|
|
4043
4054
|
`;
|
|
4044
4055
|
}
|
|
4045
4056
|
var PromptCancelled = class extends Error {
|
|
@@ -4300,6 +4311,17 @@ var CONFIG_CANDIDATES = [
|
|
|
4300
4311
|
".snapshotrc.json",
|
|
4301
4312
|
".snapshotrc"
|
|
4302
4313
|
];
|
|
4314
|
+
function chooseConfigFileName(cwd) {
|
|
4315
|
+
if (!supportsTypeScriptImports()) return "snapshot.config.mjs";
|
|
4316
|
+
const pkgPath = path10__namespace.join(cwd, "package.json");
|
|
4317
|
+
if (!fs9__namespace.existsSync(pkgPath)) return "snapshot.config.mjs";
|
|
4318
|
+
try {
|
|
4319
|
+
const parsed = JSON.parse(fs9__namespace.readFileSync(pkgPath, "utf8"));
|
|
4320
|
+
return parsed.type === "module" ? "snapshot.config.ts" : "snapshot.config.mjs";
|
|
4321
|
+
} catch {
|
|
4322
|
+
return "snapshot.config.mjs";
|
|
4323
|
+
}
|
|
4324
|
+
}
|
|
4303
4325
|
var SIZE_CHOICES = [
|
|
4304
4326
|
{ value: "60k", label: "Small", hint: "60 KB \xB7 ~15k tokens \xB7 tight context windows" },
|
|
4305
4327
|
{ value: "120k", label: "Medium", hint: "120 KB \xB7 ~30k tokens \xB7 recommended" },
|
|
@@ -4505,7 +4527,7 @@ async function runInit(options) {
|
|
|
4505
4527
|
if (!proceed) throw new PromptCancelled();
|
|
4506
4528
|
}
|
|
4507
4529
|
const configText = options.minimal ? renderMinimalConfig(answers) : renderConfig(answers);
|
|
4508
|
-
const configName =
|
|
4530
|
+
const configName = chooseConfigFileName(options.cwd);
|
|
4509
4531
|
const configPath = path10__namespace.join(options.cwd, configName);
|
|
4510
4532
|
if (options.dryRun) {
|
|
4511
4533
|
return {
|
|
@@ -4543,7 +4565,7 @@ async function runInit(options) {
|
|
|
4543
4565
|
|
|
4544
4566
|
// src/index.ts
|
|
4545
4567
|
var GENERATOR_NAME = "@ankurhalder/codebase-snapshot";
|
|
4546
|
-
var GENERATOR_VERSION = "2.
|
|
4568
|
+
var GENERATOR_VERSION = "2.2.0";
|
|
4547
4569
|
var SCHEMA_VERSION = "3.0.0";
|
|
4548
4570
|
function sha256Hex2(input) {
|
|
4549
4571
|
return crypto__namespace.createHash("sha256").update(input).digest("hex");
|
|
@@ -4787,7 +4809,9 @@ function artifactWarnings(artifacts) {
|
|
|
4787
4809
|
);
|
|
4788
4810
|
}
|
|
4789
4811
|
if (ARTIFACT_NAMES.every((name) => artifacts[name] === false)) {
|
|
4790
|
-
messages.push(
|
|
4812
|
+
messages.push(
|
|
4813
|
+
"every artifact is disabled: only the .codebase-snapshot ownership marker will be written."
|
|
4814
|
+
);
|
|
4791
4815
|
}
|
|
4792
4816
|
return messages;
|
|
4793
4817
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -497,7 +497,7 @@ declare function matchesExclude(relativePath: string, pattern: string): boolean;
|
|
|
497
497
|
declare function collectFiles(cwd: string, options?: CollectOptions): CollectResult;
|
|
498
498
|
|
|
499
499
|
declare const GENERATOR_NAME = "@ankurhalder/codebase-snapshot";
|
|
500
|
-
declare const GENERATOR_VERSION = "2.
|
|
500
|
+
declare const GENERATOR_VERSION = "2.2.0";
|
|
501
501
|
declare const SCHEMA_VERSION = "3.0.0";
|
|
502
502
|
interface GenerateOptions {
|
|
503
503
|
/** Compute the full manifest but write nothing to disk. */
|
package/dist/index.d.ts
CHANGED
|
@@ -497,7 +497,7 @@ declare function matchesExclude(relativePath: string, pattern: string): boolean;
|
|
|
497
497
|
declare function collectFiles(cwd: string, options?: CollectOptions): CollectResult;
|
|
498
498
|
|
|
499
499
|
declare const GENERATOR_NAME = "@ankurhalder/codebase-snapshot";
|
|
500
|
-
declare const GENERATOR_VERSION = "2.
|
|
500
|
+
declare const GENERATOR_VERSION = "2.2.0";
|
|
501
501
|
declare const SCHEMA_VERSION = "3.0.0";
|
|
502
502
|
interface GenerateOptions {
|
|
503
503
|
/** Compute the full manifest but write nothing to disk. */
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { ARTIFACT_NAMES, DEFAULT_ANSWERS, DEFAULT_SECRET_PATTERNS, GENERATOR_NAME, GENERATOR_VERSION, LANGUAGE_PROFILES, MIN_MAX_PART_BYTES, PRESETS, PromptCancelled, SCHEMA_VERSION, anchorForPath, assertReplaceable, assertUsableConfigInput, collectFiles, compareManifests, countLines, defineConfig, detectLanguage, detectPreset, explainPath, fenceForContent, generateProjectTree, generateSnapshot, generateSnapshotAsync, getPreset, globToRegExp, isBinaryBuffer, isBinaryExtension, isBinaryFile, isInteractive, isResolvedConfig, isSensitivePath, listPresetNames, loadConfigFile, loadConfigFileDetailed, loadManifest, markResolvedConfig, matchRule, matchesExclude, normalizeResolvedConfig, parseByteSize, partitionFiles, profileForPath, renderConfig, renderDiffText, renderExplainText, renderMinimalConfig, renumberParts, resolveConfig, resolveSafeOutputDirs, runInit, scanAndRedactSecrets, stripJsonComments, summarizeJson, summarizeMarkdown, summarizeTypeScriptSource, summarizeWithProfile, supportsTypeScriptImports, truncatePreview, truncateToBytes, validateSnapshot } from './chunk-
|
|
1
|
+
export { ARTIFACT_NAMES, DEFAULT_ANSWERS, DEFAULT_SECRET_PATTERNS, GENERATOR_NAME, GENERATOR_VERSION, LANGUAGE_PROFILES, MIN_MAX_PART_BYTES, PRESETS, PromptCancelled, SCHEMA_VERSION, anchorForPath, assertReplaceable, assertUsableConfigInput, collectFiles, compareManifests, countLines, defineConfig, detectLanguage, detectPreset, explainPath, fenceForContent, generateProjectTree, generateSnapshot, generateSnapshotAsync, getPreset, globToRegExp, isBinaryBuffer, isBinaryExtension, isBinaryFile, isInteractive, isResolvedConfig, isSensitivePath, listPresetNames, loadConfigFile, loadConfigFileDetailed, loadManifest, markResolvedConfig, matchRule, matchesExclude, normalizeResolvedConfig, parseByteSize, partitionFiles, profileForPath, renderConfig, renderDiffText, renderExplainText, renderMinimalConfig, renumberParts, resolveConfig, resolveSafeOutputDirs, runInit, scanAndRedactSecrets, stripJsonComments, summarizeJson, summarizeMarkdown, summarizeTypeScriptSource, summarizeWithProfile, supportsTypeScriptImports, truncatePreview, truncateToBytes, validateSnapshot } from './chunk-5I564BGX.js';
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
package/docs/agent.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Using this with an AI agent
|
|
2
|
+
|
|
3
|
+
The package ships two things that make it usable by an agent that has never heard of it:
|
|
4
|
+
a **machine-readable contract** and a **bundled skill**.
|
|
5
|
+
|
|
6
|
+
Nothing here needs to be installed globally, and no prior knowledge of the tool is
|
|
7
|
+
required.
|
|
8
|
+
|
|
9
|
+
## The three commands an agent needs
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# 1. Read the contract. This is the machine-readable replacement for --help.
|
|
13
|
+
npx @ankurhalder/codebase-snapshot@latest describe --format json
|
|
14
|
+
|
|
15
|
+
# 2. Write a commented config that lists every setting (non-interactive).
|
|
16
|
+
npx @ankurhalder/codebase-snapshot@latest init --yes
|
|
17
|
+
|
|
18
|
+
# 3. Generate and verify.
|
|
19
|
+
npx @ankurhalder/codebase-snapshot@latest generate
|
|
20
|
+
npx @ankurhalder/codebase-snapshot@latest validate
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`describe` returns the invocation that works, an ordered `workflow` array, every command,
|
|
24
|
+
every option with its default, the exit-code contract, all 25 presets, the artifact list,
|
|
25
|
+
the config file names, the output layout, safety rules and a troubleshooting table — all
|
|
26
|
+
as JSON on stdout.
|
|
27
|
+
|
|
28
|
+
**Always pin the tag** (`@latest` or `@2`). A bare scoped spec can resolve to a bin-less
|
|
29
|
+
placeholder manifest on some npm builds and exit 0 printing nothing; the pinned form
|
|
30
|
+
cannot.
|
|
31
|
+
|
|
32
|
+
## The skill
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npx @ankurhalder/codebase-snapshot@latest skill # print it (stdout)
|
|
36
|
+
npx @ankurhalder/codebase-snapshot@latest skill --install # write it into the repo
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Printing goes to stdout deliberately, so it can be redirected:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npx @ankurhalder/codebase-snapshot@latest skill > SKILL.md
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`--install` writes the same skill to every location agents look at by default:
|
|
46
|
+
|
|
47
|
+
| Target | Path | Read by |
|
|
48
|
+
| :--- | :--- | :--- |
|
|
49
|
+
| `agents` | `AGENTS.md` (a marked section) | most tools, by convention |
|
|
50
|
+
| `claude` | `.claude/skills/codebase-snapshot/SKILL.md` | Claude Code auto-discovery |
|
|
51
|
+
| `cursor` | `.cursor/rules/codebase-snapshot.mdc` | Cursor rules |
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
skill --install --target claude # one location
|
|
55
|
+
skill --install # all three (default)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The install is **idempotent and non-destructive**: re-running replaces only the block
|
|
59
|
+
between `<!-- codebase-snapshot:skill:start -->` and
|
|
60
|
+
`<!-- codebase-snapshot:skill:end -->`, so an `AGENTS.md` that already documents your
|
|
61
|
+
conventions keeps them. `--format json` reports what was created, updated or unchanged.
|
|
62
|
+
|
|
63
|
+
`snap init` prints a reminder about this; it never installs the skill on its own.
|
|
64
|
+
|
|
65
|
+
## Why both a contract and a skill
|
|
66
|
+
|
|
67
|
+
The contract is for **doing** — it is the data an agent needs to pick correct commands and
|
|
68
|
+
options, and it is asserted against the real defaults in the test suite so it cannot drift
|
|
69
|
+
into lying.
|
|
70
|
+
|
|
71
|
+
The skill is for **knowing when to act** — it carries the trigger conditions, the ordered
|
|
72
|
+
workflow, and the surprises (sensitive files excluded by default, secrets redacted, the
|
|
73
|
+
output directory being tool-owned) that an agent cannot infer from a schema.
|
|
74
|
+
|
|
75
|
+
## Keeping the contract honest
|
|
76
|
+
|
|
77
|
+
`describe` is generated from the same code the CLI runs — the preset list, artifact names,
|
|
78
|
+
schema version and generator version are read directly, and a test compares its documented
|
|
79
|
+
defaults against a real `generate --dry-run` manifest. If a default changes, the test fails
|
|
80
|
+
rather than the contract silently misleading an agent.
|
package/docs/install.md
CHANGED
|
@@ -12,6 +12,20 @@ Requires **Node.js 18 or newer**.
|
|
|
12
12
|
npx @ankurhalder/codebase-snapshot generate
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
+
If that exits with **no output and no error** instead of generating, your npm has
|
|
16
|
+
resolved the bare spec to a placeholder manifest that carries no executable — npm creates
|
|
17
|
+
a `0.0.0-stage` placeholder version during staged publishing, and some npm builds pick it
|
|
18
|
+
when no version is given. Pin the version or the tag and it works:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npx @ankurhalder/codebase-snapshot@latest generate
|
|
22
|
+
npx @ankurhalder/codebase-snapshot@2 generate
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`npx -p @ankurhalder/codebase-snapshot codebase-snapshot generate`, `pnpm dlx`,
|
|
26
|
+
`yarn dlx` and `bunx` are unaffected. A normal `npm install -D` followed by `npx snap`
|
|
27
|
+
is unaffected too, and is the more reproducible choice for a project.
|
|
28
|
+
|
|
15
29
|
Equivalent one-liners for other clients:
|
|
16
30
|
|
|
17
31
|
```bash
|
|
@@ -68,7 +82,7 @@ if the generator version is reproducible.
|
|
|
68
82
|
|
|
69
83
|
```bash
|
|
70
84
|
npx @ankurhalder/codebase-snapshot@2 generate
|
|
71
|
-
pnpm dlx @ankurhalder/codebase-snapshot@2.
|
|
85
|
+
pnpm dlx @ankurhalder/codebase-snapshot@2.2.0 generate
|
|
72
86
|
```
|
|
73
87
|
|
|
74
88
|
## Verified clients
|