@diegonogueiradev_/mcp-graph 1.0.0 → 2.0.1

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 (220) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +240 -0
  3. package/dist/api/middleware/error-handler.d.ts +3 -0
  4. package/dist/api/middleware/error-handler.d.ts.map +1 -0
  5. package/dist/api/middleware/error-handler.js +35 -0
  6. package/dist/api/middleware/error-handler.js.map +1 -0
  7. package/dist/api/middleware/validate.d.ts +5 -0
  8. package/dist/api/middleware/validate.d.ts.map +1 -0
  9. package/dist/api/middleware/validate.js +23 -0
  10. package/dist/api/middleware/validate.js.map +1 -0
  11. package/dist/api/router.d.ts +11 -0
  12. package/dist/api/router.d.ts.map +1 -0
  13. package/dist/api/router.js +41 -0
  14. package/dist/api/router.js.map +1 -0
  15. package/dist/api/routes/capture.d.ts +3 -0
  16. package/dist/api/routes/capture.d.ts.map +1 -0
  17. package/dist/api/routes/capture.js +31 -0
  18. package/dist/api/routes/capture.js.map +1 -0
  19. package/dist/api/routes/context.d.ts +4 -0
  20. package/dist/api/routes/context.d.ts.map +1 -0
  21. package/dist/api/routes/context.js +25 -0
  22. package/dist/api/routes/context.js.map +1 -0
  23. package/dist/api/routes/docs-cache.d.ts +4 -0
  24. package/dist/api/routes/docs-cache.d.ts.map +1 -0
  25. package/dist/api/routes/docs-cache.js +79 -0
  26. package/dist/api/routes/docs-cache.js.map +1 -0
  27. package/dist/api/routes/edges.d.ts +4 -0
  28. package/dist/api/routes/edges.d.ts.map +1 -0
  29. package/dist/api/routes/edges.js +50 -0
  30. package/dist/api/routes/edges.js.map +1 -0
  31. package/dist/api/routes/events.d.ts +4 -0
  32. package/dist/api/routes/events.d.ts.map +1 -0
  33. package/dist/api/routes/events.js +37 -0
  34. package/dist/api/routes/events.js.map +1 -0
  35. package/dist/api/routes/graph.d.ts +4 -0
  36. package/dist/api/routes/graph.d.ts.map +1 -0
  37. package/dist/api/routes/graph.js +39 -0
  38. package/dist/api/routes/graph.js.map +1 -0
  39. package/dist/api/routes/import.d.ts +4 -0
  40. package/dist/api/routes/import.d.ts.map +1 -0
  41. package/dist/api/routes/import.js +92 -0
  42. package/dist/api/routes/import.js.map +1 -0
  43. package/dist/api/routes/insights.d.ts +4 -0
  44. package/dist/api/routes/insights.d.ts.map +1 -0
  45. package/dist/api/routes/insights.js +40 -0
  46. package/dist/api/routes/insights.js.map +1 -0
  47. package/dist/api/routes/integrations.d.ts +4 -0
  48. package/dist/api/routes/integrations.d.ts.map +1 -0
  49. package/dist/api/routes/integrations.js +56 -0
  50. package/dist/api/routes/integrations.js.map +1 -0
  51. package/dist/api/routes/nodes.d.ts +4 -0
  52. package/dist/api/routes/nodes.d.ts.map +1 -0
  53. package/dist/api/routes/nodes.js +123 -0
  54. package/dist/api/routes/nodes.js.map +1 -0
  55. package/dist/api/routes/project.d.ts +4 -0
  56. package/dist/api/routes/project.d.ts.map +1 -0
  57. package/dist/api/routes/project.js +33 -0
  58. package/dist/api/routes/project.js.map +1 -0
  59. package/dist/api/routes/search.d.ts +4 -0
  60. package/dist/api/routes/search.d.ts.map +1 -0
  61. package/dist/api/routes/search.js +25 -0
  62. package/dist/api/routes/search.js.map +1 -0
  63. package/dist/api/routes/skills.d.ts +3 -0
  64. package/dist/api/routes/skills.d.ts.map +1 -0
  65. package/dist/api/routes/skills.js +16 -0
  66. package/dist/api/routes/skills.js.map +1 -0
  67. package/dist/api/routes/stats.d.ts +4 -0
  68. package/dist/api/routes/stats.d.ts.map +1 -0
  69. package/dist/api/routes/stats.js +14 -0
  70. package/dist/api/routes/stats.js.map +1 -0
  71. package/dist/cli/commands/import-cmd.d.ts +3 -0
  72. package/dist/cli/commands/import-cmd.d.ts.map +1 -0
  73. package/dist/cli/commands/import-cmd.js +38 -0
  74. package/dist/cli/commands/import-cmd.js.map +1 -0
  75. package/dist/cli/commands/init.d.ts +3 -0
  76. package/dist/cli/commands/init.d.ts.map +1 -0
  77. package/dist/cli/commands/init.js +55 -0
  78. package/dist/cli/commands/init.js.map +1 -0
  79. package/dist/cli/commands/serve.d.ts +3 -0
  80. package/dist/cli/commands/serve.d.ts.map +1 -0
  81. package/dist/cli/commands/serve.js +18 -0
  82. package/dist/cli/commands/serve.js.map +1 -0
  83. package/dist/cli/commands/stats.d.ts +3 -0
  84. package/dist/cli/commands/stats.d.ts.map +1 -0
  85. package/dist/cli/commands/stats.js +39 -0
  86. package/dist/cli/commands/stats.js.map +1 -0
  87. package/dist/cli/index.d.ts +3 -0
  88. package/dist/cli/index.d.ts.map +1 -0
  89. package/dist/cli/index.js +17 -0
  90. package/dist/cli/index.js.map +1 -0
  91. package/dist/core/capture/content-extractor.d.ts +21 -0
  92. package/dist/core/capture/content-extractor.d.ts.map +1 -0
  93. package/dist/core/capture/content-extractor.js +74 -0
  94. package/dist/core/capture/content-extractor.js.map +1 -0
  95. package/dist/core/capture/web-capture.d.ts +20 -0
  96. package/dist/core/capture/web-capture.d.ts.map +1 -0
  97. package/dist/core/capture/web-capture.js +51 -0
  98. package/dist/core/capture/web-capture.js.map +1 -0
  99. package/dist/core/config/config-loader.d.ts +3 -0
  100. package/dist/core/config/config-loader.d.ts.map +1 -0
  101. package/dist/core/config/config-loader.js +43 -0
  102. package/dist/core/config/config-loader.js.map +1 -0
  103. package/dist/core/config/config-schema.d.ts +11 -0
  104. package/dist/core/config/config-schema.d.ts.map +1 -0
  105. package/dist/core/config/config-schema.js +12 -0
  106. package/dist/core/config/config-schema.js.map +1 -0
  107. package/dist/core/docs/docs-cache-store.d.ts +24 -0
  108. package/dist/core/docs/docs-cache-store.d.ts.map +1 -0
  109. package/dist/core/docs/docs-cache-store.js +61 -0
  110. package/dist/core/docs/docs-cache-store.js.map +1 -0
  111. package/dist/core/docs/docs-syncer.d.ts +13 -0
  112. package/dist/core/docs/docs-syncer.d.ts.map +1 -0
  113. package/dist/core/docs/docs-syncer.js +38 -0
  114. package/dist/core/docs/docs-syncer.js.map +1 -0
  115. package/dist/core/events/event-bus.d.ts +26 -0
  116. package/dist/core/events/event-bus.d.ts.map +1 -0
  117. package/dist/core/events/event-bus.js +47 -0
  118. package/dist/core/events/event-bus.js.map +1 -0
  119. package/dist/core/events/event-types.d.ts +57 -0
  120. package/dist/core/events/event-types.d.ts.map +1 -0
  121. package/dist/core/events/event-types.js +2 -0
  122. package/dist/core/events/event-types.js.map +1 -0
  123. package/dist/core/graph/mermaid-export.d.ts +9 -0
  124. package/dist/core/graph/mermaid-export.d.ts.map +1 -0
  125. package/dist/core/graph/mermaid-export.js +80 -0
  126. package/dist/core/graph/mermaid-export.js.map +1 -0
  127. package/dist/core/importer/prd-to-graph.d.ts.map +1 -1
  128. package/dist/core/importer/prd-to-graph.js +7 -0
  129. package/dist/core/importer/prd-to-graph.js.map +1 -1
  130. package/dist/core/insights/bottleneck-detector.d.ts +31 -0
  131. package/dist/core/insights/bottleneck-detector.d.ts.map +1 -0
  132. package/dist/core/insights/bottleneck-detector.js +69 -0
  133. package/dist/core/insights/bottleneck-detector.js.map +1 -0
  134. package/dist/core/insights/metrics-calculator.d.ts +31 -0
  135. package/dist/core/insights/metrics-calculator.d.ts.map +1 -0
  136. package/dist/core/insights/metrics-calculator.js +78 -0
  137. package/dist/core/insights/metrics-calculator.js.map +1 -0
  138. package/dist/core/insights/skill-recommender.d.ts +21 -0
  139. package/dist/core/insights/skill-recommender.d.ts.map +1 -0
  140. package/dist/core/insights/skill-recommender.js +129 -0
  141. package/dist/core/insights/skill-recommender.js.map +1 -0
  142. package/dist/core/integrations/serena-reader.d.ts +18 -0
  143. package/dist/core/integrations/serena-reader.d.ts.map +1 -0
  144. package/dist/core/integrations/serena-reader.js +50 -0
  145. package/dist/core/integrations/serena-reader.js.map +1 -0
  146. package/dist/core/integrations/tool-status.d.ts +18 -0
  147. package/dist/core/integrations/tool-status.d.ts.map +1 -0
  148. package/dist/core/integrations/tool-status.js +92 -0
  149. package/dist/core/integrations/tool-status.js.map +1 -0
  150. package/dist/core/parser/file-reader.d.ts +13 -0
  151. package/dist/core/parser/file-reader.d.ts.map +1 -0
  152. package/dist/core/parser/file-reader.js +52 -0
  153. package/dist/core/parser/file-reader.js.map +1 -0
  154. package/dist/core/parser/read-html.d.ts +7 -0
  155. package/dist/core/parser/read-html.d.ts.map +1 -0
  156. package/dist/core/parser/read-html.js +51 -0
  157. package/dist/core/parser/read-html.js.map +1 -0
  158. package/dist/core/parser/read-pdf.d.ts +10 -0
  159. package/dist/core/parser/read-pdf.d.ts.map +1 -0
  160. package/dist/core/parser/read-pdf.js +16 -0
  161. package/dist/core/parser/read-pdf.js.map +1 -0
  162. package/dist/core/planner/next-task.d.ts.map +1 -1
  163. package/dist/core/planner/next-task.js +4 -1
  164. package/dist/core/planner/next-task.js.map +1 -1
  165. package/dist/core/search/fts-search.d.ts.map +1 -1
  166. package/dist/core/search/fts-search.js +6 -1
  167. package/dist/core/search/fts-search.js.map +1 -1
  168. package/dist/core/store/migrations.d.ts.map +1 -1
  169. package/dist/core/store/migrations.js +38 -0
  170. package/dist/core/store/migrations.js.map +1 -1
  171. package/dist/core/store/sqlite-store.d.ts +7 -0
  172. package/dist/core/store/sqlite-store.d.ts.map +1 -1
  173. package/dist/core/store/sqlite-store.js +28 -3
  174. package/dist/core/store/sqlite-store.js.map +1 -1
  175. package/dist/core/utils/logger.d.ts +1 -0
  176. package/dist/core/utils/logger.d.ts.map +1 -1
  177. package/dist/core/utils/logger.js +5 -0
  178. package/dist/core/utils/logger.js.map +1 -1
  179. package/dist/mcp/init-project.d.ts.map +1 -1
  180. package/dist/mcp/init-project.js +12 -16
  181. package/dist/mcp/init-project.js.map +1 -1
  182. package/dist/mcp/server.d.ts +1 -0
  183. package/dist/mcp/server.js +17 -2
  184. package/dist/mcp/server.js.map +1 -1
  185. package/dist/mcp/stdio.js +0 -0
  186. package/dist/mcp/tools/export-mermaid.d.ts +4 -0
  187. package/dist/mcp/tools/export-mermaid.d.ts.map +1 -0
  188. package/dist/mcp/tools/export-mermaid.js +27 -0
  189. package/dist/mcp/tools/export-mermaid.js.map +1 -0
  190. package/dist/mcp/tools/index.d.ts.map +1 -1
  191. package/dist/mcp/tools/index.js +2 -0
  192. package/dist/mcp/tools/index.js.map +1 -1
  193. package/dist/web/dashboard/dist/assets/code-graph-tab-jvBo8Q9t.js +1 -0
  194. package/dist/web/dashboard/dist/assets/constants-CLJl-f3f.js +1 -0
  195. package/dist/web/dashboard/dist/assets/graph-tab-BoKfDlvO.js +1 -0
  196. package/dist/web/dashboard/dist/assets/graph-utils-BZV40eAE.css +1 -0
  197. package/dist/web/dashboard/dist/assets/graph-utils-uGOH4eMw.js +23 -0
  198. package/dist/web/dashboard/dist/assets/index-DM_LGeRr.css +1 -0
  199. package/dist/web/dashboard/dist/assets/index-DrHbgcp5.js +53 -0
  200. package/dist/web/dashboard/dist/assets/insights-tab-D7sHV2xV.js +1 -0
  201. package/dist/web/dashboard/dist/assets/prd-backlog-tab-C_Uq1Qte.js +1 -0
  202. package/dist/web/dashboard/dist/index.html +13 -0
  203. package/dist/web/public/css/styles.css +646 -0
  204. package/dist/web/public/index.html +209 -0
  205. package/dist/web/public/js/api-client.js +85 -0
  206. package/dist/web/public/js/app.js +112 -0
  207. package/dist/web/public/js/capture-form.js +196 -0
  208. package/dist/web/public/js/filters.js +94 -0
  209. package/dist/web/public/js/force-graph-renderer.js +498 -0
  210. package/dist/web/public/js/graph-renderer.js +62 -0
  211. package/dist/web/public/js/import-form.js +105 -0
  212. package/dist/web/public/js/node-detail.js +106 -0
  213. package/dist/web/public/js/tabs/code-graph-tab.js +66 -0
  214. package/dist/web/public/js/tabs/graph-tab.js +238 -0
  215. package/dist/web/public/js/tabs/insights-tab.js +236 -0
  216. package/dist/web/public/js/tabs/knowledge-tab.js +201 -0
  217. package/dist/web/public/js/tabs/prd-backlog-tab.js +167 -0
  218. package/dist/web/public/vendor/force-graph.min.js +5 -0
  219. package/dist/web/public/vendor/mermaid.min.js +2843 -0
  220. package/package.json +22 -3
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Copilot Ecosystem Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,240 @@
1
+ # @diegonogueiradev_/mcp-graph
2
+
3
+ A local-first CLI tool (TypeScript) that converts PRD text files into persistent execution graphs (SQLite), enabling structured, token-efficient agentic workflows.
4
+
5
+ ## Features
6
+
7
+ - **PRD to Graph** — Parse PRD text files into structured task graphs with nodes and edges
8
+ - **Local-first** — SQLite persistence, zero external dependencies, no Docker
9
+ - **Smart Routing** — `next` command suggests the best task based on priority, dependencies, and blockers
10
+ - **Context Compression** — Reduce LLM context payload by 70-85% via structural summarization
11
+ - **MCP Protocol** — 26 tools accessible via HTTP or Stdio transport
12
+ - **Web Dashboard** — Real-time browser UI with Mermaid diagrams, backlog management, and insights
13
+ - **REST API** — Full CRUD + search + import + insights via Express
14
+ - **Cross-platform** — Windows, macOS, and Linux compatible
15
+
16
+ ## Quick Start
17
+
18
+ ### As MCP Server (recommended)
19
+
20
+ Add to your Claude Code `.mcp.json` or Cursor MCP config:
21
+
22
+ ```json
23
+ {
24
+ "mcpServers": {
25
+ "mcp-graph": {
26
+ "command": "npx",
27
+ "args": ["@diegonogueiradev_/mcp-graph"]
28
+ }
29
+ }
30
+ }
31
+ ```
32
+
33
+ Then use the tools via your MCP client:
34
+
35
+ ```
36
+ init → import_prd → list → next → update_status → stats
37
+ ```
38
+
39
+ ### From source
40
+
41
+ ```bash
42
+ git clone <repo-url>
43
+ cd mcp-graph-workflow
44
+ npm install
45
+ npm run build
46
+ npm run dev # Start HTTP server + dashboard
47
+ npm run dev:stdio # Start MCP Stdio server
48
+ ```
49
+
50
+ ### CLI
51
+
52
+ ```bash
53
+ mcp-graph init # Initialize project
54
+ mcp-graph import docs/my-prd.md # Import PRD file
55
+ mcp-graph stats # Show graph statistics
56
+ mcp-graph stats --json # JSON output
57
+ mcp-graph serve --port 3000 # Start dashboard
58
+ ```
59
+
60
+ ## Architecture
61
+
62
+ ```mermaid
63
+ graph TD
64
+ CLI[CLI — Commander.js] --> Core
65
+ MCP[MCP Server — HTTP/Stdio] --> Core
66
+ API[REST API — Express] --> Core
67
+ Web[Web Dashboard — Vanilla JS] --> API
68
+
69
+ Core --> Store[SQLite Store — WAL + FTS5]
70
+ Core --> Parser[Parser — classify, extract, segment]
71
+ Core --> Planner[Planner — next-task selection]
72
+ Core --> Context[Context Builder — 70-85% reduction]
73
+ Core --> Insights[Insights — bottlenecks, metrics]
74
+ Core --> Search[Search — FTS5 + TF-IDF reranking]
75
+ ```
76
+
77
+ ```
78
+ src/
79
+ cli/ # Commander.js commands (thin orchestration)
80
+ core/
81
+ graph/ # SQLite persistence + queries + Mermaid export
82
+ importer/ # PRD import pipeline
83
+ parser/ # classify, extract, normalize, segment
84
+ planner/ # next-task selection logic
85
+ context/ # compact context builder
86
+ insights/ # bottleneck detection, metrics
87
+ search/ # FTS5 + TF-IDF search
88
+ events/ # SSE event bus
89
+ store/ # SQLite store, migrations
90
+ config/ # Configuration loader
91
+ docs/ # Docs cache syncer
92
+ utils/ # errors, fs, id, logger, time
93
+ api/ # Express REST API routes + middleware
94
+ mcp/ # MCP server (HTTP + Stdio) + tool wrappers
95
+ schemas/ # Zod v4 schemas
96
+ web/public/ # Dashboard (HTML, CSS, vanilla JS)
97
+ tests/ # Vitest unit/integration + Playwright E2E
98
+ ```
99
+
100
+ ## MCP Tools
101
+
102
+ | Tool | Description |
103
+ |---|---|
104
+ | `init` | Initialize project and SQLite database |
105
+ | `import_prd` | Parse PRD file and generate task graph |
106
+ | `list` | List nodes filtered by type/status/sprint |
107
+ | `show` | Show node details with edges and children |
108
+ | `next` | Suggest next task based on priority and dependencies |
109
+ | `update_status` | Update node status (backlog/ready/in_progress/blocked/done) |
110
+ | `update_node` | Edit node fields (title, description, priority, tags, etc.) |
111
+ | `stats` | Show graph statistics and context reduction metrics |
112
+ | `context` | Build compact context payload for a specific task |
113
+ | `search` | Full-text search across nodes |
114
+ | `rag_context` | RAG-based contextual search via FTS5+TF-IDF |
115
+ | `add_node` | Add a new node to the graph |
116
+ | `add_edge` | Add an edge between nodes |
117
+ | `delete_node` | Delete node with cascade edge cleanup |
118
+ | `delete_edge` | Delete an edge |
119
+ | `list_edges` | List edges filtered by node or type |
120
+ | `move_node` | Move node to a different parent |
121
+ | `clone_node` | Clone a node (optionally with children) |
122
+ | `bulk_update_status` | Update status of multiple nodes at once |
123
+ | `decompose` | Detect large tasks and suggest breakdown |
124
+ | `velocity` | Calculate team velocity and sprint metrics |
125
+ | `dependencies` | Analyze dependency chains, critical path, blockers |
126
+ | `export_graph` | Export the complete graph as JSON |
127
+ | `export_mermaid` | Export the graph as a Mermaid diagram (flowchart or mindmap) |
128
+ | `create_snapshot` | Create a named snapshot of the current graph state |
129
+ | `restore_snapshot` | Restore graph from a snapshot |
130
+ | `list_snapshots` | List available snapshots |
131
+
132
+ ## REST API
133
+
134
+ All endpoints under `/api/v1/`:
135
+
136
+ | Method | Endpoint | Description |
137
+ |--------|----------|-------------|
138
+ | POST | `/project/init` | Initialize project |
139
+ | GET | `/nodes` | List all nodes |
140
+ | POST | `/nodes` | Create node |
141
+ | GET | `/edges` | List edges |
142
+ | POST | `/edges` | Create edge |
143
+ | GET | `/stats` | Graph statistics |
144
+ | GET | `/search?q=term` | Full-text search |
145
+ | POST | `/import` | Import PRD file (multipart) |
146
+ | GET | `/graph/document` | Full graph document |
147
+ | GET | `/graph/mermaid` | Mermaid diagram |
148
+ | GET | `/insights/bottlenecks` | Bottleneck report |
149
+ | GET | `/context/preview?nodeId=x` | Compact context for node |
150
+ | GET | `/docs` | Docs cache entries |
151
+ | GET | `/events` | SSE real-time events |
152
+ | GET | `/integrations/status` | Integration status (Serena, GitNexus) |
153
+ | GET | `/skills` | Available skills |
154
+
155
+ ## Web Dashboard
156
+
157
+ The dashboard runs at `http://localhost:3000` via `mcp-graph serve` and provides 5 tabs:
158
+
159
+ 1. **Graph** — Interactive Mermaid diagram with filters (status, type, direction, format), node table with search/sort, and detail panel
160
+ 2. **PRD & Backlog** — PRD source view, backlog list, next task badge, progress bars per epic
161
+ 3. **Code Graph** — Integration with GitNexus/Serena code analysis
162
+ 4. **Knowledge** — Docs cache and context preview
163
+ 5. **Insights** — Bottleneck detection, metrics, and reports
164
+
165
+ Real-time updates via Server-Sent Events (SSE). Dark/light theme toggle.
166
+
167
+ ## Testing
168
+
169
+ ```bash
170
+ npm test # Unit + integration tests (Vitest)
171
+ npm run test:watch # Watch mode
172
+ npm run test:e2e # Browser E2E tests (Playwright)
173
+ npm run test:coverage # Coverage report (V8)
174
+ npm run test:bench # Benchmark tests
175
+ npm run test:all # All tests (unit + E2E)
176
+ ```
177
+
178
+ See [docs/TEST-GUIDE.md](docs/TEST-GUIDE.md) for the full testing guide.
179
+
180
+ ## How It Works
181
+
182
+ 1. **Parse** — Read PRD text, normalize, segment by headings, classify blocks heuristically
183
+ 2. **Transform** — Convert blocks to nodes (epic, task, subtask, requirement, constraint, risk) with edges (depends_on, parent_of, blocks, related_to)
184
+ 3. **Persist** — Store graph in local SQLite with WAL mode, FTS5 indexes, and snapshots
185
+ 4. **Execute** — Route tasks by priority, dependency resolution, and blocker analysis
186
+ 5. **Compress** — Generate minimal context payloads for LLM consumption (70-85% token reduction)
187
+
188
+ ## Node Types
189
+
190
+ `epic` | `task` | `subtask` | `requirement` | `constraint` | `milestone` | `acceptance_criteria` | `risk` | `decision`
191
+
192
+ ## Status Flow
193
+
194
+ ```
195
+ backlog → ready → in_progress → done
196
+ → blocked
197
+ ```
198
+
199
+ ## XP Anti-Vibe-Coding Workflow
200
+
201
+ The project follows an anti-vibe-coding methodology based on Extreme Programming (XP). Discipline over intuition. Every line of code has a tested purpose, and the execution graph ensures no progress is lost between sessions.
202
+
203
+ ### Why a Graph?
204
+
205
+ | Problem | List/Kanban | Graph (mcp-graph) |
206
+ |---------|------------|-------------------|
207
+ | Task dependencies | Invisible or manual | Explicit edges: `blocks`, `depends_on`, `parent_of` |
208
+ | Execution order | Decided by dev each time | `next` auto-resolves based on priority + dependencies |
209
+ | AI context | Dev explains everything | `context` generates compact payload (70-85% fewer tokens) |
210
+ | Session continuity | Lost — "where was I?" | `stats` + `list` show exact state |
211
+ | Hierarchical decomposition | Flat | Tree: PRD → Feature → Story → Task → Subtask |
212
+
213
+ ### Key Skills
214
+
215
+ | Skill | Purpose |
216
+ |-------|---------|
217
+ | `/xp-bootstrap` | Sequential workflow: Isolation → Foundation → TDD → Implementation → Optimization → Interface → Deploy |
218
+ | `/project-scaffold` | Auto-setup: `.mcp.json` + `CLAUDE.md` template + `.claude/rules/` + mcp-graph init |
219
+ | `/dev-flow-orchestrator` | Continuous XP cycle: ANALYZE → DESIGN → PLAN → IMPLEMENT → VALIDATE → REVIEW → HANDOFF → LISTENING |
220
+ | `/track-with-mcp-graph` | Keep graph in sync with real work state |
221
+
222
+ ## Contributing
223
+
224
+ 1. Fork the repository
225
+ 2. Create a feature branch: `git checkout -b feature/my-feature`
226
+ 3. Follow TDD: write failing test first, then implement
227
+ 4. Ensure all checks pass: `npm run build && npm test && npm run test:e2e`
228
+ 5. Submit a PR with clear description
229
+
230
+ ## Documentation
231
+
232
+ | Document | Description |
233
+ |---|---|
234
+ | [CLAUDE.md](CLAUDE.md) | AI agent instructions, conventions, and rules |
235
+ | [docs/ARCHITECTURE-GUIDE.md](docs/ARCHITECTURE-GUIDE.md) | Complete architecture guide |
236
+ | [docs/TEST-GUIDE.md](docs/TEST-GUIDE.md) | Testing guide and best practices |
237
+
238
+ ## License
239
+
240
+ [MIT](LICENSE)
@@ -0,0 +1,3 @@
1
+ import type { Request, Response, NextFunction } from "express";
2
+ export declare function errorHandler(err: Error, _req: Request, res: Response, _next: NextFunction): void;
3
+ //# sourceMappingURL=error-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-handler.d.ts","sourceRoot":"","sources":["../../../src/api/middleware/error-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AA4B/D,wBAAgB,YAAY,CAC1B,GAAG,EAAE,KAAK,EACV,IAAI,EAAE,OAAO,EACb,GAAG,EAAE,QAAQ,EACb,KAAK,EAAE,YAAY,GAClB,IAAI,CAeN"}
@@ -0,0 +1,35 @@
1
+ import { z } from "zod/v4";
2
+ import { McpGraphError, GraphNotInitializedError, NodeNotFoundError, ValidationError, SnapshotNotFoundError, FileNotFoundError, } from "../../core/utils/errors.js";
3
+ import { logger } from "../../core/utils/logger.js";
4
+ function mapErrorToStatus(err) {
5
+ if (err instanceof GraphNotInitializedError)
6
+ return 409;
7
+ if (err instanceof NodeNotFoundError)
8
+ return 404;
9
+ if (err instanceof FileNotFoundError)
10
+ return 404;
11
+ if (err instanceof SnapshotNotFoundError)
12
+ return 404;
13
+ if (err instanceof ValidationError)
14
+ return 400;
15
+ if (err instanceof z.ZodError)
16
+ return 400;
17
+ if (err instanceof McpGraphError)
18
+ return 400;
19
+ return 500;
20
+ }
21
+ export function errorHandler(err, _req, res, _next) {
22
+ const status = mapErrorToStatus(err);
23
+ const body = { error: err.message };
24
+ if (err instanceof ValidationError) {
25
+ body.details = err.issues;
26
+ }
27
+ else if (err instanceof z.ZodError) {
28
+ body.details = err.issues;
29
+ }
30
+ if (status >= 500) {
31
+ logger.error("Unhandled API error", { error: err.message, stack: err.stack });
32
+ }
33
+ res.status(status).json(body);
34
+ }
35
+ //# sourceMappingURL=error-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-handler.js","sourceRoot":"","sources":["../../../src/api/middleware/error-handler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAC3B,OAAO,EACL,aAAa,EACb,wBAAwB,EACxB,iBAAiB,EACjB,eAAe,EACf,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAOpD,SAAS,gBAAgB,CAAC,GAAU;IAClC,IAAI,GAAG,YAAY,wBAAwB;QAAE,OAAO,GAAG,CAAC;IACxD,IAAI,GAAG,YAAY,iBAAiB;QAAE,OAAO,GAAG,CAAC;IACjD,IAAI,GAAG,YAAY,iBAAiB;QAAE,OAAO,GAAG,CAAC;IACjD,IAAI,GAAG,YAAY,qBAAqB;QAAE,OAAO,GAAG,CAAC;IACrD,IAAI,GAAG,YAAY,eAAe;QAAE,OAAO,GAAG,CAAC;IAC/C,IAAI,GAAG,YAAY,CAAC,CAAC,QAAQ;QAAE,OAAO,GAAG,CAAC;IAC1C,IAAI,GAAG,YAAY,aAAa;QAAE,OAAO,GAAG,CAAC;IAC7C,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,GAAU,EACV,IAAa,EACb,GAAa,EACb,KAAmB;IAEnB,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACrC,MAAM,IAAI,GAAsB,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC;IAEvD,IAAI,GAAG,YAAY,eAAe,EAAE,CAAC;QACnC,IAAI,CAAC,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC;IAC5B,CAAC;SAAM,IAAI,GAAG,YAAY,CAAC,CAAC,QAAQ,EAAE,CAAC;QACrC,IAAI,CAAC,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC;IAC5B,CAAC;IAED,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC;QAClB,MAAM,CAAC,KAAK,CAAC,qBAAqB,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;IAChF,CAAC;IAED,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAChC,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { Request, Response, NextFunction } from "express";
2
+ import type { ZodType } from "zod/v4";
3
+ export declare function validateBody(schema: ZodType): (req: Request, res: Response, next: NextFunction) => void;
4
+ export declare function validateQuery(schema: ZodType): (req: Request, res: Response, next: NextFunction) => void;
5
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../../src/api/middleware/validate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAItC,wBAAgB,YAAY,CAAC,MAAM,EAAE,OAAO,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CAUvG;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,OAAO,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CASxG"}
@@ -0,0 +1,23 @@
1
+ import { ValidationError } from "../../core/utils/errors.js";
2
+ export function validateBody(schema) {
3
+ return (req, _res, next) => {
4
+ const result = schema.safeParse(req.body);
5
+ if (!result.success) {
6
+ next(new ValidationError("Invalid request body", result.error.issues));
7
+ return;
8
+ }
9
+ req.body = result.data;
10
+ next();
11
+ };
12
+ }
13
+ export function validateQuery(schema) {
14
+ return (req, _res, next) => {
15
+ const result = schema.safeParse(req.query);
16
+ if (!result.success) {
17
+ next(new ValidationError("Invalid query parameters", result.error.issues));
18
+ return;
19
+ }
20
+ next();
21
+ };
22
+ }
23
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../../src/api/middleware/validate.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAE7D,MAAM,UAAU,YAAY,CAAC,MAAe;IAC1C,OAAO,CAAC,GAAY,EAAE,IAAc,EAAE,IAAkB,EAAQ,EAAE;QAChE,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,IAAI,CAAC,IAAI,eAAe,CAAC,sBAAsB,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;YACvE,OAAO;QACT,CAAC;QACD,GAAG,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;QACvB,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAAe;IAC3C,OAAO,CAAC,GAAY,EAAE,IAAc,EAAE,IAAkB,EAAQ,EAAE;QAChE,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC3C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,IAAI,CAAC,IAAI,eAAe,CAAC,0BAA0B,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;YAC3E,OAAO;QACT,CAAC;QACD,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { Router } from "express";
2
+ import type { SqliteStore } from "../core/store/sqlite-store.js";
3
+ import type { GraphEventBus } from "../core/events/event-bus.js";
4
+ export interface ApiRouterOptions {
5
+ store: SqliteStore;
6
+ basePath?: string;
7
+ eventBus?: GraphEventBus;
8
+ }
9
+ export declare function createApiRouter(options: ApiRouterOptions): Router;
10
+ export declare function createApiRouter(store: SqliteStore): Router;
11
+ //# sourceMappingURL=router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/api/router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAiBjE,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,WAAW,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,aAAa,CAAC;CAC1B;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAAC;AACnE,wBAAgB,eAAe,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAAC"}
@@ -0,0 +1,41 @@
1
+ import { Router } from "express";
2
+ import { createProjectRouter } from "./routes/project.js";
3
+ import { createNodesRouter } from "./routes/nodes.js";
4
+ import { createEdgesRouter } from "./routes/edges.js";
5
+ import { createStatsRouter } from "./routes/stats.js";
6
+ import { createSearchRouter } from "./routes/search.js";
7
+ import { createGraphRouter } from "./routes/graph.js";
8
+ import { createImportRouter } from "./routes/import.js";
9
+ import { createIntegrationsRouter } from "./routes/integrations.js";
10
+ import { createInsightsRouter } from "./routes/insights.js";
11
+ import { createSkillsRouter } from "./routes/skills.js";
12
+ import { createCaptureRouter } from "./routes/capture.js";
13
+ import { createDocsCacheRouter } from "./routes/docs-cache.js";
14
+ import { createContextRouter } from "./routes/context.js";
15
+ import { createEventsRouter } from "./routes/events.js";
16
+ import { errorHandler } from "./middleware/error-handler.js";
17
+ export function createApiRouter(storeOrOptions) {
18
+ const store = "store" in storeOrOptions ? storeOrOptions.store : storeOrOptions;
19
+ const basePath = "basePath" in storeOrOptions ? (storeOrOptions.basePath ?? process.cwd()) : process.cwd();
20
+ const eventBus = "eventBus" in storeOrOptions ? storeOrOptions.eventBus : undefined;
21
+ const router = Router();
22
+ router.use("/project", createProjectRouter(store));
23
+ router.use("/nodes", createNodesRouter(store));
24
+ router.use("/edges", createEdgesRouter(store));
25
+ router.use("/stats", createStatsRouter(store));
26
+ router.use("/search", createSearchRouter(store));
27
+ router.use("/graph", createGraphRouter(store));
28
+ router.use("/import", createImportRouter(store));
29
+ router.use("/integrations", createIntegrationsRouter(store, basePath));
30
+ router.use("/insights", createInsightsRouter(store, basePath));
31
+ router.use("/skills", createSkillsRouter(basePath));
32
+ router.use("/capture", createCaptureRouter());
33
+ router.use("/docs", createDocsCacheRouter(store));
34
+ router.use("/context", createContextRouter(store));
35
+ if (eventBus) {
36
+ router.use("/events", createEventsRouter(eventBus));
37
+ }
38
+ router.use(errorHandler);
39
+ return router;
40
+ }
41
+ //# sourceMappingURL=router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/api/router.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Router } from "express";
2
+ export declare function createCaptureRouter(): Router;
3
+ //# sourceMappingURL=capture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capture.d.ts","sourceRoot":"","sources":["../../../src/api/routes/capture.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAajC,wBAAgB,mBAAmB,IAAI,MAAM,CAsB5C"}
@@ -0,0 +1,31 @@
1
+ import { Router } from "express";
2
+ import { z } from "zod/v4";
3
+ import { validateBody } from "../middleware/validate.js";
4
+ import { captureWebPage } from "../../core/capture/web-capture.js";
5
+ import { logger } from "../../core/utils/logger.js";
6
+ const CaptureRequestSchema = z.object({
7
+ url: z.url("url must be a valid URL"),
8
+ selector: z.string().optional(),
9
+ timeout: z.number().int().positive().max(60_000).optional(),
10
+ waitForSelector: z.string().optional(),
11
+ });
12
+ export function createCaptureRouter() {
13
+ const router = Router();
14
+ /**
15
+ * POST /capture
16
+ * Capture a web page and extract structured content.
17
+ */
18
+ router.post("/", validateBody(CaptureRequestSchema), async (req, res, next) => {
19
+ try {
20
+ const { url, selector, timeout, waitForSelector } = req.body;
21
+ logger.info("Capture request received", { url, selector });
22
+ const result = await captureWebPage(url, { selector, timeout, waitForSelector });
23
+ res.status(200).json(result);
24
+ }
25
+ catch (err) {
26
+ next(err);
27
+ }
28
+ });
29
+ return router;
30
+ }
31
+ //# sourceMappingURL=capture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capture.js","sourceRoot":"","sources":["../../../src/api/routes/capture.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACnE,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAEpD,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,yBAAyB,CAAC;IACrC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE;IAC3D,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACvC,CAAC,CAAC;AAEH,MAAM,UAAU,mBAAmB;IACjC,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;IAExB;;;OAGG;IACH,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,oBAAoB,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QAC5E,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,eAAe,EAAE,GAAG,GAAG,CAAC,IAA4C,CAAC;YAErG,MAAM,CAAC,IAAI,CAAC,0BAA0B,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;YAE3D,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,eAAe,EAAE,CAAC,CAAC;YAEjF,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC/B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,GAAG,CAAC,CAAC;QACZ,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { Router } from "express";
2
+ import type { SqliteStore } from "../../core/store/sqlite-store.js";
3
+ export declare function createContextRouter(store: SqliteStore): Router;
4
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../src/api/routes/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAGpE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAwB9D"}
@@ -0,0 +1,25 @@
1
+ import { Router } from "express";
2
+ import { buildTaskContext } from "../../core/context/compact-context.js";
3
+ export function createContextRouter(store) {
4
+ const router = Router();
5
+ router.get("/preview", (req, res, next) => {
6
+ try {
7
+ const nodeId = req.query.nodeId;
8
+ if (!nodeId) {
9
+ res.status(400).json({ error: "Missing 'nodeId' query parameter" });
10
+ return;
11
+ }
12
+ const context = buildTaskContext(store, nodeId);
13
+ if (!context) {
14
+ res.status(404).json({ error: `Node not found: ${nodeId}` });
15
+ return;
16
+ }
17
+ res.json(context);
18
+ }
19
+ catch (err) {
20
+ next(err);
21
+ }
22
+ });
23
+ return router;
24
+ }
25
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../../src/api/routes/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AAEzE,MAAM,UAAU,mBAAmB,CAAC,KAAkB;IACpD,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;IAExB,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACxC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,MAA4B,CAAC;YACtD,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,kCAAkC,EAAE,CAAC,CAAC;gBACpE,OAAO;YACT,CAAC;YAED,MAAM,OAAO,GAAG,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YAChD,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,mBAAmB,MAAM,EAAE,EAAE,CAAC,CAAC;gBAC7D,OAAO;YACT,CAAC;YAED,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,GAAG,CAAC,CAAC;QACZ,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { Router } from "express";
2
+ import type { SqliteStore } from "../../core/store/sqlite-store.js";
3
+ export declare function createDocsCacheRouter(store: SqliteStore): Router;
4
+ //# sourceMappingURL=docs-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docs-cache.d.ts","sourceRoot":"","sources":["../../../src/api/routes/docs-cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAcpE,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAyEhE"}
@@ -0,0 +1,79 @@
1
+ import { Router } from "express";
2
+ import { DocsCacheStore } from "../../core/docs/docs-cache-store.js";
3
+ import { DocsSyncer } from "../../core/docs/docs-syncer.js";
4
+ const ONE_DAY_MS = 24 * 60 * 60 * 1000;
5
+ const SEVEN_DAYS_MS = 7 * ONE_DAY_MS;
6
+ function getFreshness(fetchedAt) {
7
+ const age = Date.now() - new Date(fetchedAt).getTime();
8
+ if (age < ONE_DAY_MS)
9
+ return "fresh";
10
+ if (age < SEVEN_DAYS_MS)
11
+ return "aging";
12
+ return "stale";
13
+ }
14
+ export function createDocsCacheRouter(store) {
15
+ const router = Router();
16
+ const cacheStore = new DocsCacheStore(store.getDb());
17
+ router.get("/", (req, res, next) => {
18
+ try {
19
+ const lib = req.query.lib;
20
+ if (lib) {
21
+ const results = cacheStore.searchDocs(lib);
22
+ res.json(results.map((d) => ({
23
+ ...d,
24
+ freshness: getFreshness(d.fetchedAt),
25
+ })));
26
+ return;
27
+ }
28
+ const docs = cacheStore.listCached();
29
+ res.json(docs.map((d) => ({
30
+ ...d,
31
+ freshness: getFreshness(d.fetchedAt),
32
+ })));
33
+ }
34
+ catch (err) {
35
+ next(err);
36
+ }
37
+ });
38
+ router.get("/:libId", (req, res, next) => {
39
+ try {
40
+ const doc = cacheStore.getDoc(req.params.libId);
41
+ if (!doc) {
42
+ res.status(404).json({ error: `Doc not found: ${req.params.libId}` });
43
+ return;
44
+ }
45
+ res.json({ ...doc, freshness: getFreshness(doc.fetchedAt) });
46
+ }
47
+ catch (err) {
48
+ next(err);
49
+ }
50
+ });
51
+ router.post("/sync", async (req, res, next) => {
52
+ try {
53
+ const { lib } = req.body;
54
+ if (!lib) {
55
+ res.status(400).json({ error: "Missing 'lib' in request body" });
56
+ return;
57
+ }
58
+ // Create a no-op fetcher that returns a placeholder — real Context7 integration
59
+ // requires MCP client connection which is not available in the REST API context.
60
+ // The sync endpoint stores the lib name for future MCP-based sync.
61
+ const placeholderFetcher = {
62
+ async resolveLibraryId(name) {
63
+ return `context7:${name}`;
64
+ },
65
+ async queryDocs() {
66
+ return `Documentation placeholder for ${lib}. Use Context7 MCP tools for full sync.`;
67
+ },
68
+ };
69
+ const syncer = new DocsSyncer(cacheStore, placeholderFetcher);
70
+ const doc = await syncer.syncLib(lib);
71
+ res.status(201).json({ ...doc, freshness: getFreshness(doc.fetchedAt) });
72
+ }
73
+ catch (err) {
74
+ next(err);
75
+ }
76
+ });
77
+ return router;
78
+ }
79
+ //# sourceMappingURL=docs-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docs-cache.js","sourceRoot":"","sources":["../../../src/api/routes/docs-cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { Router } from "express";
2
+ import type { SqliteStore } from "../../core/store/sqlite-store.js";
3
+ export declare function createEdgesRouter(store: SqliteStore): Router;
4
+ //# sourceMappingURL=edges.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edges.d.ts","sourceRoot":"","sources":["../../../src/api/routes/edges.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAUpE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CA4C5D"}