@prih/mcp-graph-memory 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +15 -0
- package/README.md +512 -0
- package/dist/api/index.js +473 -0
- package/dist/api/rest/code.js +78 -0
- package/dist/api/rest/docs.js +80 -0
- package/dist/api/rest/files.js +64 -0
- package/dist/api/rest/graph.js +56 -0
- package/dist/api/rest/index.js +117 -0
- package/dist/api/rest/knowledge.js +238 -0
- package/dist/api/rest/skills.js +284 -0
- package/dist/api/rest/tasks.js +272 -0
- package/dist/api/rest/tools.js +126 -0
- package/dist/api/rest/validation.js +191 -0
- package/dist/api/rest/websocket.js +65 -0
- package/dist/api/tools/code/get-file-symbols.js +30 -0
- package/dist/api/tools/code/get-symbol.js +22 -0
- package/dist/api/tools/code/list-files.js +18 -0
- package/dist/api/tools/code/search-code.js +27 -0
- package/dist/api/tools/code/search-files.js +22 -0
- package/dist/api/tools/context/get-context.js +19 -0
- package/dist/api/tools/docs/cross-references.js +76 -0
- package/dist/api/tools/docs/explain-symbol.js +55 -0
- package/dist/api/tools/docs/find-examples.js +52 -0
- package/dist/api/tools/docs/get-node.js +24 -0
- package/dist/api/tools/docs/get-toc.js +22 -0
- package/dist/api/tools/docs/list-snippets.js +46 -0
- package/dist/api/tools/docs/list-topics.js +18 -0
- package/dist/api/tools/docs/search-files.js +22 -0
- package/dist/api/tools/docs/search-snippets.js +43 -0
- package/dist/api/tools/docs/search.js +27 -0
- package/dist/api/tools/file-index/get-file-info.js +21 -0
- package/dist/api/tools/file-index/list-all-files.js +28 -0
- package/dist/api/tools/file-index/search-all-files.js +24 -0
- package/dist/api/tools/knowledge/add-attachment.js +31 -0
- package/dist/api/tools/knowledge/create-note.js +20 -0
- package/dist/api/tools/knowledge/create-relation.js +29 -0
- package/dist/api/tools/knowledge/delete-note.js +19 -0
- package/dist/api/tools/knowledge/delete-relation.js +23 -0
- package/dist/api/tools/knowledge/find-linked-notes.js +25 -0
- package/dist/api/tools/knowledge/get-note.js +20 -0
- package/dist/api/tools/knowledge/list-notes.js +18 -0
- package/dist/api/tools/knowledge/list-relations.js +17 -0
- package/dist/api/tools/knowledge/remove-attachment.js +19 -0
- package/dist/api/tools/knowledge/search-notes.js +25 -0
- package/dist/api/tools/knowledge/update-note.js +34 -0
- package/dist/api/tools/skills/add-attachment.js +31 -0
- package/dist/api/tools/skills/bump-usage.js +19 -0
- package/dist/api/tools/skills/create-skill-link.js +25 -0
- package/dist/api/tools/skills/create-skill.js +26 -0
- package/dist/api/tools/skills/delete-skill-link.js +23 -0
- package/dist/api/tools/skills/delete-skill.js +20 -0
- package/dist/api/tools/skills/find-linked-skills.js +25 -0
- package/dist/api/tools/skills/get-skill.js +21 -0
- package/dist/api/tools/skills/link-skill.js +23 -0
- package/dist/api/tools/skills/list-skills.js +20 -0
- package/dist/api/tools/skills/recall-skills.js +18 -0
- package/dist/api/tools/skills/remove-attachment.js +19 -0
- package/dist/api/tools/skills/search-skills.js +25 -0
- package/dist/api/tools/skills/update-skill.js +58 -0
- package/dist/api/tools/tasks/add-attachment.js +31 -0
- package/dist/api/tools/tasks/create-task-link.js +25 -0
- package/dist/api/tools/tasks/create-task.js +25 -0
- package/dist/api/tools/tasks/delete-task-link.js +23 -0
- package/dist/api/tools/tasks/delete-task.js +20 -0
- package/dist/api/tools/tasks/find-linked-tasks.js +25 -0
- package/dist/api/tools/tasks/get-task.js +20 -0
- package/dist/api/tools/tasks/link-task.js +23 -0
- package/dist/api/tools/tasks/list-tasks.js +24 -0
- package/dist/api/tools/tasks/move-task.js +38 -0
- package/dist/api/tools/tasks/remove-attachment.js +19 -0
- package/dist/api/tools/tasks/search-tasks.js +25 -0
- package/dist/api/tools/tasks/update-task.js +55 -0
- package/dist/cli/index.js +451 -0
- package/dist/cli/indexer.js +277 -0
- package/dist/graphs/attachment-types.js +74 -0
- package/dist/graphs/code-types.js +10 -0
- package/dist/graphs/code.js +172 -0
- package/dist/graphs/docs.js +198 -0
- package/dist/graphs/file-index-types.js +10 -0
- package/dist/graphs/file-index.js +310 -0
- package/dist/graphs/file-lang.js +119 -0
- package/dist/graphs/knowledge-types.js +32 -0
- package/dist/graphs/knowledge.js +764 -0
- package/dist/graphs/manager-types.js +87 -0
- package/dist/graphs/skill-types.js +10 -0
- package/dist/graphs/skill.js +1013 -0
- package/dist/graphs/task-types.js +17 -0
- package/dist/graphs/task.js +960 -0
- package/dist/lib/embedder.js +101 -0
- package/dist/lib/events-log.js +400 -0
- package/dist/lib/file-import.js +327 -0
- package/dist/lib/file-mirror.js +446 -0
- package/dist/lib/frontmatter.js +17 -0
- package/dist/lib/mirror-watcher.js +637 -0
- package/dist/lib/multi-config.js +254 -0
- package/dist/lib/parsers/code.js +246 -0
- package/dist/lib/parsers/codeblock.js +66 -0
- package/dist/lib/parsers/docs.js +196 -0
- package/dist/lib/project-manager.js +418 -0
- package/dist/lib/promise-queue.js +22 -0
- package/dist/lib/search/bm25.js +167 -0
- package/dist/lib/search/code.js +103 -0
- package/dist/lib/search/docs.js +108 -0
- package/dist/lib/search/file-index.js +31 -0
- package/dist/lib/search/files.js +61 -0
- package/dist/lib/search/knowledge.js +101 -0
- package/dist/lib/search/skills.js +104 -0
- package/dist/lib/search/tasks.js +103 -0
- package/dist/lib/watcher.js +67 -0
- package/package.json +83 -0
- package/ui/README.md +54 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.parseNoteFile = parseNoteFile;
|
|
37
|
+
exports.parseTaskFile = parseTaskFile;
|
|
38
|
+
exports.parseSkillFile = parseSkillFile;
|
|
39
|
+
exports.parseNoteDir = parseNoteDir;
|
|
40
|
+
exports.parseTaskDir = parseTaskDir;
|
|
41
|
+
exports.parseSkillDir = parseSkillDir;
|
|
42
|
+
exports.diffRelations = diffRelations;
|
|
43
|
+
const fs = __importStar(require("fs"));
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
const frontmatter_1 = require("./frontmatter");
|
|
46
|
+
const attachment_types_1 = require("../graphs/attachment-types");
|
|
47
|
+
const events_log_1 = require("./events-log");
|
|
48
|
+
const VALID_STATUSES = ['backlog', 'todo', 'in_progress', 'review', 'done', 'cancelled'];
|
|
49
|
+
const VALID_PRIORITIES = ['critical', 'high', 'medium', 'low'];
|
|
50
|
+
const VALID_SOURCES = ['user', 'learned'];
|
|
51
|
+
function isoToMs(value) {
|
|
52
|
+
if (value == null)
|
|
53
|
+
return null;
|
|
54
|
+
const d = new Date(value);
|
|
55
|
+
return isNaN(d.getTime()) ? null : d.getTime();
|
|
56
|
+
}
|
|
57
|
+
function parseTags(raw) {
|
|
58
|
+
if (Array.isArray(raw))
|
|
59
|
+
return raw.filter(t => typeof t === 'string');
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
function parseRelations(raw) {
|
|
63
|
+
if (!Array.isArray(raw))
|
|
64
|
+
return [];
|
|
65
|
+
return raw
|
|
66
|
+
.filter(r => r && typeof r === 'object' && typeof r.to === 'string' && typeof r.kind === 'string')
|
|
67
|
+
.map(r => {
|
|
68
|
+
const entry = { to: r.to, kind: r.kind };
|
|
69
|
+
if (typeof r.graph === 'string')
|
|
70
|
+
entry.graph = r.graph;
|
|
71
|
+
return entry;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function parseAuthorString(raw) {
|
|
75
|
+
if (typeof raw === 'string' && raw.trim())
|
|
76
|
+
return raw.trim();
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
function extractTitleAndContent(body) {
|
|
80
|
+
const lines = body.split('\n');
|
|
81
|
+
const headingMatch = lines[0]?.match(/^#\s+(.+)/);
|
|
82
|
+
if (!headingMatch)
|
|
83
|
+
return { title: '', content: body.trim() };
|
|
84
|
+
const title = headingMatch[1].trim();
|
|
85
|
+
// Skip heading line and the blank line after it
|
|
86
|
+
let start = 1;
|
|
87
|
+
if (lines[start] === '')
|
|
88
|
+
start++;
|
|
89
|
+
const content = lines.slice(start).join('\n').trim();
|
|
90
|
+
return { title, content };
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Determine the entity ID from a file path.
|
|
94
|
+
* New format: .notes/{id}/note.md or .tasks/{id}/task.md or .skills/{id}/skill.md → id from dirname
|
|
95
|
+
* Legacy format: .notes/{id}.md or .tasks/{id}.md → id from basename
|
|
96
|
+
*/
|
|
97
|
+
function extractId(filePath) {
|
|
98
|
+
const basename = path.basename(filePath, '.md');
|
|
99
|
+
if (basename === 'note' || basename === 'task' || basename === 'skill') {
|
|
100
|
+
return path.basename(path.dirname(filePath));
|
|
101
|
+
}
|
|
102
|
+
return basename;
|
|
103
|
+
}
|
|
104
|
+
function parseNoteFile(filePath) {
|
|
105
|
+
try {
|
|
106
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
107
|
+
const { frontmatter: fm, body } = (0, frontmatter_1.parseMarkdown)(raw);
|
|
108
|
+
const { title, content } = extractTitleAndContent(body);
|
|
109
|
+
const id = extractId(filePath);
|
|
110
|
+
if (!title && !id)
|
|
111
|
+
return null;
|
|
112
|
+
const attachments = (0, attachment_types_1.scanAttachments)(path.dirname(filePath));
|
|
113
|
+
return {
|
|
114
|
+
id,
|
|
115
|
+
title: title || id,
|
|
116
|
+
content,
|
|
117
|
+
tags: parseTags(fm.tags),
|
|
118
|
+
createdAt: isoToMs(fm.createdAt),
|
|
119
|
+
updatedAt: isoToMs(fm.updatedAt),
|
|
120
|
+
version: typeof fm.version === 'number' ? fm.version : null,
|
|
121
|
+
createdBy: parseAuthorString(fm.createdBy),
|
|
122
|
+
updatedBy: parseAuthorString(fm.updatedBy),
|
|
123
|
+
relations: parseRelations(fm.relations),
|
|
124
|
+
attachments,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
process.stderr.write(`[file-import] failed to parse note ${filePath}: ${err}\n`);
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function parseTaskFile(filePath) {
|
|
133
|
+
try {
|
|
134
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
135
|
+
const { frontmatter: fm, body } = (0, frontmatter_1.parseMarkdown)(raw);
|
|
136
|
+
const { title, content: description } = extractTitleAndContent(body);
|
|
137
|
+
const id = extractId(filePath);
|
|
138
|
+
if (!title && !id)
|
|
139
|
+
return null;
|
|
140
|
+
const status = VALID_STATUSES.includes(fm.status)
|
|
141
|
+
? fm.status : 'backlog';
|
|
142
|
+
const priority = VALID_PRIORITIES.includes(fm.priority)
|
|
143
|
+
? fm.priority : 'medium';
|
|
144
|
+
const attachments = (0, attachment_types_1.scanAttachments)(path.dirname(filePath));
|
|
145
|
+
return {
|
|
146
|
+
id,
|
|
147
|
+
title: title || id,
|
|
148
|
+
description,
|
|
149
|
+
status,
|
|
150
|
+
priority,
|
|
151
|
+
tags: parseTags(fm.tags),
|
|
152
|
+
dueDate: isoToMs(fm.dueDate),
|
|
153
|
+
estimate: typeof fm.estimate === 'number' ? fm.estimate : null,
|
|
154
|
+
completedAt: isoToMs(fm.completedAt),
|
|
155
|
+
createdAt: isoToMs(fm.createdAt),
|
|
156
|
+
updatedAt: isoToMs(fm.updatedAt),
|
|
157
|
+
version: typeof fm.version === 'number' ? fm.version : null,
|
|
158
|
+
createdBy: parseAuthorString(fm.createdBy),
|
|
159
|
+
updatedBy: parseAuthorString(fm.updatedBy),
|
|
160
|
+
relations: parseRelations(fm.relations),
|
|
161
|
+
attachments,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
catch (err) {
|
|
165
|
+
process.stderr.write(`[file-import] failed to parse task ${filePath}: ${err}\n`);
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function parseStringArray(raw) {
|
|
170
|
+
if (Array.isArray(raw))
|
|
171
|
+
return raw.filter(t => typeof t === 'string');
|
|
172
|
+
return [];
|
|
173
|
+
}
|
|
174
|
+
function extractDescriptionAndSteps(body) {
|
|
175
|
+
const lines = body.split('\n');
|
|
176
|
+
const headingMatch = lines[0]?.match(/^#\s+(.+)/);
|
|
177
|
+
if (!headingMatch)
|
|
178
|
+
return { title: '', description: body.trim(), steps: [] };
|
|
179
|
+
const title = headingMatch[1].trim();
|
|
180
|
+
let start = 1;
|
|
181
|
+
if (lines[start] === '')
|
|
182
|
+
start++;
|
|
183
|
+
// Find ## Steps section
|
|
184
|
+
const stepsIdx = lines.findIndex((l, i) => i >= start && /^##\s+Steps/i.test(l));
|
|
185
|
+
if (stepsIdx === -1) {
|
|
186
|
+
return { title, description: lines.slice(start).join('\n').trim(), steps: [] };
|
|
187
|
+
}
|
|
188
|
+
const description = lines.slice(start, stepsIdx).join('\n').trim();
|
|
189
|
+
// Parse numbered list after ## Steps
|
|
190
|
+
const steps = [];
|
|
191
|
+
for (let i = stepsIdx + 1; i < lines.length; i++) {
|
|
192
|
+
const stepMatch = lines[i].match(/^\d+\.\s+(.+)/);
|
|
193
|
+
if (stepMatch) {
|
|
194
|
+
steps.push(stepMatch[1].trim());
|
|
195
|
+
}
|
|
196
|
+
else if (lines[i].trim() === '') {
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
else if (/^##\s+/.test(lines[i])) {
|
|
200
|
+
break; // another heading section
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return { title, description, steps };
|
|
204
|
+
}
|
|
205
|
+
function parseSkillFile(filePath) {
|
|
206
|
+
try {
|
|
207
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
208
|
+
const { frontmatter: fm, body } = (0, frontmatter_1.parseMarkdown)(raw);
|
|
209
|
+
const { title, description, steps } = extractDescriptionAndSteps(body);
|
|
210
|
+
const id = extractId(filePath);
|
|
211
|
+
if (!title && !id)
|
|
212
|
+
return null;
|
|
213
|
+
const source = VALID_SOURCES.includes(fm.source)
|
|
214
|
+
? fm.source : 'user';
|
|
215
|
+
const confidence = typeof fm.confidence === 'number' ? Math.max(0, Math.min(1, fm.confidence)) : 1;
|
|
216
|
+
const attachments = (0, attachment_types_1.scanAttachments)(path.dirname(filePath));
|
|
217
|
+
return {
|
|
218
|
+
id,
|
|
219
|
+
title: title || id,
|
|
220
|
+
description,
|
|
221
|
+
steps,
|
|
222
|
+
triggers: parseStringArray(fm.triggers),
|
|
223
|
+
inputHints: parseStringArray(fm.inputHints),
|
|
224
|
+
filePatterns: parseStringArray(fm.filePatterns),
|
|
225
|
+
tags: parseTags(fm.tags),
|
|
226
|
+
source,
|
|
227
|
+
confidence,
|
|
228
|
+
usageCount: typeof fm.usageCount === 'number' ? fm.usageCount : null,
|
|
229
|
+
lastUsedAt: isoToMs(fm.lastUsedAt),
|
|
230
|
+
createdAt: isoToMs(fm.createdAt),
|
|
231
|
+
updatedAt: isoToMs(fm.updatedAt),
|
|
232
|
+
version: typeof fm.version === 'number' ? fm.version : null,
|
|
233
|
+
createdBy: parseAuthorString(fm.createdBy),
|
|
234
|
+
updatedBy: parseAuthorString(fm.updatedBy),
|
|
235
|
+
relations: parseRelations(fm.relations),
|
|
236
|
+
attachments,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
catch (err) {
|
|
240
|
+
process.stderr.write(`[file-import] failed to parse skill ${filePath}: ${err}\n`);
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
// ---------------------------------------------------------------------------
|
|
245
|
+
// Event-sourced directory parsers (new format)
|
|
246
|
+
// ---------------------------------------------------------------------------
|
|
247
|
+
/** Parse a note from its entity directory (events.jsonl + content.md). */
|
|
248
|
+
function parseNoteDir(dirPath) {
|
|
249
|
+
try {
|
|
250
|
+
const eventsPath = path.join(dirPath, 'events.jsonl');
|
|
251
|
+
if (!fs.existsSync(eventsPath))
|
|
252
|
+
return null;
|
|
253
|
+
const events = (0, events_log_1.readEvents)(eventsPath);
|
|
254
|
+
const contentPath = path.join(dirPath, 'content.md');
|
|
255
|
+
const content = fs.existsSync(contentPath) ? fs.readFileSync(contentPath, 'utf-8') : '';
|
|
256
|
+
const parsed = (0, events_log_1.replayNoteEvents)(events, content);
|
|
257
|
+
if (!parsed)
|
|
258
|
+
return null;
|
|
259
|
+
// Refresh attachments from disk (overrides event-derived list with accurate metadata)
|
|
260
|
+
parsed.attachments = (0, attachment_types_1.scanAttachments)(dirPath);
|
|
261
|
+
return parsed;
|
|
262
|
+
}
|
|
263
|
+
catch (err) {
|
|
264
|
+
process.stderr.write(`[file-import] failed to parse note dir ${dirPath}: ${err}\n`);
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
/** Parse a task from its entity directory (events.jsonl + description.md). */
|
|
269
|
+
function parseTaskDir(dirPath) {
|
|
270
|
+
try {
|
|
271
|
+
const eventsPath = path.join(dirPath, 'events.jsonl');
|
|
272
|
+
if (!fs.existsSync(eventsPath))
|
|
273
|
+
return null;
|
|
274
|
+
const events = (0, events_log_1.readEvents)(eventsPath);
|
|
275
|
+
const descPath = path.join(dirPath, 'description.md');
|
|
276
|
+
const description = fs.existsSync(descPath) ? fs.readFileSync(descPath, 'utf-8') : '';
|
|
277
|
+
const parsed = (0, events_log_1.replayTaskEvents)(events, description);
|
|
278
|
+
if (!parsed)
|
|
279
|
+
return null;
|
|
280
|
+
parsed.attachments = (0, attachment_types_1.scanAttachments)(dirPath);
|
|
281
|
+
return parsed;
|
|
282
|
+
}
|
|
283
|
+
catch (err) {
|
|
284
|
+
process.stderr.write(`[file-import] failed to parse task dir ${dirPath}: ${err}\n`);
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
/** Parse a skill from its entity directory (events.jsonl + description.md). */
|
|
289
|
+
function parseSkillDir(dirPath) {
|
|
290
|
+
try {
|
|
291
|
+
const eventsPath = path.join(dirPath, 'events.jsonl');
|
|
292
|
+
if (!fs.existsSync(eventsPath))
|
|
293
|
+
return null;
|
|
294
|
+
const events = (0, events_log_1.readEvents)(eventsPath);
|
|
295
|
+
const descPath = path.join(dirPath, 'description.md');
|
|
296
|
+
const description = fs.existsSync(descPath) ? fs.readFileSync(descPath, 'utf-8') : '';
|
|
297
|
+
const parsed = (0, events_log_1.replaySkillEvents)(events, description);
|
|
298
|
+
if (!parsed)
|
|
299
|
+
return null;
|
|
300
|
+
parsed.attachments = (0, attachment_types_1.scanAttachments)(dirPath);
|
|
301
|
+
return parsed;
|
|
302
|
+
}
|
|
303
|
+
catch (err) {
|
|
304
|
+
process.stderr.write(`[file-import] failed to parse skill dir ${dirPath}: ${err}\n`);
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
function relationKey(r) {
|
|
309
|
+
return `${r.to}:${r.kind}:${r.graph ?? ''}`;
|
|
310
|
+
}
|
|
311
|
+
function diffRelations(current, desired) {
|
|
312
|
+
const currentKeys = new Set(current.map(relationKey));
|
|
313
|
+
const desiredKeys = new Set(desired.map(relationKey));
|
|
314
|
+
const desiredMap = new Map(desired.map(r => [relationKey(r), r]));
|
|
315
|
+
const currentMap = new Map(current.map(r => [relationKey(r), r]));
|
|
316
|
+
const toAdd = [];
|
|
317
|
+
const toRemove = [];
|
|
318
|
+
for (const key of desiredKeys) {
|
|
319
|
+
if (!currentKeys.has(key))
|
|
320
|
+
toAdd.push(desiredMap.get(key));
|
|
321
|
+
}
|
|
322
|
+
for (const key of currentKeys) {
|
|
323
|
+
if (!desiredKeys.has(key))
|
|
324
|
+
toRemove.push(currentMap.get(key));
|
|
325
|
+
}
|
|
326
|
+
return { toAdd, toRemove };
|
|
327
|
+
}
|