@imenam/mcp-documentor 1.0.35 → 1.0.37
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/dist/shared/schemas/doc.schema.d.ts +1 -0
- package/dist/shared/schemas/doc.schema.js +1 -0
- package/dist/src/gui-worker.js +16 -0
- package/dist/src/index.js +37 -0
- package/dist/src/tools/check-freshness.js +27 -6
- package/dist/src/tools/refresh-file-snapshot.js +18 -3
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/gui/dist/assets/{index-DSzHVuYN.js → index-CJaZKexC.js} +28 -28
- package/gui/dist/assets/index-DW6tXmF5.css +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/gui/dist/assets/index-CY4GOI9d.css +0 -1
|
@@ -81,6 +81,7 @@ export declare const FreshnessReportSchema: z.ZodObject<{
|
|
|
81
81
|
modified: z.ZodArray<z.ZodString>;
|
|
82
82
|
missing: z.ZodArray<z.ZodString>;
|
|
83
83
|
unchanged: z.ZodArray<z.ZodString>;
|
|
84
|
+
anchors_lost: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
84
85
|
total: z.ZodNumber;
|
|
85
86
|
}, z.core.$strip>;
|
|
86
87
|
export type FreshnessReport = z.infer<typeof FreshnessReportSchema>;
|
|
@@ -39,6 +39,7 @@ export const FreshnessReportSchema = z.object({
|
|
|
39
39
|
modified: z.array(z.string()),
|
|
40
40
|
missing: z.array(z.string()),
|
|
41
41
|
unchanged: z.array(z.string()),
|
|
42
|
+
anchors_lost: z.array(z.string()).default([]),
|
|
42
43
|
total: z.number(),
|
|
43
44
|
});
|
|
44
45
|
export const CreateDocArgsSchema = z.object({
|
package/dist/src/gui-worker.js
CHANGED
|
@@ -268,6 +268,22 @@ app.get("/api/doc/*", async (c) => {
|
|
|
268
268
|
return c.json({ error: result.error }, 400);
|
|
269
269
|
return c.json(result.data);
|
|
270
270
|
});
|
|
271
|
+
app.put("/api/doc/*", async (c) => {
|
|
272
|
+
try {
|
|
273
|
+
const id = decodeURIComponent(c.req.path.slice("/api/doc/".length));
|
|
274
|
+
const body = await c.req.json();
|
|
275
|
+
if (body.content === undefined) {
|
|
276
|
+
return c.json({ error: "content is required" }, 400);
|
|
277
|
+
}
|
|
278
|
+
const result = await ipcRequest("UPDATE_DOC", { id, content: body.content, title: body.title }, 15000);
|
|
279
|
+
if (result.error)
|
|
280
|
+
return c.json({ error: result.error }, 500);
|
|
281
|
+
return c.json(result.data ?? { success: true });
|
|
282
|
+
}
|
|
283
|
+
catch (err) {
|
|
284
|
+
return c.json({ error: err.message }, 500);
|
|
285
|
+
}
|
|
286
|
+
});
|
|
271
287
|
app.get("/api/freshness/*", async (c) => {
|
|
272
288
|
const id = decodeURIComponent(c.req.path.slice("/api/freshness/".length));
|
|
273
289
|
const result = await ipcRequest("CHECK_FRESHNESS", { id });
|
package/dist/src/index.js
CHANGED
|
@@ -126,6 +126,9 @@ function handleIPCMessage(msg) {
|
|
|
126
126
|
else if (msg.type === "GET_DOC_VERSION") {
|
|
127
127
|
handleGetDocVersionIPC(msg);
|
|
128
128
|
}
|
|
129
|
+
else if (msg.type === "UPDATE_DOC") {
|
|
130
|
+
handleUpdateDocIPC(msg);
|
|
131
|
+
}
|
|
129
132
|
else {
|
|
130
133
|
logger.debug(`Unhandled message from GUI: ${msg.type}`);
|
|
131
134
|
}
|
|
@@ -254,6 +257,40 @@ async function handleGetDoc(msg) {
|
|
|
254
257
|
});
|
|
255
258
|
}
|
|
256
259
|
}
|
|
260
|
+
async function handleUpdateDocIPC(msg) {
|
|
261
|
+
try {
|
|
262
|
+
const result = await updateDoc(storageEngine, embeddingEngine, {
|
|
263
|
+
path: msg.data.id,
|
|
264
|
+
content: msg.data.content,
|
|
265
|
+
...(msg.data.title !== undefined && { title: msg.data.title }),
|
|
266
|
+
});
|
|
267
|
+
if (result.isError) {
|
|
268
|
+
const text = result.content[0]?.type === "text" ? result.content[0].text : "Update failed";
|
|
269
|
+
broadcastToGUI({
|
|
270
|
+
type: "UPDATE_DOC_RESPONSE",
|
|
271
|
+
correlationId: msg.correlationId,
|
|
272
|
+
error: text,
|
|
273
|
+
timestamp: new Date().toISOString(),
|
|
274
|
+
});
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
const message = result.content[0]?.type === "text" ? result.content[0].text : undefined;
|
|
278
|
+
broadcastToGUI({
|
|
279
|
+
type: "UPDATE_DOC_RESPONSE",
|
|
280
|
+
correlationId: msg.correlationId,
|
|
281
|
+
data: { success: true, message },
|
|
282
|
+
timestamp: new Date().toISOString(),
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
catch (err) {
|
|
286
|
+
broadcastToGUI({
|
|
287
|
+
type: "UPDATE_DOC_RESPONSE",
|
|
288
|
+
correlationId: msg.correlationId,
|
|
289
|
+
error: err.message,
|
|
290
|
+
timestamp: new Date().toISOString(),
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
}
|
|
257
294
|
async function handleCheckFreshness(msg) {
|
|
258
295
|
try {
|
|
259
296
|
const resolved = await storageEngine.resolveDoc(msg.data.id ?? msg.data.path);
|
|
@@ -8,11 +8,12 @@ export function computeFileHash(absolutePath) {
|
|
|
8
8
|
}
|
|
9
9
|
export function computeFreshness(trackedFiles) {
|
|
10
10
|
if (!trackedFiles || trackedFiles.length === 0) {
|
|
11
|
-
return { status: "no_tracking", modified: [], missing: [], unchanged: [], total: 0 };
|
|
11
|
+
return { status: "no_tracking", modified: [], missing: [], unchanged: [], anchors_lost: [], total: 0 };
|
|
12
12
|
}
|
|
13
13
|
const modified = [];
|
|
14
14
|
const missing = [];
|
|
15
15
|
const unchanged = [];
|
|
16
|
+
const anchors_lost = [];
|
|
16
17
|
for (const tracked of trackedFiles) {
|
|
17
18
|
const absolutePath = path.resolve(process.cwd(), tracked.path);
|
|
18
19
|
if (!fs.existsSync(absolutePath)) {
|
|
@@ -21,15 +22,26 @@ export function computeFreshness(trackedFiles) {
|
|
|
21
22
|
}
|
|
22
23
|
if (tracked.sections && tracked.sections.length > 0) {
|
|
23
24
|
let sectionStale = false;
|
|
25
|
+
let anchorLost = false;
|
|
24
26
|
for (const section of tracked.sections) {
|
|
25
27
|
const result = findAndHashSection(absolutePath, section);
|
|
26
|
-
if (!result.found
|
|
28
|
+
if (!result.found) {
|
|
29
|
+
sectionStale = true;
|
|
30
|
+
anchorLost = true;
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
else if (result.currentHash !== section.sectionHash) {
|
|
27
34
|
sectionStale = true;
|
|
28
35
|
break;
|
|
29
36
|
}
|
|
30
37
|
}
|
|
31
38
|
if (sectionStale) {
|
|
32
|
-
|
|
39
|
+
if (anchorLost) {
|
|
40
|
+
anchors_lost.push(tracked.path);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
modified.push(tracked.path);
|
|
44
|
+
}
|
|
33
45
|
}
|
|
34
46
|
else {
|
|
35
47
|
unchanged.push(tracked.path);
|
|
@@ -56,12 +68,13 @@ export function computeFreshness(trackedFiles) {
|
|
|
56
68
|
}
|
|
57
69
|
}
|
|
58
70
|
}
|
|
59
|
-
const isStale = modified.length > 0 || missing.length > 0;
|
|
71
|
+
const isStale = modified.length > 0 || missing.length > 0 || anchors_lost.length > 0;
|
|
60
72
|
return {
|
|
61
73
|
status: isStale ? "stale" : "up_to_date",
|
|
62
74
|
modified,
|
|
63
75
|
missing,
|
|
64
76
|
unchanged,
|
|
77
|
+
anchors_lost,
|
|
65
78
|
total: trackedFiles.length,
|
|
66
79
|
};
|
|
67
80
|
}
|
|
@@ -72,13 +85,19 @@ export function formatFreshnessReport(title, report) {
|
|
|
72
85
|
let text = report.status === "stale"
|
|
73
86
|
? `STALE: "${title}" — tracked files have changed since the documentation was last updated.\n`
|
|
74
87
|
: `UP TO DATE: "${title}" — no tracked files have been modified.\n`;
|
|
75
|
-
|
|
88
|
+
const anchorLostCount = report.anchors_lost?.length ?? 0;
|
|
89
|
+
text += `\nSummary: ${report.modified.length} modified, ${report.missing.length} missing, ${anchorLostCount} with lost anchors, ${report.unchanged.length} unchanged out of ${report.total} tracked file(s).`;
|
|
76
90
|
if (report.modified.length > 0) {
|
|
77
|
-
text += `\n\nModified files:\n${report.modified.map((f) => ` - ${f}`).join("\n")}`;
|
|
91
|
+
text += `\n\nModified files (call refresh_file_snapshot to acknowledge):\n${report.modified.map((f) => ` - ${f}`).join("\n")}`;
|
|
78
92
|
}
|
|
79
93
|
if (report.missing.length > 0) {
|
|
80
94
|
text += `\n\nMissing files:\n${report.missing.map((f) => ` - ${f}`).join("\n")}`;
|
|
81
95
|
}
|
|
96
|
+
if (anchorLostCount > 0) {
|
|
97
|
+
text += `\n\n⚠ Files with lost anchors — refresh_file_snapshot cannot fix this:\n${report.anchors_lost.map((f) => ` - ${f}`).join("\n")}`;
|
|
98
|
+
text += `\n → The tracked section(s) could not be located in the current file content (anchors were likely modified by a recent commit).`;
|
|
99
|
+
text += `\n → Required action: call update_doc with the new line numbers to re-establish section tracking.`;
|
|
100
|
+
}
|
|
82
101
|
return text;
|
|
83
102
|
}
|
|
84
103
|
export async function checkFreshness(storageEngine, args) {
|
|
@@ -113,6 +132,7 @@ export async function checkFreshness(storageEngine, args) {
|
|
|
113
132
|
const details = [
|
|
114
133
|
...report.modified.map((f) => ` • modified: ${f}`),
|
|
115
134
|
...report.missing.map((f) => ` • missing: ${f}`),
|
|
135
|
+
...(report.anchors_lost ?? []).map((f) => ` • ⚠ anchors lost (needs update_doc): ${f}`),
|
|
116
136
|
].join("\n");
|
|
117
137
|
stale.push(` - **${doc.title}** [${doc.id}]\n${details}`);
|
|
118
138
|
}
|
|
@@ -153,6 +173,7 @@ export async function checkFreshness(storageEngine, args) {
|
|
|
153
173
|
const details = [
|
|
154
174
|
...report.modified.map((f) => ` • modified: ${f}`),
|
|
155
175
|
...report.missing.map((f) => ` • missing: ${f}`),
|
|
176
|
+
...(report.anchors_lost ?? []).map((f) => ` • ⚠ anchors lost (needs update_doc): ${f}`),
|
|
156
177
|
].join("\n");
|
|
157
178
|
stale.push(` - **${doc.title}** [${doc.id}]\n${details}`);
|
|
158
179
|
}
|
|
@@ -16,6 +16,7 @@ export async function refreshFileSnapshot(storageEngine, historyStore, args) {
|
|
|
16
16
|
const refreshed = [];
|
|
17
17
|
const missing = [];
|
|
18
18
|
const skipped = [];
|
|
19
|
+
const anchorsLost = [];
|
|
19
20
|
for (const tracked of doc.tracked_files) {
|
|
20
21
|
if (validated.file_paths && !validated.file_paths.includes(tracked.path)) {
|
|
21
22
|
skipped.push(tracked.path);
|
|
@@ -27,24 +28,38 @@ export async function refreshFileSnapshot(storageEngine, historyStore, args) {
|
|
|
27
28
|
}
|
|
28
29
|
tracked.lastModifiedAt = fs.statSync(tracked.path).mtime.toISOString();
|
|
29
30
|
if (tracked.sections && tracked.sections.length > 0) {
|
|
31
|
+
let hasLostAnchors = false;
|
|
30
32
|
const refreshedSections = tracked.sections.map((section) => {
|
|
31
33
|
const result = relocateAndRefreshSection(tracked.path, section);
|
|
34
|
+
if (!result.found)
|
|
35
|
+
hasLostAnchors = true;
|
|
32
36
|
return result.found ? result.refreshed : section;
|
|
33
37
|
});
|
|
34
38
|
tracked.sections = refreshedSections;
|
|
39
|
+
if (hasLostAnchors) {
|
|
40
|
+
anchorsLost.push(tracked.path);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
refreshed.push(tracked.path);
|
|
44
|
+
}
|
|
35
45
|
}
|
|
36
46
|
else {
|
|
37
47
|
tracked.contentHash = computeFileHash(tracked.path);
|
|
48
|
+
refreshed.push(tracked.path);
|
|
38
49
|
}
|
|
39
|
-
refreshed.push(tracked.path);
|
|
40
50
|
}
|
|
41
51
|
await storageEngine.saveDoc(doc, resolvedProject);
|
|
42
52
|
historyStore.record(resolvedProject, doc, "snapshot");
|
|
43
|
-
let section = `**${doc.title}** [${docPath}]: refreshed ${refreshed.length}, missing ${missing.length}, skipped ${skipped.length}`;
|
|
53
|
+
let section = `**${doc.title}** [${docPath}]: refreshed ${refreshed.length}, anchors lost ${anchorsLost.length}, missing ${missing.length}, skipped ${skipped.length}`;
|
|
44
54
|
if (refreshed.length > 0)
|
|
45
55
|
section += `\n${refreshed.map((f) => ` ✓ ${f}`).join("\n")}`;
|
|
46
56
|
if (missing.length > 0)
|
|
47
|
-
section += `\n${missing.map((f) => ` ✗ ${f}`).join("\n")}`;
|
|
57
|
+
section += `\n${missing.map((f) => ` ✗ missing: ${f}`).join("\n")}`;
|
|
58
|
+
if (anchorsLost.length > 0) {
|
|
59
|
+
section += `\n${anchorsLost.map((f) => ` ⚠ anchors lost: ${f}`).join("\n")}`;
|
|
60
|
+
section += `\n → refresh_file_snapshot cannot fix this. The tracked section(s) could not be located in the current file content.`;
|
|
61
|
+
section += `\n → Required action: call update_doc with the new line numbers to re-establish section tracking.`;
|
|
62
|
+
}
|
|
48
63
|
docSections.push(section);
|
|
49
64
|
}
|
|
50
65
|
return { content: [{ type: "text", text: docSections.join("\n\n") }] };
|
package/dist/src/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const APP_VERSION = "1.0.
|
|
1
|
+
export declare const APP_VERSION = "1.0.37";
|
package/dist/src/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Auto-generated version file - do not edit manually
|
|
2
|
-
export const APP_VERSION = '1.0.
|
|
2
|
+
export const APP_VERSION = '1.0.37';
|