@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.
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- var VERSION = true ? "0.2.0" : "0.0.0";
4
+ var VERSION = true ? "0.3.0" : "0.0.0";
5
5
  async function runCli(argv) {
6
6
  if (argv.includes("--version") || argv.includes("-v")) {
7
7
  console.log(`logbook-mcp v${VERSION}`);
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-CIZE5MHB.js");
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-UFNP35WT.js");
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 topicId = item.topic ? resolveTopicId(db2, item.topic) : null;
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.2.0" : "0.0.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 {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cocaxcode/logbook-mcp",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "MCP server — cuaderno de bitácora del developer. Notas, TODOs y code TODOs sin salir de tu AI.",
5
5
  "type": "module",
6
6
  "bin": {