@jacklika/dsh-tool-memory-graph 0.1.0-alpha.6 → 0.1.0-alpha.7
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 +5 -4
- package/README.zh.md +5 -4
- package/lib/index.js +74 -13
- package/lib/types/index.d.ts +2 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -41,17 +41,18 @@ It resolves the same vault root per call as `tool-memory-filesystem`: an explici
|
|
|
41
41
|
| `indexHiddenDirs` | `boolean` | `false` | Index directories whose names start with `.` (`.git` and `node_modules` stay excluded). Enable when `vaultRoot` points at the workspace root so `.dsh/memory/` notes are included. |
|
|
42
42
|
| `maxDepth` | `number` | `1` | Maximum BFS depth when a center id is given. |
|
|
43
43
|
| `maxNodes` | `number` | `200` | Maximum nodes returned in one graph. |
|
|
44
|
+
| `maxEdges` | `number` | `1000` | Maximum edges returned in one graph. |
|
|
44
45
|
|
|
45
46
|
### Tools
|
|
46
47
|
|
|
47
|
-
- `wiki_graph(id?, depth?)` — without `id`, return every note and directed edge in the vault. With `id`, return the reachable subgraph within `depth` hops; edges from boundary nodes are included when both endpoints are selected. Edges carry `type` (`link` or `keyword`), `keywords` (for `keyword` edges), and `weight` (combined term frequency).
|
|
48
|
+
- `wiki_graph(id?, depth?)` — without `id`, return every note and directed edge in the vault. With `id`, return the reachable subgraph within `depth` hops; edges from boundary nodes are included when both endpoints are selected. Edges carry `type` (`link` or `keyword`), `keywords` (for `keyword` edges), and `weight` (combined term frequency). Output is `{ nodes, edges, truncated, edgesTruncated }`: `truncated` reports the `maxNodes` cut and `edgesTruncated` the `maxEdges` cut, kept separate so a complete node set is not misread as a complete graph.
|
|
48
49
|
|
|
49
50
|
-----
|
|
50
51
|
|
|
51
52
|
<a id="understand-the-implementation"></a>
|
|
52
53
|
## Understand the implementation
|
|
53
54
|
|
|
54
|
-
The package is a single Cordis function plugin that registers `wiki_graph` on `ctx.tools`. Per call it resolves the vault root through the shared `resolveMemoryVaultRoot` helper, lists notes through the shared `listNotePaths` walker, and extracts `[[link]]` targets through `extractLinks`/`resolveLinkTarget`. Targets resolve with the same Obsidian-style tiers as `wiki_read` — exact vault-relative path, then path suffix, then basename anywhere in the vault, with deterministic ambiguity handling. In addition to explicit links, the tool extracts significant terms from each note (frontmatter `tags` ×3, frontmatter `title` ×2, body terms ×1) and creates `keyword` edges between any two notes that share one or more terms. A breadth-first walk
|
|
55
|
+
The package is a single Cordis function plugin that registers `wiki_graph` on `ctx.tools`. Per call it resolves the vault root through the shared `resolveMemoryVaultRoot` helper, lists notes through the shared `listNotePaths` walker, and extracts `[[link]]` targets through `extractLinks`/`resolveLinkTarget`. Targets resolve with the same Obsidian-style tiers as `wiki_read` — exact vault-relative path, then path suffix, then basename anywhere in the vault, with deterministic ambiguity handling. In addition to explicit links, the tool extracts significant terms from each note (frontmatter `tags` ×3, frontmatter `title` ×2, body terms ×1) and creates `keyword` edges between any two notes that share one or more terms. A breadth-first walk with a visited set (so cyclical link graphs terminate) follows both `link` and `keyword` edges to select the node set first; edges are then collected from every selected node so boundary-node outgoing edges are not dropped. When the node set exceeds `maxNodes`, a whole-vault graph keeps the lowest ids in code-unit order (the ordering `tool-memory-filesystem` tie-breaks with) and a subgraph keeps the notes nearest the center — BFS level first, then id — so which notes survive the cap never depends on directory-listing order and a subgraph never drops its own center. Because `wiki_graph` recomputes from disk on every call, rewriting a note removes stale `keyword` edges and creates new ones for the terms that survive or appear in the new text. Edges are capped at `maxEdges` after node selection: explicit `link` edges first, then `keyword` edges by descending weight, with a deterministic id tie-break, so the surviving set never depends on directory-listing order. Requesting a center id that is not an indexed note fails with `note not found`, and an unreadable note is skipped (logged as a warning) instead of failing the call. Output is `{ nodes: [{ id, title }], edges: [{ from, to, type, keywords?, weight? }], truncated: boolean, edgesTruncated: boolean }`, where `truncated` covers the node cap and `edgesTruncated` the edge cap.
|
|
55
56
|
|
|
56
57
|
-----
|
|
57
58
|
|
|
@@ -67,7 +68,7 @@ The model sees the generated `wiki_graph` schema. The description states that th
|
|
|
67
68
|
##### Verbatim description for `wiki_graph`
|
|
68
69
|
|
|
69
70
|
```markdown
|
|
70
|
-
Return the link and keyword graph of the wiki vault: note ids, titles, and directed edges. Explicit `[[link]]` edges are type `link`; edges derived from shared significant terms are type `keyword` with `keywords` and `weight`. Without an id it returns the whole vault graph (node-capped); with an id it returns the subgraph reachable from that note within the given depth.
|
|
71
|
+
Return the link and keyword graph of the wiki vault: note ids, titles, and directed edges. Explicit `[[link]]` edges are type `link`; edges derived from shared significant terms are type `keyword` with `keywords` and `weight`. Without an id it returns the whole vault graph (node-capped); with an id it returns the subgraph reachable from that note within the given depth. Nodes are capped at maxNodes (`truncated`) and edges at maxEdges (`edgesTruncated`).
|
|
71
72
|
```
|
|
72
73
|
|
|
73
74
|
#### Token effect
|
|
@@ -83,7 +84,7 @@ Independent. The plugin only supplies tool results; it does not change the reque
|
|
|
83
84
|
<a id="known-limitations-and-deferred-work"></a>
|
|
84
85
|
|
|
85
86
|
- **No visualization** — the tool returns JSON nodes and edges; rendering (Obsidian graph view, Mermaid) is left to consumers.
|
|
86
|
-
- **Edges are scoped to selected nodes** — only links whose source and target both survive the `maxNodes` cap are returned, so boundary links outside the selected set are omitted.
|
|
87
|
+
- **Edges are scoped to selected nodes** — only links whose source and target both survive the `maxNodes` cap are returned, so boundary links outside the selected set are omitted. A dense vault can then also hit the `maxEdges` cap; `edgesTruncated` reports when the returned edges are only a deterministic slice.
|
|
87
88
|
|
|
88
89
|
<a id="dev-note"></a>
|
|
89
90
|
### Dev Note
|
package/README.zh.md
CHANGED
|
@@ -41,17 +41,18 @@ kind: "package-reference"
|
|
|
41
41
|
| `indexHiddenDirs` | `boolean` | `false` | 是否索引以 `.` 开头的目录(`.git` 与 `node_modules` 始终排除)。当 `vaultRoot` 指向工作区根时开启,以纳入 `.dsh/memory/`。 |
|
|
42
42
|
| `maxDepth` | `number` | `1` | 指定中心笔记时 BFS 的最大深度。 |
|
|
43
43
|
| `maxNodes` | `number` | `200` | 单次图查询返回的最大节点数。 |
|
|
44
|
+
| `maxEdges` | `number` | `1000` | 单次图查询返回的最大边数。 |
|
|
44
45
|
|
|
45
46
|
### 工具
|
|
46
47
|
|
|
47
|
-
- `wiki_graph(id?, depth?)` —— 不传 `id` 时返回全仓库的节点与有向边;传 `id` 时返回 `depth` 跳内的可达子图,两个端点都被选中时边界节点的出边也会包含。边携带 `type`(`link` 或 `keyword`)、`keywords`(仅 `keyword` 边)和 `weight
|
|
48
|
+
- `wiki_graph(id?, depth?)` —— 不传 `id` 时返回全仓库的节点与有向边;传 `id` 时返回 `depth` 跳内的可达子图,两个端点都被选中时边界节点的出边也会包含。边携带 `type`(`link` 或 `keyword`)、`keywords`(仅 `keyword` 边)和 `weight`(合并词频)。输出为 `{ nodes, edges, truncated, edgesTruncated }`:`truncated` 对应 `maxNodes` 截断,`edgesTruncated` 对应 `maxEdges` 截断,两者分开报告,节点完整不会被误读为整图完整。
|
|
48
49
|
|
|
49
50
|
-----
|
|
50
51
|
|
|
51
52
|
<a id="understand-the-implementation"></a>
|
|
52
53
|
## 实现说明
|
|
53
54
|
|
|
54
|
-
本包是单个 Cordis 函数插件,在 `ctx.tools` 上注册 `wiki_graph`。每次调用通过共享的 `resolveMemoryVaultRoot` 解析仓库根,用 `listNotePaths` 遍历笔记,用 `extractLinks`/`resolveLinkTarget` 提取 `[[link]]` 目标。目标按与 `wiki_read` 相同的 Obsidian 语义解析——先精确仓库相对路径,再路径后缀,最后全仓库 basename 匹配,多重命中按确定性规则选择。除显式链接外,工具还会从每条笔记提取显著词(frontmatter `tags` ×3、frontmatter `title` ×2、正文词 ×1),并为任意两篇共享一个或多个词的笔记创建 `keyword`
|
|
55
|
+
本包是单个 Cordis 函数插件,在 `ctx.tools` 上注册 `wiki_graph`。每次调用通过共享的 `resolveMemoryVaultRoot` 解析仓库根,用 `listNotePaths` 遍历笔记,用 `extractLinks`/`resolveLinkTarget` 提取 `[[link]]` 目标。目标按与 `wiki_read` 相同的 Obsidian 语义解析——先精确仓库相对路径,再路径后缀,最后全仓库 basename 匹配,多重命中按确定性规则选择。除显式链接外,工具还会从每条笔记提取显著词(frontmatter `tags` ×3、frontmatter `title` ×2、正文词 ×1),并为任意两篇共享一个或多个词的笔记创建 `keyword` 边。广度优先遍历带 visited 集合(循环链接图因此可终止),同时跟随 `link` 边与 `keyword` 边以确定节点集合,再从所有已选节点收集边,因此边界节点的出边不会丢失。节点集合超过 `maxNodes` 时,全仓库图保留码点序最小的若干 id(与 `tool-memory-filesystem` 的并列排序规则一致),子图则保留离中心最近的笔记——先按 BFS 层级、再按 id——因此保留哪些笔记不依赖目录列举顺序,子图也不会丢掉自己的中心节点。由于 `wiki_graph` 每次调用都会从磁盘重新计算,重写一条笔记会移除旧的 `keyword` 边,并为新正文中保留下来的或新出现的词建立新边。边在节点集合确定后按 `maxEdges` 截断:显式 `link` 边优先,其后是 `keyword` 边按 `weight` 降序,并以 id 作确定性并列裁决——保留哪些边同样不依赖目录列举顺序。以非已索引笔记作为中心 id 会以 `note not found` 失败;无法读取的笔记会被跳过(记入 logger 警告),不会让整次调用失败。输出为 `{ nodes: [{ id, title }], edges: [{ from, to, type, keywords?, weight? }], truncated: boolean, edgesTruncated: boolean }`,其中 `truncated` 对应节点上限、`edgesTruncated` 对应边上限。
|
|
55
56
|
|
|
56
57
|
-----
|
|
57
58
|
|
|
@@ -67,7 +68,7 @@ kind: "package-reference"
|
|
|
67
68
|
##### `wiki_graph` 的原文描述
|
|
68
69
|
|
|
69
70
|
```markdown
|
|
70
|
-
Return the link and keyword graph of the wiki vault: note ids, titles, and directed edges. Explicit `[[link]]` edges are type `link`; edges derived from shared significant terms are type `keyword` with `keywords` and `weight`. Without an id it returns the whole vault graph (node-capped); with an id it returns the subgraph reachable from that note within the given depth.
|
|
71
|
+
Return the link and keyword graph of the wiki vault: note ids, titles, and directed edges. Explicit `[[link]]` edges are type `link`; edges derived from shared significant terms are type `keyword` with `keywords` and `weight`. Without an id it returns the whole vault graph (node-capped); with an id it returns the subgraph reachable from that note within the given depth. Nodes are capped at maxNodes (`truncated`) and edges at maxEdges (`edgesTruncated`).
|
|
71
72
|
```
|
|
72
73
|
|
|
73
74
|
#### Token 影响
|
|
@@ -83,7 +84,7 @@ Return the link and keyword graph of the wiki vault: note ids, titles, and direc
|
|
|
83
84
|
<a id="known-limitations-and-deferred-work"></a>
|
|
84
85
|
|
|
85
86
|
- **无可视化** —— 工具返回 JSON 节点与边;渲染(Obsidian 图谱、Mermaid)由消费方负责。
|
|
86
|
-
- **边限于已选节点** —— 只有两端都在 `maxNodes`
|
|
87
|
+
- **边限于已选节点** —— 只有两端都在 `maxNodes` 截断后保留下来的节点之间的链接才会出现,子图边界外的出边被省略;密集仓库随后可能触及 `maxEdges` 上限,`edgesTruncated` 会表明返回的边只是确定性切片。
|
|
87
88
|
|
|
88
89
|
<a id="dev-note"></a>
|
|
89
90
|
### 开发备注
|
package/lib/index.js
CHANGED
|
@@ -25,6 +25,7 @@ export const Config = z.object({
|
|
|
25
25
|
extensions: z.array(z.string()).default(['.md']),
|
|
26
26
|
maxDepth: z.number().default(1),
|
|
27
27
|
maxNodes: z.number().default(200),
|
|
28
|
+
maxEdges: z.number().default(1000),
|
|
28
29
|
indexHiddenDirs: z.boolean().default(false),
|
|
29
30
|
});
|
|
30
31
|
function assertPositiveInteger(name, value) {
|
|
@@ -161,20 +162,64 @@ function buildKeywordEdges(noteTerms) {
|
|
|
161
162
|
}
|
|
162
163
|
return edges;
|
|
163
164
|
}
|
|
164
|
-
async function loadVaultNotes(root, resolved) {
|
|
165
|
-
const paths = await listNotePaths(root, resolved.extensions, resolved.indexHiddenDirs);
|
|
165
|
+
async function loadVaultNotes(root, resolved, onSkip) {
|
|
166
|
+
const paths = await listNotePaths(root, resolved.extensions, resolved.indexHiddenDirs, onSkip);
|
|
166
167
|
const ids = paths.map(path => vaultRelativeId(root, path));
|
|
167
168
|
const linkResolver = createLinkResolver(ids);
|
|
168
169
|
const parsed = [];
|
|
169
170
|
const parsedById = new Map();
|
|
171
|
+
const skipped = new Map();
|
|
170
172
|
for (const path of paths) {
|
|
171
173
|
const id = vaultRelativeId(root, path);
|
|
172
|
-
|
|
174
|
+
let text;
|
|
175
|
+
try {
|
|
176
|
+
text = await readFile(path, 'utf8');
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
// One unreadable note must not fail the graph for the whole vault; the
|
|
180
|
+
// note is omitted and the loader logs the degradation.
|
|
181
|
+
skipped.set(id, error);
|
|
182
|
+
onSkip?.(path, error);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
173
185
|
const note = { id, path, text, terms: extractTerms(text) };
|
|
174
186
|
parsed.push(note);
|
|
175
187
|
parsedById.set(id, note);
|
|
176
188
|
}
|
|
177
|
-
return { parsed, parsedById, linkResolver };
|
|
189
|
+
return { parsed, parsedById, linkResolver, skipped };
|
|
190
|
+
}
|
|
191
|
+
/** Build the skip callback that reports unreadable entries to the Loader logger. */
|
|
192
|
+
function skipLogger(ctx, root) {
|
|
193
|
+
return (path, error) => {
|
|
194
|
+
const code = error.code ?? 'unknown';
|
|
195
|
+
ctx.logger?.warn(`tool-memory-graph: skipped unreadable entry ${vaultRelativeId(root, path)} (${code})`);
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
/** Validate that at least one note extension is configured. */
|
|
199
|
+
function assertExtensions(extensions) {
|
|
200
|
+
if (extensions.length === 0) {
|
|
201
|
+
throw new Error('tool-memory-graph: extensions must list at least one file extension');
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Apply the edge budget deterministically: explicit `link` edges are kept
|
|
206
|
+
* first, then `keyword` edges by descending weight and id order, so the same
|
|
207
|
+
* vault returns the same subset regardless of directory-listing order. Below
|
|
208
|
+
* the budget the edge list is returned untouched.
|
|
209
|
+
*/
|
|
210
|
+
function capEdges(edges, maxEdges) {
|
|
211
|
+
if (edges.length <= maxEdges)
|
|
212
|
+
return { edges, truncated: false };
|
|
213
|
+
const ordered = [...edges].sort((a, b) => {
|
|
214
|
+
const linkFirst = (a.type === 'keyword' ? 1 : 0) - (b.type === 'keyword' ? 1 : 0);
|
|
215
|
+
if (linkFirst !== 0)
|
|
216
|
+
return linkFirst;
|
|
217
|
+
const weightOrder = (b.weight ?? 0) - (a.weight ?? 0);
|
|
218
|
+
if (weightOrder !== 0)
|
|
219
|
+
return weightOrder;
|
|
220
|
+
return codeUnitCompare(a.from, b.from) || codeUnitCompare(a.to, b.to);
|
|
221
|
+
});
|
|
222
|
+
return { edges: ordered.slice(0, maxEdges), truncated: true };
|
|
178
223
|
}
|
|
179
224
|
/**
|
|
180
225
|
* Build the vault-wide link graph: every note is a node; every `[[link]]`
|
|
@@ -184,8 +229,8 @@ async function loadVaultNotes(root, resolved) {
|
|
|
184
229
|
* @param resolved - applied plugin configuration.
|
|
185
230
|
* @returns the full graph, node-capped.
|
|
186
231
|
*/
|
|
187
|
-
async function buildGraph(root, resolved) {
|
|
188
|
-
const { parsed, linkResolver } = await loadVaultNotes(root, resolved);
|
|
232
|
+
async function buildGraph(root, resolved, onSkip) {
|
|
233
|
+
const { parsed, linkResolver } = await loadVaultNotes(root, resolved, onSkip);
|
|
189
234
|
const truncated = parsed.length > resolved.maxNodes;
|
|
190
235
|
const capped = [...parsed].sort((a, b) => codeUnitCompare(a.id, b.id)).slice(0, resolved.maxNodes);
|
|
191
236
|
const idSet = new Set(capped.map(p => p.id));
|
|
@@ -205,7 +250,8 @@ async function buildGraph(root, resolved) {
|
|
|
205
250
|
edges.push(...buildKeywordEdges(noteTerms));
|
|
206
251
|
// Safety: every edge endpoint must be in the returned node set.
|
|
207
252
|
const safeEdges = edges.filter(e => idSet.has(e.from) && idSet.has(e.to));
|
|
208
|
-
|
|
253
|
+
const budgeted = capEdges(safeEdges, resolved.maxEdges);
|
|
254
|
+
return { nodes, edges: budgeted.edges, truncated, edgesTruncated: budgeted.truncated };
|
|
209
255
|
}
|
|
210
256
|
/**
|
|
211
257
|
* Collect the subgraph reachable from one note by following `[[link]]`
|
|
@@ -216,10 +262,21 @@ async function buildGraph(root, resolved) {
|
|
|
216
262
|
* @param depth - maximum link/keyword hops from the center.
|
|
217
263
|
* @returns the neighborhood graph, node-capped.
|
|
218
264
|
*/
|
|
219
|
-
async function buildSubgraph(root, resolved, startId, depth) {
|
|
220
|
-
const { parsed, parsedById, linkResolver } = await loadVaultNotes(root, resolved);
|
|
265
|
+
async function buildSubgraph(root, resolved, startId, depth, onSkip) {
|
|
266
|
+
const { parsed, parsedById, linkResolver, skipped } = await loadVaultNotes(root, resolved, onSkip);
|
|
221
267
|
const startPath = await containedPathReal(root, startId);
|
|
222
268
|
const startNoteId = vaultRelativeId(root, startPath);
|
|
269
|
+
// A missing path, a directory, or a file outside the configured extensions
|
|
270
|
+
// resolves fine but has no indexed note; seeding the BFS with it would crash
|
|
271
|
+
// on the first `parsedById.get(id)!` lookup.
|
|
272
|
+
if (!parsedById.has(startNoteId)) {
|
|
273
|
+
const skipError = skipped.get(startNoteId);
|
|
274
|
+
if (skipError !== undefined) {
|
|
275
|
+
const code = skipError.code ?? 'unknown error';
|
|
276
|
+
throw new Error(`tool-memory-graph: cannot read note ${JSON.stringify(startNoteId)}: ${code}`);
|
|
277
|
+
}
|
|
278
|
+
throw new Error(`tool-memory-graph: note not found: ${startNoteId}`);
|
|
279
|
+
}
|
|
223
280
|
const idSetAll = new Set(parsed.map(p => p.id));
|
|
224
281
|
const adjacency = new Map();
|
|
225
282
|
for (const p of parsed) {
|
|
@@ -279,7 +336,8 @@ async function buildSubgraph(root, resolved, startId, depth) {
|
|
|
279
336
|
}
|
|
280
337
|
const filteredKeywordEdges = keywordEdges.filter(e => idSet.has(e.from) && idSet.has(e.to));
|
|
281
338
|
edges.push(...filteredKeywordEdges);
|
|
282
|
-
|
|
339
|
+
const budgeted = capEdges(edges, resolved.maxEdges);
|
|
340
|
+
return { nodes, edges: budgeted.edges, truncated, edgesTruncated: budgeted.truncated };
|
|
283
341
|
}
|
|
284
342
|
/**
|
|
285
343
|
* Register the `wiki_graph` tool on `ctx.tools`.
|
|
@@ -295,9 +353,11 @@ export function apply(ctx, config) {
|
|
|
295
353
|
const resolved = config;
|
|
296
354
|
assertPositiveInteger('maxDepth', resolved.maxDepth);
|
|
297
355
|
assertPositiveInteger('maxNodes', resolved.maxNodes);
|
|
356
|
+
assertPositiveInteger('maxEdges', resolved.maxEdges);
|
|
357
|
+
assertExtensions(resolved.extensions);
|
|
298
358
|
ctx.effect(() => ctx.tools.register(defineTool({
|
|
299
359
|
name: 'wiki_graph',
|
|
300
|
-
description: 'Return the link and keyword graph of the wiki vault: note ids, titles, and directed edges. Explicit `[[link]]` edges are type `link`; edges derived from shared significant terms are type `keyword` with `keywords` and `weight`. Without an id it returns the whole vault graph (node-capped); with an id it returns the subgraph reachable from that note within the given depth.',
|
|
360
|
+
description: 'Return the link and keyword graph of the wiki vault: note ids, titles, and directed edges. Explicit `[[link]]` edges are type `link`; edges derived from shared significant terms are type `keyword` with `keywords` and `weight`. Without an id it returns the whole vault graph (node-capped); with an id it returns the subgraph reachable from that note within the given depth. Nodes are capped at maxNodes (`truncated`) and edges at maxEdges (`edgesTruncated`).',
|
|
301
361
|
parameters: {
|
|
302
362
|
id: {
|
|
303
363
|
type: 'string',
|
|
@@ -316,9 +376,10 @@ export function apply(ctx, config) {
|
|
|
316
376
|
const vaultRoot = resolveMemoryVaultRoot(resolved.vaultRoot, exec);
|
|
317
377
|
const depth = args.depth ?? resolved.maxDepth;
|
|
318
378
|
assertPositiveInteger('depth', depth);
|
|
379
|
+
const onSkip = skipLogger(ctx, vaultRoot);
|
|
319
380
|
const graph = args.id === undefined
|
|
320
|
-
? await buildGraph(vaultRoot, resolved)
|
|
321
|
-
: await buildSubgraph(vaultRoot, resolved, args.id, depth);
|
|
381
|
+
? await buildGraph(vaultRoot, resolved, onSkip)
|
|
382
|
+
: await buildSubgraph(vaultRoot, resolved, args.id, depth, onSkip);
|
|
322
383
|
return graph;
|
|
323
384
|
},
|
|
324
385
|
})));
|
package/lib/types/index.d.ts
CHANGED
|
@@ -31,6 +31,8 @@ export interface Config {
|
|
|
31
31
|
maxDepth?: number;
|
|
32
32
|
/** Maximum nodes returned in one graph. */
|
|
33
33
|
maxNodes?: number;
|
|
34
|
+
/** Maximum edges returned in one graph. */
|
|
35
|
+
maxEdges?: number;
|
|
34
36
|
/** Descend into dot-directories besides the fixed exclusions while indexing. */
|
|
35
37
|
indexHiddenDirs?: boolean;
|
|
36
38
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jacklika/dsh-tool-memory-graph",
|
|
3
3
|
"description": "Model-facing link-graph tool over a Markdown memory vault: returns note nodes and [[link]] edges, or the subgraph reachable from one note.",
|
|
4
|
-
"version": "0.1.0-alpha.
|
|
4
|
+
"version": "0.1.0-alpha.7",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"license": "MIT",
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@deepseek-ai/schemastery": "3.18.4",
|
|
35
|
-
"@jacklika/dsh-tool-memory-filesystem": "^0.1.3-alpha.
|
|
35
|
+
"@jacklika/dsh-tool-memory-filesystem": "^0.1.3-alpha.10"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"@deepseek-ai/cordis": "4.0.4",
|