@cocaxcode/logbook-mcp 0.2.1 → 0.3.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-YXLXUSPK.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-VGOVRY45.js");
|
|
16
16
|
await runCli(argv);
|
|
17
17
|
}
|
|
18
18
|
}
|
|
@@ -45,6 +45,7 @@ CREATE TABLE IF NOT EXISTS todos (
|
|
|
45
45
|
content TEXT NOT NULL,
|
|
46
46
|
status TEXT NOT NULL DEFAULT 'pending',
|
|
47
47
|
priority TEXT NOT NULL DEFAULT 'normal',
|
|
48
|
+
remind_at TEXT,
|
|
48
49
|
completed_at TEXT,
|
|
49
50
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
50
51
|
);
|
|
@@ -56,6 +57,7 @@ CREATE INDEX IF NOT EXISTS idx_todos_repo ON todos(repo_id);
|
|
|
56
57
|
CREATE INDEX IF NOT EXISTS idx_todos_topic ON todos(topic_id);
|
|
57
58
|
CREATE INDEX IF NOT EXISTS idx_todos_status ON todos(status);
|
|
58
59
|
CREATE INDEX IF NOT EXISTS idx_todos_date ON todos(created_at);
|
|
60
|
+
CREATE INDEX IF NOT EXISTS idx_todos_remind ON todos(remind_at);
|
|
59
61
|
|
|
60
62
|
CREATE TABLE IF NOT EXISTS code_todo_snapshots (
|
|
61
63
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -114,7 +116,8 @@ INSERT OR IGNORE INTO topics (name, description, commit_prefix, is_custom) VALUE
|
|
|
114
116
|
('chore', 'Mantenimiento general', 'refactor,docs,ci,build,chore,test,perf', 0),
|
|
115
117
|
('idea', 'Ideas y propuestas futuras', NULL, 0),
|
|
116
118
|
('decision', 'Decisiones tomadas', NULL, 0),
|
|
117
|
-
('blocker', 'Bloqueos activos', NULL, 0)
|
|
119
|
+
('blocker', 'Bloqueos activos', NULL, 0),
|
|
120
|
+
('reminder', 'Recordatorios con fecha', NULL, 0);
|
|
118
121
|
`;
|
|
119
122
|
|
|
120
123
|
// src/db/connection.ts
|
|
@@ -207,11 +210,11 @@ var TODO_WITH_META_SQL = `
|
|
|
207
210
|
LEFT JOIN repos r ON t.repo_id = r.id
|
|
208
211
|
LEFT JOIN topics tp ON t.topic_id = tp.id
|
|
209
212
|
`;
|
|
210
|
-
function insertTodo(db2, repoId, topicId, content, priority = "normal") {
|
|
213
|
+
function insertTodo(db2, repoId, topicId, content, priority = "normal", remindAt) {
|
|
211
214
|
const stmt = db2.prepare(
|
|
212
|
-
"INSERT INTO todos (repo_id, topic_id, content, priority) VALUES (?, ?, ?, ?)"
|
|
215
|
+
"INSERT INTO todos (repo_id, topic_id, content, priority, remind_at) VALUES (?, ?, ?, ?, ?)"
|
|
213
216
|
);
|
|
214
|
-
const result = stmt.run(repoId, topicId, content, priority);
|
|
217
|
+
const result = stmt.run(repoId, topicId, content, priority, remindAt ?? null);
|
|
215
218
|
return db2.prepare(`${TODO_WITH_META_SQL} WHERE t.id = ?`).get(result.lastInsertRowid);
|
|
216
219
|
}
|
|
217
220
|
function getTodos(db2, filters = {}) {
|
|
@@ -365,6 +368,33 @@ function getCompletedTodos(db2, filters = {}) {
|
|
|
365
368
|
`${TODO_WITH_META_SQL} WHERE ${where} ORDER BY t.completed_at DESC`
|
|
366
369
|
).all(...params);
|
|
367
370
|
}
|
|
371
|
+
function getDueReminders(db2) {
|
|
372
|
+
const now = /* @__PURE__ */ new Date();
|
|
373
|
+
const today = now.toISOString().split("T")[0];
|
|
374
|
+
const tomorrow = new Date(now.getTime() + 864e5).toISOString().split("T")[0];
|
|
375
|
+
const todayItems = db2.prepare(
|
|
376
|
+
`${TODO_WITH_META_SQL} WHERE t.status = 'pending' AND t.remind_at >= ? AND t.remind_at < ? ORDER BY t.remind_at`
|
|
377
|
+
).all(today, tomorrow);
|
|
378
|
+
const overdueItems = db2.prepare(
|
|
379
|
+
`${TODO_WITH_META_SQL} WHERE t.status = 'pending' AND t.remind_at < ? ORDER BY t.remind_at`
|
|
380
|
+
).all(today);
|
|
381
|
+
return {
|
|
382
|
+
today: groupByRepo(todayItems),
|
|
383
|
+
overdue: groupByRepo(overdueItems)
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
function groupByRepo(items) {
|
|
387
|
+
const map = /* @__PURE__ */ new Map();
|
|
388
|
+
for (const item of items) {
|
|
389
|
+
const key = item.repo_name ?? "global";
|
|
390
|
+
if (!map.has(key)) map.set(key, []);
|
|
391
|
+
map.get(key).push(item);
|
|
392
|
+
}
|
|
393
|
+
return Array.from(map.entries()).sort(([a], [b]) => a.localeCompare(b)).map(([repo_name, reminders]) => ({
|
|
394
|
+
repo_name: repo_name === "global" ? null : repo_name,
|
|
395
|
+
reminders
|
|
396
|
+
}));
|
|
397
|
+
}
|
|
368
398
|
function resolveTopicId(db2, name) {
|
|
369
399
|
const existing = getTopicByName(db2, name);
|
|
370
400
|
if (existing) return existing.id;
|
|
@@ -543,15 +573,17 @@ function registerTodoAddTool(server) {
|
|
|
543
573
|
content: z3.string().min(1).max(2e3).optional().describe("Contenido del TODO (para crear uno solo)"),
|
|
544
574
|
topic: z3.string().optional().describe("Topic para el TODO individual"),
|
|
545
575
|
priority: priorityEnum.optional().default("normal").describe("Prioridad del TODO individual"),
|
|
576
|
+
remind_at: z3.string().optional().describe('Fecha recordatorio (YYYY-MM-DD). Se asigna topic "reminder" automaticamente.'),
|
|
546
577
|
items: z3.array(
|
|
547
578
|
z3.object({
|
|
548
579
|
content: z3.string().min(1).max(2e3).describe("Contenido del TODO"),
|
|
549
580
|
topic: z3.string().optional().describe("Topic"),
|
|
550
|
-
priority: priorityEnum.optional().default("normal").describe("Prioridad")
|
|
581
|
+
priority: priorityEnum.optional().default("normal").describe("Prioridad"),
|
|
582
|
+
remind_at: z3.string().optional().describe("Fecha recordatorio (YYYY-MM-DD)")
|
|
551
583
|
})
|
|
552
584
|
).max(50).optional().describe("Array de TODOs para crear varios a la vez (max 50)")
|
|
553
585
|
},
|
|
554
|
-
async ({ content, topic, priority, items }) => {
|
|
586
|
+
async ({ content, topic, priority, remind_at, items }) => {
|
|
555
587
|
try {
|
|
556
588
|
if (!content && (!items || items.length === 0)) {
|
|
557
589
|
return {
|
|
@@ -565,16 +597,18 @@ function registerTodoAddTool(server) {
|
|
|
565
597
|
const db2 = getDb();
|
|
566
598
|
const repo = autoRegisterRepo(db2);
|
|
567
599
|
const repoId = repo?.id ?? null;
|
|
568
|
-
const todoItems = items ? items : [{ content, topic, priority: priority ?? "normal" }];
|
|
600
|
+
const todoItems = items ? items : [{ content, topic, priority: priority ?? "normal", remind_at }];
|
|
569
601
|
const results = [];
|
|
570
602
|
for (const item of todoItems) {
|
|
571
|
-
const
|
|
603
|
+
const effectiveTopic = item.remind_at && !item.topic ? "reminder" : item.topic;
|
|
604
|
+
const topicId = effectiveTopic ? resolveTopicId(db2, effectiveTopic) : null;
|
|
572
605
|
const todo = insertTodo(
|
|
573
606
|
db2,
|
|
574
607
|
repoId,
|
|
575
608
|
topicId,
|
|
576
609
|
item.content,
|
|
577
|
-
item.priority ?? "normal"
|
|
610
|
+
item.priority ?? "normal",
|
|
611
|
+
item.remind_at
|
|
578
612
|
);
|
|
579
613
|
results.push(todo);
|
|
580
614
|
}
|
|
@@ -994,8 +1028,44 @@ function registerSearchTool(server) {
|
|
|
994
1028
|
);
|
|
995
1029
|
}
|
|
996
1030
|
|
|
1031
|
+
// src/resources/reminders.ts
|
|
1032
|
+
function registerRemindersResource(server) {
|
|
1033
|
+
server.resource(
|
|
1034
|
+
"reminders",
|
|
1035
|
+
"logbook://reminders",
|
|
1036
|
+
{
|
|
1037
|
+
description: "Recordatorios pendientes para hoy y atrasados, agrupados por proyecto",
|
|
1038
|
+
mimeType: "application/json"
|
|
1039
|
+
},
|
|
1040
|
+
async (uri) => {
|
|
1041
|
+
try {
|
|
1042
|
+
const db2 = getDb();
|
|
1043
|
+
const { today, overdue } = getDueReminders(db2);
|
|
1044
|
+
const hasReminders = today.length > 0 || overdue.length > 0;
|
|
1045
|
+
return {
|
|
1046
|
+
contents: [{
|
|
1047
|
+
uri: uri.href,
|
|
1048
|
+
text: JSON.stringify({
|
|
1049
|
+
hasReminders,
|
|
1050
|
+
today,
|
|
1051
|
+
overdue
|
|
1052
|
+
})
|
|
1053
|
+
}]
|
|
1054
|
+
};
|
|
1055
|
+
} catch {
|
|
1056
|
+
return {
|
|
1057
|
+
contents: [{
|
|
1058
|
+
uri: uri.href,
|
|
1059
|
+
text: JSON.stringify({ hasReminders: false, today: [], overdue: [] })
|
|
1060
|
+
}]
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
);
|
|
1065
|
+
}
|
|
1066
|
+
|
|
997
1067
|
// src/server.ts
|
|
998
|
-
var VERSION = true ? "0.
|
|
1068
|
+
var VERSION = true ? "0.3.0" : "0.0.0";
|
|
999
1069
|
function createServer() {
|
|
1000
1070
|
const server = new McpServer({
|
|
1001
1071
|
name: "logbook-mcp",
|
|
@@ -1010,6 +1080,7 @@ function createServer() {
|
|
|
1010
1080
|
registerTodoRmTool(server);
|
|
1011
1081
|
registerLogTool(server);
|
|
1012
1082
|
registerSearchTool(server);
|
|
1083
|
+
registerRemindersResource(server);
|
|
1013
1084
|
return server;
|
|
1014
1085
|
}
|
|
1015
1086
|
export {
|