@descryy/mcp 0.1.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/LICENSE +21 -0
- package/dist/action-handshake.d.ts +86 -0
- package/dist/action-handshake.d.ts.map +1 -0
- package/dist/action-handshake.js +144 -0
- package/dist/action-handshake.js.map +1 -0
- package/dist/bin/descry-mcp.d.ts +18 -0
- package/dist/bin/descry-mcp.d.ts.map +1 -0
- package/dist/bin/descry-mcp.js +54 -0
- package/dist/bin/descry-mcp.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/language-guess.d.ts +17 -0
- package/dist/language-guess.d.ts.map +1 -0
- package/dist/language-guess.js +43 -0
- package/dist/language-guess.js.map +1 -0
- package/dist/protocol.d.ts +91 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +127 -0
- package/dist/protocol.js.map +1 -0
- package/dist/registry.d.ts +72 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +136 -0
- package/dist/registry.js.map +1 -0
- package/dist/render.d.ts +275 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +333 -0
- package/dist/render.js.map +1 -0
- package/dist/server.d.ts +42 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +263 -0
- package/dist/server.js.map +1 -0
- package/dist/session.d.ts +164 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +302 -0
- package/dist/session.js.map +1 -0
- package/dist/tools/analyze.d.ts +124 -0
- package/dist/tools/analyze.d.ts.map +1 -0
- package/dist/tools/analyze.js +411 -0
- package/dist/tools/analyze.js.map +1 -0
- package/dist/tools/contracts.d.ts +105 -0
- package/dist/tools/contracts.d.ts.map +1 -0
- package/dist/tools/contracts.js +141 -0
- package/dist/tools/contracts.js.map +1 -0
- package/dist/tools/cross-pr.d.ts +137 -0
- package/dist/tools/cross-pr.d.ts.map +1 -0
- package/dist/tools/cross-pr.js +216 -0
- package/dist/tools/cross-pr.js.map +1 -0
- package/dist/tools/git-diff.d.ts +29 -0
- package/dist/tools/git-diff.d.ts.map +1 -0
- package/dist/tools/git-diff.js +117 -0
- package/dist/tools/git-diff.js.map +1 -0
- package/dist/tools/git-history.d.ts +27 -0
- package/dist/tools/git-history.d.ts.map +1 -0
- package/dist/tools/git-history.js +91 -0
- package/dist/tools/git-history.js.map +1 -0
- package/dist/tools/history.d.ts +63 -0
- package/dist/tools/history.d.ts.map +1 -0
- package/dist/tools/history.js +114 -0
- package/dist/tools/history.js.map +1 -0
- package/dist/tools/impact.d.ts +81 -0
- package/dist/tools/impact.d.ts.map +1 -0
- package/dist/tools/impact.js +229 -0
- package/dist/tools/impact.js.map +1 -0
- package/dist/tools/index.d.ts +33 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +47 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/kit.d.ts +183 -0
- package/dist/tools/kit.d.ts.map +1 -0
- package/dist/tools/kit.js +117 -0
- package/dist/tools/kit.js.map +1 -0
- package/dist/tools/link-workspace.d.ts +62 -0
- package/dist/tools/link-workspace.d.ts.map +1 -0
- package/dist/tools/link-workspace.js +156 -0
- package/dist/tools/link-workspace.js.map +1 -0
- package/dist/tools/lookup.d.ts +29 -0
- package/dist/tools/lookup.d.ts.map +1 -0
- package/dist/tools/lookup.js +73 -0
- package/dist/tools/lookup.js.map +1 -0
- package/dist/tools/pr-analysis.d.ts +89 -0
- package/dist/tools/pr-analysis.d.ts.map +1 -0
- package/dist/tools/pr-analysis.js +169 -0
- package/dist/tools/pr-analysis.js.map +1 -0
- package/dist/tools/propagation.d.ts +74 -0
- package/dist/tools/propagation.d.ts.map +1 -0
- package/dist/tools/propagation.js +194 -0
- package/dist/tools/propagation.js.map +1 -0
- package/dist/tools/questions.d.ts +80 -0
- package/dist/tools/questions.d.ts.map +1 -0
- package/dist/tools/questions.js +213 -0
- package/dist/tools/questions.js.map +1 -0
- package/dist/tools/refusal-fetch.d.ts +71 -0
- package/dist/tools/refusal-fetch.d.ts.map +1 -0
- package/dist/tools/refusal-fetch.js +137 -0
- package/dist/tools/refusal-fetch.js.map +1 -0
- package/dist/tools/scope.d.ts +60 -0
- package/dist/tools/scope.d.ts.map +1 -0
- package/dist/tools/scope.js +160 -0
- package/dist/tools/scope.js.map +1 -0
- package/dist/tools/similar-incidents.d.ts +105 -0
- package/dist/tools/similar-incidents.d.ts.map +1 -0
- package/dist/tools/similar-incidents.js +228 -0
- package/dist/tools/similar-incidents.js.map +1 -0
- package/dist/tools/validate.d.ts +103 -0
- package/dist/tools/validate.d.ts.map +1 -0
- package/dist/tools/validate.js +176 -0
- package/dist/tools/validate.js.map +1 -0
- package/dist/tools/verification-status.d.ts +68 -0
- package/dist/tools/verification-status.d.ts.map +1 -0
- package/dist/tools/verification-status.js +179 -0
- package/dist/tools/verification-status.js.map +1 -0
- package/dist/transport.d.ts +58 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +115 -0
- package/dist/transport.js.map +1 -0
- package/package.json +33 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `get_git_diff` — §18's "other useful Git tool": a live textual diff
|
|
3
|
+
* between two refs. P27's disclosed remainder — `ai-layer-plan.md` §3's
|
|
4
|
+
* re-derivation found `history.ts`'s own header already stating why it
|
|
5
|
+
* cannot be stretched into this shape: it is scoped to the stored
|
|
6
|
+
* `CHANGES_WITH` ledger, not a live `git diff`. `changedFiles`
|
|
7
|
+
* (`cross-pr.ts`) answers "which files changed" as a bare path list; this
|
|
8
|
+
* answers "what changed in them" — per-file status and line counts always,
|
|
9
|
+
* plus the unified-diff patch text when it is cheap enough to hand to a
|
|
10
|
+
* model (§4: the client's context window is not this tool's to spend).
|
|
11
|
+
*/
|
|
12
|
+
import { gitDiff } from "@descryy/core";
|
|
13
|
+
import { answer, optionalInteger, optionalString, optionalStringArray, requiredString, } from "./kit.js";
|
|
14
|
+
const DEFAULT_LIMIT = 200;
|
|
15
|
+
const SCHEMA = {
|
|
16
|
+
type: "object",
|
|
17
|
+
properties: {
|
|
18
|
+
base: {
|
|
19
|
+
type: "string",
|
|
20
|
+
description: "The ref to diff against. Required — there is no defensible default, and guessing one " +
|
|
21
|
+
"silently changes which diff the answer describes.",
|
|
22
|
+
},
|
|
23
|
+
head: { type: "string", description: "The tip being diffed. Default HEAD." },
|
|
24
|
+
files: {
|
|
25
|
+
type: "array",
|
|
26
|
+
items: { type: "string" },
|
|
27
|
+
description: "Restrict the diff to these repo-relative paths. Default: the whole diff.",
|
|
28
|
+
},
|
|
29
|
+
maxPatchBytes: {
|
|
30
|
+
type: "integer",
|
|
31
|
+
minimum: 1,
|
|
32
|
+
description: "A file's patch text is omitted (its status/additions/deletions are still reported) above " +
|
|
33
|
+
"this many bytes. Default 20,000 — narrow \"files\" to fetch a large file's patch on its own.",
|
|
34
|
+
},
|
|
35
|
+
limit: { type: "integer", minimum: 1, description: "Cap on files in the reply. Default 200." },
|
|
36
|
+
},
|
|
37
|
+
required: ["base"],
|
|
38
|
+
additionalProperties: false,
|
|
39
|
+
};
|
|
40
|
+
async function run(args, ctx) {
|
|
41
|
+
const base = requiredString(args, "base");
|
|
42
|
+
const head = optionalString(args, "head") ?? "HEAD";
|
|
43
|
+
const files = optionalStringArray(args, "files");
|
|
44
|
+
const maxPatchBytes = optionalInteger(args, "maxPatchBytes", 1);
|
|
45
|
+
const limit = optionalInteger(args, "limit", 1) ?? DEFAULT_LIMIT;
|
|
46
|
+
const { session } = ctx;
|
|
47
|
+
const stamp = session.provider().baseStamp();
|
|
48
|
+
const envelope = {
|
|
49
|
+
resolutionFloor: 0, // a live filesystem read, not a graph traversal claim — same convention as similar_incidents' R0 floor
|
|
50
|
+
commitSha: stamp.commitSha,
|
|
51
|
+
graphBuiltAt: stamp.graphBuiltAt,
|
|
52
|
+
irSchemaVersion: stamp.irSchemaVersion,
|
|
53
|
+
// "This file changed, N lines added/removed" is a name-level fact about
|
|
54
|
+
// the diff, not a claim about program shape.
|
|
55
|
+
nameLevel: true,
|
|
56
|
+
};
|
|
57
|
+
ctx.progress(`Diffing ${base}...${head}`);
|
|
58
|
+
let all;
|
|
59
|
+
try {
|
|
60
|
+
all = await gitDiff(session.repoPath, base, head, {
|
|
61
|
+
...(files === undefined ? {} : { files }),
|
|
62
|
+
...(maxPatchBytes === undefined ? {} : { maxPatchBytes }),
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
return answer({
|
|
67
|
+
...envelope,
|
|
68
|
+
headline: `Could not diff ${base}...${head}.`,
|
|
69
|
+
state: "refused",
|
|
70
|
+
data: { base, head, files: [], filesTotal: 0 },
|
|
71
|
+
notes: [
|
|
72
|
+
`git rejected the range: ${error.message.trim()}.`,
|
|
73
|
+
"No diff was read, which is not a finding that nothing changed.",
|
|
74
|
+
],
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
const shown = all.slice(0, limit);
|
|
78
|
+
const notes = [];
|
|
79
|
+
if (shown.length < all.length) {
|
|
80
|
+
notes.push(`${all.length - shown.length} further changed file(s) exist but are not listed — the reply ` +
|
|
81
|
+
`is capped at ${limit}.`);
|
|
82
|
+
}
|
|
83
|
+
const omittedPatches = shown.filter((f) => f.patchOmittedReason !== null).length;
|
|
84
|
+
if (omittedPatches > 0) {
|
|
85
|
+
notes.push(`${omittedPatches} of the listed file(s) had their patch text withheld — see each file's own ` +
|
|
86
|
+
'"patchOmittedReason".');
|
|
87
|
+
}
|
|
88
|
+
return answer({
|
|
89
|
+
...envelope,
|
|
90
|
+
headline: all.length === 0
|
|
91
|
+
? `${base}...${head} has no changes.`
|
|
92
|
+
: `${base}...${head} changes ${all.length} file(s)` +
|
|
93
|
+
(shown.length < all.length ? `, showing the first ${shown.length}.` : "."),
|
|
94
|
+
...(all.length === 0 ? { state: "empty" } : {}),
|
|
95
|
+
data: { base, head, files: shown, filesTotal: all.length },
|
|
96
|
+
truncated: shown.length < all.length,
|
|
97
|
+
...(shown.length < all.length
|
|
98
|
+
? { truncatedDetail: { shown: shown.length, total: all.length, more: null } }
|
|
99
|
+
: {}),
|
|
100
|
+
notes,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
export const gitDiffTool = {
|
|
104
|
+
name: "get_git_diff",
|
|
105
|
+
class: "read",
|
|
106
|
+
tier: "evidence",
|
|
107
|
+
version: "1.0.0",
|
|
108
|
+
title: "Live textual diff between two refs",
|
|
109
|
+
description: "A live git diff between two refs — per-file status (added/modified/deleted/renamed), " +
|
|
110
|
+
"additions/deletions, and unified-diff patch text where it is small enough to include. Reads " +
|
|
111
|
+
"the working repository directly, not the stored graph — for the graph's stored co-change " +
|
|
112
|
+
"history use \"history\" instead. A file's patch is withheld above \"maxPatchBytes\" with a " +
|
|
113
|
+
"named reason; its stats are still reported.",
|
|
114
|
+
inputSchema: SCHEMA,
|
|
115
|
+
run,
|
|
116
|
+
};
|
|
117
|
+
//# sourceMappingURL=git-diff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-diff.js","sourceRoot":"","sources":["../../src/tools/git-diff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAGxC,OAAO,EACL,MAAM,EACN,eAAe,EACf,cAAc,EACd,mBAAmB,EACnB,cAAc,GAGf,MAAM,UAAU,CAAC;AAGlB,MAAM,aAAa,GAAG,GAAG,CAAC;AAkB1B,MAAM,MAAM,GAAG;IACb,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE;QACV,IAAI,EAAE;YACJ,IAAI,EAAE,QAAQ;YACd,WAAW,EACT,uFAAuF;gBACvF,mDAAmD;SACtD;QACD,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,qCAAqC,EAAE;QAC5E,KAAK,EAAE;YACL,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACzB,WAAW,EAAE,0EAA0E;SACxF;QACD,aAAa,EAAE;YACb,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,CAAC;YACV,WAAW,EACT,2FAA2F;gBAC3F,8FAA8F;SACjG;QACD,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,WAAW,EAAE,yCAAyC,EAAE;KAC/F;IACD,QAAQ,EAAE,CAAC,MAAM,CAAC;IAClB,oBAAoB,EAAE,KAAK;CACnB,CAAC;AAEX,KAAK,UAAU,GAAG,CAAC,IAA6B,EAAE,GAAgB;IAChE,MAAM,IAAI,GAAG,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC;IACpD,MAAM,KAAK,GAAG,mBAAmB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACjD,MAAM,aAAa,GAAG,eAAe,CAAC,IAAI,EAAE,eAAe,EAAE,CAAC,CAAC,CAAC;IAChE,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,aAAa,CAAC;IAEjE,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC;IACxB,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,CAAC;IAC7C,MAAM,QAAQ,GAAG;QACf,eAAe,EAAE,CAAU,EAAE,uGAAuG;QACpI,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,eAAe,EAAE,KAAK,CAAC,eAAe;QACtC,wEAAwE;QACxE,6CAA6C;QAC7C,SAAS,EAAE,IAAI;KAChB,CAAC;IAEF,GAAG,CAAC,QAAQ,CAAC,WAAW,IAAI,MAAM,IAAI,EAAE,CAAC,CAAC;IAC1C,IAAI,GAA2B,CAAC;IAChC,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE;YAChD,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;YACzC,GAAG,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC;SAC1D,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,MAAM,CAAc;YACzB,GAAG,QAAQ;YACX,QAAQ,EAAE,kBAAkB,IAAI,MAAM,IAAI,GAAG;YAC7C,KAAK,EAAE,SAAS;YAChB,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE;YAC9C,KAAK,EAAE;gBACL,2BAA4B,KAAe,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG;gBAC7D,gEAAgE;aACjE;SACF,CAAC,CAAC;IACL,CAAC;IAED,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAClC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC;QAC9B,KAAK,CAAC,IAAI,CACR,GAAG,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,gEAAgE;YAC1F,gBAAgB,KAAK,GAAG,CAC3B,CAAC;IACJ,CAAC;IACD,MAAM,cAAc,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,kBAAkB,KAAK,IAAI,CAAC,CAAC,MAAM,CAAC;IACjF,IAAI,cAAc,GAAG,CAAC,EAAE,CAAC;QACvB,KAAK,CAAC,IAAI,CACR,GAAG,cAAc,6EAA6E;YAC5F,uBAAuB,CAC1B,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAc;QACzB,GAAG,QAAQ;QACX,QAAQ,EACN,GAAG,CAAC,MAAM,KAAK,CAAC;YACd,CAAC,CAAC,GAAG,IAAI,MAAM,IAAI,kBAAkB;YACrC,CAAC,CAAC,GAAG,IAAI,MAAM,IAAI,YAAY,GAAG,CAAC,MAAM,UAAU;gBACjD,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,uBAAuB,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;QAChF,GAAG,CAAC,GAAG,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACxD,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE;QAC1D,SAAS,EAAE,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM;QACpC,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM;YAC3B,CAAC,CAAC,EAAE,eAAe,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;YAC7E,CAAC,CAAC,EAAE,CAAC;QACP,KAAK;KACN,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAmB;IACzC,IAAI,EAAE,cAAc;IACpB,KAAK,EAAE,MAAM;IACb,IAAI,EAAE,UAAU;IAChB,OAAO,EAAE,OAAO;IAChB,KAAK,EAAE,oCAAoC;IAC3C,WAAW,EACT,uFAAuF;QACvF,8FAA8F;QAC9F,2FAA2F;QAC3F,6FAA6F;QAC7F,6CAA6C;IAC/C,WAAW,EAAE,MAA4C;IACzD,GAAG;CACJ,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `get_git_history` — §18's "other useful Git tool": live commit history
|
|
3
|
+
* for one file, right now. P27's disclosed remainder.
|
|
4
|
+
*
|
|
5
|
+
* `history.ts`'s own `history` tool answers a different question — "which
|
|
6
|
+
* files does this one tend to change alongside", from the graph's stored
|
|
7
|
+
* `CHANGES_WITH` ledger, built once at `analyze` time. This tool answers
|
|
8
|
+
* "what commits touched this file" — a live `git log --follow`, with no
|
|
9
|
+
* graph involved and no staleness relative to the last `analyze` run.
|
|
10
|
+
*/
|
|
11
|
+
import type { PathLogCommit } from "@descryy/core";
|
|
12
|
+
import { type ToolDefinition } from "./kit.ts";
|
|
13
|
+
export type GitHistoryCommit = PathLogCommit;
|
|
14
|
+
export interface GitHistoryData {
|
|
15
|
+
readonly file: string;
|
|
16
|
+
/** Capped by `maxCommits`, newest-first. */
|
|
17
|
+
readonly commits: readonly GitHistoryCommit[];
|
|
18
|
+
/**
|
|
19
|
+
* Every commit touching this file, independent of `maxCommits` — a second,
|
|
20
|
+
* cheap `git log` count, not `commits.length`. Lets a caller tell "here
|
|
21
|
+
* are the 20 most recent" from "here are all 3 there are" without
|
|
22
|
+
* guessing from the cap.
|
|
23
|
+
*/
|
|
24
|
+
readonly totalTouching: number;
|
|
25
|
+
}
|
|
26
|
+
export declare const gitHistoryTool: ToolDefinition;
|
|
27
|
+
//# sourceMappingURL=git-history.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-history.d.ts","sourceRoot":"","sources":["../../src/tools/git-history.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,EAA6D,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AAK1G,MAAM,MAAM,gBAAgB,GAAG,aAAa,CAAC;AAE7C,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4CAA4C;IAC5C,QAAQ,CAAC,OAAO,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC9C;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AA0ED,eAAO,MAAM,cAAc,EAAE,cAa5B,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `get_git_history` — §18's "other useful Git tool": live commit history
|
|
3
|
+
* for one file, right now. P27's disclosed remainder.
|
|
4
|
+
*
|
|
5
|
+
* `history.ts`'s own `history` tool answers a different question — "which
|
|
6
|
+
* files does this one tend to change alongside", from the graph's stored
|
|
7
|
+
* `CHANGES_WITH` ledger, built once at `analyze` time. This tool answers
|
|
8
|
+
* "what commits touched this file" — a live `git log --follow`, with no
|
|
9
|
+
* graph involved and no staleness relative to the last `analyze` run.
|
|
10
|
+
*/
|
|
11
|
+
import { logForPath } from "@descryy/core";
|
|
12
|
+
import { answer, optionalInteger, requiredString } from "./kit.js";
|
|
13
|
+
const DEFAULT_MAX_COMMITS = 20;
|
|
14
|
+
const SCHEMA = {
|
|
15
|
+
type: "object",
|
|
16
|
+
properties: {
|
|
17
|
+
file: { type: "string", description: "Repository-relative path to read live commit history for." },
|
|
18
|
+
maxCommits: {
|
|
19
|
+
type: "integer",
|
|
20
|
+
minimum: 1,
|
|
21
|
+
description: "Cap on commits returned, newest-first. Default 20.",
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
required: ["file"],
|
|
25
|
+
additionalProperties: false,
|
|
26
|
+
};
|
|
27
|
+
async function run(args, ctx) {
|
|
28
|
+
const file = requiredString(args, "file");
|
|
29
|
+
const maxCommits = optionalInteger(args, "maxCommits", 1) ?? DEFAULT_MAX_COMMITS;
|
|
30
|
+
const { session } = ctx;
|
|
31
|
+
const stamp = session.provider().baseStamp();
|
|
32
|
+
const envelope = {
|
|
33
|
+
resolutionFloor: 0, // a live git log read, not a graph traversal claim
|
|
34
|
+
commitSha: stamp.commitSha,
|
|
35
|
+
graphBuiltAt: stamp.graphBuiltAt,
|
|
36
|
+
irSchemaVersion: stamp.irSchemaVersion,
|
|
37
|
+
nameLevel: true,
|
|
38
|
+
};
|
|
39
|
+
ctx.progress(`Reading live commit history for ${file}`);
|
|
40
|
+
let result;
|
|
41
|
+
try {
|
|
42
|
+
result = await logForPath(session.repoPath, file, { maxCommits });
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
return answer({
|
|
46
|
+
...envelope,
|
|
47
|
+
headline: `Could not read history for "${file}".`,
|
|
48
|
+
state: "refused",
|
|
49
|
+
data: { file, commits: [], totalTouching: 0 },
|
|
50
|
+
notes: [
|
|
51
|
+
`git rejected the read: ${error.message.trim()}.`,
|
|
52
|
+
"No history was read, which is not a finding that this file has none.",
|
|
53
|
+
],
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
const notes = [];
|
|
57
|
+
if (result.totalTouching > result.commits.length) {
|
|
58
|
+
notes.push(`${result.totalTouching - result.commits.length} further commit(s) touch this file but are ` +
|
|
59
|
+
`not listed — the reply is capped at ${maxCommits}, newest-first.`);
|
|
60
|
+
}
|
|
61
|
+
return answer({
|
|
62
|
+
...envelope,
|
|
63
|
+
headline: result.totalTouching === 0
|
|
64
|
+
? `No commit reachable from HEAD touches "${file}".`
|
|
65
|
+
: `${result.totalTouching} commit(s) touch "${file}"` +
|
|
66
|
+
(result.commits.length < result.totalTouching
|
|
67
|
+
? `; showing the ${result.commits.length} most recent.`
|
|
68
|
+
: "."),
|
|
69
|
+
...(result.totalTouching === 0 ? { state: "empty" } : {}),
|
|
70
|
+
data: { file, commits: result.commits, totalTouching: result.totalTouching },
|
|
71
|
+
truncated: result.commits.length < result.totalTouching,
|
|
72
|
+
...(result.commits.length < result.totalTouching
|
|
73
|
+
? { truncatedDetail: { shown: result.commits.length, total: result.totalTouching, more: null } }
|
|
74
|
+
: {}),
|
|
75
|
+
notes,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
export const gitHistoryTool = {
|
|
79
|
+
name: "get_git_history",
|
|
80
|
+
class: "read",
|
|
81
|
+
tier: "evidence",
|
|
82
|
+
version: "1.0.0",
|
|
83
|
+
title: "Live commit history for one file",
|
|
84
|
+
description: "Live git log for a repository-relative file path, following it across renames — sha, date, " +
|
|
85
|
+
"subject, body and this commit's status for the file, newest-first. Reads the working " +
|
|
86
|
+
"repository directly, not the stored graph — for the graph's stored file-to-file co-change " +
|
|
87
|
+
"ledger use \"history\" instead.",
|
|
88
|
+
inputSchema: SCHEMA,
|
|
89
|
+
run,
|
|
90
|
+
};
|
|
91
|
+
//# sourceMappingURL=git-history.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-history.js","sourceRoot":"","sources":["../../src/tools/git-history.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `history` — "what else tends to change alongside this file?"
|
|
3
|
+
*
|
|
4
|
+
* §18 names `get_git_history` as a tool the AI layer consumes. `CHANGES_WITH`
|
|
5
|
+
* edges have existed in the graph since Step 11, produced by `analyze`'s git
|
|
6
|
+
* source when history reading is on (the default) — nothing queried them per
|
|
7
|
+
* node until this tool and `changesWith` (`@descryy/core`) beneath it.
|
|
8
|
+
*
|
|
9
|
+
* **Scoped to the stored co-change graph, not a live `git log`/`git diff`
|
|
10
|
+
* call.** §18's own line says "the tool names are implementation details" —
|
|
11
|
+
* what matters is the AI layer can ask a deterministic question about
|
|
12
|
+
* history and get a graph-backed answer, and the graph already holds exactly
|
|
13
|
+
* one such fact: which files changed together, how often, in the window
|
|
14
|
+
* `analyze` read. A raw diff or commit log is a different capability this
|
|
15
|
+
* tool does not claim.
|
|
16
|
+
*
|
|
17
|
+
* ## Why the lookup does not go through `resolveOneNode`
|
|
18
|
+
*
|
|
19
|
+
* `resolveOneNode({ file })` matches every node whose `file` column equals
|
|
20
|
+
* the given path — the `FILE` node the git source mints *and* every
|
|
21
|
+
* `MODULE`/`FUNCTION`/`CLASS` the language adapter mints from the same file.
|
|
22
|
+
* For a symbol lookup that ambiguity is the point; for a file-scoped
|
|
23
|
+
* question it is noise; `CHANGES_WITH` only ever connects `FILE` nodes
|
|
24
|
+
* (`packages/core/src/sources/git/source.ts`), so this tool resolves the
|
|
25
|
+
* path directly against the store and keeps only `type === "FILE"`, rather
|
|
26
|
+
* than reporting a spurious multi-candidate ambiguity for every file that
|
|
27
|
+
* has been parsed at all.
|
|
28
|
+
*/
|
|
29
|
+
import { type BriefNode, type ToolDefinition } from "./kit.ts";
|
|
30
|
+
/** A file this one moves with, and how strongly. */
|
|
31
|
+
export interface CoChangedFile extends BriefNode {
|
|
32
|
+
/** Commits in which the two files were touched together. */
|
|
33
|
+
readonly support: number;
|
|
34
|
+
/** `support` over the more-frequently-changed file's own total. */
|
|
35
|
+
readonly confidence: number;
|
|
36
|
+
}
|
|
37
|
+
/** The resolved FILE node. */
|
|
38
|
+
export interface HistoryFileResolved extends BriefNode {
|
|
39
|
+
readonly resolved: true;
|
|
40
|
+
}
|
|
41
|
+
/** No FILE node at the path, so nothing was read — only the path is known. */
|
|
42
|
+
export interface HistoryFileUnresolved {
|
|
43
|
+
readonly resolved: false;
|
|
44
|
+
readonly path: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* One key, one shape, always. `resolved` tells a caller whether the
|
|
48
|
+
* `BriefNode` fields are meaningful — replacing an earlier design where
|
|
49
|
+
* `file` was a bare path string on this branch and a `BriefNode` on the
|
|
50
|
+
* other: same key, two types, so a caller reading `data.file.name` on the
|
|
51
|
+
* unresolved branch got `undefined` back with no error.
|
|
52
|
+
*/
|
|
53
|
+
export type HistoryFile = HistoryFileResolved | HistoryFileUnresolved;
|
|
54
|
+
export interface HistoryReport {
|
|
55
|
+
readonly file: HistoryFile;
|
|
56
|
+
/** Capped by `limit`; `coChangedTotal` is the uncapped count. Always
|
|
57
|
+
* present, including when `file.resolved` is `false` (0, not omitted). */
|
|
58
|
+
readonly coChanged: readonly CoChangedFile[];
|
|
59
|
+
readonly coChangedTotal: number;
|
|
60
|
+
}
|
|
61
|
+
export type HistoryData = HistoryReport;
|
|
62
|
+
export declare const historyTool: ToolDefinition;
|
|
63
|
+
//# sourceMappingURL=history.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history.d.ts","sourceRoot":"","sources":["../../src/tools/history.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAIH,OAAO,EAKL,KAAK,SAAS,EAEd,KAAK,cAAc,EACpB,MAAM,UAAU,CAAC;AAGlB,oDAAoD;AACpD,MAAM,WAAW,aAAc,SAAQ,SAAS;IAC9C,4DAA4D;IAC5D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,mEAAmE;IACnE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,8BAA8B;AAC9B,MAAM,WAAW,mBAAoB,SAAQ,SAAS;IACpD,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC;CACzB;AAED,8EAA8E;AAC9E,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GAAG,mBAAmB,GAAG,qBAAqB,CAAC;AAEtE,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B;+EAC2E;IAC3E,QAAQ,CAAC,SAAS,EAAE,SAAS,aAAa,EAAE,CAAC;IAC7C,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,MAAM,WAAW,GAAG,aAAa,CAAC;AAkFxC,eAAO,MAAM,WAAW,EAAE,cAYzB,CAAC"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `history` — "what else tends to change alongside this file?"
|
|
3
|
+
*
|
|
4
|
+
* §18 names `get_git_history` as a tool the AI layer consumes. `CHANGES_WITH`
|
|
5
|
+
* edges have existed in the graph since Step 11, produced by `analyze`'s git
|
|
6
|
+
* source when history reading is on (the default) — nothing queried them per
|
|
7
|
+
* node until this tool and `changesWith` (`@descryy/core`) beneath it.
|
|
8
|
+
*
|
|
9
|
+
* **Scoped to the stored co-change graph, not a live `git log`/`git diff`
|
|
10
|
+
* call.** §18's own line says "the tool names are implementation details" —
|
|
11
|
+
* what matters is the AI layer can ask a deterministic question about
|
|
12
|
+
* history and get a graph-backed answer, and the graph already holds exactly
|
|
13
|
+
* one such fact: which files changed together, how often, in the window
|
|
14
|
+
* `analyze` read. A raw diff or commit log is a different capability this
|
|
15
|
+
* tool does not claim.
|
|
16
|
+
*
|
|
17
|
+
* ## Why the lookup does not go through `resolveOneNode`
|
|
18
|
+
*
|
|
19
|
+
* `resolveOneNode({ file })` matches every node whose `file` column equals
|
|
20
|
+
* the given path — the `FILE` node the git source mints *and* every
|
|
21
|
+
* `MODULE`/`FUNCTION`/`CLASS` the language adapter mints from the same file.
|
|
22
|
+
* For a symbol lookup that ambiguity is the point; for a file-scoped
|
|
23
|
+
* question it is noise; `CHANGES_WITH` only ever connects `FILE` nodes
|
|
24
|
+
* (`packages/core/src/sources/git/source.ts`), so this tool resolves the
|
|
25
|
+
* path directly against the store and keeps only `type === "FILE"`, rather
|
|
26
|
+
* than reporting a spurious multi-candidate ambiguity for every file that
|
|
27
|
+
* has been parsed at all.
|
|
28
|
+
*/
|
|
29
|
+
import { changesWith, findNodes } from "@descryy/core";
|
|
30
|
+
import { briefNode, optionalInteger, optionalString, ToolInputError, } from "./kit.js";
|
|
31
|
+
const SCHEMA = {
|
|
32
|
+
type: "object",
|
|
33
|
+
properties: {
|
|
34
|
+
file: { type: "string", description: "Repository-relative path of the file to ask about." },
|
|
35
|
+
limit: { type: "integer", minimum: 1, description: "Cap on co-changed files in the reply. Default 50." },
|
|
36
|
+
},
|
|
37
|
+
required: ["file"],
|
|
38
|
+
additionalProperties: false,
|
|
39
|
+
};
|
|
40
|
+
async function run(args, ctx) {
|
|
41
|
+
const file = optionalString(args, "file");
|
|
42
|
+
if (file === undefined)
|
|
43
|
+
throw new ToolInputError('give "file"');
|
|
44
|
+
const limit = optionalInteger(args, "limit", 1) ?? 50;
|
|
45
|
+
const driver = ctx.session.store().driver;
|
|
46
|
+
const candidates = findNodes(driver, { file, limit: 10 }).filter((n) => n.type === "FILE");
|
|
47
|
+
const provider = ctx.session.provider();
|
|
48
|
+
const base = provider.baseStamp();
|
|
49
|
+
if (candidates.length === 0) {
|
|
50
|
+
return {
|
|
51
|
+
result: {
|
|
52
|
+
data: { file: { resolved: false, path: file }, coChanged: [], coChangedTotal: 0 },
|
|
53
|
+
stamp: { commitSha: base.commitSha, graphBuiltAt: base.graphBuiltAt, irSchemaVersion: base.irSchemaVersion, resolutionFloor: 0 },
|
|
54
|
+
truncated: false,
|
|
55
|
+
notes: [
|
|
56
|
+
"No FILE node at this path. Either the path is wrong, git history was skipped when the " +
|
|
57
|
+
"graph was built (analyze's \"history\" option), or this file has never appeared in a " +
|
|
58
|
+
"commit the git source read — a file with zero history is not distinguishable here from " +
|
|
59
|
+
"one this graph never analysed.",
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
nameLevel: true,
|
|
63
|
+
state: "refused",
|
|
64
|
+
headline: `No FILE node at "${file}".`,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
const node = candidates[0];
|
|
68
|
+
ctx.progress(`Reading co-change history for ${file}`);
|
|
69
|
+
const history = changesWith(provider, node.id);
|
|
70
|
+
const shown = history.data.slice(0, limit);
|
|
71
|
+
const notes = [...history.notes];
|
|
72
|
+
if (shown.length < history.data.length) {
|
|
73
|
+
notes.push(`${history.data.length - shown.length} further co-changed file(s) exist but are not listed — ` +
|
|
74
|
+
`the reply is capped at ${limit}.`);
|
|
75
|
+
}
|
|
76
|
+
// `history.truncated` alone is the query layer's own internal cap; it says
|
|
77
|
+
// nothing about this tool's own `limit` slice, which can cap further still.
|
|
78
|
+
// A reply capped only by `limit` still has to disclose it structurally, not
|
|
79
|
+
// just in prose — the exact defect P27's truncation-cursor pass exists to
|
|
80
|
+
// close (§4 technique 5).
|
|
81
|
+
const cappedByLimit = shown.length < history.data.length;
|
|
82
|
+
return {
|
|
83
|
+
result: {
|
|
84
|
+
data: {
|
|
85
|
+
file: { ...briefNode(node), resolved: true },
|
|
86
|
+
coChanged: shown.map((c) => ({ ...briefNode(c.node), support: c.support, confidence: Number(c.confidence.toFixed(4)) })),
|
|
87
|
+
coChangedTotal: history.data.length,
|
|
88
|
+
},
|
|
89
|
+
stamp: history.stamp,
|
|
90
|
+
truncated: history.truncated || cappedByLimit,
|
|
91
|
+
notes,
|
|
92
|
+
},
|
|
93
|
+
...(shown.length === 0 ? { state: "empty" } : {}),
|
|
94
|
+
...(cappedByLimit ? { truncatedDetail: { shown: shown.length, total: history.data.length, more: null } } : {}),
|
|
95
|
+
nameLevel: false,
|
|
96
|
+
headline: shown.length === 0
|
|
97
|
+
? `"${file}" has no recorded co-change history above the graph's own support/confidence floor.`
|
|
98
|
+
: `"${file}" tends to change alongside ${history.data.length} other file(s); most-coupled: ` +
|
|
99
|
+
`${shown[0].node.file ?? shown[0].node.name} (${shown[0].support} commit(s) together).`,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
export const historyTool = {
|
|
103
|
+
name: "history",
|
|
104
|
+
class: "read",
|
|
105
|
+
tier: "evidence",
|
|
106
|
+
version: "1.0.0",
|
|
107
|
+
title: "Which files tend to change together with this one",
|
|
108
|
+
description: "Given a repository-relative file path, report the files it co-changes with (CHANGES_WITH " +
|
|
109
|
+
"edges), how many commits they were touched together in, and the observed confidence. Reads " +
|
|
110
|
+
"the stored graph only — not a live git log or diff.",
|
|
111
|
+
inputSchema: SCHEMA,
|
|
112
|
+
run,
|
|
113
|
+
};
|
|
114
|
+
//# sourceMappingURL=history.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history.js","sourceRoot":"","sources":["../../src/tools/history.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `impact` — "what breaks if I change this, what covers it, have we seen it before?"
|
|
3
|
+
*
|
|
4
|
+
* §18's tool-name list is deliberately not a build list ("the tool names are
|
|
5
|
+
* implementation details") — but three of the functions it names were already
|
|
6
|
+
* built and correct in the query layer with no MCP tool ever calling them:
|
|
7
|
+
* `impactOf` (blast radius — find_dependents/find_callers/find_usages),
|
|
8
|
+
* `testsCovering` (find_tests) and `rootCauseSearch` (find_related_incidents,
|
|
9
|
+
* find_fix_patterns). This wires all three under one call, the same way
|
|
10
|
+
* `validate` bundles env, rollback and the config graph — a developer asking
|
|
11
|
+
* "what does changing this touch" wants the blast radius, what already tests
|
|
12
|
+
* it and what has broken near it before in one answer, not three round trips.
|
|
13
|
+
*
|
|
14
|
+
* A fourth, `traceApi` (find_dependencies read the other direction, or "how
|
|
15
|
+
* does this reach that"), runs only when a second node is given — it answers
|
|
16
|
+
* a different question (a path between two specific symbols) and forcing it
|
|
17
|
+
* into every call would mean walking the graph for a question nobody asked.
|
|
18
|
+
*
|
|
19
|
+
* ## Direction, and why the default is structural rather than data-carrying
|
|
20
|
+
*
|
|
21
|
+
* `propagation` already owns PROPAGATES_TO/READS/WRITES/RETURNS — what data
|
|
22
|
+
* carries. This tool defaults to CALLS, IMPORTS, IMPLEMENTS, INHERITS,
|
|
23
|
+
* USES_API and SERVES_API — what structure depends on what. The two tools
|
|
24
|
+
* answer different questions over the same primitives; a caller after "who
|
|
25
|
+
* calls this" should never have to remember that CALLS lives in a tool named
|
|
26
|
+
* for propagation.
|
|
27
|
+
*/
|
|
28
|
+
import type { EdgeType } from "@descryy/ir";
|
|
29
|
+
import { type BriefNode, type NodeCandidates, type ToolDefinition } from "./kit.ts";
|
|
30
|
+
/** A node in the blast radius, with how far out and how hard it is hit. */
|
|
31
|
+
export interface ImpactedNode extends BriefNode {
|
|
32
|
+
readonly depth: number;
|
|
33
|
+
readonly score: number;
|
|
34
|
+
}
|
|
35
|
+
/** An INCIDENT or FIX_PATTERN linked to the start node, and by which edge. */
|
|
36
|
+
export interface RelatedNode extends BriefNode {
|
|
37
|
+
readonly via: EdgeType;
|
|
38
|
+
}
|
|
39
|
+
/** Present only when `to` was given and resolved. */
|
|
40
|
+
export interface TracedPath {
|
|
41
|
+
readonly to: BriefNode;
|
|
42
|
+
readonly hops: number;
|
|
43
|
+
readonly nodes: readonly BriefNode[];
|
|
44
|
+
}
|
|
45
|
+
export interface ImpactReport {
|
|
46
|
+
readonly from: BriefNode;
|
|
47
|
+
/** Capped by `limit`; `affectedTotal` is the uncapped count. */
|
|
48
|
+
readonly affected: readonly ImpactedNode[];
|
|
49
|
+
readonly affectedTotal: number;
|
|
50
|
+
readonly coveringTests: readonly BriefNode[];
|
|
51
|
+
/**
|
|
52
|
+
* Incidents and fix patterns joined to this node by a graph edge.
|
|
53
|
+
*
|
|
54
|
+
* Structural only — `rootCauseSearch` walks edges and runs no similarity
|
|
55
|
+
* search, so an empty list is never evidence that no similar incident
|
|
56
|
+
* exists. For a failure with no node yet, `similar_incidents` is the tool.
|
|
57
|
+
*/
|
|
58
|
+
readonly related: readonly RelatedNode[];
|
|
59
|
+
/** Null when no `to` was given, or when the one given did not resolve. */
|
|
60
|
+
readonly path: TracedPath | null;
|
|
61
|
+
/**
|
|
62
|
+
* False when no edge of any walked type touches the start node.
|
|
63
|
+
*
|
|
64
|
+
* **An empty `affected` with `analysed: false` means the walk did not run**,
|
|
65
|
+
* not that nothing depends on this symbol.
|
|
66
|
+
*/
|
|
67
|
+
readonly analysed: boolean;
|
|
68
|
+
readonly edgeTypesWalked: readonly EdgeType[];
|
|
69
|
+
/** The subset of `edgeTypesWalked` actually found on an edge touching this node. */
|
|
70
|
+
readonly edgeTypesPresent: readonly string[];
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* One call, two shapes.
|
|
74
|
+
*
|
|
75
|
+
* `NodeCandidates` arrives when the symbol did not resolve to exactly one
|
|
76
|
+
* node; narrow on `"from" in data`. Nothing was walked in that case, so none
|
|
77
|
+
* of `ImpactReport`'s counts exist rather than reading zero.
|
|
78
|
+
*/
|
|
79
|
+
export type ImpactData = ImpactReport | NodeCandidates;
|
|
80
|
+
export declare const impactTool: ToolDefinition;
|
|
81
|
+
//# sourceMappingURL=impact.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"impact.d.ts","sourceRoot":"","sources":["../../src/tools/impact.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,EAQL,KAAK,SAAS,EACd,KAAK,cAAc,EAEnB,KAAK,cAAc,EACpB,MAAM,UAAU,CAAC;AAIlB,2EAA2E;AAC3E,MAAM,WAAW,YAAa,SAAQ,SAAS;IAC7C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,8EAA8E;AAC9E,MAAM,WAAW,WAAY,SAAQ,SAAS;IAC5C,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC;CACxB;AAED,qDAAqD;AACrD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,CAAC;CACtC;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,CAAC;IAC3C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,aAAa,EAAE,SAAS,SAAS,EAAE,CAAC;IAC7C;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;IACzC,0EAA0E;IAC1E,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;IACjC;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,eAAe,EAAE,SAAS,QAAQ,EAAE,CAAC;IAC9C,oFAAoF;IACpF,QAAQ,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9C;AAED;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,cAAc,CAAC;AAsNvD,eAAO,MAAM,UAAU,EAAE,cAgBxB,CAAC"}
|