@cocaxcode/logbook-mcp 0.1.2 → 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/dist/index.js
CHANGED
|
@@ -6,13 +6,13 @@ async function main() {
|
|
|
6
6
|
const hasMcpFlag = argv.includes("--mcp");
|
|
7
7
|
if (hasMcpFlag) {
|
|
8
8
|
const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
|
|
9
|
-
const { createServer } = await import("./server-
|
|
9
|
+
const { createServer } = await import("./server-RBETEXFK.js");
|
|
10
10
|
const server = createServer();
|
|
11
11
|
const transport = new StdioServerTransport();
|
|
12
12
|
await server.connect(transport);
|
|
13
13
|
console.error("logbook-mcp server running on stdio");
|
|
14
14
|
} else {
|
|
15
|
-
const { runCli } = await import("./cli-
|
|
15
|
+
const { runCli } = await import("./cli-T2BAPOJO.js");
|
|
16
16
|
await runCli(argv);
|
|
17
17
|
}
|
|
18
18
|
}
|
|
@@ -57,6 +57,22 @@ CREATE INDEX IF NOT EXISTS idx_todos_topic ON todos(topic_id);
|
|
|
57
57
|
CREATE INDEX IF NOT EXISTS idx_todos_status ON todos(status);
|
|
58
58
|
CREATE INDEX IF NOT EXISTS idx_todos_date ON todos(created_at);
|
|
59
59
|
|
|
60
|
+
CREATE TABLE IF NOT EXISTS code_todo_snapshots (
|
|
61
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
62
|
+
repo_id INTEGER NOT NULL REFERENCES repos(id) ON DELETE CASCADE,
|
|
63
|
+
file TEXT NOT NULL,
|
|
64
|
+
line INTEGER NOT NULL,
|
|
65
|
+
tag TEXT NOT NULL,
|
|
66
|
+
content TEXT NOT NULL,
|
|
67
|
+
topic_name TEXT NOT NULL,
|
|
68
|
+
first_seen_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
69
|
+
resolved_at TEXT,
|
|
70
|
+
UNIQUE(repo_id, file, content)
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
CREATE INDEX IF NOT EXISTS idx_code_snapshots_repo ON code_todo_snapshots(repo_id);
|
|
74
|
+
CREATE INDEX IF NOT EXISTS idx_code_snapshots_resolved ON code_todo_snapshots(resolved_at);
|
|
75
|
+
|
|
60
76
|
CREATE VIRTUAL TABLE IF NOT EXISTS notes_fts USING fts5(
|
|
61
77
|
content,
|
|
62
78
|
content='notes',
|
|
@@ -349,6 +365,50 @@ function getCompletedTodos(db2, filters = {}) {
|
|
|
349
365
|
`${TODO_WITH_META_SQL} WHERE ${where} ORDER BY t.completed_at DESC`
|
|
350
366
|
).all(...params);
|
|
351
367
|
}
|
|
368
|
+
function syncCodeTodos(db2, repoId, currentTodos) {
|
|
369
|
+
const sync = db2.transaction(() => {
|
|
370
|
+
const existing = db2.prepare("SELECT * FROM code_todo_snapshots WHERE repo_id = ? AND resolved_at IS NULL").all(repoId);
|
|
371
|
+
const currentSet = new Set(currentTodos.map((t) => `${t.file}::${t.content}`));
|
|
372
|
+
let resolved = 0;
|
|
373
|
+
for (const snap of existing) {
|
|
374
|
+
const key = `${snap.file}::${snap.content}`;
|
|
375
|
+
if (!currentSet.has(key)) {
|
|
376
|
+
db2.prepare("UPDATE code_todo_snapshots SET resolved_at = datetime('now') WHERE id = ?").run(snap.id);
|
|
377
|
+
resolved++;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
const existingSet = new Set(existing.map((s) => `${s.file}::${s.content}`));
|
|
381
|
+
const allSnapshots = db2.prepare("SELECT file, content FROM code_todo_snapshots WHERE repo_id = ?").all(repoId);
|
|
382
|
+
const allSet = new Set(allSnapshots.map((s) => `${s.file}::${s.content}`));
|
|
383
|
+
let added = 0;
|
|
384
|
+
for (const todo of currentTodos) {
|
|
385
|
+
const key = `${todo.file}::${todo.content}`;
|
|
386
|
+
if (!allSet.has(key)) {
|
|
387
|
+
db2.prepare(
|
|
388
|
+
"INSERT INTO code_todo_snapshots (repo_id, file, line, tag, content, topic_name) VALUES (?, ?, ?, ?, ?, ?)"
|
|
389
|
+
).run(repoId, todo.file, todo.line, todo.tag, todo.content, todo.topic_name);
|
|
390
|
+
added++;
|
|
391
|
+
} else if (existingSet.has(key)) {
|
|
392
|
+
db2.prepare("UPDATE code_todo_snapshots SET line = ? WHERE repo_id = ? AND file = ? AND content = ? AND resolved_at IS NULL").run(todo.line, repoId, todo.file, todo.content);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
return { added, resolved };
|
|
396
|
+
});
|
|
397
|
+
return sync();
|
|
398
|
+
}
|
|
399
|
+
function getResolvedCodeTodos(db2, repoId, from, to) {
|
|
400
|
+
const conditions = ["repo_id = ?", "resolved_at IS NOT NULL"];
|
|
401
|
+
const params = [repoId];
|
|
402
|
+
if (from) {
|
|
403
|
+
conditions.push("resolved_at >= ?");
|
|
404
|
+
params.push(from);
|
|
405
|
+
}
|
|
406
|
+
if (to) {
|
|
407
|
+
conditions.push("resolved_at < ?");
|
|
408
|
+
params.push(to);
|
|
409
|
+
}
|
|
410
|
+
return db2.prepare(`SELECT * FROM code_todo_snapshots WHERE ${conditions.join(" AND ")} ORDER BY resolved_at DESC`).all(...params);
|
|
411
|
+
}
|
|
352
412
|
function sanitizeFts(query) {
|
|
353
413
|
const words = query.replace(/\0/g, "").split(/\s+/).filter(Boolean).map((word) => word.replace(/"/g, "")).filter(Boolean);
|
|
354
414
|
if (words.length === 0) return '""';
|
|
@@ -599,7 +659,7 @@ function parseGitGrepOutput(output) {
|
|
|
599
659
|
function registerTodoListTool(server) {
|
|
600
660
|
server.tool(
|
|
601
661
|
"logbook_todo_list",
|
|
602
|
-
"Lista TODOs agrupados por topic. Incluye
|
|
662
|
+
"Lista TODOs agrupados por topic. Incluye manuales y del codigo (TODO/FIXME/HACK/BUG). Sincroniza automaticamente: code TODOs que desaparecen del codigo se marcan como resueltos.",
|
|
603
663
|
{
|
|
604
664
|
status: z4.enum(["pending", "done", "all"]).optional().default("pending").describe("Filtrar por estado (default: pending)"),
|
|
605
665
|
topic: z4.string().optional().describe("Filtrar por topic"),
|
|
@@ -629,10 +689,12 @@ function registerTodoListTool(server) {
|
|
|
629
689
|
limit
|
|
630
690
|
});
|
|
631
691
|
let codeTodos = [];
|
|
692
|
+
let syncResult = null;
|
|
632
693
|
if (source !== "manual" && status !== "done") {
|
|
633
694
|
const repoPath = scope === "project" ? detectRepoPath() : null;
|
|
634
|
-
if (repoPath) {
|
|
695
|
+
if (repoPath && repo) {
|
|
635
696
|
codeTodos = scanCodeTodos(repoPath);
|
|
697
|
+
syncResult = syncCodeTodos(db2, repo.id, codeTodos);
|
|
636
698
|
if (topic) {
|
|
637
699
|
codeTodos = codeTodos.filter((ct) => ct.topic_name === topic);
|
|
638
700
|
}
|
|
@@ -658,7 +720,8 @@ function registerTodoListTool(server) {
|
|
|
658
720
|
summary: {
|
|
659
721
|
manual: manualTodos.length,
|
|
660
722
|
code: codeTodos.length,
|
|
661
|
-
total: manualTodos.length + codeTodos.length
|
|
723
|
+
total: manualTodos.length + codeTodos.length,
|
|
724
|
+
...syncResult && (syncResult.added > 0 || syncResult.resolved > 0) ? { sync: syncResult } : {}
|
|
662
725
|
}
|
|
663
726
|
})
|
|
664
727
|
}]
|
|
@@ -824,6 +887,7 @@ function registerLogTool(server) {
|
|
|
824
887
|
};
|
|
825
888
|
const notes = type === "todos" ? [] : getNotes(db2, filters);
|
|
826
889
|
const completedTodos = type === "notes" ? [] : getCompletedTodos(db2, filters);
|
|
890
|
+
const resolvedCodeTodos = type === "notes" || !repo ? [] : getResolvedCodeTodos(db2, repo.id, dateFrom, dateTo);
|
|
827
891
|
const entries = [
|
|
828
892
|
...notes.map((n) => ({
|
|
829
893
|
type: "note",
|
|
@@ -834,6 +898,11 @@ function registerLogTool(server) {
|
|
|
834
898
|
type: "todo",
|
|
835
899
|
data: t,
|
|
836
900
|
timestamp: t.completed_at ?? t.created_at
|
|
901
|
+
})),
|
|
902
|
+
...resolvedCodeTodos.map((ct) => ({
|
|
903
|
+
type: "code_todo_resolved",
|
|
904
|
+
data: { file: ct.file, line: ct.line, tag: ct.tag, content: ct.content, topic_name: ct.topic_name },
|
|
905
|
+
timestamp: ct.resolved_at
|
|
837
906
|
}))
|
|
838
907
|
].sort((a, b) => b.timestamp.localeCompare(a.timestamp));
|
|
839
908
|
return {
|
|
@@ -847,6 +916,7 @@ function registerLogTool(server) {
|
|
|
847
916
|
summary: {
|
|
848
917
|
notes: notes.length,
|
|
849
918
|
todos_completed: completedTodos.length,
|
|
919
|
+
code_todos_resolved: resolvedCodeTodos.length,
|
|
850
920
|
total: entries.length
|
|
851
921
|
}
|
|
852
922
|
})
|
|
@@ -957,7 +1027,7 @@ function registerSearchTool(server) {
|
|
|
957
1027
|
}
|
|
958
1028
|
|
|
959
1029
|
// src/server.ts
|
|
960
|
-
var VERSION = true ? "0.1.
|
|
1030
|
+
var VERSION = true ? "0.1.2" : "0.0.0";
|
|
961
1031
|
function createServer() {
|
|
962
1032
|
const server = new McpServer({
|
|
963
1033
|
name: "logbook-mcp",
|