codebase-snapshot 0.9.8 → 0.9.12
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 +536 -0
- package/README.md +3 -3
- package/dist/bin.js +5262 -3459
- package/dist/{chunk-C24OAT6T.js → chunk-3H45YMBG.js} +1867 -509
- package/dist/{chunk-RQKXZOD7.js → chunk-5OR3WVPW.js} +366 -84
- package/dist/cli.cjs +5264 -3481
- package/dist/cli.d.cts +282 -3
- package/dist/cli.d.ts +282 -3
- package/dist/cli.js +181 -39
- package/dist/generate-worker-D5Xgh27c.d.cts +57 -0
- package/dist/generate-worker-D5Xgh27c.d.ts +57 -0
- package/dist/index.cjs +1820 -506
- package/dist/index.d.cts +310 -5
- package/dist/index.d.ts +310 -5
- package/dist/index.js +1 -3
- package/dist/mcp.cjs +5670 -4877
- package/dist/mcp.d.cts +70 -2
- package/dist/mcp.d.ts +70 -2
- package/dist/mcp.js +2 -4
- package/dist/{types-Ca4PmkiQ.d.cts → types-CTewen3J.d.cts} +115 -2
- package/dist/{types-Ca4PmkiQ.d.ts → types-CTewen3J.d.ts} +115 -2
- package/docs/agent.md +1 -1
- package/docs/cli.md +27 -4
- package/docs/configuration.md +5 -0
- package/docs/how-to-feed-a-repo-to-claude.md +61 -0
- package/docs/mcp.md +23 -10
- package/docs/output.md +36 -4
- package/docs/stale-context-is-your-bug.md +13 -0
- package/docs/vs-repomix.md +1 -1
- package/package.json +5 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { GENERATOR_VERSION, MIN_MAX_PART_BYTES, SCHEMA_VERSION, GENERATOR_NAME, ARTIFACT_NAMES, PRESETS, generateSnapshot, validateSnapshot, explainPath, renderExplainText, describeManifestShapeProblem, loadConfigFileDetailed, parseByteSize, resolveConfig } from './chunk-
|
|
1
|
+
import { GENERATOR_VERSION, anchorForPath, displayPath, MIN_MAX_PART_BYTES, SCHEMA_VERSION, GENERATOR_NAME, ARTIFACT_NAMES, PRESETS, generateSnapshot, generateInWorker, validateSnapshot, buildImportResolver, resolveSpecifier, truncateToBytes, explainPath, renderExplainText, describeManifestShapeProblem, loadConfigFileDetailed, parseByteSize, resolveConfig } from './chunk-3H45YMBG.js';
|
|
2
2
|
import * as fs from 'fs';
|
|
3
3
|
import * as path2 from 'path';
|
|
4
4
|
|
|
@@ -520,6 +520,8 @@ function createMcpServer(options) {
|
|
|
520
520
|
// src/mcp/part.ts
|
|
521
521
|
var FILE_HEADING = "### FILE: ";
|
|
522
522
|
var CONTENT_MARKERS = /* @__PURE__ */ new Set(["#### Content", "#### Bounded Preview"]);
|
|
523
|
+
var ANCHOR_LINE = /^<a id="([^"]+)"><\/a>$/;
|
|
524
|
+
var INCLUSION_MODES = /* @__PURE__ */ new Set(["full", "preview", "signatures"]);
|
|
523
525
|
function parsePartSections(partText) {
|
|
524
526
|
const lines = partText.split("\n");
|
|
525
527
|
const sections = [];
|
|
@@ -530,16 +532,26 @@ function parsePartSections(partText) {
|
|
|
530
532
|
continue;
|
|
531
533
|
}
|
|
532
534
|
const heading = lines[index].slice(FILE_HEADING.length);
|
|
535
|
+
const anchorMatch = index > 0 ? ANCHOR_LINE.exec(lines[index - 1]) : null;
|
|
533
536
|
let cursor = index + 1;
|
|
534
537
|
let inclusion = null;
|
|
535
|
-
let sha256 = null;
|
|
536
538
|
let includedLines = null;
|
|
537
539
|
while (cursor < lines.length && !CONTENT_MARKERS.has(lines[cursor])) {
|
|
538
540
|
const line = lines[cursor];
|
|
539
541
|
if (line.startsWith(FILE_HEADING)) break;
|
|
540
|
-
if (line.startsWith("-
|
|
541
|
-
|
|
542
|
-
|
|
542
|
+
if (line.startsWith("- Meta: ")) {
|
|
543
|
+
for (const field of line.slice("- Meta: ".length).split("|")) {
|
|
544
|
+
const value = field.trim();
|
|
545
|
+
if (INCLUSION_MODES.has(value)) inclusion = value;
|
|
546
|
+
}
|
|
547
|
+
const counts = /(-?\d+) of (-?\d+) lines|(\d+) lines/.exec(line);
|
|
548
|
+
if (counts) {
|
|
549
|
+
const emitted = counts[1] ?? counts[3];
|
|
550
|
+
includedLines = Number.isFinite(Number(emitted)) ? Number(emitted) : null;
|
|
551
|
+
}
|
|
552
|
+
} else if (line.startsWith("- Inclusion: ")) {
|
|
553
|
+
inclusion = line.slice("- Inclusion: ".length).trim();
|
|
554
|
+
} else if (line.startsWith("- Included Lines: ")) {
|
|
543
555
|
const value = Number(line.slice("- Included Lines: ".length).trim());
|
|
544
556
|
includedLines = Number.isFinite(value) ? value : null;
|
|
545
557
|
}
|
|
@@ -563,40 +575,42 @@ function parsePartSections(partText) {
|
|
|
563
575
|
body.push(lines[bodyLine]);
|
|
564
576
|
bodyLine += 1;
|
|
565
577
|
}
|
|
566
|
-
sections.push({ path: heading, body: body.join("\n"), inclusion,
|
|
578
|
+
sections.push({ path: heading, anchor: anchorMatch?.[1] ?? null, body: body.join("\n"), inclusion, includedLines });
|
|
567
579
|
index = bodyLine + 1;
|
|
568
580
|
}
|
|
569
581
|
return sections;
|
|
570
582
|
}
|
|
571
|
-
function findSection(sections, relativePath
|
|
572
|
-
const
|
|
583
|
+
function findSection(sections, relativePath) {
|
|
584
|
+
const anchor = anchorForPath(relativePath);
|
|
585
|
+
const byAnchor = sections.find((section) => section.anchor === anchor);
|
|
586
|
+
if (byAnchor) return byAnchor;
|
|
587
|
+
const rendered = displayPath(relativePath);
|
|
588
|
+
const samePath = (section) => section.path === relativePath || section.path === rendered;
|
|
589
|
+
const exact = sections.find(samePath);
|
|
573
590
|
if (exact) return exact;
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
(section) => section.path.toLowerCase() === relativePath.toLowerCase()
|
|
580
|
-
);
|
|
581
|
-
if (byPath) return byPath;
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
const folded = sections.filter((section) => section.path.toLowerCase() === relativePath.toLowerCase());
|
|
591
|
+
const foldedPath = rendered.toLowerCase();
|
|
592
|
+
const folded = sections.filter((section) => {
|
|
593
|
+
const path4 = section.path.toLowerCase();
|
|
594
|
+
return path4 === relativePath.toLowerCase() || path4 === foldedPath;
|
|
595
|
+
});
|
|
585
596
|
return folded.length === 1 ? folded[0] : null;
|
|
586
597
|
}
|
|
587
598
|
function windowText(body, fromLine, lineCount) {
|
|
588
599
|
const lines = body.split("\n");
|
|
589
|
-
const total = lines.length;
|
|
590
|
-
const
|
|
600
|
+
const total = body.length === 0 ? 0 : lines.length;
|
|
601
|
+
const requested = Math.max(1, Math.trunc(fromLine));
|
|
602
|
+
const pastEnd = total > 0 && requested > total;
|
|
603
|
+
const start = pastEnd ? requested : Math.min(requested, Math.max(1, total));
|
|
591
604
|
const end = Math.min(total, start + Math.max(1, Math.trunc(lineCount)) - 1);
|
|
592
|
-
const slice = lines.slice(start - 1, end);
|
|
605
|
+
const slice = pastEnd ? [] : lines.slice(start - 1, Math.max(start - 1, end));
|
|
593
606
|
return {
|
|
594
607
|
text: slice.join("\n"),
|
|
595
608
|
fromLine: start,
|
|
596
609
|
returnedLines: slice.length,
|
|
597
610
|
totalLines: total,
|
|
598
|
-
truncated: end < total,
|
|
599
|
-
nextFromLine: end < total ? end + 1 : null
|
|
611
|
+
truncated: !pastEnd && end < total,
|
|
612
|
+
nextFromLine: !pastEnd && end < total ? end + 1 : null,
|
|
613
|
+
pastEnd
|
|
600
614
|
};
|
|
601
615
|
}
|
|
602
616
|
function foldForComparison(value) {
|
|
@@ -667,7 +681,7 @@ async function openWorkspace(root, overrides = {}) {
|
|
|
667
681
|
}
|
|
668
682
|
try {
|
|
669
683
|
const config = resolveConfig(loaded.config, cliOptions, root);
|
|
670
|
-
return { ok: true, workspace: { root, config, configFile: loaded.file } };
|
|
684
|
+
return { ok: true, workspace: { root, config, configFile: loaded.file, cliOptions } };
|
|
671
685
|
} catch (error) {
|
|
672
686
|
return { ok: false, message: error instanceof Error ? error.message : String(error) };
|
|
673
687
|
}
|
|
@@ -741,6 +755,11 @@ var ROOT_PROPERTY = {
|
|
|
741
755
|
type: "string",
|
|
742
756
|
description: "Repository root. Defaults to the directory this server was started in."
|
|
743
757
|
};
|
|
758
|
+
var OUT_DIR_PROPERTY = {
|
|
759
|
+
type: "string",
|
|
760
|
+
description: "Snapshot directory, relative to the root. Defaults to the project configuration's outDir."
|
|
761
|
+
};
|
|
762
|
+
var outDirOverride = (args) => ({ outDir: args.outDir });
|
|
744
763
|
function failure(message) {
|
|
745
764
|
return { text: message, isError: true };
|
|
746
765
|
}
|
|
@@ -781,9 +800,39 @@ function toRelativePath(root, value) {
|
|
|
781
800
|
}
|
|
782
801
|
function symbolsOf(entry) {
|
|
783
802
|
const summary = entry.summary;
|
|
784
|
-
if (!summary || typeof summary !== "object"
|
|
785
|
-
const
|
|
786
|
-
|
|
803
|
+
if (!summary || typeof summary !== "object") return [];
|
|
804
|
+
const names = [];
|
|
805
|
+
const collect = (value) => {
|
|
806
|
+
if (!Array.isArray(value)) return;
|
|
807
|
+
for (const item of value) {
|
|
808
|
+
if (typeof item === "string") names.push(item);
|
|
809
|
+
}
|
|
810
|
+
};
|
|
811
|
+
const record = summary;
|
|
812
|
+
for (const field of [
|
|
813
|
+
"exports",
|
|
814
|
+
"reactComponents",
|
|
815
|
+
"routeHandlers",
|
|
816
|
+
"functions",
|
|
817
|
+
"classes",
|
|
818
|
+
"interfaces",
|
|
819
|
+
"types",
|
|
820
|
+
"enums",
|
|
821
|
+
"hooks",
|
|
822
|
+
"headings",
|
|
823
|
+
// The rest of the same records. `variables` is where a `const`, a Python constant, a Go
|
|
824
|
+
// `var`, a Rust `mod`, a C `#define` and a Terraform `variable` block all land, so leaving it
|
|
825
|
+
// out made `snapshot_locate` miss most of what a non-JavaScript file declares; `directives`
|
|
826
|
+
// carries `"use client"`-style markers, and `frontmatterKeys`/`keys` are the headings and
|
|
827
|
+
// top-level names of a Markdown or JSON file.
|
|
828
|
+
"variables",
|
|
829
|
+
"directives",
|
|
830
|
+
"frontmatterKeys",
|
|
831
|
+
"keys"
|
|
832
|
+
]) {
|
|
833
|
+
collect(record[field]);
|
|
834
|
+
}
|
|
835
|
+
return names;
|
|
787
836
|
}
|
|
788
837
|
async function prepare(args, context, overrides = {}) {
|
|
789
838
|
const rootOutcome = resolveRoot(args.root, context);
|
|
@@ -798,7 +847,7 @@ var statusTool = {
|
|
|
798
847
|
description: "Report whether a snapshot exists for this repository, when it was built, and what it covers. Reads the manifest rather than the repository, so it is cheap. Call it before deciding whether a snapshot needs to be generated.",
|
|
799
848
|
inputSchema: {
|
|
800
849
|
type: "object",
|
|
801
|
-
properties: { root: ROOT_PROPERTY },
|
|
850
|
+
properties: { root: ROOT_PROPERTY, outDir: OUT_DIR_PROPERTY },
|
|
802
851
|
additionalProperties: false
|
|
803
852
|
},
|
|
804
853
|
outputSchema: {
|
|
@@ -819,7 +868,7 @@ var statusTool = {
|
|
|
819
868
|
},
|
|
820
869
|
annotations: READ_ONLY,
|
|
821
870
|
async handler(args, context) {
|
|
822
|
-
const prepared = await prepare(args, context);
|
|
871
|
+
const prepared = await prepare(args, context, outDirOverride(args));
|
|
823
872
|
if (!prepared.ok) return prepared.outcome;
|
|
824
873
|
const workspace = prepared.workspace;
|
|
825
874
|
const read = readManifest(workspace);
|
|
@@ -878,6 +927,7 @@ var planTool = {
|
|
|
878
927
|
type: "object",
|
|
879
928
|
properties: {
|
|
880
929
|
root: ROOT_PROPERTY,
|
|
930
|
+
outDir: OUT_DIR_PROPERTY,
|
|
881
931
|
maxPartBytes: {
|
|
882
932
|
type: "string",
|
|
883
933
|
description: 'Byte budget per part, for example 307200 or "300k". Defaults to the project configuration.'
|
|
@@ -898,7 +948,7 @@ var planTool = {
|
|
|
898
948
|
},
|
|
899
949
|
annotations: READ_ONLY,
|
|
900
950
|
async handler(args, context) {
|
|
901
|
-
const prepared = await prepare(args, context, { maxPartBytes: args.maxPartBytes });
|
|
951
|
+
const prepared = await prepare(args, context, { maxPartBytes: args.maxPartBytes, outDir: args.outDir });
|
|
902
952
|
if (!prepared.ok) return prepared.outcome;
|
|
903
953
|
const workspace = prepared.workspace;
|
|
904
954
|
let manifest;
|
|
@@ -952,13 +1002,10 @@ var generateTool = {
|
|
|
952
1002
|
type: "object",
|
|
953
1003
|
properties: {
|
|
954
1004
|
root: ROOT_PROPERTY,
|
|
1005
|
+
outDir: OUT_DIR_PROPERTY,
|
|
955
1006
|
maxPartBytes: {
|
|
956
1007
|
type: "string",
|
|
957
1008
|
description: 'Byte budget per part, for example 307200 or "300k". Defaults to the project configuration.'
|
|
958
|
-
},
|
|
959
|
-
outDir: {
|
|
960
|
-
type: "string",
|
|
961
|
-
description: "Output directory relative to the root. Defaults to the project configuration."
|
|
962
1009
|
}
|
|
963
1010
|
},
|
|
964
1011
|
additionalProperties: false
|
|
@@ -981,35 +1028,32 @@ var generateTool = {
|
|
|
981
1028
|
});
|
|
982
1029
|
if (!prepared.ok) return prepared.outcome;
|
|
983
1030
|
const workspace = prepared.workspace;
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
} catch (error) {
|
|
988
|
-
return failure(error instanceof Error ? error.message : String(error));
|
|
989
|
-
}
|
|
1031
|
+
const result = await generateInWorker(workspace.root, workspace.cliOptions);
|
|
1032
|
+
if (!result.ok) return failure(result.message);
|
|
1033
|
+
const summary = result.summary;
|
|
990
1034
|
forgetManifest(manifestPathFor(workspace));
|
|
991
1035
|
const written = relativeFor(workspace, path2.resolve(workspace.root, workspace.config.outDir));
|
|
992
1036
|
const lines = [
|
|
993
1037
|
`Wrote ${written}/`,
|
|
994
|
-
`Files: ${
|
|
995
|
-
`Parts: ${
|
|
996
|
-
`Fingerprint: ${
|
|
1038
|
+
`Files: ${summary.totalFiles} included, ${summary.skippedCount} skipped, ${plural(summary.partsCount, "part")}`,
|
|
1039
|
+
`Parts: ${summary.parts.map((part) => `${part.filename} (${formatBytes(part.byteSize)})`).join(", ")}`,
|
|
1040
|
+
`Fingerprint: ${summary.fingerprint}`
|
|
997
1041
|
];
|
|
998
|
-
if (
|
|
999
|
-
lines.push(`Warnings (${
|
|
1000
|
-
for (const warning of
|
|
1042
|
+
if (summary.warnings.length > 0) {
|
|
1043
|
+
lines.push(`Warnings (${summary.warnings.length}):`);
|
|
1044
|
+
for (const warning of summary.warnings.slice(0, 5)) lines.push(` - ${warning}`);
|
|
1001
1045
|
}
|
|
1002
1046
|
return {
|
|
1003
1047
|
text: lines.join("\n"),
|
|
1004
1048
|
structured: {
|
|
1005
|
-
fingerprint:
|
|
1006
|
-
files:
|
|
1007
|
-
skipped:
|
|
1008
|
-
parts:
|
|
1009
|
-
emittedBytes:
|
|
1049
|
+
fingerprint: summary.fingerprint,
|
|
1050
|
+
files: summary.totalFiles,
|
|
1051
|
+
skipped: summary.skippedCount,
|
|
1052
|
+
parts: summary.partsCount,
|
|
1053
|
+
emittedBytes: summary.totalContentBytes,
|
|
1010
1054
|
outDir: written,
|
|
1011
|
-
partFiles:
|
|
1012
|
-
warningCount:
|
|
1055
|
+
partFiles: summary.parts.map((part) => part.filename),
|
|
1056
|
+
warningCount: summary.warnings.length
|
|
1013
1057
|
}
|
|
1014
1058
|
};
|
|
1015
1059
|
}
|
|
@@ -1020,7 +1064,7 @@ var validateTool = {
|
|
|
1020
1064
|
description: "Re-read the repository and report whether the snapshot on disk still matches it. Use it before trusting context that came from an earlier session, or after changing the config.",
|
|
1021
1065
|
inputSchema: {
|
|
1022
1066
|
type: "object",
|
|
1023
|
-
properties: { root: ROOT_PROPERTY },
|
|
1067
|
+
properties: { root: ROOT_PROPERTY, outDir: OUT_DIR_PROPERTY },
|
|
1024
1068
|
additionalProperties: false
|
|
1025
1069
|
},
|
|
1026
1070
|
outputSchema: {
|
|
@@ -1032,7 +1076,7 @@ var validateTool = {
|
|
|
1032
1076
|
},
|
|
1033
1077
|
annotations: READ_ONLY,
|
|
1034
1078
|
async handler(args, context) {
|
|
1035
|
-
const prepared = await prepare(args, context);
|
|
1079
|
+
const prepared = await prepare(args, context, outDirOverride(args));
|
|
1036
1080
|
if (!prepared.ok) return prepared.outcome;
|
|
1037
1081
|
const workspace = prepared.workspace;
|
|
1038
1082
|
let result;
|
|
@@ -1099,7 +1143,8 @@ var locateTool = {
|
|
|
1099
1143
|
default: 20,
|
|
1100
1144
|
description: "Maximum matches to return."
|
|
1101
1145
|
},
|
|
1102
|
-
root: ROOT_PROPERTY
|
|
1146
|
+
root: ROOT_PROPERTY,
|
|
1147
|
+
outDir: OUT_DIR_PROPERTY
|
|
1103
1148
|
},
|
|
1104
1149
|
required: ["query"],
|
|
1105
1150
|
additionalProperties: false
|
|
@@ -1110,7 +1155,7 @@ var locateTool = {
|
|
|
1110
1155
|
},
|
|
1111
1156
|
annotations: READ_ONLY,
|
|
1112
1157
|
async handler(args, context) {
|
|
1113
|
-
const prepared = await prepare(args, context);
|
|
1158
|
+
const prepared = await prepare(args, context, outDirOverride(args));
|
|
1114
1159
|
if (!prepared.ok) return prepared.outcome;
|
|
1115
1160
|
const workspace = prepared.workspace;
|
|
1116
1161
|
const read = readManifest(workspace);
|
|
@@ -1145,12 +1190,13 @@ var locateTool = {
|
|
|
1145
1190
|
continue;
|
|
1146
1191
|
}
|
|
1147
1192
|
}
|
|
1148
|
-
if (wants("layer") && entry.layer.toLowerCase()
|
|
1149
|
-
else if (wants("kind") && entry.kind.toLowerCase()
|
|
1193
|
+
if (wants("layer") && entry.layer.toLowerCase().includes(needle)) add(entry, "layer");
|
|
1194
|
+
else if (wants("kind") && entry.kind.toLowerCase().includes(needle)) add(entry, "kind");
|
|
1150
1195
|
}
|
|
1151
1196
|
if (wants("route")) {
|
|
1152
1197
|
for (const route of manifest.indexes.routes) {
|
|
1153
|
-
|
|
1198
|
+
const method = route.methods.find((name) => name.toLowerCase().includes(needle));
|
|
1199
|
+
if (!route.path.toLowerCase().includes(needle) && method === void 0) continue;
|
|
1154
1200
|
if (matches.some((match) => match.path === route.path)) continue;
|
|
1155
1201
|
matches.push({
|
|
1156
1202
|
path: route.path,
|
|
@@ -1185,6 +1231,127 @@ var locateTool = {
|
|
|
1185
1231
|
};
|
|
1186
1232
|
}
|
|
1187
1233
|
};
|
|
1234
|
+
var dependentsTool = {
|
|
1235
|
+
name: "snapshot_dependents",
|
|
1236
|
+
title: "Follow the import graph",
|
|
1237
|
+
description: 'For one file in the snapshot: what it imports, and which files import it. Answers "what breaks if I change this?" without reading the parts.',
|
|
1238
|
+
inputSchema: {
|
|
1239
|
+
type: "object",
|
|
1240
|
+
properties: {
|
|
1241
|
+
path: {
|
|
1242
|
+
type: "string",
|
|
1243
|
+
description: "Snapshot-relative path, exactly as snapshot_locate or manifest.files[] reports it."
|
|
1244
|
+
},
|
|
1245
|
+
direction: {
|
|
1246
|
+
type: "string",
|
|
1247
|
+
enum: ["both", "imports", "imported-by"],
|
|
1248
|
+
default: "both",
|
|
1249
|
+
description: "Which edges to return."
|
|
1250
|
+
},
|
|
1251
|
+
limit: {
|
|
1252
|
+
type: "integer",
|
|
1253
|
+
minimum: 1,
|
|
1254
|
+
maximum: 200,
|
|
1255
|
+
default: 50,
|
|
1256
|
+
description: "Maximum entries per direction."
|
|
1257
|
+
},
|
|
1258
|
+
root: ROOT_PROPERTY,
|
|
1259
|
+
outDir: OUT_DIR_PROPERTY
|
|
1260
|
+
},
|
|
1261
|
+
required: ["path"],
|
|
1262
|
+
additionalProperties: false
|
|
1263
|
+
},
|
|
1264
|
+
outputSchema: {
|
|
1265
|
+
type: "object",
|
|
1266
|
+
properties: {
|
|
1267
|
+
path: { type: "string" },
|
|
1268
|
+
imports: { type: "array" },
|
|
1269
|
+
importedBy: { type: "array" },
|
|
1270
|
+
unresolved: { type: "array" }
|
|
1271
|
+
}
|
|
1272
|
+
},
|
|
1273
|
+
annotations: READ_ONLY,
|
|
1274
|
+
async handler(args, context) {
|
|
1275
|
+
const prepared = await prepare(args, context, outDirOverride(args));
|
|
1276
|
+
if (!prepared.ok) return prepared.outcome;
|
|
1277
|
+
const read = readManifest(prepared.workspace);
|
|
1278
|
+
if (!read.ok) return failure(read.message);
|
|
1279
|
+
const manifest = read.manifest;
|
|
1280
|
+
const requested = String(args.path ?? "").trim().replace(/\\/g, "/");
|
|
1281
|
+
if (requested.length === 0) return failure('"path" must not be empty.');
|
|
1282
|
+
const filesByPath = new Map(manifest.files.map((file) => [file.relativePath, file]));
|
|
1283
|
+
const entry = filesByPath.get(requested) ?? manifest.files.find((file) => file.relativePath.toLowerCase() === requested.toLowerCase());
|
|
1284
|
+
if (!entry) {
|
|
1285
|
+
return failure(
|
|
1286
|
+
`${requested} is not in the snapshot. Use snapshot_locate to find its path, or snapshot_explain to see whether it was excluded.`
|
|
1287
|
+
);
|
|
1288
|
+
}
|
|
1289
|
+
const direction = args.direction === "imports" || args.direction === "imported-by" ? String(args.direction) : "both";
|
|
1290
|
+
const limit = typeof args.limit === "number" && args.limit > 0 ? Math.floor(args.limit) : 50;
|
|
1291
|
+
const resolver = buildImportResolver(manifest.files);
|
|
1292
|
+
const imports = [];
|
|
1293
|
+
const unresolved = [];
|
|
1294
|
+
if (direction !== "imported-by") {
|
|
1295
|
+
const own = manifest.indexes.imports.byFile.find((file) => file.path === entry.relativePath);
|
|
1296
|
+
for (const specifier of own?.imports ?? []) {
|
|
1297
|
+
const resolved = resolveSpecifier(entry.relativePath, specifier, resolver);
|
|
1298
|
+
if (resolved === null) {
|
|
1299
|
+
unresolved.push(specifier);
|
|
1300
|
+
continue;
|
|
1301
|
+
}
|
|
1302
|
+
const target = filesByPath.get(resolved);
|
|
1303
|
+
if (target) imports.push({ path: resolved, specifier, part: target.part });
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
const importedBy = [];
|
|
1307
|
+
if (direction !== "imports") {
|
|
1308
|
+
for (const candidate of manifest.indexes.imports.byFile) {
|
|
1309
|
+
if (candidate.path === entry.relativePath) continue;
|
|
1310
|
+
const edge = candidate.imports.some(
|
|
1311
|
+
(specifier) => resolveSpecifier(candidate.path, specifier, resolver) === entry.relativePath
|
|
1312
|
+
);
|
|
1313
|
+
if (!edge) continue;
|
|
1314
|
+
const source = filesByPath.get(candidate.path);
|
|
1315
|
+
importedBy.push({ path: candidate.path, part: source?.part ?? "" });
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
const shownImports = imports.slice(0, limit);
|
|
1319
|
+
const shownImportedBy = importedBy.slice(0, limit);
|
|
1320
|
+
const lines = [
|
|
1321
|
+
`${entry.relativePath} \u2014 ${importedBy.length} importer(s), ${imports.length} import(s), ${unresolved.length} outside the snapshot.`
|
|
1322
|
+
];
|
|
1323
|
+
if (direction !== "imports") {
|
|
1324
|
+
lines.push("", "Imported by:");
|
|
1325
|
+
if (shownImportedBy.length === 0) lines.push(" (nothing in the snapshot imports it)");
|
|
1326
|
+
for (const item of shownImportedBy) lines.push(` ${item.path} [${item.part}]`);
|
|
1327
|
+
if (importedBy.length > shownImportedBy.length) {
|
|
1328
|
+
lines.push(` \u2026 ${importedBy.length - shownImportedBy.length} more; raise \`limit\`.`);
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
if (direction !== "imported-by") {
|
|
1332
|
+
lines.push("", "Imports:");
|
|
1333
|
+
if (shownImports.length === 0) lines.push(" (none recorded for this file)");
|
|
1334
|
+
for (const item of shownImports) {
|
|
1335
|
+
lines.push(` ${item.path} (from "${item.specifier}") [${item.part}]`);
|
|
1336
|
+
}
|
|
1337
|
+
if (imports.length > shownImports.length) {
|
|
1338
|
+
lines.push(` \u2026 ${imports.length - shownImports.length} more; raise \`limit\`.`);
|
|
1339
|
+
}
|
|
1340
|
+
if (unresolved.length > 0) {
|
|
1341
|
+
lines.push("", `Outside the snapshot (${unresolved.length}): ${unresolved.join(", ")}`);
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
return {
|
|
1345
|
+
text: lines.join("\n"),
|
|
1346
|
+
structured: {
|
|
1347
|
+
path: entry.relativePath,
|
|
1348
|
+
imports: shownImports,
|
|
1349
|
+
importedBy: shownImportedBy,
|
|
1350
|
+
unresolved
|
|
1351
|
+
}
|
|
1352
|
+
};
|
|
1353
|
+
}
|
|
1354
|
+
};
|
|
1188
1355
|
var readTool = {
|
|
1189
1356
|
name: "snapshot_read",
|
|
1190
1357
|
title: "Read a file from the snapshot",
|
|
@@ -1209,7 +1376,8 @@ var readTool = {
|
|
|
1209
1376
|
default: 200,
|
|
1210
1377
|
description: "How many lines to return."
|
|
1211
1378
|
},
|
|
1212
|
-
root: ROOT_PROPERTY
|
|
1379
|
+
root: ROOT_PROPERTY,
|
|
1380
|
+
outDir: OUT_DIR_PROPERTY
|
|
1213
1381
|
},
|
|
1214
1382
|
required: ["path"],
|
|
1215
1383
|
additionalProperties: false
|
|
@@ -1227,7 +1395,7 @@ var readTool = {
|
|
|
1227
1395
|
},
|
|
1228
1396
|
annotations: READ_ONLY,
|
|
1229
1397
|
async handler(args, context) {
|
|
1230
|
-
const prepared = await prepare(args, context);
|
|
1398
|
+
const prepared = await prepare(args, context, outDirOverride(args));
|
|
1231
1399
|
if (!prepared.ok) return prepared.outcome;
|
|
1232
1400
|
const workspace = prepared.workspace;
|
|
1233
1401
|
if (typeof args.path !== "string") return failure('"path" must be a string.');
|
|
@@ -1262,7 +1430,7 @@ var readTool = {
|
|
|
1262
1430
|
`${entry.part} could not be read, so the snapshot is damaged. Run snapshot_generate to rebuild it.`
|
|
1263
1431
|
);
|
|
1264
1432
|
}
|
|
1265
|
-
const section = findSection(parsePartSections(partText), entry.relativePath
|
|
1433
|
+
const section = findSection(parsePartSections(partText), entry.relativePath);
|
|
1266
1434
|
if (!section) {
|
|
1267
1435
|
return failure(
|
|
1268
1436
|
`${relativePath} is recorded in ${entry.part} but its section could not be located there. Run snapshot_generate to rebuild the snapshot.`
|
|
@@ -1274,16 +1442,28 @@ var readTool = {
|
|
|
1274
1442
|
let body = window.text;
|
|
1275
1443
|
let truncatedByBytes = false;
|
|
1276
1444
|
if (Buffer.byteLength(body, "utf8") > MAX_RESULT_BYTES) {
|
|
1277
|
-
|
|
1445
|
+
const clipped = truncateToBytes(body, MAX_RESULT_BYTES);
|
|
1446
|
+
body = clipped.content;
|
|
1278
1447
|
const lastBreak = body.lastIndexOf("\n");
|
|
1279
1448
|
body = lastBreak > 0 ? body.slice(0, lastBreak) : body;
|
|
1280
1449
|
truncatedByBytes = true;
|
|
1281
1450
|
}
|
|
1282
1451
|
const returnedLines = body.length === 0 ? 0 : body.split("\n").length;
|
|
1283
|
-
const moreRemain = window.truncated || truncatedByBytes;
|
|
1452
|
+
const moreRemain = !window.pastEnd && (window.truncated || truncatedByBytes);
|
|
1284
1453
|
const header = [
|
|
1285
1454
|
`${entry.relativePath} \u2014 ${entry.part}, ${entry.layer}/${entry.kind}, ${entry.language}`,
|
|
1286
|
-
|
|
1455
|
+
// The line range is the answer to "where am I in this file", and it is computed from the
|
|
1456
|
+
// body the window was taken from, so it is just as meaningful for a preview as for a full
|
|
1457
|
+
// file. It used to be replaced — not augmented — by the preview note, so a 40-line window
|
|
1458
|
+
// into a 300-line preview came back with no range at all and the caller could not tell
|
|
1459
|
+
// where the window ended. Both facts are stated now, and `signatures` is no longer
|
|
1460
|
+
// silently treated as a full file.
|
|
1461
|
+
...section.inclusion === "preview" ? [
|
|
1462
|
+
"Bounded preview: the snapshot holds this file's head and tail only, so these line numbers count the preview, not the source."
|
|
1463
|
+
] : section.inclusion === "signatures" ? [
|
|
1464
|
+
"Signatures: function and method bodies were replaced with `{ /* ... */ }` when the snapshot was generated."
|
|
1465
|
+
] : [],
|
|
1466
|
+
window.totalLines === 0 ? "This file is empty." : window.pastEnd ? `This file has ${window.totalLines} line(s); fromLine=${window.fromLine} is past its end.` : `Lines ${window.fromLine}\u2013${window.fromLine + returnedLines - 1} of ${window.totalLines}.`
|
|
1287
1467
|
];
|
|
1288
1468
|
const lines = [...header, "", body];
|
|
1289
1469
|
if (moreRemain) {
|
|
@@ -1320,7 +1500,8 @@ var explainTool = {
|
|
|
1320
1500
|
type: "string",
|
|
1321
1501
|
description: "Path relative to the repository root. Directories are explained as a whole."
|
|
1322
1502
|
},
|
|
1323
|
-
root: ROOT_PROPERTY
|
|
1503
|
+
root: ROOT_PROPERTY,
|
|
1504
|
+
outDir: OUT_DIR_PROPERTY
|
|
1324
1505
|
},
|
|
1325
1506
|
required: ["path"],
|
|
1326
1507
|
additionalProperties: false
|
|
@@ -1335,7 +1516,7 @@ var explainTool = {
|
|
|
1335
1516
|
},
|
|
1336
1517
|
annotations: READ_ONLY,
|
|
1337
1518
|
async handler(args, context) {
|
|
1338
|
-
const prepared = await prepare(args, context);
|
|
1519
|
+
const prepared = await prepare(args, context, outDirOverride(args));
|
|
1339
1520
|
if (!prepared.ok) return prepared.outcome;
|
|
1340
1521
|
const workspace = prepared.workspace;
|
|
1341
1522
|
if (typeof args.path !== "string") return failure('"path" must be a string.');
|
|
@@ -1375,6 +1556,7 @@ function buildTools() {
|
|
|
1375
1556
|
generateTool,
|
|
1376
1557
|
validateTool,
|
|
1377
1558
|
locateTool,
|
|
1559
|
+
dependentsTool,
|
|
1378
1560
|
readTool,
|
|
1379
1561
|
explainTool
|
|
1380
1562
|
];
|
|
@@ -1446,9 +1628,15 @@ var RESOURCE_TEMPLATE_URIS = ["snapshot://parts/{index}"];
|
|
|
1446
1628
|
|
|
1447
1629
|
// src/agent/option-scope.ts
|
|
1448
1630
|
var VALIDATE_OPTIONS = [
|
|
1631
|
+
"sarif",
|
|
1632
|
+
"skeleton",
|
|
1633
|
+
"changed-since",
|
|
1634
|
+
"staged",
|
|
1635
|
+
"with-dependents",
|
|
1449
1636
|
"cwd",
|
|
1450
1637
|
"out-dir",
|
|
1451
1638
|
"max-part-bytes",
|
|
1639
|
+
"max-part-tokens",
|
|
1452
1640
|
"large-file-bytes",
|
|
1453
1641
|
"max-monolith-bytes",
|
|
1454
1642
|
"include-monolith",
|
|
@@ -1465,9 +1653,15 @@ var VALIDATE_OPTIONS = [
|
|
|
1465
1653
|
];
|
|
1466
1654
|
var COMMAND_OPTION_SCOPE = {
|
|
1467
1655
|
generate: [
|
|
1656
|
+
"sarif",
|
|
1657
|
+
"skeleton",
|
|
1658
|
+
"changed-since",
|
|
1659
|
+
"staged",
|
|
1660
|
+
"with-dependents",
|
|
1468
1661
|
"cwd",
|
|
1469
1662
|
"out-dir",
|
|
1470
1663
|
"max-part-bytes",
|
|
1664
|
+
"max-part-tokens",
|
|
1471
1665
|
"large-file-bytes",
|
|
1472
1666
|
"max-monolith-bytes",
|
|
1473
1667
|
"include-monolith",
|
|
@@ -1505,6 +1699,12 @@ var COMMAND_OPTION_SCOPE = {
|
|
|
1505
1699
|
"version"
|
|
1506
1700
|
],
|
|
1507
1701
|
explain: [
|
|
1702
|
+
// The scope decides what `explain` says about a file, dependents included: without these,
|
|
1703
|
+
// a file that is in the snapshot only because it imports a changed one was explained as
|
|
1704
|
+
// `included: false, reason: "unchanged"`.
|
|
1705
|
+
"changed-since",
|
|
1706
|
+
"staged",
|
|
1707
|
+
"with-dependents",
|
|
1508
1708
|
"cwd",
|
|
1509
1709
|
"out-dir",
|
|
1510
1710
|
"max-part-bytes",
|
|
@@ -1525,6 +1725,7 @@ var COMMAND_OPTION_SCOPE = {
|
|
|
1525
1725
|
diff: ["format", "mode", "no-config", "no-update-check", "help", "version"],
|
|
1526
1726
|
describe: ["format", "mode", "help", "version"],
|
|
1527
1727
|
init: [
|
|
1728
|
+
"install-hook",
|
|
1528
1729
|
"cwd",
|
|
1529
1730
|
"out-dir",
|
|
1530
1731
|
"max-part-bytes",
|
|
@@ -1547,13 +1748,29 @@ var COMMAND_OPTION_SCOPE = {
|
|
|
1547
1748
|
// `mcp` reads the repository, so it takes --cwd. It has no --format: stdout carries JSON-RPC
|
|
1548
1749
|
// messages, and there is no second shape to choose, so offering the flag would be a lie.
|
|
1549
1750
|
mcp: ["cwd", "mode", "help", "version"],
|
|
1751
|
+
// `setup` generates through the `generate` command, so every generation setting it can
|
|
1752
|
+
// honour is in scope. Rejecting them made the one-command path unable to express a walk
|
|
1753
|
+
// scan, a monolith or a different large-file threshold; the alternative was to run
|
|
1754
|
+
// `setup` and then edit the config it had just written.
|
|
1550
1755
|
setup: [
|
|
1756
|
+
"install-hook",
|
|
1551
1757
|
"cwd",
|
|
1552
1758
|
"out-dir",
|
|
1553
1759
|
"max-part-bytes",
|
|
1760
|
+
// Persisted into the config `setup` writes, then forwarded to the generation it runs — so
|
|
1761
|
+
// the snapshot it produces validates against the config beside it. Accepting a setting here
|
|
1762
|
+
// without writing it down made `setup --large-file-bytes 100k` finish with a snapshot its
|
|
1763
|
+
// own `validate` called drifted, on the first run of a brand-new project.
|
|
1764
|
+
"max-part-tokens",
|
|
1765
|
+
"skeleton",
|
|
1766
|
+
"sarif",
|
|
1767
|
+
"large-file-bytes",
|
|
1768
|
+
"max-monolith-bytes",
|
|
1769
|
+
"include-monolith",
|
|
1554
1770
|
"include-sensitive-files",
|
|
1555
1771
|
"skip-artifacts",
|
|
1556
1772
|
"only-artifacts",
|
|
1773
|
+
"discovery",
|
|
1557
1774
|
"preset",
|
|
1558
1775
|
"include",
|
|
1559
1776
|
"exclude",
|
|
@@ -1839,6 +2056,13 @@ function buildAgentContract() {
|
|
|
1839
2056
|
appliesTo: optionAppliesTo("max-part-bytes"),
|
|
1840
2057
|
summary: 'Byte budget per markdown part. Accepts "120k" / "1.5mb". Minimum 4096.'
|
|
1841
2058
|
},
|
|
2059
|
+
{
|
|
2060
|
+
flag: "--max-part-tokens",
|
|
2061
|
+
type: "string",
|
|
2062
|
+
default: "unset",
|
|
2063
|
+
appliesTo: optionAppliesTo("max-part-tokens"),
|
|
2064
|
+
summary: "Ceiling on a part expressed in tokens. Narrows --max-part-bytes instead of replacing it; the manifest records both units. Minimum 1024."
|
|
2065
|
+
},
|
|
1842
2066
|
{
|
|
1843
2067
|
flag: "--large-file-bytes",
|
|
1844
2068
|
type: "string",
|
|
@@ -1888,6 +2112,41 @@ function buildAgentContract() {
|
|
|
1888
2112
|
appliesTo: optionAppliesTo("only-artifacts"),
|
|
1889
2113
|
summary: "Write only these artifacts. Cannot be combined with --skip-artifacts."
|
|
1890
2114
|
},
|
|
2115
|
+
{
|
|
2116
|
+
flag: "--skeleton",
|
|
2117
|
+
type: "boolean",
|
|
2118
|
+
default: false,
|
|
2119
|
+
appliesTo: optionAppliesTo("skeleton"),
|
|
2120
|
+
summary: 'Replace function and method bodies with `{ /* ... */ }` in TypeScript/TSX files, keeping signatures and comments. The part records inclusionMode "signatures" for each file it touched.'
|
|
2121
|
+
},
|
|
2122
|
+
{
|
|
2123
|
+
flag: "--sarif",
|
|
2124
|
+
type: "boolean",
|
|
2125
|
+
default: false,
|
|
2126
|
+
appliesTo: optionAppliesTo("sarif"),
|
|
2127
|
+
summary: "Also write reports/audit.sarif (SARIF 2.1.0) so a CI security gate can read the redaction findings. The matched value is never included."
|
|
2128
|
+
},
|
|
2129
|
+
{
|
|
2130
|
+
flag: "--changed-since",
|
|
2131
|
+
type: "string",
|
|
2132
|
+
default: "unset",
|
|
2133
|
+
appliesTo: optionAppliesTo("changed-since"),
|
|
2134
|
+
summary: 'Snapshot only the files that differ from this git ref, plus untracked ones. Everything else is recorded in skipped[] with reason "unchanged". Requires a git checkout.'
|
|
2135
|
+
},
|
|
2136
|
+
{
|
|
2137
|
+
flag: "--staged",
|
|
2138
|
+
type: "boolean",
|
|
2139
|
+
default: false,
|
|
2140
|
+
appliesTo: optionAppliesTo("staged"),
|
|
2141
|
+
summary: "Snapshot only the files staged in the index. The pre-commit counterpart of --changed-since."
|
|
2142
|
+
},
|
|
2143
|
+
{
|
|
2144
|
+
flag: "--with-dependents",
|
|
2145
|
+
type: "boolean",
|
|
2146
|
+
default: false,
|
|
2147
|
+
appliesTo: optionAppliesTo("with-dependents"),
|
|
2148
|
+
summary: "Widen a changed-files scope by one hop: files that import a changed file are included too. Only meaningful with --changed-since or --staged."
|
|
2149
|
+
},
|
|
1891
2150
|
{
|
|
1892
2151
|
flag: "--format",
|
|
1893
2152
|
type: "string",
|
|
@@ -1995,6 +2254,13 @@ function buildAgentContract() {
|
|
|
1995
2254
|
appliesTo: optionAppliesTo("no-install"),
|
|
1996
2255
|
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)."
|
|
1997
2256
|
},
|
|
2257
|
+
{
|
|
2258
|
+
flag: "--install-hook",
|
|
2259
|
+
type: "boolean",
|
|
2260
|
+
default: false,
|
|
2261
|
+
appliesTo: optionAppliesTo("install-hook"),
|
|
2262
|
+
summary: "After writing the config, install a pre-commit hook that runs `validate`. An existing hook this tool did not write is left alone."
|
|
2263
|
+
},
|
|
1998
2264
|
{
|
|
1999
2265
|
flag: "--no-update-check",
|
|
2000
2266
|
type: "boolean",
|
|
@@ -2031,6 +2297,12 @@ function buildAgentContract() {
|
|
|
2031
2297
|
{ key: "branch", type: "string", default: "main", summary: "Branch recorded when git cannot report one." },
|
|
2032
2298
|
{ key: "preset", type: "string", default: "detected", summary: "Force a preset. Unknown names are an error." },
|
|
2033
2299
|
{ key: "maxPartBytes", type: "number | string", default: 307200, summary: "Per-part byte budget." },
|
|
2300
|
+
{ key: "maxPartTokens", type: "number | string | null", default: null, summary: "Per-part token ceiling; narrows maxPartBytes. Minimum 1024." },
|
|
2301
|
+
{ key: "skeleton", type: "boolean", default: false, summary: "Collapse function bodies to `{ /* ... */ }` in TypeScript files." },
|
|
2302
|
+
{ key: "sarif", type: "boolean", default: false, summary: "Also write reports/audit.sarif." },
|
|
2303
|
+
{ key: "changedSince", type: "string | null", default: null, summary: "Snapshot only files that differ from this git ref." },
|
|
2304
|
+
{ key: "staged", type: "boolean", default: false, summary: "Snapshot only files staged in the index." },
|
|
2305
|
+
{ key: "withDependents", type: "boolean", default: false, summary: "With a changed-files scope, also include one hop of importers." },
|
|
2034
2306
|
{ key: "largeFileBytes", type: "number | string", default: 204800, summary: "Preview threshold for large files." },
|
|
2035
2307
|
{ key: "previewHeadLines", type: "number", default: 80, summary: "Lines kept from the head of a large file." },
|
|
2036
2308
|
{ key: "previewTailLines", type: "number", default: 40, summary: "Lines kept from the tail of a large file." },
|
|
@@ -2213,14 +2485,19 @@ do not assume the repository is broken.
|
|
|
2213
2485
|
matches the files on disk. Exit 1 means drift \u2014 re-run step 5.
|
|
2214
2486
|
|
|
2215
2487
|
7. **Read the output in this order:**
|
|
2216
|
-
- \`snapshot/parts/part-*.md\` \u2014 the code itself
|
|
2217
|
-
language and, when
|
|
2218
|
-
classes, functions). A
|
|
2488
|
+
- \`snapshot/parts/part-*.md\` \u2014 the code itself. Each file carries one \`- Meta:\` line
|
|
2489
|
+
(layer/kind, language, inclusion, size) and, only when the body is **not** the whole
|
|
2490
|
+
file, a structural summary (imports, exports, classes, functions). A full file has no
|
|
2491
|
+
summary line, because the summary would repeat the code three lines below it \u2014 read
|
|
2492
|
+
\`manifest.json\` for it. A summary dropped to fit the budget appears as a
|
|
2493
|
+
\`Summary: (omitted \u2026)\` line.
|
|
2219
2494
|
- \`snapshot/manifest.json\` \u2014 the index. \`files[]\` has \`relativePath\`, \`layer\`,
|
|
2220
|
-
\`kind\`, \`language\`, \`part\`, \`inclusionMode\` and
|
|
2221
|
-
when the file was not plain UTF-8. \`summary\` is
|
|
2222
|
-
languages with no structural summary, and
|
|
2223
|
-
to fit the part budget \u2014 never assume it
|
|
2495
|
+
\`kind\`, \`language\`, \`part\`, \`inclusionMode\`, \`hashSha256\` and the structural
|
|
2496
|
+
\`summary\`, plus \`encoding\` when the file was not plain UTF-8. \`summary\` is
|
|
2497
|
+
**optional**: it is absent for languages with no structural summary, and
|
|
2498
|
+
\`summaryOmitted: true\` marks one dropped to fit the part budget \u2014 never assume it
|
|
2499
|
+
exists. The per-file \`hashSha256\` is only here and in the manifest; it is not printed
|
|
2500
|
+
into the parts.
|
|
2224
2501
|
\`skipped[]\` records every excluded file **and why**. Prefer querying this over
|
|
2225
2502
|
re-reading the parts.
|
|
2226
2503
|
- \`snapshot/reports/audit.md\` \u2014 secrets found, large files, parse diagnostics, git
|
|
@@ -2274,7 +2551,7 @@ diff, and the commit, are then yours.
|
|
|
2274
2551
|
## When the client speaks MCP
|
|
2275
2552
|
|
|
2276
2553
|
If the agent runs inside an MCP client, \`npx ${PACKAGE_NAME2}@latest mcp\` serves this repository
|
|
2277
|
-
as
|
|
2554
|
+
as eight tools over stdio instead of shelling out. Its reads come from the snapshot rather than
|
|
2278
2555
|
the working tree, so excluded files and redacted values never reach the context. Use
|
|
2279
2556
|
\`snapshot_locate\` before \`snapshot_read\`, and \`snapshot_validate\` before trusting a snapshot
|
|
2280
2557
|
from an earlier session.
|
|
@@ -2431,7 +2708,8 @@ var SERVER_INSTRUCTIONS = [
|
|
|
2431
2708
|
"1. snapshot_status \u2014 is there a snapshot here, and has the tree moved since it was built?",
|
|
2432
2709
|
"2. snapshot_locate \u2014 find which part holds the code you need.",
|
|
2433
2710
|
"3. snapshot_read \u2014 read a window of one file. Never read a part end to end.",
|
|
2434
|
-
"4.
|
|
2711
|
+
"4. snapshot_dependents \u2014 before changing a file, see what imports it.",
|
|
2712
|
+
"5. snapshot_validate \u2014 before trusting context that came from an earlier conversation.",
|
|
2435
2713
|
"",
|
|
2436
2714
|
"Rules that matter:",
|
|
2437
2715
|
"- Parts are budgeted, not small: a 12-part snapshot is hundreds of kilobytes. Use snapshot_locate first.",
|
|
@@ -2474,7 +2752,13 @@ function createLineReader(input, options) {
|
|
|
2474
2752
|
while (index >= 0) {
|
|
2475
2753
|
const line = buffer.slice(0, index);
|
|
2476
2754
|
buffer = buffer.slice(index + 1);
|
|
2477
|
-
|
|
2755
|
+
if (discarding) {
|
|
2756
|
+
deliver(line);
|
|
2757
|
+
} else if (line.length > MAX_LINE_BYTES) {
|
|
2758
|
+
options.onOverflow?.(line.length);
|
|
2759
|
+
} else {
|
|
2760
|
+
deliver(line);
|
|
2761
|
+
}
|
|
2478
2762
|
index = buffer.indexOf("\n");
|
|
2479
2763
|
}
|
|
2480
2764
|
};
|
|
@@ -2491,14 +2775,14 @@ function createLineReader(input, options) {
|
|
|
2491
2775
|
return;
|
|
2492
2776
|
}
|
|
2493
2777
|
buffer += chunk;
|
|
2494
|
-
|
|
2778
|
+
drain();
|
|
2779
|
+
if (buffer.length > MAX_LINE_BYTES) {
|
|
2495
2780
|
discarding = true;
|
|
2496
2781
|
discardedBytes = buffer.length;
|
|
2497
2782
|
buffer = "";
|
|
2498
2783
|
options.onOverflow?.(discardedBytes);
|
|
2499
2784
|
return;
|
|
2500
2785
|
}
|
|
2501
|
-
drain();
|
|
2502
2786
|
};
|
|
2503
2787
|
const onEnd = () => {
|
|
2504
2788
|
if (buffer.length > 0) {
|
|
@@ -2598,6 +2882,4 @@ async function runStdioServer(options) {
|
|
|
2598
2882
|
return code;
|
|
2599
2883
|
}
|
|
2600
2884
|
|
|
2601
|
-
export { COMMAND_OPTION_SCOPE, ERROR_CODES, LEGACY_PROTOCOL_VERSIONS, MAX_LINE_BYTES, METHOD, MODERN_PROTOCOL_VERSIONS, SERVER_INSTRUCTIONS, SKILL_NAME, SUPPORTED_PROTOCOL_VERSIONS, buildAgentContract, buildPrompts, buildResourceProvider, buildTools, classifyRequest, coerceNumericStrings, commandsForOption, createLineReader, createMcpServer, createServer, createSession, createWriter, inspectModernMeta, negotiateLegacyVersion, renderContractText, renderSkill, runStdioServer, validateArguments };
|
|
2602
|
-
//# sourceMappingURL=chunk-RQKXZOD7.js.map
|
|
2603
|
-
//# sourceMappingURL=chunk-RQKXZOD7.js.map
|
|
2885
|
+
export { COMMAND_OPTION_SCOPE, ERROR_CODES, LEGACY_PROTOCOL_VERSIONS, MAX_LINE_BYTES, METHOD, MODERN_PROTOCOL_VERSIONS, SERVER_INSTRUCTIONS, SKILL_NAME, SUPPORTED_PROTOCOL_VERSIONS, buildAgentContract, buildPrompts, buildResourceProvider, buildTools, classifyRequest, coerceNumericStrings, commandsForOption, createLineReader, createMcpServer, createServer, createSession, createWriter, findSection, inspectModernMeta, negotiateLegacyVersion, parsePartSections, renderContractText, renderSkill, runStdioServer, validateArguments, windowText };
|