@nanonets/graft 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -29
- package/dist/ask/ask.d.ts +28 -0
- package/dist/ask/ask.d.ts.map +1 -0
- package/dist/ask/ask.js +239 -0
- package/dist/ask/ask.js.map +1 -0
- package/dist/cli.js +50 -34
- package/dist/cli.js.map +1 -1
- package/dist/context/check.js +3 -3
- package/dist/context/check.js.map +1 -1
- package/dist/context/node-file.d.ts +3 -1
- package/dist/context/node-file.d.ts.map +1 -1
- package/dist/context/node-file.js +6 -4
- package/dist/context/node-file.js.map +1 -1
- package/dist/engine.d.ts +10 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +9 -0
- package/dist/engine.js.map +1 -1
- package/dist/graph/build.d.ts +2 -2
- package/dist/graph/build.d.ts.map +1 -1
- package/dist/graph/build.js +12 -4
- package/dist/graph/build.js.map +1 -1
- package/dist/graph/cards.d.ts +45 -0
- package/dist/graph/cards.d.ts.map +1 -0
- package/dist/graph/cards.js +238 -0
- package/dist/graph/cards.js.map +1 -0
- package/dist/graph/check.js +9 -9
- package/dist/graph/check.js.map +1 -1
- package/dist/graph/write.d.ts +6 -2
- package/dist/graph/write.d.ts.map +1 -1
- package/dist/graph/write.js +16 -7
- package/dist/graph/write.js.map +1 -1
- package/dist/viz/serve.js +4 -4
- package/dist/viz/serve.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
<a href="https://nodejs.org"><img src="https://img.shields.io/node/v/%40nanonets%2Fgraft?style=for-the-badge&logo=nodedotjs&logoColor=white" /></a>
|
|
15
15
|
<img src="https://img.shields.io/badge/TypeScript-strict-3178C6?style=for-the-badge&logo=typescript&logoColor=white" />
|
|
16
16
|
<img src="https://img.shields.io/badge/License-MIT-20C997?style=for-the-badge" />
|
|
17
|
-
<img src="https://img.shields.io/badge/
|
|
17
|
+
<img src="https://img.shields.io/badge/telemetry-none-546FFF?style=for-the-badge" />
|
|
18
18
|
</p>
|
|
19
19
|
|
|
20
20
|
<!-- placeholder: numbers pending a committed benchmark run, see Benchmark below -->
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
|
|
27
27
|
```mermaid
|
|
28
28
|
flowchart LR
|
|
29
|
-
A["Your code"] -->|"graft
|
|
29
|
+
A["Your code"] -->|"graft build --deep"| B["graft/<br/>a graph of linked<br/>markdown nodes"]
|
|
30
30
|
B -->|"git commit"| C[("your repo")]
|
|
31
31
|
C -->|"git clone"| D["teammate + their agent"]
|
|
32
32
|
D -->|"reads the graph first"| E["starts already knowing<br/>how the repo fits together"]
|
|
@@ -37,10 +37,13 @@ flowchart LR
|
|
|
37
37
|
## Quick start
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
|
-
|
|
41
|
-
#
|
|
40
|
+
export OPENROUTER_API_KEY=sk-or-...
|
|
41
|
+
# the summaries are LLM-written — get a key at https://openrouter.ai/keys
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
npx @nanonets/graft build --deep
|
|
44
|
+
# builds graft/ from your code, one node per system, API, or concept
|
|
45
|
+
|
|
46
|
+
git add graft && git commit -m "add context graph"
|
|
44
47
|
# commit it so everyone who clones the repo (and their agents) gets the graph
|
|
45
48
|
```
|
|
46
49
|
|
|
@@ -48,10 +51,10 @@ Or install it once and get the `graft` command everywhere:
|
|
|
48
51
|
|
|
49
52
|
```bash
|
|
50
53
|
npm install -g @nanonets/graft
|
|
51
|
-
graft
|
|
54
|
+
graft build --deep
|
|
52
55
|
```
|
|
53
56
|
|
|
54
|
-
That is it. Point your agent at
|
|
57
|
+
That is it. Point your agent at `graft/` and it reads the graph before it starts working.
|
|
55
58
|
|
|
56
59
|
---
|
|
57
60
|
|
|
@@ -73,9 +76,9 @@ Graft builds that understanding **once** and writes it into your repo as a folde
|
|
|
73
76
|
|
|
74
77
|
- **Real explanations, not a list of symbols.** Each node says, in plain English, what a part of the system does and how it connects to the rest, the way a senior engineer would explain it. That is the part an agent actually needs so it can skip the exploration. It is not a dump of function names.
|
|
75
78
|
- **A real graph you can read.** No embeddings, no similarity search, no index to keep warm. The graph is a set of linked files your agent opens, greps, and follows, exactly the way it reads any other file in the repo.
|
|
76
|
-
- **Grafted into git.** The graph is just files in
|
|
79
|
+
- **Grafted into git.** The graph is just files in `graft/`. Commit it, and anyone who clones the repo has it. No database, no server, no setup. Git does the syncing, and a stale graph shows up as a diff in review instead of rotting in some external store.
|
|
77
80
|
- **The diff lives with the code.** When a change moves things around, you see it in the graph diff in the same pull request, right next to the code that caused it.
|
|
78
|
-
- **
|
|
81
|
+
- **Your key, your model.** Summaries are written by a model you pick on [OpenRouter](https://openrouter.ai), under your own key. The structural code graph (`graft build`, `graft check`) is deterministic tree-sitter and never calls a model at all.
|
|
79
82
|
|
|
80
83
|
---
|
|
81
84
|
|
|
@@ -88,6 +91,8 @@ Graft builds the graph in two passes, both powered by a language model:
|
|
|
88
91
|
|
|
89
92
|
Both passes are cached by content hash. Re-running only touches the files that changed, so the second build is fast and cheap.
|
|
90
93
|
|
|
94
|
+
Alongside the markdown graph, `graft build` builds `graft/.graph/wiring.json` — a per-symbol code graph — plus a per-file wiring card mirroring your source tree. Tier 1 is pure tree-sitter (every function, class, and call edge; deterministic, no model, no network), which is why plain `graft build` needs no key. The `--deep` pass adds a one-line summary and a crux excerpt per symbol, cached by body hash.
|
|
95
|
+
|
|
91
96
|
---
|
|
92
97
|
|
|
93
98
|
## What's in a node
|
|
@@ -108,7 +113,7 @@ That is three depths in one file: the summary says *what* the code does, the cru
|
|
|
108
113
|
|
|
109
114
|
The crux is stored as the code itself, not as a line range, on purpose. Line numbers drift whenever unrelated code above them shifts, but the lines that matter do not. Keeping the text, not the numbers, means the crux stays correct even as the file around it moves.
|
|
110
115
|
|
|
111
|
-
_Summary, sources, links, and notes ship today. The
|
|
116
|
+
_Summary, sources, links, and notes ship today in markdown nodes. The crux ships per-symbol in the code graph (`graft build --deep`); inlining it into markdown nodes is next._
|
|
112
117
|
|
|
113
118
|
---
|
|
114
119
|
|
|
@@ -117,42 +122,45 @@ _Summary, sources, links, and notes ship today. The inline crux excerpt is rolli
|
|
|
117
122
|
A graph that has drifted from the code is worse than no graph. `graft check` compares each node's tracked source hashes against the current files and fails if the graph is stale. Drop it in CI so a pull request cannot merge with an out-of-date map.
|
|
118
123
|
|
|
119
124
|
```bash
|
|
120
|
-
graft check # exits non-zero if
|
|
125
|
+
graft check # exits non-zero if graft/ has drifted
|
|
121
126
|
graft check --json # machine-readable drift report
|
|
122
127
|
```
|
|
123
128
|
|
|
129
|
+
When the wiring graph exists, `graft check` validates it too — structural drift (symbols added, removed, or changed since the last `graft build`) and stale summaries both fail the check. A repo without a wiring graph is not penalized; the markdown graph stands alone.
|
|
130
|
+
|
|
124
131
|
<!-- placeholder: planned, not yet in the CLI -->
|
|
125
|
-
> **Planned: auto-
|
|
132
|
+
> **Planned: auto-regen on PR.** A single CI bot will regenerate changed nodes on a pull request and push the update, so the graph stays current without anyone remembering to run `build`. Until then, regenerate with `graft build --deep` and commit it alongside your code.
|
|
126
133
|
|
|
127
134
|
---
|
|
128
135
|
|
|
129
|
-
##
|
|
136
|
+
## What runs where
|
|
130
137
|
|
|
131
|
-
|
|
138
|
+
- **On your machine, no key, no network:** the structural code graph. `graft build` (wiring graph + per-file cards), `graft check`, and `graft ask` are deterministic tree-sitter — they never call a model.
|
|
139
|
+
- **Through your OpenRouter key:** the LLM-written parts — `graft build --deep` adds the concept nodes (file summaries + node synthesis) and the per-symbol summaries and cruxes. Set `OPENROUTER_API_KEY` and optionally pick the model with `GRAFT_OPENROUTER_MODEL` (default: `openai/gpt-4o-mini`).
|
|
140
|
+
- **No telemetry** and no analytics — the only network calls are the LLM requests you configured.
|
|
132
141
|
|
|
133
|
-
|
|
134
|
-
- **Optional.** Set `OPENROUTER_API_KEY` to use a cloud model for richer summaries. Pass `--local` to force local even when a key is set.
|
|
135
|
-
- **No telemetry** and no analytics.
|
|
136
|
-
|
|
137
|
-
See [`.env.example`](.env.example) for the full list of settings (model names, base URLs).
|
|
142
|
+
See [`.env.example`](.env.example) for the full list of settings (model, base URL, graph directory).
|
|
138
143
|
|
|
139
144
|
---
|
|
140
145
|
|
|
141
146
|
## CLI
|
|
142
147
|
|
|
143
148
|
```bash
|
|
144
|
-
graft
|
|
145
|
-
graft
|
|
146
|
-
graft
|
|
149
|
+
graft build [dir] # build graft/ from the code at [dir]: wiring graph + per-file cards (no LLM, no key)
|
|
150
|
+
graft build --deep # add the LLM layer: concept nodes + per-symbol summary/crux (cached)
|
|
151
|
+
graft build --extensions .ts .py # only include these code extensions
|
|
152
|
+
|
|
153
|
+
graft ask "<task>" [dir] # query the graph — ranked nodes + exact file:line (no LLM, no key)
|
|
154
|
+
graft ask "<task>" --json # machine-readable result
|
|
147
155
|
|
|
148
|
-
graft check [dir] # fail (exit 1) if
|
|
156
|
+
graft check [dir] # fail (exit 1) if graft/ has drifted from the code
|
|
149
157
|
graft check --json # print the drift report as JSON
|
|
150
158
|
|
|
151
159
|
graft viz [dir] # see the graph: serves an interactive viewer on localhost
|
|
152
160
|
graft viz --port 5000 --no-open # pick a port; don't auto-open the browser
|
|
153
161
|
|
|
154
162
|
# global
|
|
155
|
-
graft --dir <path> # use a context dir other than <repo
|
|
163
|
+
graft --dir <path> # use a context dir other than <repo>/graft
|
|
156
164
|
```
|
|
157
165
|
|
|
158
166
|
## Visualize it (`graft viz`)
|
|
@@ -160,9 +168,9 @@ graft --dir <path> # use a context dir other than <repo>/.cont
|
|
|
160
168
|
`graft viz` opens a local, interactive view of both graphs — no install, no dev
|
|
161
169
|
server; the viewer ships prebuilt inside the package.
|
|
162
170
|
|
|
163
|
-
- **Context** tab — the architecture graph from
|
|
171
|
+
- **Context** tab — the architecture graph from `graft/*.md`. Nodes colored by
|
|
164
172
|
type, sized by connectedness.
|
|
165
|
-
- **Code** tab — the per-symbol graph from `graph.json` (run `graft
|
|
173
|
+
- **Code** tab — the per-symbol graph from `graft/.graph/wiring.json` (run `graft build` first).
|
|
166
174
|
- **Outline** tab — the file → class → method hierarchy as a collapsible tree.
|
|
167
175
|
|
|
168
176
|
Edges speak the code's language. Every link is one of a closed set of verbs, each
|
|
@@ -180,7 +188,7 @@ answering a question someone building or reviewing code actually asks:
|
|
|
180
188
|
Select a node and its edges take on direction: **amber = what it depends on,
|
|
181
189
|
teal = what depends on it**, with the verb written on each highlighted edge.
|
|
182
190
|
Chips above the canvas filter by verb; tree-sitter-extracted edges draw solid
|
|
183
|
-
while LLM-inferred ones draw dashed. The viewer live-reloads when
|
|
191
|
+
while LLM-inferred ones draw dashed. The viewer live-reloads when `graft/`
|
|
184
192
|
changes on disk. Older graphs with vague verbs (`influences`, `supports`) are
|
|
185
193
|
normalized on load — no regeneration needed.
|
|
186
194
|
|
|
@@ -190,7 +198,7 @@ normalized on load — no regeneration needed.
|
|
|
190
198
|
|
|
191
199
|
<!-- placeholder: fill from a committed bench/results/ run before launch -->
|
|
192
200
|
|
|
193
|
-
The claim Graft has to earn is simple: an agent that reads the graph first is cheaper and faster without getting more answers wrong. The harness runs every task twice through the same agent with the same file tools. One run is **cold** (it explores from zero) and one is **graph** (it gets the
|
|
201
|
+
The claim Graft has to earn is simple: an agent that reads the graph first is cheaper and faster without getting more answers wrong. The harness runs every task twice through the same agent with the same file tools. One run is **cold** (it explores from zero) and one is **graph** (it gets the `graft/` bundle up front). A separate model judges correctness, with a required-keyword floor so a fast-but-wrong answer cannot win.
|
|
194
202
|
|
|
195
203
|
| Metric | Cold | Graph | Change |
|
|
196
204
|
|---|---|---|---|
|
|
@@ -218,7 +226,7 @@ npm install
|
|
|
218
226
|
npm run build
|
|
219
227
|
npm test
|
|
220
228
|
|
|
221
|
-
npm run cli --
|
|
229
|
+
npm run cli -- build --deep . # run the CLI from source
|
|
222
230
|
```
|
|
223
231
|
|
|
224
232
|
---
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Relation } from "../graph/types.js";
|
|
2
|
+
export interface AskHit {
|
|
3
|
+
kind: "concept" | "symbol" | "caller" | "callee";
|
|
4
|
+
title: string;
|
|
5
|
+
/** A `file` or `file:Lx-Ly` pointer the agent can open directly. */
|
|
6
|
+
pointer: string;
|
|
7
|
+
snippet: string;
|
|
8
|
+
relation?: Relation;
|
|
9
|
+
related?: string[];
|
|
10
|
+
score: number;
|
|
11
|
+
}
|
|
12
|
+
export interface AskResult {
|
|
13
|
+
query: string;
|
|
14
|
+
mode: "structural" | "lexical" | "empty";
|
|
15
|
+
/** For structural mode: the symbol whose neighbours we walked. */
|
|
16
|
+
subject?: string;
|
|
17
|
+
hits: AskHit[];
|
|
18
|
+
note?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface AskOptions {
|
|
21
|
+
contextDir?: string;
|
|
22
|
+
limit?: number;
|
|
23
|
+
}
|
|
24
|
+
/** Answer a query from the graft/ graph at `dir`. Deterministic, $0. */
|
|
25
|
+
export declare function ask(dir: string, query: string, opts?: AskOptions): AskResult;
|
|
26
|
+
/** Render an {@link AskResult} as a compact markdown context pack. */
|
|
27
|
+
export declare function formatAsk(r: AskResult): string;
|
|
28
|
+
//# sourceMappingURL=ask.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ask.d.ts","sourceRoot":"","sources":["../../src/ask/ask.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAA2B,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE3E,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACjD,KAAK,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,YAAY,GAAG,SAAS,GAAG,OAAO,CAAC;IACzC,kEAAkE;IAClE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAmMD,MAAM,WAAW,UAAU;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wEAAwE;AACxE,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,UAAe,GAAG,SAAS,CAUhF;AAED,sEAAsE;AACtE,wBAAgB,SAAS,CAAC,CAAC,EAAE,SAAS,GAAG,MAAM,CAqB9C"}
|
package/dist/ask/ask.js
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `graft ask "<task>"` — the ACTIVE channel.
|
|
3
|
+
*
|
|
4
|
+
* One tool that routes a plain-words query to the right graph and returns a lean,
|
|
5
|
+
* ranked context pack (prose + exact `file:line` + related), never raw JSON and
|
|
6
|
+
* never whole files:
|
|
7
|
+
*
|
|
8
|
+
* - Structural intent ("what calls X", "callers of X", "what does X import")
|
|
9
|
+
* → walk the wiring graph's edges and return the neighbour symbols with spans.
|
|
10
|
+
* - Everything else → lexical rank over concept nodes (prose) and wiring symbols
|
|
11
|
+
* (name + signature + summary), returning the best few with pointers.
|
|
12
|
+
*
|
|
13
|
+
* v1 is deterministic and $0 (term-overlap scoring, no LLM, no embeddings). The
|
|
14
|
+
* output is markdown so it drops straight into an agent's context.
|
|
15
|
+
*/
|
|
16
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
17
|
+
import { join, resolve } from "node:path";
|
|
18
|
+
import matter from "gray-matter";
|
|
19
|
+
import { contextDirFor } from "../context/node-file.js";
|
|
20
|
+
import { readGraph, wiringPath } from "../graph/write.js";
|
|
21
|
+
const STOP = new Set([
|
|
22
|
+
"the", "a", "an", "of", "to", "in", "is", "are", "how", "does", "do", "what",
|
|
23
|
+
"where", "which", "that", "this", "it", "for", "on", "and", "or", "with",
|
|
24
|
+
"i", "we", "get", "set", "use", "used", "using", "when", "why", "can",
|
|
25
|
+
]);
|
|
26
|
+
/** Split prose + identifiers into lowercased subword tokens (camelCase, snake, kebab). */
|
|
27
|
+
function tokenize(text) {
|
|
28
|
+
return text
|
|
29
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1 $2") // camelCase → camel Case
|
|
30
|
+
.toLowerCase()
|
|
31
|
+
.split(/[^a-z0-9]+/)
|
|
32
|
+
.filter((t) => t.length > 1 && !STOP.has(t));
|
|
33
|
+
}
|
|
34
|
+
/** Term-frequency count map. */
|
|
35
|
+
function counts(tokens) {
|
|
36
|
+
const m = new Map();
|
|
37
|
+
for (const t of tokens)
|
|
38
|
+
m.set(t, (m.get(t) ?? 0) + 1);
|
|
39
|
+
return m;
|
|
40
|
+
}
|
|
41
|
+
/** First real prose line of a node body (skips headings, markers, blanks). */
|
|
42
|
+
function firstProse(body) {
|
|
43
|
+
for (const raw of body.split("\n")) {
|
|
44
|
+
const line = raw.trim();
|
|
45
|
+
if (!line || line.startsWith("#") || line.startsWith("<!--") || line.startsWith("-"))
|
|
46
|
+
continue;
|
|
47
|
+
return line;
|
|
48
|
+
}
|
|
49
|
+
return "";
|
|
50
|
+
}
|
|
51
|
+
function loadCorpus(outDir) {
|
|
52
|
+
const concepts = [];
|
|
53
|
+
if (existsSync(outDir)) {
|
|
54
|
+
for (const entry of readdirSync(outDir)) {
|
|
55
|
+
if (!entry.endsWith(".md") || entry === "INDEX.md")
|
|
56
|
+
continue;
|
|
57
|
+
const parsed = matter(readFileSync(join(outDir, entry), "utf8"));
|
|
58
|
+
const fm = parsed.data;
|
|
59
|
+
const sources = Array.isArray(fm.sources)
|
|
60
|
+
? fm.sources.map((s) => s.path)
|
|
61
|
+
: [];
|
|
62
|
+
const related = Array.isArray(fm.links)
|
|
63
|
+
? fm.links.map((l) => l.to)
|
|
64
|
+
: [];
|
|
65
|
+
const snippet = firstProse(parsed.content);
|
|
66
|
+
concepts.push({
|
|
67
|
+
slug: String(fm.slug ?? entry.replace(/\.md$/, "")),
|
|
68
|
+
name: String(fm.name ?? ""),
|
|
69
|
+
sources,
|
|
70
|
+
related,
|
|
71
|
+
snippet,
|
|
72
|
+
text: `${fm.name ?? ""} ${snippet} ${sources.join(" ")}`,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return { concepts, graph: readGraph(wiringPath(outDir)) };
|
|
77
|
+
}
|
|
78
|
+
/** Score a document's token counts against the query counts (name field pre-weighted by caller). */
|
|
79
|
+
function score(query, doc) {
|
|
80
|
+
let s = 0;
|
|
81
|
+
for (const [t, qn] of query) {
|
|
82
|
+
const dn = doc.get(t);
|
|
83
|
+
if (dn)
|
|
84
|
+
s += qn * dn;
|
|
85
|
+
}
|
|
86
|
+
return s;
|
|
87
|
+
}
|
|
88
|
+
// ── Structural intent ──────────────────────────────────────────────────────
|
|
89
|
+
const INCOMING = /\b(caller|callers|calls?\s+into|who\s+calls|what\s+calls|called\s+by|used\s+by|uses)\b/;
|
|
90
|
+
const OUTGOING = /\b(callee|callees|what\s+does\s+\w+\s+call|calls\s+what|imports?|depends\s+on)\b/;
|
|
91
|
+
const INCOMING_RELS = ["calls", "references", "implements", "extends"];
|
|
92
|
+
const OUTGOING_RELS = ["calls", "references", "imports", "implements", "extends"];
|
|
93
|
+
/** Pick the query word that names a real symbol (longest exact name match wins). */
|
|
94
|
+
function findSubject(query, graph) {
|
|
95
|
+
const byName = new Map();
|
|
96
|
+
for (const n of graph.nodes) {
|
|
97
|
+
if (n.kind === "file")
|
|
98
|
+
continue;
|
|
99
|
+
const key = n.name.toLowerCase();
|
|
100
|
+
byName.set(key, [...(byName.get(key) ?? []), n]);
|
|
101
|
+
}
|
|
102
|
+
const words = query.split(/[^A-Za-z0-9_.]+/).filter(Boolean);
|
|
103
|
+
let best = [];
|
|
104
|
+
let bestLen = 0;
|
|
105
|
+
for (const w of words) {
|
|
106
|
+
const hit = byName.get(w.toLowerCase());
|
|
107
|
+
if (hit && w.length > bestLen) {
|
|
108
|
+
best = hit;
|
|
109
|
+
bestLen = w.length;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return best;
|
|
113
|
+
}
|
|
114
|
+
function structural(query, graph, limit) {
|
|
115
|
+
const wantsIn = INCOMING.test(query);
|
|
116
|
+
const wantsOut = OUTGOING.test(query);
|
|
117
|
+
if (!wantsIn && !wantsOut)
|
|
118
|
+
return null;
|
|
119
|
+
const subjects = findSubject(query, graph);
|
|
120
|
+
if (subjects.length === 0)
|
|
121
|
+
return null;
|
|
122
|
+
const ids = new Set(subjects.map((n) => n.id));
|
|
123
|
+
const byId = new Map(graph.nodes.map((n) => [n.id, n]));
|
|
124
|
+
const outgoing = wantsOut && !wantsIn; // "what does X call / import" — else default to callers
|
|
125
|
+
const rels = new Set(outgoing ? OUTGOING_RELS : INCOMING_RELS);
|
|
126
|
+
const hits = [];
|
|
127
|
+
const seen = new Set();
|
|
128
|
+
for (const e of graph.edges) {
|
|
129
|
+
if (!rels.has(e.relation))
|
|
130
|
+
continue;
|
|
131
|
+
const anchor = outgoing ? e.source : e.target;
|
|
132
|
+
const other = outgoing ? e.target : e.source;
|
|
133
|
+
if (!ids.has(anchor) || seen.has(other + e.relation))
|
|
134
|
+
continue;
|
|
135
|
+
seen.add(other + e.relation);
|
|
136
|
+
const node = byId.get(other);
|
|
137
|
+
hits.push({
|
|
138
|
+
kind: outgoing ? "callee" : "caller",
|
|
139
|
+
title: node ? node.name : other, // unresolved import target → raw module string
|
|
140
|
+
pointer: node ? `${node.path}:${node.span}` : other,
|
|
141
|
+
snippet: node?.summary?.split("\n")[0].trim() ?? node?.signature ?? "",
|
|
142
|
+
relation: e.relation,
|
|
143
|
+
score: 1,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
hits.sort((a, b) => a.pointer.localeCompare(b.pointer));
|
|
147
|
+
return {
|
|
148
|
+
query,
|
|
149
|
+
mode: "structural",
|
|
150
|
+
subject: subjects[0].name,
|
|
151
|
+
hits: hits.slice(0, limit),
|
|
152
|
+
note: outgoing
|
|
153
|
+
? `outgoing edges from ${subjects[0].name}`
|
|
154
|
+
: `callers / references of ${subjects[0].name}`,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
// ── Lexical rank ─────────────────────────────────────────────────────────────
|
|
158
|
+
function lexical(query, corpus, limit) {
|
|
159
|
+
const q = counts(tokenize(query));
|
|
160
|
+
const scored = [];
|
|
161
|
+
for (const c of corpus.concepts) {
|
|
162
|
+
const nameScore = score(q, counts(tokenize(c.name))) * 3;
|
|
163
|
+
const bodyScore = score(q, counts(tokenize(c.text)));
|
|
164
|
+
const total = nameScore + bodyScore;
|
|
165
|
+
if (total > 0) {
|
|
166
|
+
scored.push({
|
|
167
|
+
kind: "concept",
|
|
168
|
+
title: c.name || c.slug,
|
|
169
|
+
pointer: c.sources.join(", ") || c.slug,
|
|
170
|
+
snippet: c.snippet,
|
|
171
|
+
related: c.related,
|
|
172
|
+
score: total,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
for (const n of corpus.graph?.nodes ?? []) {
|
|
177
|
+
if (n.kind === "file")
|
|
178
|
+
continue;
|
|
179
|
+
const nameScore = score(q, counts(tokenize(n.name))) * 3;
|
|
180
|
+
const pathScore = score(q, counts(tokenize(n.path))) * 2;
|
|
181
|
+
const bodyScore = score(q, counts(tokenize(`${n.signature ?? ""} ${n.summary ?? ""}`)));
|
|
182
|
+
const total = nameScore + pathScore + bodyScore;
|
|
183
|
+
if (total > 0) {
|
|
184
|
+
scored.push({
|
|
185
|
+
kind: "symbol",
|
|
186
|
+
title: `${n.name} · ${n.kind}`,
|
|
187
|
+
pointer: `${n.path}:${n.span}`,
|
|
188
|
+
snippet: n.summary?.split("\n")[0].trim() ?? n.signature ?? "",
|
|
189
|
+
score: total,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
scored.sort((a, b) => b.score - a.score || a.title.localeCompare(b.title));
|
|
194
|
+
return {
|
|
195
|
+
query,
|
|
196
|
+
mode: scored.length ? "lexical" : "empty",
|
|
197
|
+
hits: scored.slice(0, limit),
|
|
198
|
+
note: scored.length ? undefined : "no matching nodes — try different words, or `graft build` if graft/ is empty",
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
/** Answer a query from the graft/ graph at `dir`. Deterministic, $0. */
|
|
202
|
+
export function ask(dir, query, opts = {}) {
|
|
203
|
+
const outDir = contextDirFor(resolve(dir), opts.contextDir);
|
|
204
|
+
const limit = opts.limit ?? 8;
|
|
205
|
+
const corpus = loadCorpus(outDir);
|
|
206
|
+
if (corpus.graph) {
|
|
207
|
+
const s = structural(query, corpus.graph, limit);
|
|
208
|
+
if (s)
|
|
209
|
+
return s;
|
|
210
|
+
}
|
|
211
|
+
return lexical(query, corpus, limit);
|
|
212
|
+
}
|
|
213
|
+
/** Render an {@link AskResult} as a compact markdown context pack. */
|
|
214
|
+
export function formatAsk(r) {
|
|
215
|
+
const head = `graft ask — "${r.query}" (${r.mode}${r.note ? `: ${r.note}` : ""})`;
|
|
216
|
+
if (r.hits.length === 0) {
|
|
217
|
+
return `${head}\n\n${r.note ?? "no matches."}`;
|
|
218
|
+
}
|
|
219
|
+
const lines = [head, ""];
|
|
220
|
+
if (r.mode === "structural") {
|
|
221
|
+
for (const h of r.hits) {
|
|
222
|
+
const tail = h.snippet ? ` — ${h.snippet}` : "";
|
|
223
|
+
lines.push(`- ${h.title} ${h.pointer} (${h.relation})${tail}`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
r.hits.forEach((h, i) => {
|
|
228
|
+
lines.push(`${i + 1}. ${h.title} [${h.kind}]`);
|
|
229
|
+
lines.push(` ${h.pointer}`);
|
|
230
|
+
if (h.snippet)
|
|
231
|
+
lines.push(` ${h.snippet}`);
|
|
232
|
+
if (h.related?.length)
|
|
233
|
+
lines.push(` related: ${h.related.join(", ")}`);
|
|
234
|
+
lines.push("");
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
return lines.join("\n").trimEnd() + "\n";
|
|
238
|
+
}
|
|
239
|
+
//# sourceMappingURL=ask.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ask.js","sourceRoot":"","sources":["../../src/ask/ask.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -19,58 +19,74 @@ const program = new Command();
|
|
|
19
19
|
program
|
|
20
20
|
.name("graft")
|
|
21
21
|
.description("Build a repo's context graph as linked markdown, and keep it in sync with the code.")
|
|
22
|
-
.option("--dir <path>", "context graph directory (default: <repo
|
|
22
|
+
.option("--dir <path>", "context graph directory (default: <repo>/graft)");
|
|
23
23
|
function engineFrom() {
|
|
24
24
|
const opts = program.opts();
|
|
25
25
|
return new Graft({ contextDir: opts.dir });
|
|
26
26
|
}
|
|
27
27
|
program
|
|
28
|
-
.command("
|
|
29
|
-
.description("Build
|
|
28
|
+
.command("build")
|
|
29
|
+
.description("Build graft/ from your code — wiring graph + per-file cards ($0, no key). " +
|
|
30
|
+
"Add --deep for the LLM concept map + per-symbol summaries/crux.")
|
|
30
31
|
.argument("[dir]", "repository root", ".")
|
|
32
|
+
.option("--deep", "run the LLM pass: concept nodes (graft/*.md) + per-symbol summary/crux")
|
|
31
33
|
.option("-e, --extensions <exts...>", 'code extensions to include (e.g. ".ts" ".py")')
|
|
32
34
|
.action(async (dir, opts) => {
|
|
33
35
|
const engine = engineFrom();
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
const fmt = (o) => Object.entries(o)
|
|
37
|
+
.sort((a, b) => b[1] - a[1])
|
|
38
|
+
.map(([k, n]) => `${n} ${k}`)
|
|
39
|
+
.join(", ");
|
|
40
|
+
// --deep: concept nodes first (they're LLM prose; the wiring cards link up to
|
|
41
|
+
// them), then the wiring graph rewrites the cards + INDEX with those up-links.
|
|
42
|
+
if (opts.deep) {
|
|
43
|
+
const c = await engine.init(dir, {
|
|
44
|
+
extensions: opts.extensions,
|
|
45
|
+
onProgress: ({ phase, index, total, file }) => process.stderr.write(`\r${phase === "summarize" ? "reading" : "writing"} concepts ${index + 1}/${total}: ${file.slice(0, 40).padEnd(40)}`),
|
|
46
|
+
});
|
|
47
|
+
process.stderr.write("\n");
|
|
48
|
+
console.log(`✓ concepts: ${c.nodes} nodes, ${c.links} links from ${c.files} files (${c.summarized} read, ${c.cached} cached)`);
|
|
49
|
+
for (const e of c.errors)
|
|
50
|
+
console.error(`✗ ${e}`);
|
|
51
|
+
}
|
|
52
|
+
// Wiring graph (Tier-2 cards + Tier-3 wiring.json) — always. LLM meaning only with --deep.
|
|
53
|
+
const g = await engine.graph(dir, {
|
|
54
|
+
llm: opts.deep,
|
|
55
|
+
onProgress: ({ phase, index, total, file }) => process.stderr.write(`\r${phase === "enrich" ? "summarizing" : "parsing"} ${index + 1}/${total}: ${file.slice(0, 50).padEnd(50)}`),
|
|
37
56
|
});
|
|
38
57
|
process.stderr.write("\n");
|
|
39
|
-
console.log(`✓ ${
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
58
|
+
console.log(`✓ wiring: ${g.nodes} nodes (${fmt(g.byKind)}), ${g.edges} edges, ${g.cards} cards [${g.languages.join(", ")}]`);
|
|
59
|
+
if (opts.deep) {
|
|
60
|
+
const m = g.meaning;
|
|
61
|
+
console.log(` meaning: ${m.computed} computed, ${m.cached} cached, ${m.stale} stale, ${m.pending} pending`);
|
|
62
|
+
}
|
|
63
|
+
console.log(` → ${g.contextDir}`);
|
|
64
|
+
for (const e of g.errors)
|
|
43
65
|
console.error(`✗ ${e}`);
|
|
44
|
-
const rel = relative(process.cwd(),
|
|
45
|
-
console.log(` commit it: git add ${rel} && git commit -m "update
|
|
66
|
+
const rel = relative(process.cwd(), g.contextDir) || "graft";
|
|
67
|
+
console.log(` commit it: git add ${rel} && git commit -m "update graft"`);
|
|
46
68
|
});
|
|
47
69
|
program
|
|
48
|
-
.command("
|
|
49
|
-
.description("
|
|
70
|
+
.command("ask")
|
|
71
|
+
.description("Query the graft/ graph — returns ranked nodes + exact file:line, routed to prose or wiring ($0, no key)")
|
|
72
|
+
.argument("<query>", "what you want to understand, in plain words")
|
|
50
73
|
.argument("[dir]", "repository root", ".")
|
|
51
|
-
.option("--
|
|
52
|
-
.
|
|
74
|
+
.option("-n, --limit <n>", "max results", "8")
|
|
75
|
+
.option("--json", "output the result as JSON")
|
|
76
|
+
.action(async (query, dir, opts) => {
|
|
53
77
|
const engine = engineFrom();
|
|
54
|
-
const r =
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
.
|
|
61
|
-
|
|
62
|
-
.join(", ");
|
|
63
|
-
console.log(`✓ ${r.nodes} nodes (${fmt(r.byKind)}) from ${r.files} files [${r.languages.join(", ")}]`);
|
|
64
|
-
console.log(` ${r.edges} edges (${fmt(r.byRelation)})`);
|
|
65
|
-
const m = r.meaning;
|
|
66
|
-
console.log(` meaning: ${m.computed} computed, ${m.cached} cached, ${m.stale} stale, ${m.pending} pending`);
|
|
67
|
-
console.log(` → ${r.graphPath}`);
|
|
68
|
-
for (const e of r.errors)
|
|
69
|
-
console.error(`✗ ${e}`);
|
|
78
|
+
const r = engine.ask(dir, query, { limit: Number(opts.limit) });
|
|
79
|
+
if (opts.json) {
|
|
80
|
+
console.log(JSON.stringify(r, null, 2));
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
const { formatAsk } = await import("./ask/ask.js");
|
|
84
|
+
process.stdout.write(formatAsk(r));
|
|
85
|
+
}
|
|
70
86
|
});
|
|
71
87
|
program
|
|
72
88
|
.command("check")
|
|
73
|
-
.description("Fail if
|
|
89
|
+
.description("Fail if graft/ is stale relative to the code (for CI)")
|
|
74
90
|
.argument("[dir]", "repository root", ".")
|
|
75
91
|
.option("-e, --extensions <exts...>", "code extensions to include")
|
|
76
92
|
.option("--json", "output the drift as JSON")
|
|
@@ -107,7 +123,7 @@ program
|
|
|
107
123
|
const globalOpts = program.opts();
|
|
108
124
|
const contextDir = contextDirFor(root, globalOpts.dir);
|
|
109
125
|
if (!existsSync(contextDir)) {
|
|
110
|
-
console.error(`✗ no context graph at ${contextDir} — run \`graft
|
|
126
|
+
console.error(`✗ no context graph at ${contextDir} — run \`graft build --deep\` first`);
|
|
111
127
|
process.exit(1);
|
|
112
128
|
}
|
|
113
129
|
// dist/cli.js → dist/viewer/ (prebuilt at package build time)
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;GASG;AACH,OAAO,eAAe,CAAC;AACvB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,OAAO,CAAC;KACb,WAAW,CAAC,qFAAqF,CAAC;KAClG,MAAM,CAAC,cAAc,EAAE,
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;GASG;AACH,OAAO,eAAe,CAAC;AACvB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,OAAO,CAAC;KACb,WAAW,CAAC,qFAAqF,CAAC;KAClG,MAAM,CAAC,cAAc,EAAE,iDAAiD,CAAC,CAAC;AAE7E,SAAS,UAAU;IACjB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAoB,CAAC;IAC9C,OAAO,IAAI,KAAK,CAAC,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;AAC7C,CAAC;AAED,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CACV,4EAA4E;IAC1E,iEAAiE,CACpE;KACA,QAAQ,CAAC,OAAO,EAAE,iBAAiB,EAAE,GAAG,CAAC;KACzC,MAAM,CAAC,QAAQ,EAAE,wEAAwE,CAAC;KAC1F,MAAM,CAAC,4BAA4B,EAAE,+CAA+C,CAAC;KACrF,MAAM,CAAC,KAAK,EAAE,GAAW,EAAE,IAA+C,EAAE,EAAE;IAC7E,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,GAAG,GAAG,CAAC,CAAyB,EAAE,EAAE,CACxC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;SACd,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;SAC3B,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;SAC5B,IAAI,CAAC,IAAI,CAAC,CAAC;IAEhB,8EAA8E;IAC9E,+EAA+E;IAC/E,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,MAAM,CAAC,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;YAC/B,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,CAC5C,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,KAAK,KAAK,KAAK,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,aAAa,KAAK,GAAG,CAAC,IAAI,KAAK,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CACrH;SACJ,CAAC,CAAC;QACH,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3B,OAAO,CAAC,GAAG,CACT,eAAe,CAAC,CAAC,KAAK,WAAW,CAAC,CAAC,KAAK,eAAe,CAAC,CAAC,KAAK,WAAW,CAAC,CAAC,UAAU,UAAU,CAAC,CAAC,MAAM,UAAU,CAClH,CAAC;QACF,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM;YAAE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACpD,CAAC;IAED,2FAA2F;IAC3F,MAAM,CAAC,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE;QAChC,GAAG,EAAE,IAAI,CAAC,IAAI;QACd,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,CAC5C,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,KAAK,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAC7G;KACJ,CAAC,CAAC;IACH,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3B,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,KAAK,WAAW,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,WAAW,CAAC,CAAC,KAAK,WAAW,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC7H,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC;QACpB,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,QAAQ,cAAc,CAAC,CAAC,MAAM,YAAY,CAAC,CAAC,KAAK,WAAW,CAAC,CAAC,OAAO,UAAU,CAAC,CAAC;IAC/G,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;IACnC,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM;QAAE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAElD,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,IAAI,OAAO,CAAC;IAC7D,OAAO,CAAC,GAAG,CAAC,yBAAyB,GAAG,kCAAkC,CAAC,CAAC;AAC9E,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,KAAK,CAAC;KACd,WAAW,CAAC,yGAAyG,CAAC;KACtH,QAAQ,CAAC,SAAS,EAAE,6CAA6C,CAAC;KAClE,QAAQ,CAAC,OAAO,EAAE,iBAAiB,EAAE,GAAG,CAAC;KACzC,MAAM,CAAC,iBAAiB,EAAE,aAAa,EAAE,GAAG,CAAC;KAC7C,MAAM,CAAC,QAAQ,EAAE,2BAA2B,CAAC;KAC7C,MAAM,CAAC,KAAK,EAAE,KAAa,EAAE,GAAW,EAAE,IAAuC,EAAE,EAAE;IACpF,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAChE,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC1C,CAAC;SAAM,CAAC;QACN,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC;QACnD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IACrC,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,uDAAuD,CAAC;KACpE,QAAQ,CAAC,OAAO,EAAE,iBAAiB,EAAE,GAAG,CAAC;KACzC,MAAM,CAAC,4BAA4B,EAAE,4BAA4B,CAAC;KAClE,MAAM,CAAC,QAAQ,EAAE,0BAA0B,CAAC;KAC5C,MAAM,CAAC,CAAC,GAAW,EAAE,IAA+C,EAAE,EAAE;IACvE,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;IAC7D,MAAM,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,2CAA2C;IAE7E,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACpF,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC;QAClC,IAAI,CAAC,CAAC,CAAC,OAAO;YAAE,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAAC;IAChE,CAAC;IAED,2EAA2E;IAC3E,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACtD,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,KAAK,CAAC;KACd,WAAW,CAAC,uFAAuF,CAAC;KACpG,QAAQ,CAAC,OAAO,EAAE,iBAAiB,EAAE,GAAG,CAAC;KACzC,MAAM,CAAC,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,CAAC;KACvD,MAAM,CAAC,WAAW,EAAE,wBAAwB,CAAC;KAC7C,MAAM,CAAC,KAAK,EAAE,GAAW,EAAE,IAAqC,EAAE,EAAE;IACnE,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;IAC/C,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;IACxD,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;IACrD,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;IACnD,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,wBAAwB,CAAC,CAAC;IACjE,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC;IAE1D,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC1B,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,EAAoB,CAAC;IACpD,MAAM,UAAU,GAAG,aAAa,CAAC,IAAI,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC;IACvD,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,OAAO,CAAC,KAAK,CAAC,yBAAyB,UAAU,qCAAqC,CAAC,CAAC;QACxF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,8DAA8D;IAC9D,MAAM,SAAS,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACvE,MAAM,GAAG,GAAG,MAAM,cAAc,CAAC;QAC/B,UAAU;QACV,SAAS;QACT,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;QACvB,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC;KACzB,CAAC,CAAC;IACH,OAAO,CAAC,GAAG,CAAC,eAAe,GAAG,CAAC,GAAG,oBAAoB,CAAC,CAAC;IACxD,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC;QAC5G,KAAK,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;IAC7G,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,UAAU,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACjC,OAAO,CAAC,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACxD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|