@eeymoo/context-todos 0.1.0-alpha.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.
Files changed (98) hide show
  1. package/README.md +261 -0
  2. package/dist/src/index.d.ts +3 -0
  3. package/dist/src/index.d.ts.map +1 -0
  4. package/dist/src/index.js +88 -0
  5. package/dist/src/index.js.map +1 -0
  6. package/dist/src/mcp/db.d.ts +29 -0
  7. package/dist/src/mcp/db.d.ts.map +1 -0
  8. package/dist/src/mcp/db.js +111 -0
  9. package/dist/src/mcp/db.js.map +1 -0
  10. package/dist/src/mcp/gitignore.d.ts +6 -0
  11. package/dist/src/mcp/gitignore.d.ts.map +1 -0
  12. package/dist/src/mcp/gitignore.js +32 -0
  13. package/dist/src/mcp/gitignore.js.map +1 -0
  14. package/dist/src/mcp/index.d.ts +7 -0
  15. package/dist/src/mcp/index.d.ts.map +1 -0
  16. package/dist/src/mcp/index.js +87 -0
  17. package/dist/src/mcp/index.js.map +1 -0
  18. package/dist/src/mcp/scanner.d.ts +7 -0
  19. package/dist/src/mcp/scanner.d.ts.map +1 -0
  20. package/dist/src/mcp/scanner.js +45 -0
  21. package/dist/src/mcp/scanner.js.map +1 -0
  22. package/dist/src/mcp/tools/get-todo-stats.d.ts +3 -0
  23. package/dist/src/mcp/tools/get-todo-stats.d.ts.map +1 -0
  24. package/dist/src/mcp/tools/get-todo-stats.js +37 -0
  25. package/dist/src/mcp/tools/get-todo-stats.js.map +1 -0
  26. package/dist/src/mcp/tools/list-extensions.d.ts +3 -0
  27. package/dist/src/mcp/tools/list-extensions.d.ts.map +1 -0
  28. package/dist/src/mcp/tools/list-extensions.js +28 -0
  29. package/dist/src/mcp/tools/list-extensions.js.map +1 -0
  30. package/dist/src/mcp/tools/list-todos.d.ts +3 -0
  31. package/dist/src/mcp/tools/list-todos.d.ts.map +1 -0
  32. package/dist/src/mcp/tools/list-todos.js +60 -0
  33. package/dist/src/mcp/tools/list-todos.js.map +1 -0
  34. package/dist/src/mcp/tools/scan-directory.d.ts +3 -0
  35. package/dist/src/mcp/tools/scan-directory.d.ts.map +1 -0
  36. package/dist/src/mcp/tools/scan-directory.js +78 -0
  37. package/dist/src/mcp/tools/scan-directory.js.map +1 -0
  38. package/dist/src/mcp/tools/scan-file.d.ts +3 -0
  39. package/dist/src/mcp/tools/scan-file.d.ts.map +1 -0
  40. package/dist/src/mcp/tools/scan-file.js +63 -0
  41. package/dist/src/mcp/tools/scan-file.js.map +1 -0
  42. package/dist/src/mcp/tools/watch.d.ts +4 -0
  43. package/dist/src/mcp/tools/watch.d.ts.map +1 -0
  44. package/dist/src/mcp/tools/watch.js +110 -0
  45. package/dist/src/mcp/tools/watch.js.map +1 -0
  46. package/dist/src/mcp/types.d.ts +35 -0
  47. package/dist/src/mcp/types.d.ts.map +1 -0
  48. package/dist/src/mcp/types.js +23 -0
  49. package/dist/src/mcp/types.js.map +1 -0
  50. package/dist/src/mcp/watcher.d.ts +7 -0
  51. package/dist/src/mcp/watcher.d.ts.map +1 -0
  52. package/dist/src/mcp/watcher.js +74 -0
  53. package/dist/src/mcp/watcher.js.map +1 -0
  54. package/dist/tests/db.test.d.ts +2 -0
  55. package/dist/tests/db.test.d.ts.map +1 -0
  56. package/dist/tests/db.test.js +339 -0
  57. package/dist/tests/db.test.js.map +1 -0
  58. package/dist/tests/mcp/index.test.d.ts +2 -0
  59. package/dist/tests/mcp/index.test.d.ts.map +1 -0
  60. package/dist/tests/mcp/index.test.js +276 -0
  61. package/dist/tests/mcp/index.test.js.map +1 -0
  62. package/dist/tests/scanner.test.d.ts +2 -0
  63. package/dist/tests/scanner.test.d.ts.map +1 -0
  64. package/dist/tests/scanner.test.js +101 -0
  65. package/dist/tests/scanner.test.js.map +1 -0
  66. package/dist/tests/tools/get-todo-stats.test.d.ts +2 -0
  67. package/dist/tests/tools/get-todo-stats.test.d.ts.map +1 -0
  68. package/dist/tests/tools/get-todo-stats.test.js +256 -0
  69. package/dist/tests/tools/get-todo-stats.test.js.map +1 -0
  70. package/dist/tests/tools/list-extensions.test.d.ts +2 -0
  71. package/dist/tests/tools/list-extensions.test.d.ts.map +1 -0
  72. package/dist/tests/tools/list-extensions.test.js +123 -0
  73. package/dist/tests/tools/list-extensions.test.js.map +1 -0
  74. package/dist/tests/tools/list-todos.test.d.ts +2 -0
  75. package/dist/tests/tools/list-todos.test.d.ts.map +1 -0
  76. package/dist/tests/tools/list-todos.test.js +279 -0
  77. package/dist/tests/tools/list-todos.test.js.map +1 -0
  78. package/dist/tests/tools/scan-directory.test.d.ts +2 -0
  79. package/dist/tests/tools/scan-directory.test.d.ts.map +1 -0
  80. package/dist/tests/tools/scan-directory.test.js +194 -0
  81. package/dist/tests/tools/scan-directory.test.js.map +1 -0
  82. package/dist/tests/tools/scan-file.test.d.ts +2 -0
  83. package/dist/tests/tools/scan-file.test.d.ts.map +1 -0
  84. package/dist/tests/tools/scan-file.test.js +176 -0
  85. package/dist/tests/tools/scan-file.test.js.map +1 -0
  86. package/dist/tests/tools/watch.test.d.ts +2 -0
  87. package/dist/tests/tools/watch.test.d.ts.map +1 -0
  88. package/dist/tests/tools/watch.test.js +286 -0
  89. package/dist/tests/tools/watch.test.js.map +1 -0
  90. package/dist/tests/watcher.test.d.ts +2 -0
  91. package/dist/tests/watcher.test.d.ts.map +1 -0
  92. package/dist/tests/watcher.test.js +372 -0
  93. package/dist/tests/watcher.test.js.map +1 -0
  94. package/dist/vitest.config.d.ts +3 -0
  95. package/dist/vitest.config.d.ts.map +1 -0
  96. package/dist/vitest.config.js +15 -0
  97. package/dist/vitest.config.js.map +1 -0
  98. package/package.json +67 -0
package/README.md ADDED
@@ -0,0 +1,261 @@
1
+ # Context-Todos
2
+
3
+ [简体中文](./README_ZH.md) | English
4
+
5
+ A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that scans and manages TODO/FIXME/HACK/XXX comments in your codebase.
6
+
7
+ ## What It Does
8
+
9
+ Context-Todos helps you track and manage code annotations across your entire project. It scans source files for common comment tags and provides tools to query, watch, and analyze them through the MCP protocol.
10
+
11
+ ### Features
12
+
13
+ - **Scan Files**: Extract TODO comments from a single file
14
+ - **Scan Directories**: Recursively scan all supported files in a directory
15
+ - **List Extensions**: View all supported file extensions
16
+ - **Watch Mode**: Real-time monitoring of file changes (max/labs mode)
17
+ - **TODO Database**: Persistent storage of all TODO items (max/labs mode)
18
+ - **Statistics**: Get TODO statistics by tag and file (labs mode)
19
+
20
+ ### Supported TODO Tags
21
+
22
+ | Tag | Usage |
23
+ |-----|-------|
24
+ | `TODO` | General tasks to be done |
25
+ | `FIXME` | Code that needs to be fixed |
26
+ | `HACK` | Temporary workarounds |
27
+ | `XXX` | Warning or problematic code |
28
+
29
+ ### Supported File Extensions
30
+
31
+ | Category | Extensions |
32
+ |----------|------------|
33
+ | JavaScript/TypeScript | `.ts`, `.tsx`, `.js`, `.jsx`, `.mjs`, `.cjs` |
34
+ | Python | `.py` |
35
+ | Ruby | `.rb` |
36
+ | Java | `.java` |
37
+ | Go | `.go` |
38
+ | Rust | `.rs` |
39
+ | C/C++ | `.c`, `.cpp`, `.h`, `.hpp` |
40
+ | C# | `.cs` |
41
+ | PHP | `.php` |
42
+ | Swift | `.swift` |
43
+ | Kotlin/Scala | `.kt`, `.scala` |
44
+ | Shell | `.sh`, `.bash` |
45
+ | Frontend | `.css`, `.scss`, `.less`, `.html`, `.vue`, `.svelte` |
46
+ | Config | `.yaml`, `.yml`, `.toml`, `.ini`, `.cfg` |
47
+ | Database | `.sql`, `.lua` |
48
+ | Other | `.r`, `.m`, `.mm`, `.pl`, `.pm`, `.ex`, `.exs`, `.erl`, `.hs`, `.elm`, `.clj`, `.cljs`, `.tf`, `.hcl`, `.dockerfile` |
49
+
50
+ ## How to Use
51
+
52
+ ### Quick Start (npx)
53
+
54
+ ```bash
55
+ # Run directly with npx (Stable Standard)
56
+ npx @eeymoo/context-todos mcp
57
+
58
+ # Stable Max (with watching)
59
+ npx @eeymoo/context-todos mcp --max
60
+
61
+ # Labs Standard
62
+ npx @eeymoo/context-todos mcp --labs
63
+
64
+ # Labs Max (all features including experimental)
65
+ npx @eeymoo/context-todos mcp --labs --max
66
+ ```
67
+
68
+ ### Installation
69
+
70
+ ```bash
71
+ # Install globally
72
+ npm install -g @eeymoo/context-todos
73
+
74
+ # Or with pnpm
75
+ pnpm add -g @eeymoo/context-todos
76
+
77
+ # Then run
78
+ context-todos mcp
79
+ ```
80
+
81
+ ### Configure MCP Client
82
+
83
+ Add to your MCP client configuration (e.g., Claude Desktop, Cursor, etc.):
84
+
85
+ ```json
86
+ {
87
+ "mcpServers": {
88
+ "context-todos": {
89
+ "command": "npx",
90
+ "args": ["-y", "@eeymoo/context-todos", "mcp"]
91
+ }
92
+ }
93
+ }
94
+ ```
95
+
96
+ Or with max/labs mode:
97
+
98
+ ```json
99
+ {
100
+ "mcpServers": {
101
+ "context-todos": {
102
+ "command": "npx",
103
+ "args": ["-y", "@eeymoo/context-todos", "mcp", "--max"]
104
+ }
105
+ }
106
+ }
107
+ ```
108
+
109
+ ### Development
110
+
111
+ ```bash
112
+ # Clone the repository
113
+ git clone https://github.com/eeymoo/context-todos.git
114
+ cd context-todos
115
+
116
+ # Install dependencies
117
+ pnpm install
118
+
119
+ # Build
120
+ pnpm build
121
+
122
+ # Run in development
123
+ pnpm dev
124
+ ```
125
+
126
+ ### Server Modes
127
+
128
+ The product has two categories of features:
129
+
130
+ - **Stable**: Production-ready features with `Standard` and `Max` variants
131
+ - **Labs**: Experimental features, also with `Standard` and `Max` variants
132
+
133
+ | Category | Mode | CLI | Description | Tools |
134
+ |----------|------|-----|-------------|-------|
135
+ | Stable | Standard | (default) | Basic scanning tools | `scan-file`, `scan-directory`, `list-supported-extensions` |
136
+ | Stable | Max | `--max` | Standard + file watching & database | All standard + `watch`, `unwatch`, `list-todos` |
137
+ | Labs | Standard | `--labs` | Experimental standard mode | Same as Stable Standard |
138
+ | Labs | Max | `--labs --max` | All features including experimental | All max + `get-todo-stats` |
139
+
140
+ ```bash
141
+ # Stable Standard (default)
142
+ npx @eeymoo/context-todos mcp
143
+
144
+ # Stable Max (with watching)
145
+ npx @eeymoo/context-todos mcp --max
146
+
147
+ # Labs Standard
148
+ npx @eeymoo/context-todos mcp --labs
149
+
150
+ # Labs Max (all features)
151
+ npx @eeymoo/context-todos mcp --labs --max
152
+ ```
153
+
154
+ ### MCP Tools
155
+
156
+ #### `scan-file`
157
+
158
+ Scan a single file for TODO comments.
159
+
160
+ ```json
161
+ {
162
+ "name": "scan-file",
163
+ "arguments": {
164
+ "path": "/path/to/file.ts"
165
+ }
166
+ }
167
+ ```
168
+
169
+ #### `scan-directory`
170
+
171
+ Recursively scan a directory for TODO comments.
172
+
173
+ ```json
174
+ {
175
+ "name": "scan-directory",
176
+ "arguments": {
177
+ "path": "/path/to/project",
178
+ "extensions": [".ts", ".js"]
179
+ }
180
+ }
181
+ ```
182
+
183
+ #### `list-supported-extensions`
184
+
185
+ List all supported file extensions.
186
+
187
+ ```json
188
+ {
189
+ "name": "list-supported-extensions",
190
+ "arguments": {}
191
+ }
192
+ ```
193
+
194
+ #### `watch` (max / labs --max)
195
+
196
+ Start watching a directory for file changes.
197
+
198
+ ```json
199
+ {
200
+ "name": "watch",
201
+ "arguments": {
202
+ "path": "/path/to/project",
203
+ "extensions": [".ts", ".js"]
204
+ }
205
+ }
206
+ ```
207
+
208
+ #### `unwatch` (max / labs --max)
209
+
210
+ Stop watching.
211
+
212
+ ```json
213
+ {
214
+ "name": "unwatch",
215
+ "arguments": {}
216
+ }
217
+ ```
218
+
219
+ #### `list-todos` (max / labs --max)
220
+
221
+ List all tracked TODOs from the database.
222
+
223
+ ```json
224
+ {
225
+ "name": "list-todos",
226
+ "arguments": {
227
+ "tag": "TODO",
228
+ "file": "src/index.ts"
229
+ }
230
+ }
231
+ ```
232
+
233
+ #### `get-todo-stats` (labs --max only)
234
+
235
+ Get TODO statistics grouped by tag and file.
236
+
237
+ ```json
238
+ {
239
+ "name": "get-todo-stats",
240
+ "arguments": {}
241
+ }
242
+ ```
243
+
244
+ ### Scripts
245
+
246
+ ```bash
247
+ pnpm dev # Run in development mode
248
+ pnpm build # Build for production
249
+ pnpm test # Run tests
250
+ pnpm test:watch # Run tests with watch mode
251
+ pnpm inspector # Run with MCP inspector (for debugging)
252
+ ```
253
+
254
+ ## Requirements
255
+
256
+ - Node.js >= 18.0.0
257
+ - pnpm (recommended)
258
+
259
+ ## License
260
+
261
+ ISC
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
5
+ import { createServer } from './mcp/index.js';
6
+ import { createServer as createNodeHttpServer } from 'node:http';
7
+ import { consola } from 'consola';
8
+ const program = new Command();
9
+ const logger = consola;
10
+ program
11
+ .name('context-todos')
12
+ .description('AI context-aware TODO tracker')
13
+ .version('1.0.0');
14
+ program
15
+ .command('mcp')
16
+ .description('Start MCP server')
17
+ .option('-p, --port <number>', 'Port to run the MCP server on (enables SSE mode)')
18
+ .option('-w, --watch <path>', 'Path to watch for changes', '.')
19
+ .option('--stdio', 'Force stdio mode instead of SSE')
20
+ .option('--max', 'Enable Max mode (watcher + database persistence)')
21
+ .option('--labs', 'Enable Labs mode (experimental features)')
22
+ .option('--use-gitignore', 'Use .gitignore to filter files', false)
23
+ .option('--gitignore-path <path>', 'Custom path to gitignore file (default: .gitignore)')
24
+ .action(async (options) => {
25
+ let mode;
26
+ if (options.labs && options.max) {
27
+ mode = 'labs-max';
28
+ }
29
+ else if (options.labs) {
30
+ mode = 'labs-standard';
31
+ }
32
+ else if (options.max) {
33
+ mode = 'max';
34
+ }
35
+ else {
36
+ mode = 'standard';
37
+ }
38
+ const useSSE = options.port !== undefined && !options.stdio;
39
+ const serverOptions = {
40
+ mode,
41
+ watchPath: options.watch,
42
+ useGitignore: options.useGitignore,
43
+ ...(options.gitignorePath && { gitignorePath: options.gitignorePath }),
44
+ };
45
+ const { server, totalTodos } = await createServer(serverOptions);
46
+ if (mode !== 'standard' && mode !== 'labs-standard') {
47
+ const modeMsg = mode === 'labs-max'
48
+ ? 'Starting in labs-max mode (all features + experimental)'
49
+ : 'Starting in max mode (watcher + database)';
50
+ logger.info(modeMsg);
51
+ logger.info(`Initial scan complete: ${totalTodos} TODO(s) persisted`);
52
+ }
53
+ if (useSSE) {
54
+ const port = Number(options.port);
55
+ let sseTransport;
56
+ const httpServer = createNodeHttpServer(async (req, res) => {
57
+ if (req.method === 'GET' && req.url === '/sse') {
58
+ sseTransport = new SSEServerTransport('/messages', res);
59
+ await server.connect(sseTransport);
60
+ return;
61
+ }
62
+ if (req.method === 'POST' && req.url === '/messages') {
63
+ if (!sseTransport) {
64
+ res.writeHead(400);
65
+ res.end('No SSE connection established');
66
+ return;
67
+ }
68
+ const body = [];
69
+ req.on('data', (chunk) => body.push(chunk));
70
+ req.on('end', async () => {
71
+ await sseTransport.handlePostMessage(req, res, Buffer.concat(body).toString());
72
+ });
73
+ return;
74
+ }
75
+ res.writeHead(404);
76
+ res.end('Not found');
77
+ });
78
+ httpServer.listen(port, () => {
79
+ logger.success(`SSE server running on http://localhost:${port}/sse`);
80
+ });
81
+ }
82
+ else {
83
+ const transport = new StdioServerTransport();
84
+ await server.connect(transport);
85
+ }
86
+ });
87
+ program.parse();
88
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import { type Client } from '@libsql/client';
2
+ import type { TodoItem } from './types.js';
3
+ export declare function initDb(basePath?: string): Promise<Client>;
4
+ export declare function getDb(): Client;
5
+ export declare function syncFileTodos(file: string, todos: TodoItem[]): Promise<void>;
6
+ export declare function removeFileTodos(file: string): Promise<void>;
7
+ export interface TodoQuery {
8
+ tag?: string | undefined;
9
+ file?: string | undefined;
10
+ limit?: number | undefined;
11
+ offset?: number | undefined;
12
+ }
13
+ export declare function queryTodos(query?: TodoQuery): Promise<{
14
+ todos: TodoItem[];
15
+ total: number;
16
+ }>;
17
+ export interface TodoStats {
18
+ total: number;
19
+ byTag: {
20
+ tag: string;
21
+ count: number;
22
+ }[];
23
+ byFile: {
24
+ file: string;
25
+ count: number;
26
+ }[];
27
+ }
28
+ export declare function getTodoStats(): Promise<TodoStats>;
29
+ //# sourceMappingURL=db.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../../src/mcp/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAE3D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAM3C,wBAAsB,MAAM,CAAC,QAAQ,GAAE,MAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAyBpE;AAED,wBAAgB,KAAK,IAAI,MAAM,CAK9B;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA2BlF;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGjE;AAED,MAAM,WAAW,SAAS;IACxB,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B;AAED,wBAAsB,UAAU,CAAC,KAAK,GAAE,SAAc,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAwCrG;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACxC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC3C;AAED,wBAAsB,YAAY,IAAI,OAAO,CAAC,SAAS,CAAC,CAqBvD"}
@@ -0,0 +1,111 @@
1
+ import { createClient } from '@libsql/client';
2
+ import { resolve } from 'node:path';
3
+ const DB_FILE = '.context-todos.db';
4
+ let client = null;
5
+ export async function initDb(basePath = '.') {
6
+ const dbPath = resolve(basePath, DB_FILE);
7
+ client = createClient({ url: `file:${dbPath}` });
8
+ await client.batch([
9
+ `CREATE TABLE IF NOT EXISTS todos (
10
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
11
+ file TEXT NOT NULL,
12
+ tag TEXT NOT NULL,
13
+ line INTEGER NOT NULL,
14
+ ref TEXT NOT NULL DEFAULT '',
15
+ text TEXT NOT NULL,
16
+ created_at INTEGER NOT NULL DEFAULT (unixepoch('now', 'subsec') * 1000),
17
+ updated_at INTEGER NOT NULL DEFAULT (unixepoch('now', 'subsec') * 1000),
18
+ UNIQUE(file, line, tag)
19
+ )`,
20
+ `CREATE INDEX IF NOT EXISTS idx_todos_file ON todos(file)`,
21
+ `CREATE INDEX IF NOT EXISTS idx_todos_tag ON todos(tag)`,
22
+ `CREATE INDEX IF NOT EXISTS idx_todos_updated ON todos(updated_at DESC)`,
23
+ ], 'write');
24
+ return client;
25
+ }
26
+ export function getDb() {
27
+ if (!client) {
28
+ throw new Error('Database not initialized. Call initDb() first.');
29
+ }
30
+ return client;
31
+ }
32
+ export async function syncFileTodos(file, todos) {
33
+ const db = getDb();
34
+ if (todos.length === 0) {
35
+ await db.execute({ sql: 'DELETE FROM todos WHERE file = ?', args: [file] });
36
+ return;
37
+ }
38
+ const tx = await db.transaction('write');
39
+ try {
40
+ await tx.execute({ sql: 'DELETE FROM todos WHERE file = ?', args: [file] });
41
+ for (const todo of todos) {
42
+ await tx.execute({
43
+ sql: `INSERT INTO todos (file, tag, line, ref, text, updated_at)
44
+ VALUES (?, ?, ?, ?, ?, unixepoch('now', 'subsec') * 1000)`,
45
+ args: [todo.file, todo.tag, todo.line, todo.ref || '', todo.text],
46
+ });
47
+ }
48
+ await tx.commit();
49
+ }
50
+ catch (err) {
51
+ await tx.rollback();
52
+ throw err;
53
+ }
54
+ finally {
55
+ tx.close();
56
+ }
57
+ }
58
+ export async function removeFileTodos(file) {
59
+ const db = getDb();
60
+ await db.execute({ sql: 'DELETE FROM todos WHERE file = ?', args: [file] });
61
+ }
62
+ export async function queryTodos(query = {}) {
63
+ const db = getDb();
64
+ const conditions = [];
65
+ const args = [];
66
+ if (query.tag) {
67
+ conditions.push('tag = ?');
68
+ args.push(query.tag);
69
+ }
70
+ if (query.file) {
71
+ conditions.push('file LIKE ?');
72
+ args.push(`%${query.file}%`);
73
+ }
74
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
75
+ const countResult = await db.execute({
76
+ sql: `SELECT COUNT(*) as total FROM todos ${where}`,
77
+ args,
78
+ });
79
+ const total = Number(countResult.rows[0]?.total ?? 0);
80
+ const limit = query.limit ?? 100;
81
+ const offset = query.offset ?? 0;
82
+ const result = await db.execute({
83
+ sql: `SELECT file, tag, line, ref, text FROM todos ${where} ORDER BY updated_at DESC LIMIT ? OFFSET ?`,
84
+ args: [...args, limit, offset],
85
+ });
86
+ const todos = result.rows.map((row) => ({
87
+ file: String(row.file),
88
+ tag: String(row.tag),
89
+ line: Number(row.line),
90
+ ref: String(row.ref),
91
+ text: String(row.text),
92
+ }));
93
+ return { todos, total };
94
+ }
95
+ export async function getTodoStats() {
96
+ const db = getDb();
97
+ const results = await db.batch([
98
+ 'SELECT COUNT(*) as total FROM todos',
99
+ 'SELECT tag, COUNT(*) as count FROM todos GROUP BY tag ORDER BY count DESC',
100
+ 'SELECT file, COUNT(*) as count FROM todos GROUP BY file ORDER BY count DESC LIMIT 20',
101
+ ], 'read');
102
+ const totalResult = results[0];
103
+ const tagResult = results[1];
104
+ const fileResult = results[2];
105
+ return {
106
+ total: Number(totalResult?.rows[0]?.total ?? 0),
107
+ byTag: (tagResult?.rows ?? []).map((r) => ({ tag: String(r.tag), count: Number(r.count) })),
108
+ byFile: (fileResult?.rows ?? []).map((r) => ({ file: String(r.file), count: Number(r.count) })),
109
+ };
110
+ }
111
+ //# sourceMappingURL=db.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.js","sourceRoot":"","sources":["../../../src/mcp/db.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export interface GitignoreFilter {
2
+ ignores(pathname: string): boolean;
3
+ ignoresDir(dirPath: string): boolean;
4
+ }
5
+ export declare function createGitignoreFilter(rootDir: string, gitignorePath?: string): GitignoreFilter;
6
+ //# sourceMappingURL=gitignore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitignore.d.ts","sourceRoot":"","sources":["../../../src/mcp/gitignore.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IACnC,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;CACtC;AAOD,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,aAAa,GAAE,MAAqB,GACnC,eAAe,CA0BjB"}
@@ -0,0 +1,32 @@
1
+ import ignore from 'ignore';
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import { resolve, relative } from 'node:path';
4
+ const noopFilter = {
5
+ ignores: () => false,
6
+ ignoresDir: () => false,
7
+ };
8
+ export function createGitignoreFilter(rootDir, gitignorePath = '.gitignore') {
9
+ const absoluteGitignorePath = resolve(rootDir, gitignorePath);
10
+ if (!existsSync(absoluteGitignorePath)) {
11
+ return noopFilter;
12
+ }
13
+ const gitignoreContent = readFileSync(absoluteGitignorePath, 'utf8');
14
+ const ig = ignore().add(gitignoreContent);
15
+ return {
16
+ ignores: (pathname) => {
17
+ const relativePath = relative(rootDir, pathname);
18
+ if (!relativePath || relativePath.startsWith('..')) {
19
+ return false;
20
+ }
21
+ return ig.ignores(relativePath);
22
+ },
23
+ ignoresDir: (dirPath) => {
24
+ const relativePath = relative(rootDir, dirPath);
25
+ if (!relativePath || relativePath.startsWith('..')) {
26
+ return false;
27
+ }
28
+ return ig.ignores(relativePath) || ig.ignores(relativePath + '/');
29
+ },
30
+ };
31
+ }
32
+ //# sourceMappingURL=gitignore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitignore.js","sourceRoot":"","sources":["../../../src/mcp/gitignore.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAO9C,MAAM,UAAU,GAAoB;IAClC,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK;IACpB,UAAU,EAAE,GAAG,EAAE,CAAC,KAAK;CACxB,CAAC;AAEF,MAAM,UAAU,qBAAqB,CACnC,OAAe,EACf,gBAAwB,YAAY;IAEpC,MAAM,qBAAqB,GAAG,OAAO,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IAE9D,IAAI,CAAC,UAAU,CAAC,qBAAqB,CAAC,EAAE,CAAC;QACvC,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,MAAM,gBAAgB,GAAG,YAAY,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;IACrE,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAE1C,OAAO;QACL,OAAO,EAAE,CAAC,QAAgB,EAAE,EAAE;YAC5B,MAAM,YAAY,GAAG,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YACjD,IAAI,CAAC,YAAY,IAAI,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;gBACnD,OAAO,KAAK,CAAC;YACf,CAAC;YACD,OAAO,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAClC,CAAC;QACD,UAAU,EAAE,CAAC,OAAe,EAAE,EAAE;YAC9B,MAAM,YAAY,GAAG,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAChD,IAAI,CAAC,YAAY,IAAI,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;gBACnD,OAAO,KAAK,CAAC;YACf,CAAC;YACD,OAAO,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,YAAY,GAAG,GAAG,CAAC,CAAC;QACpE,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ServerOptions } from './types.js';
3
+ export declare function createServer(options?: ServerOptions): Promise<{
4
+ server: McpServer;
5
+ totalTodos: number;
6
+ }>;
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/mcp/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAWpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAIhD,wBAAsB,YAAY,CAAC,OAAO,GAAE,aAAoC;;;GAmF/E"}
@@ -0,0 +1,87 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { resolve, relative } from 'node:path';
3
+ import { watch } from 'chokidar';
4
+ import { registerScanFile } from './tools/scan-file.js';
5
+ import { registerScanDirectory } from './tools/scan-directory.js';
6
+ import { registerListExtensions } from './tools/list-extensions.js';
7
+ import { registerWatchTools } from './tools/watch.js';
8
+ import { registerListTodos } from './tools/list-todos.js';
9
+ import { registerGetTodoStats } from './tools/get-todo-stats.js';
10
+ import { initDb, syncFileTodos, removeFileTodos } from './db.js';
11
+ import { collectFiles, scanFile } from './scanner.js';
12
+ import { modeConfigs } from './types.js';
13
+ import { createGitignoreFilter } from './gitignore.js';
14
+ export async function createServer(options = { mode: 'standard' }) {
15
+ const { mode } = options;
16
+ const config = modeConfigs[mode];
17
+ const watchPath = resolve(options.watchPath ?? '.');
18
+ const gitignoreFilter = options.useGitignore
19
+ ? createGitignoreFilter(watchPath, options.gitignorePath)
20
+ : { ignores: () => false, ignoresDir: () => false };
21
+ const server = new McpServer({ name: 'context-todos', version: '1.0.0' }, { capabilities: { logging: {} } });
22
+ registerScanFile(server);
23
+ registerScanDirectory(server);
24
+ registerListExtensions(server);
25
+ if (config.enableWatcher) {
26
+ registerWatchTools(server, gitignoreFilter);
27
+ }
28
+ if (config.enableDatabase) {
29
+ registerListTodos(server);
30
+ await initDb(watchPath);
31
+ const files = collectFiles(watchPath, undefined, gitignoreFilter);
32
+ let totalTodos = 0;
33
+ for (const file of files) {
34
+ const todos = await scanFile(file);
35
+ if (todos.length > 0) {
36
+ const relFile = relative(watchPath, file);
37
+ const relTodos = todos.map((t) => ({ ...t, file: relFile }));
38
+ await syncFileTodos(relFile, relTodos);
39
+ totalTodos += todos.length;
40
+ }
41
+ }
42
+ const dbWatcher = watch(watchPath, {
43
+ ignored: (fp, stats) => {
44
+ const relPath = relative(watchPath, fp);
45
+ if (gitignoreFilter.ignores(relPath))
46
+ return true;
47
+ if (stats?.isFile() && options.extensions) {
48
+ const ext = '.' + fp.split('.').pop();
49
+ return !options.extensions.includes(ext);
50
+ }
51
+ return false;
52
+ },
53
+ persistent: true,
54
+ ignoreInitial: true,
55
+ });
56
+ dbWatcher
57
+ .on('add', (p) => {
58
+ void (async () => {
59
+ const relPath = relative(watchPath, p);
60
+ try {
61
+ const todos = await scanFile(p);
62
+ await syncFileTodos(relPath, todos.map((t) => ({ ...t, file: relPath })));
63
+ }
64
+ catch { /* file may be temporarily unreadable */ }
65
+ })();
66
+ })
67
+ .on('change', (p) => {
68
+ void (async () => {
69
+ const relPath = relative(watchPath, p);
70
+ try {
71
+ const todos = await scanFile(p);
72
+ await syncFileTodos(relPath, todos.map((t) => ({ ...t, file: relPath })));
73
+ }
74
+ catch { /* file may be temporarily unreadable */ }
75
+ })();
76
+ })
77
+ .on('unlink', (p) => {
78
+ void removeFileTodos(relative(watchPath, p));
79
+ });
80
+ if (config.enableGetTodoStats) {
81
+ registerGetTodoStats(server);
82
+ }
83
+ return { server, totalTodos };
84
+ }
85
+ return { server, totalTodos: 0 };
86
+ }
87
+ //# sourceMappingURL=index.js.map