@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,446 @@
|
|
|
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.mirrorNoteCreate = mirrorNoteCreate;
|
|
37
|
+
exports.mirrorNoteUpdate = mirrorNoteUpdate;
|
|
38
|
+
exports.mirrorTaskCreate = mirrorTaskCreate;
|
|
39
|
+
exports.mirrorTaskUpdate = mirrorTaskUpdate;
|
|
40
|
+
exports.mirrorSkillCreate = mirrorSkillCreate;
|
|
41
|
+
exports.mirrorSkillUpdate = mirrorSkillUpdate;
|
|
42
|
+
exports.mirrorNoteRelation = mirrorNoteRelation;
|
|
43
|
+
exports.mirrorTaskRelation = mirrorTaskRelation;
|
|
44
|
+
exports.mirrorSkillRelation = mirrorSkillRelation;
|
|
45
|
+
exports.mirrorAttachmentEvent = mirrorAttachmentEvent;
|
|
46
|
+
exports.deleteMirrorDir = deleteMirrorDir;
|
|
47
|
+
exports.sanitizeFilename = sanitizeFilename;
|
|
48
|
+
exports.writeAttachment = writeAttachment;
|
|
49
|
+
exports.deleteAttachment = deleteAttachment;
|
|
50
|
+
exports.getAttachmentPath = getAttachmentPath;
|
|
51
|
+
const fs = __importStar(require("fs"));
|
|
52
|
+
const path = __importStar(require("path"));
|
|
53
|
+
const frontmatter_1 = require("./frontmatter");
|
|
54
|
+
const events_log_1 = require("./events-log");
|
|
55
|
+
function tsToIso(ts) {
|
|
56
|
+
if (ts == null || ts === 0)
|
|
57
|
+
return null;
|
|
58
|
+
return new Date(ts).toISOString();
|
|
59
|
+
}
|
|
60
|
+
function buildOutgoingRelations(entityId, relations) {
|
|
61
|
+
return relations
|
|
62
|
+
.filter(r => r.fromId === entityId)
|
|
63
|
+
.map(r => {
|
|
64
|
+
const entry = { to: r.toId, kind: r.kind };
|
|
65
|
+
if (r.targetGraph)
|
|
66
|
+
entry.graph = r.targetGraph;
|
|
67
|
+
return entry;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/** Append a 'created' event + write content.md + regenerate note.md snapshot. */
|
|
71
|
+
function mirrorNoteCreate(notesDir, noteId, attrs, relations) {
|
|
72
|
+
try {
|
|
73
|
+
const entityDir = path.join(notesDir, noteId);
|
|
74
|
+
fs.mkdirSync(entityDir, { recursive: true });
|
|
75
|
+
const eventsPath = path.join(entityDir, 'events.jsonl');
|
|
76
|
+
if (!fs.existsSync(eventsPath)) {
|
|
77
|
+
const event = {
|
|
78
|
+
op: 'created',
|
|
79
|
+
id: noteId,
|
|
80
|
+
title: attrs.title,
|
|
81
|
+
tags: attrs.tags,
|
|
82
|
+
createdAt: attrs.createdAt,
|
|
83
|
+
};
|
|
84
|
+
if (attrs.createdBy)
|
|
85
|
+
event.createdBy = attrs.createdBy;
|
|
86
|
+
(0, events_log_1.appendEvent)(eventsPath, event);
|
|
87
|
+
}
|
|
88
|
+
fs.writeFileSync(path.join(entityDir, 'content.md'), attrs.content, 'utf-8');
|
|
89
|
+
_regenerateNoteSnapshot(notesDir, noteId, attrs, relations);
|
|
90
|
+
(0, events_log_1.ensureGitignore)(notesDir, '*/note.md');
|
|
91
|
+
(0, events_log_1.ensureGitattributes)(notesDir);
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
process.stderr.write(`[file-mirror] failed to mirror note create ${noteId}: ${err}\n`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/** Append an 'update' event + (if content changed) write content.md + regenerate note.md. */
|
|
98
|
+
function mirrorNoteUpdate(notesDir, noteId, patch, attrs, relations) {
|
|
99
|
+
try {
|
|
100
|
+
const entityDir = path.join(notesDir, noteId);
|
|
101
|
+
fs.mkdirSync(entityDir, { recursive: true });
|
|
102
|
+
const eventsPath = path.join(entityDir, 'events.jsonl');
|
|
103
|
+
const delta = { op: 'update' };
|
|
104
|
+
if (patch.title !== undefined)
|
|
105
|
+
delta.title = patch.title;
|
|
106
|
+
if (patch.tags !== undefined)
|
|
107
|
+
delta.tags = patch.tags;
|
|
108
|
+
if (patch.by !== undefined)
|
|
109
|
+
delta.by = patch.by;
|
|
110
|
+
else if (attrs.updatedBy)
|
|
111
|
+
delta.by = attrs.updatedBy;
|
|
112
|
+
if (Object.keys(delta).length > 1)
|
|
113
|
+
(0, events_log_1.appendEvent)(eventsPath, delta);
|
|
114
|
+
if (patch.content !== undefined) {
|
|
115
|
+
fs.writeFileSync(path.join(entityDir, 'content.md'), patch.content, 'utf-8');
|
|
116
|
+
}
|
|
117
|
+
_regenerateNoteSnapshot(notesDir, noteId, attrs, relations);
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
process.stderr.write(`[file-mirror] failed to mirror note update ${noteId}: ${err}\n`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function _regenerateNoteSnapshot(notesDir, noteId, attrs, relations) {
|
|
124
|
+
const outgoing = buildOutgoingRelations(noteId, relations);
|
|
125
|
+
const fm = {
|
|
126
|
+
id: noteId,
|
|
127
|
+
tags: attrs.tags,
|
|
128
|
+
createdAt: tsToIso(attrs.createdAt),
|
|
129
|
+
updatedAt: tsToIso(attrs.updatedAt),
|
|
130
|
+
version: attrs.version,
|
|
131
|
+
};
|
|
132
|
+
if (attrs.createdBy)
|
|
133
|
+
fm.createdBy = attrs.createdBy;
|
|
134
|
+
if (attrs.updatedBy)
|
|
135
|
+
fm.updatedBy = attrs.updatedBy;
|
|
136
|
+
if (outgoing.length > 0)
|
|
137
|
+
fm.relations = outgoing;
|
|
138
|
+
const body = `# ${attrs.title}\n\n${attrs.content}`;
|
|
139
|
+
const entityDir = path.join(notesDir, noteId);
|
|
140
|
+
fs.mkdirSync(entityDir, { recursive: true });
|
|
141
|
+
fs.writeFileSync(path.join(entityDir, 'note.md'), (0, frontmatter_1.serializeMarkdown)(fm, body));
|
|
142
|
+
}
|
|
143
|
+
/** Append a 'created' event + write description.md + regenerate task.md snapshot. */
|
|
144
|
+
function mirrorTaskCreate(tasksDir, taskId, attrs, relations) {
|
|
145
|
+
try {
|
|
146
|
+
const entityDir = path.join(tasksDir, taskId);
|
|
147
|
+
fs.mkdirSync(entityDir, { recursive: true });
|
|
148
|
+
const eventsPath = path.join(entityDir, 'events.jsonl');
|
|
149
|
+
if (!fs.existsSync(eventsPath)) {
|
|
150
|
+
const event = {
|
|
151
|
+
op: 'created',
|
|
152
|
+
id: taskId,
|
|
153
|
+
title: attrs.title,
|
|
154
|
+
status: attrs.status,
|
|
155
|
+
priority: attrs.priority,
|
|
156
|
+
tags: attrs.tags,
|
|
157
|
+
dueDate: attrs.dueDate,
|
|
158
|
+
estimate: attrs.estimate,
|
|
159
|
+
completedAt: attrs.completedAt,
|
|
160
|
+
createdAt: attrs.createdAt,
|
|
161
|
+
};
|
|
162
|
+
if (attrs.createdBy)
|
|
163
|
+
event.createdBy = attrs.createdBy;
|
|
164
|
+
(0, events_log_1.appendEvent)(eventsPath, event);
|
|
165
|
+
}
|
|
166
|
+
fs.writeFileSync(path.join(entityDir, 'description.md'), attrs.description, 'utf-8');
|
|
167
|
+
_regenerateTaskSnapshot(tasksDir, taskId, attrs, relations);
|
|
168
|
+
(0, events_log_1.ensureGitignore)(tasksDir, '*/task.md');
|
|
169
|
+
(0, events_log_1.ensureGitattributes)(tasksDir);
|
|
170
|
+
}
|
|
171
|
+
catch (err) {
|
|
172
|
+
process.stderr.write(`[file-mirror] failed to mirror task create ${taskId}: ${err}\n`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/** Append an 'update' event + (if description changed) write description.md + regenerate task.md. */
|
|
176
|
+
function mirrorTaskUpdate(tasksDir, taskId, patch, attrs, relations) {
|
|
177
|
+
try {
|
|
178
|
+
const entityDir = path.join(tasksDir, taskId);
|
|
179
|
+
fs.mkdirSync(entityDir, { recursive: true });
|
|
180
|
+
const eventsPath = path.join(entityDir, 'events.jsonl');
|
|
181
|
+
const delta = { op: 'update' };
|
|
182
|
+
if (patch.title !== undefined)
|
|
183
|
+
delta.title = patch.title;
|
|
184
|
+
if (patch.status !== undefined)
|
|
185
|
+
delta.status = patch.status;
|
|
186
|
+
if (patch.priority !== undefined)
|
|
187
|
+
delta.priority = patch.priority;
|
|
188
|
+
if (patch.tags !== undefined)
|
|
189
|
+
delta.tags = patch.tags;
|
|
190
|
+
if ('dueDate' in patch)
|
|
191
|
+
delta.dueDate = patch.dueDate;
|
|
192
|
+
if ('estimate' in patch)
|
|
193
|
+
delta.estimate = patch.estimate;
|
|
194
|
+
if ('completedAt' in patch)
|
|
195
|
+
delta.completedAt = patch.completedAt;
|
|
196
|
+
if (patch.by !== undefined)
|
|
197
|
+
delta.by = patch.by;
|
|
198
|
+
else if (attrs.updatedBy)
|
|
199
|
+
delta.by = attrs.updatedBy;
|
|
200
|
+
if (Object.keys(delta).length > 1)
|
|
201
|
+
(0, events_log_1.appendEvent)(eventsPath, delta);
|
|
202
|
+
if (patch.description !== undefined) {
|
|
203
|
+
fs.writeFileSync(path.join(entityDir, 'description.md'), patch.description, 'utf-8');
|
|
204
|
+
}
|
|
205
|
+
_regenerateTaskSnapshot(tasksDir, taskId, attrs, relations);
|
|
206
|
+
}
|
|
207
|
+
catch (err) {
|
|
208
|
+
process.stderr.write(`[file-mirror] failed to mirror task update ${taskId}: ${err}\n`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
function _regenerateTaskSnapshot(tasksDir, taskId, attrs, relations) {
|
|
212
|
+
const outgoing = buildOutgoingRelations(taskId, relations);
|
|
213
|
+
const fm = {
|
|
214
|
+
id: taskId,
|
|
215
|
+
status: attrs.status,
|
|
216
|
+
priority: attrs.priority,
|
|
217
|
+
tags: attrs.tags,
|
|
218
|
+
dueDate: tsToIso(attrs.dueDate),
|
|
219
|
+
estimate: attrs.estimate,
|
|
220
|
+
completedAt: tsToIso(attrs.completedAt),
|
|
221
|
+
createdAt: tsToIso(attrs.createdAt),
|
|
222
|
+
updatedAt: tsToIso(attrs.updatedAt),
|
|
223
|
+
version: attrs.version,
|
|
224
|
+
};
|
|
225
|
+
if (attrs.createdBy)
|
|
226
|
+
fm.createdBy = attrs.createdBy;
|
|
227
|
+
if (attrs.updatedBy)
|
|
228
|
+
fm.updatedBy = attrs.updatedBy;
|
|
229
|
+
if (outgoing.length > 0)
|
|
230
|
+
fm.relations = outgoing;
|
|
231
|
+
const body = `# ${attrs.title}\n\n${attrs.description}`;
|
|
232
|
+
const entityDir = path.join(tasksDir, taskId);
|
|
233
|
+
fs.mkdirSync(entityDir, { recursive: true });
|
|
234
|
+
fs.writeFileSync(path.join(entityDir, 'task.md'), (0, frontmatter_1.serializeMarkdown)(fm, body));
|
|
235
|
+
}
|
|
236
|
+
/** Append a 'created' event + write description.md + regenerate skill.md snapshot. */
|
|
237
|
+
function mirrorSkillCreate(skillsDir, skillId, attrs, relations) {
|
|
238
|
+
try {
|
|
239
|
+
const entityDir = path.join(skillsDir, skillId);
|
|
240
|
+
fs.mkdirSync(entityDir, { recursive: true });
|
|
241
|
+
const eventsPath = path.join(entityDir, 'events.jsonl');
|
|
242
|
+
if (!fs.existsSync(eventsPath)) {
|
|
243
|
+
const event = {
|
|
244
|
+
op: 'created',
|
|
245
|
+
id: skillId,
|
|
246
|
+
title: attrs.title,
|
|
247
|
+
tags: attrs.tags,
|
|
248
|
+
steps: attrs.steps,
|
|
249
|
+
triggers: attrs.triggers,
|
|
250
|
+
inputHints: attrs.inputHints,
|
|
251
|
+
filePatterns: attrs.filePatterns,
|
|
252
|
+
source: attrs.source,
|
|
253
|
+
confidence: attrs.confidence,
|
|
254
|
+
usageCount: attrs.usageCount,
|
|
255
|
+
lastUsedAt: attrs.lastUsedAt,
|
|
256
|
+
createdAt: attrs.createdAt,
|
|
257
|
+
};
|
|
258
|
+
if (attrs.createdBy)
|
|
259
|
+
event.createdBy = attrs.createdBy;
|
|
260
|
+
(0, events_log_1.appendEvent)(eventsPath, event);
|
|
261
|
+
}
|
|
262
|
+
fs.writeFileSync(path.join(entityDir, 'description.md'), attrs.description, 'utf-8');
|
|
263
|
+
_regenerateSkillSnapshot(skillsDir, skillId, attrs, relations);
|
|
264
|
+
(0, events_log_1.ensureGitignore)(skillsDir, '*/skill.md');
|
|
265
|
+
(0, events_log_1.ensureGitattributes)(skillsDir);
|
|
266
|
+
}
|
|
267
|
+
catch (err) {
|
|
268
|
+
process.stderr.write(`[file-mirror] failed to mirror skill create ${skillId}: ${err}\n`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
/** Append an 'update' event + (if description changed) write description.md + regenerate skill.md. */
|
|
272
|
+
function mirrorSkillUpdate(skillsDir, skillId, patch, attrs, relations) {
|
|
273
|
+
try {
|
|
274
|
+
const entityDir = path.join(skillsDir, skillId);
|
|
275
|
+
fs.mkdirSync(entityDir, { recursive: true });
|
|
276
|
+
const eventsPath = path.join(entityDir, 'events.jsonl');
|
|
277
|
+
const delta = { op: 'update' };
|
|
278
|
+
if (patch.title !== undefined)
|
|
279
|
+
delta.title = patch.title;
|
|
280
|
+
if (patch.tags !== undefined)
|
|
281
|
+
delta.tags = patch.tags;
|
|
282
|
+
if (patch.steps !== undefined)
|
|
283
|
+
delta.steps = patch.steps;
|
|
284
|
+
if (patch.triggers !== undefined)
|
|
285
|
+
delta.triggers = patch.triggers;
|
|
286
|
+
if (patch.inputHints !== undefined)
|
|
287
|
+
delta.inputHints = patch.inputHints;
|
|
288
|
+
if (patch.filePatterns !== undefined)
|
|
289
|
+
delta.filePatterns = patch.filePatterns;
|
|
290
|
+
if (patch.source !== undefined)
|
|
291
|
+
delta.source = patch.source;
|
|
292
|
+
if (patch.confidence !== undefined)
|
|
293
|
+
delta.confidence = patch.confidence;
|
|
294
|
+
if (patch.usageCount !== undefined)
|
|
295
|
+
delta.usageCount = patch.usageCount;
|
|
296
|
+
if ('lastUsedAt' in patch)
|
|
297
|
+
delta.lastUsedAt = patch.lastUsedAt;
|
|
298
|
+
if (patch.by !== undefined)
|
|
299
|
+
delta.by = patch.by;
|
|
300
|
+
else if (attrs.updatedBy)
|
|
301
|
+
delta.by = attrs.updatedBy;
|
|
302
|
+
if (Object.keys(delta).length > 1)
|
|
303
|
+
(0, events_log_1.appendEvent)(eventsPath, delta);
|
|
304
|
+
if (patch.description !== undefined) {
|
|
305
|
+
fs.writeFileSync(path.join(entityDir, 'description.md'), patch.description, 'utf-8');
|
|
306
|
+
}
|
|
307
|
+
_regenerateSkillSnapshot(skillsDir, skillId, attrs, relations);
|
|
308
|
+
}
|
|
309
|
+
catch (err) {
|
|
310
|
+
process.stderr.write(`[file-mirror] failed to mirror skill update ${skillId}: ${err}\n`);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
function _regenerateSkillSnapshot(skillsDir, skillId, attrs, relations) {
|
|
314
|
+
const outgoing = buildOutgoingRelations(skillId, relations);
|
|
315
|
+
const fm = {
|
|
316
|
+
id: skillId,
|
|
317
|
+
source: attrs.source,
|
|
318
|
+
confidence: attrs.confidence,
|
|
319
|
+
triggers: attrs.triggers,
|
|
320
|
+
inputHints: attrs.inputHints,
|
|
321
|
+
filePatterns: attrs.filePatterns,
|
|
322
|
+
tags: attrs.tags,
|
|
323
|
+
createdAt: tsToIso(attrs.createdAt),
|
|
324
|
+
updatedAt: tsToIso(attrs.updatedAt),
|
|
325
|
+
version: attrs.version,
|
|
326
|
+
};
|
|
327
|
+
if (attrs.createdBy)
|
|
328
|
+
fm.createdBy = attrs.createdBy;
|
|
329
|
+
if (attrs.updatedBy)
|
|
330
|
+
fm.updatedBy = attrs.updatedBy;
|
|
331
|
+
if (outgoing.length > 0)
|
|
332
|
+
fm.relations = outgoing;
|
|
333
|
+
const stepsBlock = attrs.steps.length > 0
|
|
334
|
+
? `\n\n## Steps\n${attrs.steps.map((s, i) => `${i + 1}. ${s}`).join('\n')}`
|
|
335
|
+
: '';
|
|
336
|
+
const body = `# ${attrs.title}\n\n${attrs.description}${stepsBlock}`;
|
|
337
|
+
const entityDir = path.join(skillsDir, skillId);
|
|
338
|
+
fs.mkdirSync(entityDir, { recursive: true });
|
|
339
|
+
fs.writeFileSync(path.join(entityDir, 'skill.md'), (0, frontmatter_1.serializeMarkdown)(fm, body));
|
|
340
|
+
}
|
|
341
|
+
// ---------------------------------------------------------------------------
|
|
342
|
+
// Relation + attachment event append helpers
|
|
343
|
+
// ---------------------------------------------------------------------------
|
|
344
|
+
/** Append a relation add/remove event and regenerate snapshot. */
|
|
345
|
+
function mirrorNoteRelation(notesDir, noteId, action, kind, to, attrs, relations, graph) {
|
|
346
|
+
try {
|
|
347
|
+
const entityDir = path.join(notesDir, noteId);
|
|
348
|
+
const eventsPath = path.join(entityDir, 'events.jsonl');
|
|
349
|
+
const event = { op: 'relation', action, kind, to };
|
|
350
|
+
if (graph)
|
|
351
|
+
event.graph = graph;
|
|
352
|
+
(0, events_log_1.appendEvent)(eventsPath, event);
|
|
353
|
+
_regenerateNoteSnapshot(notesDir, noteId, attrs, relations);
|
|
354
|
+
}
|
|
355
|
+
catch (err) {
|
|
356
|
+
process.stderr.write(`[file-mirror] failed to mirror note relation ${noteId}: ${err}\n`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
function mirrorTaskRelation(tasksDir, taskId, action, kind, to, attrs, relations, graph) {
|
|
360
|
+
try {
|
|
361
|
+
const entityDir = path.join(tasksDir, taskId);
|
|
362
|
+
const eventsPath = path.join(entityDir, 'events.jsonl');
|
|
363
|
+
const event = { op: 'relation', action, kind, to };
|
|
364
|
+
if (graph)
|
|
365
|
+
event.graph = graph;
|
|
366
|
+
(0, events_log_1.appendEvent)(eventsPath, event);
|
|
367
|
+
_regenerateTaskSnapshot(tasksDir, taskId, attrs, relations);
|
|
368
|
+
}
|
|
369
|
+
catch (err) {
|
|
370
|
+
process.stderr.write(`[file-mirror] failed to mirror task relation ${taskId}: ${err}\n`);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
function mirrorSkillRelation(skillsDir, skillId, action, kind, to, attrs, relations, graph) {
|
|
374
|
+
try {
|
|
375
|
+
const entityDir = path.join(skillsDir, skillId);
|
|
376
|
+
const eventsPath = path.join(entityDir, 'events.jsonl');
|
|
377
|
+
const event = { op: 'relation', action, kind, to };
|
|
378
|
+
if (graph)
|
|
379
|
+
event.graph = graph;
|
|
380
|
+
(0, events_log_1.appendEvent)(eventsPath, event);
|
|
381
|
+
_regenerateSkillSnapshot(skillsDir, skillId, attrs, relations);
|
|
382
|
+
}
|
|
383
|
+
catch (err) {
|
|
384
|
+
process.stderr.write(`[file-mirror] failed to mirror skill relation ${skillId}: ${err}\n`);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
/** Append an attachment add/remove event. */
|
|
388
|
+
function mirrorAttachmentEvent(entityDir, action, file) {
|
|
389
|
+
try {
|
|
390
|
+
const eventsPath = path.join(entityDir, 'events.jsonl');
|
|
391
|
+
(0, events_log_1.appendEvent)(eventsPath, { op: 'attachment', action, file });
|
|
392
|
+
}
|
|
393
|
+
catch (err) {
|
|
394
|
+
process.stderr.write(`[file-mirror] failed to mirror attachment event: ${err}\n`);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
// ---------------------------------------------------------------------------
|
|
398
|
+
// Delete
|
|
399
|
+
// ---------------------------------------------------------------------------
|
|
400
|
+
/** Delete the entire mirror directory for a note, task or skill (including attachments). */
|
|
401
|
+
function deleteMirrorDir(dir, id) {
|
|
402
|
+
try {
|
|
403
|
+
fs.rmSync(path.join(dir, id), { recursive: true, force: true });
|
|
404
|
+
}
|
|
405
|
+
catch (err) {
|
|
406
|
+
if (err.code !== 'ENOENT') {
|
|
407
|
+
process.stderr.write(`[file-mirror] failed to delete ${id}/: ${err}\n`);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
// ---------------------------------------------------------------------------
|
|
412
|
+
// Attachment file helpers (paths now go through attachments/ subdir)
|
|
413
|
+
// ---------------------------------------------------------------------------
|
|
414
|
+
/** Sanitize a filename: strip path separators, .., and null bytes. */
|
|
415
|
+
function sanitizeFilename(name) {
|
|
416
|
+
return name
|
|
417
|
+
.replace(/\0/g, '')
|
|
418
|
+
.replace(/\.\./g, '')
|
|
419
|
+
.replace(/[/\\]/g, '')
|
|
420
|
+
.trim();
|
|
421
|
+
}
|
|
422
|
+
/** Write an attachment file to the entity's attachments/ subdirectory. */
|
|
423
|
+
function writeAttachment(baseDir, entityId, filename, data) {
|
|
424
|
+
const attachmentsDir = path.join(baseDir, entityId, 'attachments');
|
|
425
|
+
fs.mkdirSync(attachmentsDir, { recursive: true });
|
|
426
|
+
fs.writeFileSync(path.join(attachmentsDir, sanitizeFilename(filename)), data);
|
|
427
|
+
}
|
|
428
|
+
/** Delete an attachment file from attachments/ subdir. Returns true if it existed. */
|
|
429
|
+
function deleteAttachment(baseDir, entityId, filename) {
|
|
430
|
+
const filePath = path.join(baseDir, entityId, 'attachments', sanitizeFilename(filename));
|
|
431
|
+
try {
|
|
432
|
+
fs.unlinkSync(filePath);
|
|
433
|
+
return true;
|
|
434
|
+
}
|
|
435
|
+
catch (err) {
|
|
436
|
+
if (err.code !== 'ENOENT') {
|
|
437
|
+
process.stderr.write(`[file-mirror] failed to delete attachment ${filename}: ${err}\n`);
|
|
438
|
+
}
|
|
439
|
+
return false;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
/** Get the absolute path of an attachment in attachments/ subdir, or null if not found. */
|
|
443
|
+
function getAttachmentPath(baseDir, entityId, filename) {
|
|
444
|
+
const filePath = path.join(baseDir, entityId, 'attachments', sanitizeFilename(filename));
|
|
445
|
+
return fs.existsSync(filePath) ? filePath : null;
|
|
446
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.serializeMarkdown = serializeMarkdown;
|
|
4
|
+
exports.parseMarkdown = parseMarkdown;
|
|
5
|
+
const yaml_1 = require("yaml");
|
|
6
|
+
function serializeMarkdown(frontmatter, body) {
|
|
7
|
+
const yamlStr = (0, yaml_1.stringify)(frontmatter, { lineWidth: 0 }).trimEnd();
|
|
8
|
+
return `---\n${yamlStr}\n---\n\n${body}\n`;
|
|
9
|
+
}
|
|
10
|
+
function parseMarkdown(raw) {
|
|
11
|
+
const match = raw.match(/^---\n([\s\S]*?)\n---\n\n?([\s\S]*)$/);
|
|
12
|
+
if (!match)
|
|
13
|
+
return { frontmatter: {}, body: raw };
|
|
14
|
+
const frontmatter = (0, yaml_1.parse)(match[1]) ?? {};
|
|
15
|
+
const body = match[2].replace(/\n$/, '');
|
|
16
|
+
return { frontmatter, body };
|
|
17
|
+
}
|