@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.
Files changed (118) hide show
  1. package/LICENSE +21 -0
  2. package/dist/action-handshake.d.ts +86 -0
  3. package/dist/action-handshake.d.ts.map +1 -0
  4. package/dist/action-handshake.js +144 -0
  5. package/dist/action-handshake.js.map +1 -0
  6. package/dist/bin/descry-mcp.d.ts +18 -0
  7. package/dist/bin/descry-mcp.d.ts.map +1 -0
  8. package/dist/bin/descry-mcp.js +54 -0
  9. package/dist/bin/descry-mcp.js.map +1 -0
  10. package/dist/index.d.ts +32 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +15 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/language-guess.d.ts +17 -0
  15. package/dist/language-guess.d.ts.map +1 -0
  16. package/dist/language-guess.js +43 -0
  17. package/dist/language-guess.js.map +1 -0
  18. package/dist/protocol.d.ts +91 -0
  19. package/dist/protocol.d.ts.map +1 -0
  20. package/dist/protocol.js +127 -0
  21. package/dist/protocol.js.map +1 -0
  22. package/dist/registry.d.ts +72 -0
  23. package/dist/registry.d.ts.map +1 -0
  24. package/dist/registry.js +136 -0
  25. package/dist/registry.js.map +1 -0
  26. package/dist/render.d.ts +275 -0
  27. package/dist/render.d.ts.map +1 -0
  28. package/dist/render.js +333 -0
  29. package/dist/render.js.map +1 -0
  30. package/dist/server.d.ts +42 -0
  31. package/dist/server.d.ts.map +1 -0
  32. package/dist/server.js +263 -0
  33. package/dist/server.js.map +1 -0
  34. package/dist/session.d.ts +164 -0
  35. package/dist/session.d.ts.map +1 -0
  36. package/dist/session.js +302 -0
  37. package/dist/session.js.map +1 -0
  38. package/dist/tools/analyze.d.ts +124 -0
  39. package/dist/tools/analyze.d.ts.map +1 -0
  40. package/dist/tools/analyze.js +411 -0
  41. package/dist/tools/analyze.js.map +1 -0
  42. package/dist/tools/contracts.d.ts +105 -0
  43. package/dist/tools/contracts.d.ts.map +1 -0
  44. package/dist/tools/contracts.js +141 -0
  45. package/dist/tools/contracts.js.map +1 -0
  46. package/dist/tools/cross-pr.d.ts +137 -0
  47. package/dist/tools/cross-pr.d.ts.map +1 -0
  48. package/dist/tools/cross-pr.js +216 -0
  49. package/dist/tools/cross-pr.js.map +1 -0
  50. package/dist/tools/git-diff.d.ts +29 -0
  51. package/dist/tools/git-diff.d.ts.map +1 -0
  52. package/dist/tools/git-diff.js +117 -0
  53. package/dist/tools/git-diff.js.map +1 -0
  54. package/dist/tools/git-history.d.ts +27 -0
  55. package/dist/tools/git-history.d.ts.map +1 -0
  56. package/dist/tools/git-history.js +91 -0
  57. package/dist/tools/git-history.js.map +1 -0
  58. package/dist/tools/history.d.ts +63 -0
  59. package/dist/tools/history.d.ts.map +1 -0
  60. package/dist/tools/history.js +114 -0
  61. package/dist/tools/history.js.map +1 -0
  62. package/dist/tools/impact.d.ts +81 -0
  63. package/dist/tools/impact.d.ts.map +1 -0
  64. package/dist/tools/impact.js +229 -0
  65. package/dist/tools/impact.js.map +1 -0
  66. package/dist/tools/index.d.ts +33 -0
  67. package/dist/tools/index.d.ts.map +1 -0
  68. package/dist/tools/index.js +47 -0
  69. package/dist/tools/index.js.map +1 -0
  70. package/dist/tools/kit.d.ts +183 -0
  71. package/dist/tools/kit.d.ts.map +1 -0
  72. package/dist/tools/kit.js +117 -0
  73. package/dist/tools/kit.js.map +1 -0
  74. package/dist/tools/link-workspace.d.ts +62 -0
  75. package/dist/tools/link-workspace.d.ts.map +1 -0
  76. package/dist/tools/link-workspace.js +156 -0
  77. package/dist/tools/link-workspace.js.map +1 -0
  78. package/dist/tools/lookup.d.ts +29 -0
  79. package/dist/tools/lookup.d.ts.map +1 -0
  80. package/dist/tools/lookup.js +73 -0
  81. package/dist/tools/lookup.js.map +1 -0
  82. package/dist/tools/pr-analysis.d.ts +89 -0
  83. package/dist/tools/pr-analysis.d.ts.map +1 -0
  84. package/dist/tools/pr-analysis.js +169 -0
  85. package/dist/tools/pr-analysis.js.map +1 -0
  86. package/dist/tools/propagation.d.ts +74 -0
  87. package/dist/tools/propagation.d.ts.map +1 -0
  88. package/dist/tools/propagation.js +194 -0
  89. package/dist/tools/propagation.js.map +1 -0
  90. package/dist/tools/questions.d.ts +80 -0
  91. package/dist/tools/questions.d.ts.map +1 -0
  92. package/dist/tools/questions.js +213 -0
  93. package/dist/tools/questions.js.map +1 -0
  94. package/dist/tools/refusal-fetch.d.ts +71 -0
  95. package/dist/tools/refusal-fetch.d.ts.map +1 -0
  96. package/dist/tools/refusal-fetch.js +137 -0
  97. package/dist/tools/refusal-fetch.js.map +1 -0
  98. package/dist/tools/scope.d.ts +60 -0
  99. package/dist/tools/scope.d.ts.map +1 -0
  100. package/dist/tools/scope.js +160 -0
  101. package/dist/tools/scope.js.map +1 -0
  102. package/dist/tools/similar-incidents.d.ts +105 -0
  103. package/dist/tools/similar-incidents.d.ts.map +1 -0
  104. package/dist/tools/similar-incidents.js +228 -0
  105. package/dist/tools/similar-incidents.js.map +1 -0
  106. package/dist/tools/validate.d.ts +103 -0
  107. package/dist/tools/validate.d.ts.map +1 -0
  108. package/dist/tools/validate.js +176 -0
  109. package/dist/tools/validate.js.map +1 -0
  110. package/dist/tools/verification-status.d.ts +68 -0
  111. package/dist/tools/verification-status.d.ts.map +1 -0
  112. package/dist/tools/verification-status.js +179 -0
  113. package/dist/tools/verification-status.js.map +1 -0
  114. package/dist/transport.d.ts +58 -0
  115. package/dist/transport.d.ts.map +1 -0
  116. package/dist/transport.js +115 -0
  117. package/dist/transport.js.map +1 -0
  118. package/package.json +33 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kit.js","sourceRoot":"","sources":["../../src/tools/kit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * `link_workspace` — the agent-mediated door onto the same declaration
3
+ * `descry init` writes from the command line (§ session.ts's `writeConfig`,
4
+ * shared by both so there is one writer for one file).
5
+ *
6
+ * ## Why this exists as a tool and not a smarter `analyze`
7
+ *
8
+ * `analyze` already discloses "no workspace is configured" (DEC-054) and,
9
+ * until now, that disclosure was a dead end — true, and nowhere to go. This
10
+ * tool is where it goes: an agent that reads the disclosure can act on it in
11
+ * the same conversation, in plain English, rather than sending the developer
12
+ * to a terminal.
13
+ *
14
+ * ## The constraint that makes this legal
15
+ *
16
+ * `workspace.ts`'s header is explicit that a pairing between two directories
17
+ * is a name inference and this project does not make that inference. An
18
+ * agent that can see `sherpa-frontend` sitting next to `sherpa-backend` may
19
+ * **propose** it — that is a suggestion grounded in something real, a
20
+ * directory listing — but it must never **decide** the two belong together
21
+ * and call this tool unprompted. `repos` is not filled in by this tool from
22
+ * anything it can see; it is supplied by the caller, and the caller's own
23
+ * description is instructed to obtain that list from the user's explicit
24
+ * confirmation, not from its own inference. The tool has no way to verify
25
+ * that happened — the same trust boundary every write tool in this server
26
+ * already crosses when a client approves the call — so the discipline is
27
+ * enforced by what this tool refuses to do (guess) rather than by what it
28
+ * can check.
29
+ *
30
+ * ## The one tool in this server that writes outside its own session root
31
+ *
32
+ * Every other tool operates on `session.repoPath` alone. `repos` here names
33
+ * arbitrary paths on disk, because the whole point is writing into siblings
34
+ * the current session does not own. Each path is required to exist and be a
35
+ * directory before anything is written, and a path that fails that check is
36
+ * reported rather than silently skipped — a typo'd path here is exactly the
37
+ * kind of silent failure DEC-054 exists to prevent, one level up.
38
+ */
39
+ import { type ToolDefinition } from "./kit.ts";
40
+ /** A repository the workspace id was actually written into. */
41
+ export interface LinkedRepo {
42
+ /** Resolved to absolute. */
43
+ readonly path: string;
44
+ readonly repo: string;
45
+ /** The `.descry/config.json` that was written. */
46
+ readonly configPath: string;
47
+ }
48
+ export interface LinkWorkspaceData {
49
+ readonly workspace: string;
50
+ /**
51
+ * Only the repositories actually written.
52
+ *
53
+ * A path that did not exist is **absent from this list and disclosed** — the
54
+ * workspace id is byte-identical only where it was written, and a partial
55
+ * link that looks complete is the failure this list exists to make visible.
56
+ */
57
+ readonly written: readonly LinkedRepo[];
58
+ /** How many were asked for. Compare against `written.length`. */
59
+ readonly requested: number;
60
+ }
61
+ export declare const linkWorkspaceTool: ToolDefinition;
62
+ //# sourceMappingURL=link-workspace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-workspace.d.ts","sourceRoot":"","sources":["../../src/tools/link-workspace.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAMH,OAAO,EAA4D,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AAGzG,+DAA+D;AAC/D,MAAM,WAAW,UAAU;IACzB,4BAA4B;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,kDAAkD;IAClD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,UAAU,EAAE,CAAC;IACxC,iEAAiE;IACjE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AA6HD,eAAO,MAAM,iBAAiB,EAAE,cAe/B,CAAC"}
@@ -0,0 +1,156 @@
1
+ /**
2
+ * `link_workspace` — the agent-mediated door onto the same declaration
3
+ * `descry init` writes from the command line (§ session.ts's `writeConfig`,
4
+ * shared by both so there is one writer for one file).
5
+ *
6
+ * ## Why this exists as a tool and not a smarter `analyze`
7
+ *
8
+ * `analyze` already discloses "no workspace is configured" (DEC-054) and,
9
+ * until now, that disclosure was a dead end — true, and nowhere to go. This
10
+ * tool is where it goes: an agent that reads the disclosure can act on it in
11
+ * the same conversation, in plain English, rather than sending the developer
12
+ * to a terminal.
13
+ *
14
+ * ## The constraint that makes this legal
15
+ *
16
+ * `workspace.ts`'s header is explicit that a pairing between two directories
17
+ * is a name inference and this project does not make that inference. An
18
+ * agent that can see `sherpa-frontend` sitting next to `sherpa-backend` may
19
+ * **propose** it — that is a suggestion grounded in something real, a
20
+ * directory listing — but it must never **decide** the two belong together
21
+ * and call this tool unprompted. `repos` is not filled in by this tool from
22
+ * anything it can see; it is supplied by the caller, and the caller's own
23
+ * description is instructed to obtain that list from the user's explicit
24
+ * confirmation, not from its own inference. The tool has no way to verify
25
+ * that happened — the same trust boundary every write tool in this server
26
+ * already crosses when a client approves the call — so the discipline is
27
+ * enforced by what this tool refuses to do (guess) rather than by what it
28
+ * can check.
29
+ *
30
+ * ## The one tool in this server that writes outside its own session root
31
+ *
32
+ * Every other tool operates on `session.repoPath` alone. `repos` here names
33
+ * arbitrary paths on disk, because the whole point is writing into siblings
34
+ * the current session does not own. Each path is required to exist and be a
35
+ * directory before anything is written, and a path that fails that check is
36
+ * reported rather than silently skipped — a typo'd path here is exactly the
37
+ * kind of silent failure DEC-054 exists to prevent, one level up.
38
+ */
39
+ import { existsSync, statSync } from "node:fs";
40
+ import { resolve } from "node:path";
41
+ import { writeConfig } from "../session.js";
42
+ import { answer, requiredString, ToolInputError } from "./kit.js";
43
+ const SCHEMA = {
44
+ type: "object",
45
+ properties: {
46
+ workspace: {
47
+ type: "string",
48
+ description: "The workspace id to write into every repository listed in `repos`, byte-identical. " +
49
+ "Choose a short, stable name — it becomes part of every workspace-scoped node id.",
50
+ },
51
+ repos: {
52
+ type: "array",
53
+ minItems: 2,
54
+ items: {
55
+ type: "object",
56
+ properties: {
57
+ path: { type: "string", description: "Absolute path to the repository on disk." },
58
+ repo: {
59
+ type: "string",
60
+ description: "This repository's identity, written explicitly rather than defaulted.",
61
+ },
62
+ },
63
+ required: ["path", "repo"],
64
+ additionalProperties: false,
65
+ },
66
+ description: "Every repository that should share this workspace, INCLUDING the current one if it should " +
67
+ "join. Only call this with repositories the user has explicitly confirmed belong together " +
68
+ "— a directory sitting next to another is a suggestion to put in front of the user, never " +
69
+ "a decision this tool or the caller makes on its own (workspace.ts's pairing rule, one " +
70
+ "level up: never infer, always declare).",
71
+ },
72
+ },
73
+ required: ["workspace", "repos"],
74
+ additionalProperties: false,
75
+ };
76
+ function parseRepos(value) {
77
+ if (!Array.isArray(value) || value.length < 2) {
78
+ throw new ToolInputError('"repos" must be an array of at least two {path, repo} entries — a workspace of one ' +
79
+ "repository joins nothing, so linking one is never the right call.");
80
+ }
81
+ return value.map((entry, index) => {
82
+ if (typeof entry !== "object" || entry === null) {
83
+ throw new ToolInputError(`repos[${index}] must be an object`);
84
+ }
85
+ const path = requiredString(entry, "path");
86
+ const repo = requiredString(entry, "repo");
87
+ return { path, repo };
88
+ });
89
+ }
90
+ async function run(args, ctx) {
91
+ const workspace = requiredString(args, "workspace");
92
+ const repos = parseRepos(args["repos"]);
93
+ const seen = new Set();
94
+ for (const entry of repos) {
95
+ const absolute = resolve(entry.path);
96
+ if (seen.has(absolute)) {
97
+ throw new ToolInputError(`"${entry.path}" is listed more than once`);
98
+ }
99
+ seen.add(absolute);
100
+ }
101
+ const written = [];
102
+ const notes = [];
103
+ for (const entry of repos) {
104
+ const absolute = resolve(entry.path);
105
+ if (!existsSync(absolute) || !statSync(absolute).isDirectory()) {
106
+ notes.push(`"${entry.path}" does not exist or is not a directory — nothing was written there.`);
107
+ continue;
108
+ }
109
+ ctx.progress(`Writing .descry/config.json in ${entry.repo}`, written.length, repos.length);
110
+ const result = await writeConfig(absolute, { repo: entry.repo, workspace });
111
+ written.push({ path: absolute, repo: entry.repo, configPath: result.path });
112
+ if (result.previousWorkspace !== undefined && result.previousWorkspace !== workspace) {
113
+ notes.push(`${entry.repo}: replaced previous workspace "${result.previousWorkspace}" with "${workspace}".`);
114
+ }
115
+ }
116
+ if (written.length < repos.length) {
117
+ notes.push(`${repos.length - written.length} of ${repos.length} repositories were skipped. The workspace ` +
118
+ "id is only byte-identical where it was actually written — re-run for the skipped paths " +
119
+ "once they are correct.");
120
+ }
121
+ notes.push(`Run "analyze" again in each linked repository so its graph is rebuilt under the "${workspace}" ` +
122
+ "workspace — writing the config here does not itself rebuild anything.");
123
+ return answer({
124
+ headline: written.length === 0
125
+ ? `No repository was linked — every path in "repos" failed to resolve.`
126
+ : `Linked ${written.length} of ${repos.length} repositor${repos.length === 1 ? "y" : "ies"} ` +
127
+ `to workspace "${workspace}".`,
128
+ state: written.length === 0 ? "refused" : "ok",
129
+ nameLevel: true,
130
+ // Writing a config file is not evidence about code, so there is no
131
+ // resolution to report — R0 rather than a cheerful default, the same
132
+ // choice `analyze` makes when no code-reading source ran.
133
+ resolutionFloor: 0,
134
+ commitSha: null,
135
+ graphBuiltAt: null,
136
+ irSchemaVersion: ctx.session.provider().baseStamp().irSchemaVersion,
137
+ notes,
138
+ data: { workspace, written, requested: repos.length },
139
+ });
140
+ }
141
+ export const linkWorkspaceTool = {
142
+ name: "link_workspace",
143
+ class: "stateful-read",
144
+ tier: "evidence",
145
+ version: "1.0.0",
146
+ title: "Declare which repositories share a workspace",
147
+ description: "Write the workspace id that lets multiple repositories' graphs join (DEC-054) into each " +
148
+ "repository's .descry/config.json. Call this ONLY after the user has explicitly confirmed " +
149
+ "which repositories belong together — you may point out sibling directories you can see as a " +
150
+ "suggestion, but you must never decide on your own that they are related and call this " +
151
+ "unprompted. The same file `descry init` writes from the command line; this is the " +
152
+ "conversational door onto it.",
153
+ inputSchema: SCHEMA,
154
+ run,
155
+ };
156
+ //# sourceMappingURL=link-workspace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-workspace.js","sourceRoot":"","sources":["../../src/tools/link-workspace.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Turning what a developer typed into exactly one node — or refusing to.
3
+ *
4
+ * A developer says "the `charge` function"; the graph wants a node id. Two
5
+ * functions in one repository can legitimately be called `charge`, and the
6
+ * tempting move — take the first row, or the one with the most edges — is how a
7
+ * tool ends up answering confidently about the wrong symbol. There is no signal
8
+ * available here that distinguishes them; picking would be inventing one.
9
+ *
10
+ * So ambiguity is returned, with the candidates, and the caller is told how to
11
+ * narrow it. That is the same discipline the graph builder applies to an
12
+ * unresolvable edge (never stub a node) and the adapters apply to an unreadable
13
+ * receiver (ledger row, never a guess), arriving at the interaction layer.
14
+ */
15
+ import type { IRNode } from "@descryy/ir";
16
+ import type { Session } from "../session.ts";
17
+ export interface Lookup {
18
+ /** Null when nothing matched, or when more than one thing did. */
19
+ readonly node: IRNode | null;
20
+ readonly candidates: readonly IRNode[];
21
+ readonly headline: string;
22
+ readonly notes: readonly string[];
23
+ }
24
+ export declare function resolveOneNode(session: Session, criteria: {
25
+ readonly nodeId?: string | undefined;
26
+ readonly symbol?: string | undefined;
27
+ readonly file?: string | undefined;
28
+ }): Lookup;
29
+ //# sourceMappingURL=lookup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lookup.d.ts","sourceRoot":"","sources":["../../src/tools/lookup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAE7C,MAAM,WAAW,MAAM;IACrB,kEAAkE;IAClE,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED,wBAAgB,cAAc,CAC5B,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE;IACR,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACpC,GACA,MAAM,CA+DR"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Turning what a developer typed into exactly one node — or refusing to.
3
+ *
4
+ * A developer says "the `charge` function"; the graph wants a node id. Two
5
+ * functions in one repository can legitimately be called `charge`, and the
6
+ * tempting move — take the first row, or the one with the most edges — is how a
7
+ * tool ends up answering confidently about the wrong symbol. There is no signal
8
+ * available here that distinguishes them; picking would be inventing one.
9
+ *
10
+ * So ambiguity is returned, with the candidates, and the caller is told how to
11
+ * narrow it. That is the same discipline the graph builder applies to an
12
+ * unresolvable edge (never stub a node) and the adapters apply to an unreadable
13
+ * receiver (ledger row, never a guess), arriving at the interaction layer.
14
+ */
15
+ import { findNodes, getNode } from "@descryy/core";
16
+ export function resolveOneNode(session, criteria) {
17
+ const driver = session.store().driver;
18
+ if (criteria.nodeId !== undefined) {
19
+ const node = getNode(driver, criteria.nodeId);
20
+ return node === undefined
21
+ ? {
22
+ node: null,
23
+ candidates: [],
24
+ headline: `No node with id ${criteria.nodeId} is in the graph.`,
25
+ notes: [
26
+ "The id may be from an older graph. Node ids are hashes over the repository scope, " +
27
+ "kind and qualified symbol path, so they survive file moves but not a change to the " +
28
+ "configured repository name. Run analyze and use an id from its output.",
29
+ ],
30
+ }
31
+ : { node, candidates: [node], headline: node.name, notes: [] };
32
+ }
33
+ const matches = findNodes(driver, {
34
+ ...(criteria.symbol === undefined ? {} : { name: criteria.symbol }),
35
+ ...(criteria.file === undefined ? {} : { file: criteria.file }),
36
+ });
37
+ const named = criteria.symbol ?? criteria.file ?? "";
38
+ if (matches.length === 0) {
39
+ return {
40
+ node: null,
41
+ candidates: [],
42
+ headline: `Nothing named "${named}" is in the graph.`,
43
+ notes: [
44
+ "This does not mean it does not exist. It means no configured adapter read it, or the " +
45
+ "graph predates it. Run analyze, and check its disclosures for languages that were not " +
46
+ "analysable.",
47
+ ],
48
+ };
49
+ }
50
+ if (matches.length === 1) {
51
+ return { node: matches[0], candidates: matches, headline: matches[0].name, notes: [] };
52
+ }
53
+ // More than one. Only a file narrows it, and only if the caller did not already
54
+ // give one — in which case the file genuinely declares two things of that name.
55
+ const where = matches
56
+ .slice(0, 8)
57
+ .map((m) => `${m.type} ${m.name}${m.file === null ? "" : ` (${m.file})`}`)
58
+ .join(", ");
59
+ return {
60
+ node: null,
61
+ candidates: matches,
62
+ headline: `"${named}" is ambiguous — ${matches.length} nodes match.`,
63
+ notes: [
64
+ `Candidates: ${where}${matches.length > 8 ? ", …" : ""}. ` +
65
+ (criteria.file === undefined
66
+ ? 'Narrow it with "file", or pass "nodeId" directly.'
67
+ : "They are in the same file, so pass \"nodeId\" directly."),
68
+ "No answer was computed. Choosing one of these by a heuristic would produce a confident " +
69
+ "answer about a symbol you did not ask about.",
70
+ ],
71
+ };
72
+ }
73
+ //# sourceMappingURL=lookup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lookup.js","sourceRoot":"","sources":["../../src/tools/lookup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * `pr-analysis` — "what should I test before I open this PR?"
3
+ *
4
+ * The blast radius of a diff, intersected with the tests that cover it. The
5
+ * useful half of the answer is the second list: **what is affected and has no
6
+ * covering test.** A green suite proves nothing about that set, and it is the
7
+ * only part of a PR review that a test run cannot supply.
8
+ *
9
+ * ## `base...head`, three dots
10
+ *
11
+ * `changedFiles` diffs against the merge base rather than the two tips. On a
12
+ * branch a week behind `main`, two dots would fold every change made on `main`
13
+ * since the branch point into "this PR", and the blast radius would belong to
14
+ * somebody else's work. This is stated here as well as at the git call because
15
+ * it is the kind of thing that gets "simplified" later.
16
+ *
17
+ * ## Absence of a test is not evidence of absence
18
+ *
19
+ * `TESTS` edges come from adapters, and every adapter's recall is below 100% by
20
+ * design — precision over recall, when in doubt omit the edge. So "no covering
21
+ * test" means "no test that this graph can link", and the difference is
22
+ * disclosed. Reporting the untested list without it would turn a measured recall
23
+ * gap into a confident accusation.
24
+ */
25
+ import { type BriefNode, type ToolDefinition } from "./kit.ts";
26
+ /** A node in the change's blast radius. */
27
+ export interface AffectedNode extends BriefNode {
28
+ readonly score: number;
29
+ readonly depth: number;
30
+ }
31
+ /** Shared by every branch: what was diffed, and what came out of git. */
32
+ export interface PrAnalysisRange {
33
+ readonly base: string;
34
+ readonly head: string;
35
+ /** Paths in `base...head` — three dots, so somebody else's work on base is not folded in. */
36
+ readonly files: readonly string[];
37
+ }
38
+ /**
39
+ * git rejected the range, so nothing downstream ran.
40
+ *
41
+ * The narrowest of the four shapes, and the tell is what is absent: no
42
+ * `missing`, because no path was ever looked up. Narrow on `!("affected" in data)`.
43
+ */
44
+ export type PrAnalysisGitFailure = PrAnalysisRange;
45
+ /**
46
+ * The diff is empty. A real answer: there is nothing to assess.
47
+ *
48
+ * The three lists are present and empty on purpose — the absence here **is**
49
+ * a fact about the change. No `missing`, because there was nothing to resolve.
50
+ */
51
+ export interface PrAnalysisEmptyDiff extends PrAnalysisRange {
52
+ readonly affected: readonly AffectedNode[];
53
+ readonly untested: readonly BriefNode[];
54
+ readonly tests: readonly BriefNode[];
55
+ }
56
+ /**
57
+ * The diff resolved to no graph node at all, so no blast radius was computed.
58
+ *
59
+ * Distinguished from `PrAnalysisEmptyDiff` by `missing`, which names every
60
+ * path the graph has never heard of. The empty lists here mean *nothing was
61
+ * examined* rather than *nothing is affected*.
62
+ */
63
+ export interface PrAnalysisUnresolved extends PrAnalysisEmptyDiff {
64
+ readonly missing: readonly string[];
65
+ }
66
+ /**
67
+ * The full answer.
68
+ *
69
+ * `untested` is the half a test run cannot supply, and it means *no test is
70
+ * linked to the node in this graph* — adapters omit an edge they cannot prove,
71
+ * so a genuinely covered symbol can appear here. A gap to check, never a
72
+ * proven hole. Narrow on `"changedSymbols" in data`.
73
+ */
74
+ export interface PrAnalysisReport extends PrAnalysisRange {
75
+ readonly missing: readonly string[];
76
+ readonly changedSymbols: number;
77
+ readonly affectedTotal: number;
78
+ /** Capped by `limit`. */
79
+ readonly affected: readonly AffectedNode[];
80
+ readonly untestedTotal: number;
81
+ /** Capped by `limit`. */
82
+ readonly untested: readonly BriefNode[];
83
+ /** Capped by `limit`. */
84
+ readonly tests: readonly BriefNode[];
85
+ readonly testsTotal: number;
86
+ }
87
+ export type PrAnalysisData = PrAnalysisGitFailure | PrAnalysisEmptyDiff | PrAnalysisUnresolved | PrAnalysisReport;
88
+ export declare const prAnalysisTool: ToolDefinition;
89
+ //# sourceMappingURL=pr-analysis.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pr-analysis.d.ts","sourceRoot":"","sources":["../../src/tools/pr-analysis.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAIH,OAAO,EAOL,KAAK,SAAS,EAEd,KAAK,cAAc,EACpB,MAAM,UAAU,CAAC;AAGlB,2CAA2C;AAC3C,MAAM,WAAW,YAAa,SAAQ,SAAS;IAC7C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,yEAAyE;AACzE,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,6FAA6F;IAC7F,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,GAAG,eAAe,CAAC;AAEnD;;;;;GAKG;AACH,MAAM,WAAW,mBAAoB,SAAQ,eAAe;IAC1D,QAAQ,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,CAAC;IAC3C,QAAQ,CAAC,QAAQ,EAAE,SAAS,SAAS,EAAE,CAAC;IACxC,QAAQ,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,CAAC;CACtC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,oBAAqB,SAAQ,mBAAmB;IAC/D,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,gBAAiB,SAAQ,eAAe;IACvD,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,yBAAyB;IACzB,QAAQ,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,CAAC;IAC3C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,yBAAyB;IACzB,QAAQ,CAAC,QAAQ,EAAE,SAAS,SAAS,EAAE,CAAC;IACxC,yBAAyB;IACzB,QAAQ,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,CAAC;IACrC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,MAAM,cAAc,GACtB,oBAAoB,GACpB,mBAAmB,GACnB,oBAAoB,GACpB,gBAAgB,CAAC;AA0JrB,eAAO,MAAM,cAAc,EAAE,cAa5B,CAAC"}
@@ -0,0 +1,169 @@
1
+ /**
2
+ * `pr-analysis` — "what should I test before I open this PR?"
3
+ *
4
+ * The blast radius of a diff, intersected with the tests that cover it. The
5
+ * useful half of the answer is the second list: **what is affected and has no
6
+ * covering test.** A green suite proves nothing about that set, and it is the
7
+ * only part of a PR review that a test run cannot supply.
8
+ *
9
+ * ## `base...head`, three dots
10
+ *
11
+ * `changedFiles` diffs against the merge base rather than the two tips. On a
12
+ * branch a week behind `main`, two dots would fold every change made on `main`
13
+ * since the branch point into "this PR", and the blast radius would belong to
14
+ * somebody else's work. This is stated here as well as at the git call because
15
+ * it is the kind of thing that gets "simplified" later.
16
+ *
17
+ * ## Absence of a test is not evidence of absence
18
+ *
19
+ * `TESTS` edges come from adapters, and every adapter's recall is below 100% by
20
+ * design — precision over recall, when in doubt omit the edge. So "no covering
21
+ * test" means "no test that this graph can link", and the difference is
22
+ * disclosed. Reporting the untested list without it would turn a measured recall
23
+ * gap into a confident accusation.
24
+ */
25
+ import { changedFiles, changeRisk, NODE_BUDGETS, nodesInFiles } from "@descryy/core";
26
+ import { answer, briefNode, optionalEnum, optionalInteger, optionalString, requiredString, } from "./kit.js";
27
+ const TRIGGERS = ["pre-push", "pr", "nightly"];
28
+ const SCHEMA = {
29
+ type: "object",
30
+ properties: {
31
+ base: {
32
+ type: "string",
33
+ description: "The branch or commit this change is measured against, e.g. \"main\" or \"origin/main\". " +
34
+ "Required — there is no defensible default, and guessing one silently changes the diff.",
35
+ },
36
+ head: { type: "string", description: "The tip being reviewed. Default HEAD." },
37
+ trigger: {
38
+ type: "string",
39
+ enum: [...TRIGGERS],
40
+ description: "Node budget to use. Default pr (1000).",
41
+ },
42
+ limit: { type: "integer", minimum: 1, description: "Cap on nodes per list. Default 50." },
43
+ },
44
+ required: ["base"],
45
+ additionalProperties: false,
46
+ };
47
+ async function run(args, ctx) {
48
+ // Required rather than defaulted: there is no defensible default base, and
49
+ // guessing one would silently change which diff the whole answer describes.
50
+ const base = requiredString(args, "base");
51
+ const head = optionalString(args, "head") ?? "HEAD";
52
+ const trigger = optionalEnum(args, "trigger", TRIGGERS) ?? "pr";
53
+ const limit = optionalInteger(args, "limit", 1) ?? 50;
54
+ const session = ctx.session;
55
+ const stamp = session.provider().baseStamp();
56
+ ctx.progress(`Diffing ${base}...${head}`);
57
+ let files;
58
+ try {
59
+ files = await changedFiles(session.repoPath, base, head);
60
+ }
61
+ catch (error) {
62
+ return answer({
63
+ headline: `Could not diff ${base}...${head}.`,
64
+ state: "refused",
65
+ nameLevel: true,
66
+ resolutionFloor: 0,
67
+ commitSha: stamp.commitSha,
68
+ graphBuiltAt: stamp.graphBuiltAt,
69
+ irSchemaVersion: stamp.irSchemaVersion,
70
+ notes: [
71
+ `git rejected the range: ${error.message.trim()}. Check that "${base}" exists ` +
72
+ "locally — a shallow clone or a missing fetch is the usual cause.",
73
+ ],
74
+ data: { base, head, files: [] },
75
+ });
76
+ }
77
+ const notes = [];
78
+ if (files.length === 0) {
79
+ return answer({
80
+ headline: `${base}...${head} changes no files.`,
81
+ nameLevel: true,
82
+ resolutionFloor: 4,
83
+ commitSha: stamp.commitSha,
84
+ graphBuiltAt: stamp.graphBuiltAt,
85
+ irSchemaVersion: stamp.irSchemaVersion,
86
+ notes: ["An empty diff is a real answer: there is nothing to assess."],
87
+ data: { base, head, files: [], affected: [], untested: [], tests: [] },
88
+ });
89
+ }
90
+ ctx.progress(`Resolving ${files.length} changed file(s)`, 1, 3);
91
+ const { nodes, missing } = nodesInFiles(session.store().driver, files);
92
+ if (missing.length > 0) {
93
+ notes.push(`${missing.length} of ${files.length} changed file(s) have no node in the graph ` +
94
+ `(${missing.slice(0, 5).join(", ")}${missing.length > 5 ? ", …" : ""}). They are outside ` +
95
+ "what any configured adapter reads, or the graph predates them. Their impact is absent " +
96
+ "from this analysis and they are not being reported as safe.");
97
+ }
98
+ if (nodes.length === 0) {
99
+ return answer({
100
+ headline: `None of the ${files.length} changed file(s) are in the graph.`,
101
+ state: "refused",
102
+ nameLevel: true,
103
+ resolutionFloor: 0,
104
+ commitSha: stamp.commitSha,
105
+ graphBuiltAt: stamp.graphBuiltAt,
106
+ irSchemaVersion: stamp.irSchemaVersion,
107
+ notes: [
108
+ ...notes,
109
+ "No blast radius was computed. Run analyze first; if it has already run, its disclosures " +
110
+ "name the languages and paths it could not read.",
111
+ ],
112
+ data: { base, head, files, missing, affected: [], untested: [], tests: [] },
113
+ });
114
+ }
115
+ ctx.progress(`Assessing ${nodes.length} changed symbol(s)`, 2, 3);
116
+ // `changeRisk` takes a raw node budget rather than a trigger: the trigger is a
117
+ // scoping concept and the query layer deliberately does not know about it.
118
+ // Translating here keeps the tool's vocabulary the developer's ("this is a
119
+ // PR") and the engine's its own.
120
+ const risk = changeRisk(session.provider(), nodes.map((n) => n.id), { budget: NODE_BUDGETS[trigger] });
121
+ notes.push("\"Untested\" means no test is linked to the node in this graph. Adapters omit an edge they " +
122
+ "cannot prove, so a genuinely covered symbol can appear here — treat it as a gap to check, " +
123
+ "not as a proven hole.");
124
+ const untested = risk.data.untested;
125
+ const affected = risk.data.affected.filter((entry) => entry.node.type !== "TEST_CASE");
126
+ ctx.progress("Done", 3, 3);
127
+ return {
128
+ result: {
129
+ ...risk,
130
+ data: {
131
+ base,
132
+ head,
133
+ files,
134
+ missing,
135
+ changedSymbols: nodes.length,
136
+ affectedTotal: affected.length,
137
+ affected: affected.slice(0, limit).map((entry) => ({
138
+ ...briefNode(entry.node),
139
+ score: Number(entry.score.toFixed(4)),
140
+ depth: entry.depth,
141
+ })),
142
+ untestedTotal: untested.length,
143
+ untested: untested.slice(0, limit).map(briefNode),
144
+ tests: risk.data.tests.slice(0, limit).map(briefNode),
145
+ testsTotal: risk.data.tests.length,
146
+ },
147
+ notes: [...risk.notes, ...notes],
148
+ },
149
+ ...(affected.length === 0 ? { state: "empty" } : {}),
150
+ nameLevel: true,
151
+ headline: `${nodes.length} changed symbol(s) across ${files.length} file(s) affect ${affected.length} ` +
152
+ `other node(s). ${risk.data.tests.length} test(s) cover part of that; ${untested.length} ` +
153
+ "affected node(s) have no covering test in the graph.",
154
+ };
155
+ }
156
+ export const prAnalysisTool = {
157
+ name: "pr_analysis",
158
+ class: "read",
159
+ tier: "evidence",
160
+ version: "1.0.0",
161
+ title: "What this change affects, and what is not covered",
162
+ description: "Diff a branch against its merge base, compute the blast radius of the change, and split it " +
163
+ "into what existing tests cover and what they do not. The uncovered list is the point: a " +
164
+ "passing suite says nothing about it. Files outside what the adapters read are named rather " +
165
+ "than counted as safe.",
166
+ inputSchema: SCHEMA,
167
+ run,
168
+ };
169
+ //# sourceMappingURL=pr-analysis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pr-analysis.js","sourceRoot":"","sources":["../../src/tools/pr-analysis.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAErF,OAAO,EACL,MAAM,EACN,SAAS,EACT,YAAY,EACZ,eAAe,EACf,cAAc,EACd,cAAc,GAIf,MAAM,UAAU,CAAC;AA4ElB,MAAM,QAAQ,GAAG,CAAC,UAAU,EAAE,IAAI,EAAE,SAAS,CAAU,CAAC;AAExD,MAAM,MAAM,GAAG;IACb,IAAI,EAAE,QAAQ;IACd,UAAU,EAAE;QACV,IAAI,EAAE;YACJ,IAAI,EAAE,QAAQ;YACd,WAAW,EACT,0FAA0F;gBAC1F,wFAAwF;SAC3F;QACD,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,uCAAuC,EAAE;QAC9E,OAAO,EAAE;YACP,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,CAAC,GAAG,QAAQ,CAAC;YACnB,WAAW,EAAE,wCAAwC;SACtD;QACD,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,WAAW,EAAE,oCAAoC,EAAE;KAC1F;IACD,QAAQ,EAAE,CAAC,MAAM,CAAC;IAClB,oBAAoB,EAAE,KAAK;CACnB,CAAC;AAEX,KAAK,UAAU,GAAG,CAChB,IAA6B,EAC7B,GAAgB;IAEhB,2EAA2E;IAC3E,4EAA4E;IAC5E,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,OAAO,GAAG,YAAY,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,CAAC,IAAI,IAAI,CAAC;IAChE,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACtD,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;IAC5B,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,CAAC;IAE7C,GAAG,CAAC,QAAQ,CAAC,WAAW,IAAI,MAAM,IAAI,EAAE,CAAC,CAAC;IAC1C,IAAI,KAAe,CAAC;IACpB,IAAI,CAAC;QACH,KAAK,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC3D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,MAAM,CAAiB;YAC5B,QAAQ,EAAE,kBAAkB,IAAI,MAAM,IAAI,GAAG;YAC7C,KAAK,EAAE,SAAS;YAChB,SAAS,EAAE,IAAI;YACf,eAAe,EAAE,CAAC;YAClB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,YAAY,EAAE,KAAK,CAAC,YAAY;YAChC,eAAe,EAAE,KAAK,CAAC,eAAe;YACtC,KAAK,EAAE;gBACL,2BAA4B,KAAe,CAAC,OAAO,CAAC,IAAI,EAAE,iBAAiB,IAAI,WAAW;oBACxF,kEAAkE;aACrE;YACD,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE;SAChC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,MAAM,CAAiB;YAC5B,QAAQ,EAAE,GAAG,IAAI,MAAM,IAAI,oBAAoB;YAC/C,SAAS,EAAE,IAAI;YACf,eAAe,EAAE,CAAC;YAClB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,YAAY,EAAE,KAAK,CAAC,YAAY;YAChC,eAAe,EAAE,KAAK,CAAC,eAAe;YACtC,KAAK,EAAE,CAAC,6DAA6D,CAAC;YACtE,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;SACvE,CAAC,CAAC;IACL,CAAC;IAED,GAAG,CAAC,QAAQ,CAAC,aAAa,KAAK,CAAC,MAAM,kBAAkB,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAChE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,YAAY,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACvE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,KAAK,CAAC,IAAI,CACR,GAAG,OAAO,CAAC,MAAM,OAAO,KAAK,CAAC,MAAM,6CAA6C;YAC/E,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,sBAAsB;YAC1F,wFAAwF;YACxF,6DAA6D,CAChE,CAAC;IACJ,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,MAAM,CAAiB;YAC5B,QAAQ,EAAE,eAAe,KAAK,CAAC,MAAM,oCAAoC;YACzE,KAAK,EAAE,SAAS;YAChB,SAAS,EAAE,IAAI;YACf,eAAe,EAAE,CAAC;YAClB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,YAAY,EAAE,KAAK,CAAC,YAAY;YAChC,eAAe,EAAE,KAAK,CAAC,eAAe;YACtC,KAAK,EAAE;gBACL,GAAG,KAAK;gBACR,0FAA0F;oBACxF,iDAAiD;aACpD;YACD,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;SAC5E,CAAC,CAAC;IACL,CAAC;IAED,GAAG,CAAC,QAAQ,CAAC,aAAa,KAAK,CAAC,MAAM,oBAAoB,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAClE,+EAA+E;IAC/E,2EAA2E;IAC3E,2EAA2E;IAC3E,iCAAiC;IACjC,MAAM,IAAI,GAAG,UAAU,CACrB,OAAO,CAAC,QAAQ,EAAE,EAClB,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EACtB,EAAE,MAAM,EAAE,YAAY,CAAC,OAAO,CAAC,EAAE,CAClC,CAAC;IAEF,KAAK,CAAC,IAAI,CACR,6FAA6F;QAC3F,4FAA4F;QAC5F,uBAAuB,CAC1B,CAAC;IAEF,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC;IACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC;IAEvF,GAAG,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3B,OAAO;QACL,MAAM,EAAE;YACN,GAAG,IAAI;YACP,IAAI,EAAE;gBACJ,IAAI;gBACJ,IAAI;gBACJ,KAAK;gBACL,OAAO;gBACP,cAAc,EAAE,KAAK,CAAC,MAAM;gBAC5B,aAAa,EAAE,QAAQ,CAAC,MAAM;gBAC9B,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;oBACjD,GAAG,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC;oBACxB,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;oBACrC,KAAK,EAAE,KAAK,CAAC,KAAK;iBACnB,CAAC,CAAC;gBACH,aAAa,EAAE,QAAQ,CAAC,MAAM;gBAC9B,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC;gBACjD,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC;gBACrD,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM;aACnC;YACD,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,GAAG,KAAK,CAAC;SACjC;QACD,GAAG,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7D,SAAS,EAAE,IAAI;QACf,QAAQ,EACN,GAAG,KAAK,CAAC,MAAM,6BAA6B,KAAK,CAAC,MAAM,mBAAmB,QAAQ,CAAC,MAAM,GAAG;YAC7F,kBAAkB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,gCAAgC,QAAQ,CAAC,MAAM,GAAG;YAC1F,sDAAsD;KACzD,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,cAAc,GAAmB;IAC5C,IAAI,EAAE,aAAa;IACnB,KAAK,EAAE,MAAM;IACb,IAAI,EAAE,UAAU;IAChB,OAAO,EAAE,OAAO;IAChB,KAAK,EAAE,mDAAmD;IAC1D,WAAW,EACT,6FAA6F;QAC7F,0FAA0F;QAC1F,6FAA6F;QAC7F,uBAAuB;IACzB,WAAW,EAAE,MAA4C;IACzD,GAAG;CACJ,CAAC"}