memory-journal-mcp 4.4.2 → 5.0.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 (291) hide show
  1. package/.github/workflows/codeql.yml +1 -6
  2. package/.github/workflows/docker-publish.yml +15 -49
  3. package/.github/workflows/lint-and-test.yml +1 -1
  4. package/.github/workflows/secrets-scanning.yml +4 -3
  5. package/.github/workflows/security-update.yml +3 -3
  6. package/CHANGELOG.md +213 -0
  7. package/CONTRIBUTING.md +132 -97
  8. package/DOCKER_README.md +184 -235
  9. package/Dockerfile +27 -24
  10. package/README.md +218 -190
  11. package/SECURITY.md +27 -35
  12. package/dist/cli.js +16 -1
  13. package/dist/cli.js.map +1 -1
  14. package/dist/constants/ServerInstructions.d.ts +5 -1
  15. package/dist/constants/ServerInstructions.d.ts.map +1 -1
  16. package/dist/constants/ServerInstructions.js +133 -73
  17. package/dist/constants/ServerInstructions.js.map +1 -1
  18. package/dist/constants/icons.d.ts +2 -2
  19. package/dist/constants/icons.d.ts.map +1 -1
  20. package/dist/constants/icons.js +7 -6
  21. package/dist/constants/icons.js.map +1 -1
  22. package/dist/database/SqliteAdapter.d.ts +37 -24
  23. package/dist/database/SqliteAdapter.d.ts.map +1 -1
  24. package/dist/database/SqliteAdapter.js +319 -157
  25. package/dist/database/SqliteAdapter.js.map +1 -1
  26. package/dist/database/schema.d.ts +45 -0
  27. package/dist/database/schema.d.ts.map +1 -0
  28. package/dist/database/schema.js +92 -0
  29. package/dist/database/schema.js.map +1 -0
  30. package/dist/filtering/ToolFilter.d.ts +1 -1
  31. package/dist/filtering/ToolFilter.d.ts.map +1 -1
  32. package/dist/filtering/ToolFilter.js +13 -2
  33. package/dist/filtering/ToolFilter.js.map +1 -1
  34. package/dist/github/GitHubIntegration.d.ts.map +1 -1
  35. package/dist/github/GitHubIntegration.js +1 -3
  36. package/dist/github/GitHubIntegration.js.map +1 -1
  37. package/dist/handlers/prompts/github.d.ts +12 -0
  38. package/dist/handlers/prompts/github.d.ts.map +1 -0
  39. package/dist/handlers/prompts/github.js +178 -0
  40. package/dist/handlers/prompts/github.js.map +1 -0
  41. package/dist/handlers/prompts/index.d.ts +23 -2
  42. package/dist/handlers/prompts/index.d.ts.map +1 -1
  43. package/dist/handlers/prompts/index.js +7 -432
  44. package/dist/handlers/prompts/index.js.map +1 -1
  45. package/dist/handlers/prompts/workflow.d.ts +12 -0
  46. package/dist/handlers/prompts/workflow.d.ts.map +1 -0
  47. package/dist/handlers/prompts/workflow.js +277 -0
  48. package/dist/handlers/prompts/workflow.js.map +1 -0
  49. package/dist/handlers/resources/core.d.ts +11 -0
  50. package/dist/handlers/resources/core.d.ts.map +1 -0
  51. package/dist/handlers/resources/core.js +433 -0
  52. package/dist/handlers/resources/core.js.map +1 -0
  53. package/dist/handlers/resources/github.d.ts +11 -0
  54. package/dist/handlers/resources/github.d.ts.map +1 -0
  55. package/dist/handlers/resources/github.js +314 -0
  56. package/dist/handlers/resources/github.js.map +1 -0
  57. package/dist/handlers/resources/graph.d.ts +11 -0
  58. package/dist/handlers/resources/graph.d.ts.map +1 -0
  59. package/dist/handlers/resources/graph.js +204 -0
  60. package/dist/handlers/resources/graph.js.map +1 -0
  61. package/dist/handlers/resources/index.d.ts +5 -20
  62. package/dist/handlers/resources/index.d.ts.map +1 -1
  63. package/dist/handlers/resources/index.js +16 -1278
  64. package/dist/handlers/resources/index.js.map +1 -1
  65. package/dist/handlers/resources/shared.d.ts +60 -0
  66. package/dist/handlers/resources/shared.d.ts.map +1 -0
  67. package/dist/handlers/resources/shared.js +49 -0
  68. package/dist/handlers/resources/shared.js.map +1 -0
  69. package/dist/handlers/resources/team.d.ts +13 -0
  70. package/dist/handlers/resources/team.d.ts.map +1 -0
  71. package/dist/handlers/resources/team.js +119 -0
  72. package/dist/handlers/resources/team.js.map +1 -0
  73. package/dist/handlers/resources/templates.d.ts +13 -0
  74. package/dist/handlers/resources/templates.d.ts.map +1 -0
  75. package/dist/handlers/resources/templates.js +310 -0
  76. package/dist/handlers/resources/templates.js.map +1 -0
  77. package/dist/handlers/tools/admin.d.ts +8 -0
  78. package/dist/handlers/tools/admin.d.ts.map +1 -0
  79. package/dist/handlers/tools/admin.js +270 -0
  80. package/dist/handlers/tools/admin.js.map +1 -0
  81. package/dist/handlers/tools/analytics.d.ts +8 -0
  82. package/dist/handlers/tools/analytics.d.ts.map +1 -0
  83. package/dist/handlers/tools/analytics.js +256 -0
  84. package/dist/handlers/tools/analytics.js.map +1 -0
  85. package/dist/handlers/tools/backup.d.ts +8 -0
  86. package/dist/handlers/tools/backup.d.ts.map +1 -0
  87. package/dist/handlers/tools/backup.js +224 -0
  88. package/dist/handlers/tools/backup.js.map +1 -0
  89. package/dist/handlers/tools/core.d.ts +9 -0
  90. package/dist/handlers/tools/core.d.ts.map +1 -0
  91. package/dist/handlers/tools/core.js +326 -0
  92. package/dist/handlers/tools/core.js.map +1 -0
  93. package/dist/handlers/tools/export.d.ts +8 -0
  94. package/dist/handlers/tools/export.d.ts.map +1 -0
  95. package/dist/handlers/tools/export.js +89 -0
  96. package/dist/handlers/tools/export.js.map +1 -0
  97. package/dist/handlers/tools/github/helpers.d.ts +34 -0
  98. package/dist/handlers/tools/github/helpers.d.ts.map +1 -0
  99. package/dist/handlers/tools/github/helpers.js +52 -0
  100. package/dist/handlers/tools/github/helpers.js.map +1 -0
  101. package/dist/handlers/tools/github/insights-tools.d.ts +8 -0
  102. package/dist/handlers/tools/github/insights-tools.d.ts.map +1 -0
  103. package/dist/handlers/tools/github/insights-tools.js +104 -0
  104. package/dist/handlers/tools/github/insights-tools.js.map +1 -0
  105. package/dist/handlers/tools/github/issue-tools.d.ts +8 -0
  106. package/dist/handlers/tools/github/issue-tools.d.ts.map +1 -0
  107. package/dist/handlers/tools/github/issue-tools.js +359 -0
  108. package/dist/handlers/tools/github/issue-tools.js.map +1 -0
  109. package/dist/handlers/tools/github/kanban-tools.d.ts +8 -0
  110. package/dist/handlers/tools/github/kanban-tools.d.ts.map +1 -0
  111. package/dist/handlers/tools/github/kanban-tools.js +108 -0
  112. package/dist/handlers/tools/github/kanban-tools.js.map +1 -0
  113. package/dist/handlers/tools/github/milestone-tools.d.ts +9 -0
  114. package/dist/handlers/tools/github/milestone-tools.d.ts.map +1 -0
  115. package/dist/handlers/tools/github/milestone-tools.js +302 -0
  116. package/dist/handlers/tools/github/milestone-tools.js.map +1 -0
  117. package/dist/handlers/tools/github/mutation-tools.d.ts +12 -0
  118. package/dist/handlers/tools/github/mutation-tools.d.ts.map +1 -0
  119. package/dist/handlers/tools/github/mutation-tools.js +15 -0
  120. package/dist/handlers/tools/github/mutation-tools.js.map +1 -0
  121. package/dist/handlers/tools/github/read-tools.d.ts +8 -0
  122. package/dist/handlers/tools/github/read-tools.d.ts.map +1 -0
  123. package/dist/handlers/tools/github/read-tools.js +260 -0
  124. package/dist/handlers/tools/github/read-tools.js.map +1 -0
  125. package/dist/handlers/tools/github/schemas.d.ts +467 -0
  126. package/dist/handlers/tools/github/schemas.d.ts.map +1 -0
  127. package/dist/handlers/tools/github/schemas.js +335 -0
  128. package/dist/handlers/tools/github/schemas.js.map +1 -0
  129. package/dist/handlers/tools/github.d.ts +14 -0
  130. package/dist/handlers/tools/github.d.ts.map +1 -0
  131. package/dist/handlers/tools/github.js +28 -0
  132. package/dist/handlers/tools/github.js.map +1 -0
  133. package/dist/handlers/tools/index.d.ts +15 -20
  134. package/dist/handlers/tools/index.d.ts.map +1 -1
  135. package/dist/handlers/tools/index.js +117 -2909
  136. package/dist/handlers/tools/index.js.map +1 -1
  137. package/dist/handlers/tools/relationships.d.ts +8 -0
  138. package/dist/handlers/tools/relationships.d.ts.map +1 -0
  139. package/dist/handlers/tools/relationships.js +308 -0
  140. package/dist/handlers/tools/relationships.js.map +1 -0
  141. package/dist/handlers/tools/schemas.d.ts +108 -0
  142. package/dist/handlers/tools/schemas.d.ts.map +1 -0
  143. package/dist/handlers/tools/schemas.js +122 -0
  144. package/dist/handlers/tools/schemas.js.map +1 -0
  145. package/dist/handlers/tools/search.d.ts +8 -0
  146. package/dist/handlers/tools/search.d.ts.map +1 -0
  147. package/dist/handlers/tools/search.js +282 -0
  148. package/dist/handlers/tools/search.js.map +1 -0
  149. package/dist/handlers/tools/team.d.ts +11 -0
  150. package/dist/handlers/tools/team.d.ts.map +1 -0
  151. package/dist/handlers/tools/team.js +239 -0
  152. package/dist/handlers/tools/team.js.map +1 -0
  153. package/dist/server/McpServer.d.ts +4 -0
  154. package/dist/server/McpServer.d.ts.map +1 -1
  155. package/dist/server/McpServer.js +48 -297
  156. package/dist/server/McpServer.js.map +1 -1
  157. package/dist/server/Scheduler.d.ts +91 -0
  158. package/dist/server/Scheduler.d.ts.map +1 -0
  159. package/dist/server/Scheduler.js +201 -0
  160. package/dist/server/Scheduler.js.map +1 -0
  161. package/dist/transports/http.d.ts +66 -0
  162. package/dist/transports/http.d.ts.map +1 -0
  163. package/dist/transports/http.js +519 -0
  164. package/dist/transports/http.js.map +1 -0
  165. package/dist/types/entities.d.ts +101 -0
  166. package/dist/types/entities.d.ts.map +1 -0
  167. package/dist/types/entities.js +5 -0
  168. package/dist/types/entities.js.map +1 -0
  169. package/dist/types/filtering.d.ts +34 -0
  170. package/dist/types/filtering.d.ts.map +1 -0
  171. package/dist/types/filtering.js +5 -0
  172. package/dist/types/filtering.js.map +1 -0
  173. package/dist/types/github.d.ts +166 -0
  174. package/dist/types/github.d.ts.map +1 -0
  175. package/dist/types/github.js +5 -0
  176. package/dist/types/github.js.map +1 -0
  177. package/dist/types/index.d.ts +35 -292
  178. package/dist/types/index.d.ts.map +1 -1
  179. package/dist/types/index.js +2 -2
  180. package/dist/types/index.js.map +1 -1
  181. package/dist/utils/error-helpers.d.ts +37 -0
  182. package/dist/utils/error-helpers.d.ts.map +1 -0
  183. package/dist/utils/error-helpers.js +47 -0
  184. package/dist/utils/error-helpers.js.map +1 -0
  185. package/dist/utils/logger.d.ts.map +1 -1
  186. package/dist/utils/logger.js +6 -3
  187. package/dist/utils/logger.js.map +1 -1
  188. package/dist/utils/security-utils.d.ts +0 -21
  189. package/dist/utils/security-utils.d.ts.map +1 -1
  190. package/dist/utils/security-utils.js +0 -47
  191. package/dist/utils/security-utils.js.map +1 -1
  192. package/dist/vector/VectorSearchManager.d.ts.map +1 -1
  193. package/dist/vector/VectorSearchManager.js +9 -32
  194. package/dist/vector/VectorSearchManager.js.map +1 -1
  195. package/docker-compose.yml +11 -2
  196. package/hooks/README.md +107 -0
  197. package/hooks/cursor/hooks.json +10 -0
  198. package/hooks/cursor/memory-journal.mdc +22 -0
  199. package/hooks/cursor/session-end.sh +19 -0
  200. package/hooks/kilo-code/session-end-mode.json +11 -0
  201. package/hooks/kiro/session-end.md +13 -0
  202. package/mcp-config-example.json +1 -0
  203. package/package.json +11 -9
  204. package/playwright.config.ts +29 -0
  205. package/releases/v4.5.0.md +116 -0
  206. package/releases/v5.0.0.md +105 -0
  207. package/scripts/generate-server-instructions.ts +176 -0
  208. package/scripts/server-instructions-function-body.ts +77 -0
  209. package/server.json +3 -3
  210. package/src/cli.ts +45 -1
  211. package/src/constants/ServerInstructions.ts +133 -73
  212. package/src/constants/icons.ts +8 -7
  213. package/src/constants/server-instructions.md +268 -0
  214. package/src/database/SqliteAdapter.ts +358 -192
  215. package/src/database/schema.ts +125 -0
  216. package/src/filtering/ToolFilter.ts +13 -2
  217. package/src/github/GitHubIntegration.ts +1 -3
  218. package/src/handlers/prompts/github.ts +209 -0
  219. package/src/handlers/prompts/index.ts +10 -499
  220. package/src/handlers/prompts/workflow.ts +314 -0
  221. package/src/handlers/resources/core.ts +528 -0
  222. package/src/handlers/resources/github.ts +358 -0
  223. package/src/handlers/resources/graph.ts +254 -0
  224. package/src/handlers/resources/index.ts +23 -1570
  225. package/src/handlers/resources/shared.ts +103 -0
  226. package/src/handlers/resources/team.ts +133 -0
  227. package/src/handlers/resources/templates.ts +374 -0
  228. package/src/handlers/tools/admin.ts +285 -0
  229. package/src/handlers/tools/analytics.ts +301 -0
  230. package/src/handlers/tools/backup.ts +242 -0
  231. package/src/handlers/tools/core.ts +350 -0
  232. package/src/handlers/tools/export.ts +115 -0
  233. package/src/handlers/tools/github/helpers.ts +86 -0
  234. package/src/handlers/tools/github/insights-tools.ts +119 -0
  235. package/src/handlers/tools/github/issue-tools.ts +439 -0
  236. package/src/handlers/tools/github/kanban-tools.ts +134 -0
  237. package/src/handlers/tools/github/milestone-tools.ts +392 -0
  238. package/src/handlers/tools/github/mutation-tools.ts +17 -0
  239. package/src/handlers/tools/github/read-tools.ts +328 -0
  240. package/src/handlers/tools/github/schemas.ts +369 -0
  241. package/src/handlers/tools/github.ts +36 -0
  242. package/src/handlers/tools/index.ts +144 -3325
  243. package/src/handlers/tools/relationships.ts +358 -0
  244. package/src/handlers/tools/schemas.ts +132 -0
  245. package/src/handlers/tools/search.ts +343 -0
  246. package/src/handlers/tools/team.ts +273 -0
  247. package/src/server/McpServer.ts +63 -358
  248. package/src/server/Scheduler.ts +278 -0
  249. package/src/transports/http.ts +635 -0
  250. package/src/types/entities.ts +145 -0
  251. package/src/types/filtering.ts +54 -0
  252. package/src/types/github.ts +180 -0
  253. package/src/types/index.ts +67 -375
  254. package/src/utils/error-helpers.ts +52 -0
  255. package/src/utils/logger.ts +6 -3
  256. package/src/utils/security-utils.ts +0 -52
  257. package/src/vector/VectorSearchManager.ts +9 -33
  258. package/tests/constants/icons.test.ts +1 -2
  259. package/tests/constants/server-instructions.test.ts +30 -4
  260. package/tests/database/sqlite-adapter.test.ts +91 -7
  261. package/tests/e2e/auth.spec.ts +154 -0
  262. package/tests/e2e/health.spec.ts +63 -0
  263. package/tests/e2e/protocols.spec.ts +134 -0
  264. package/tests/e2e/resources.spec.ts +103 -0
  265. package/tests/e2e/scheduler.spec.ts +79 -0
  266. package/tests/e2e/security.spec.ts +91 -0
  267. package/tests/e2e/sessions.spec.ts +95 -0
  268. package/tests/e2e/stateless.spec.ts +121 -0
  269. package/tests/e2e/tools.spec.ts +111 -0
  270. package/tests/filtering/tool-filter.test.ts +46 -0
  271. package/tests/handlers/error-path-coverage.test.ts +324 -0
  272. package/tests/handlers/github-resource-handlers.test.ts +453 -0
  273. package/tests/handlers/github-tool-handlers.test.ts +899 -0
  274. package/tests/handlers/prompt-handler-coverage.test.ts +106 -0
  275. package/tests/handlers/prompt-handlers.test.ts +40 -0
  276. package/tests/handlers/resource-handler-coverage.test.ts +181 -0
  277. package/tests/handlers/resource-handlers.test.ts +33 -9
  278. package/tests/handlers/search-tool-handlers.test.ts +272 -0
  279. package/tests/handlers/targeted-gap-closure.test.ts +387 -0
  280. package/tests/handlers/team-resource-handlers.test.ts +156 -0
  281. package/tests/handlers/team-tool-handlers.test.ts +301 -0
  282. package/tests/handlers/tool-handler-coverage.test.ts +469 -0
  283. package/tests/handlers/tool-handlers.test.ts +2 -2
  284. package/tests/security/sql-injection.test.ts +3 -54
  285. package/tests/server/mcp-server.test.ts +503 -8
  286. package/tests/server/scheduler.test.ts +400 -0
  287. package/tests/transports/http-transport.test.ts +620 -0
  288. package/tests/vector/vector-search-manager.test.ts +60 -0
  289. package/vitest.config.ts +4 -1
  290. package/.memory-journal-team.db +0 -0
  291. package/.vscode/settings.json +0 -84
@@ -0,0 +1,358 @@
1
+ /**
2
+ * Memory Journal MCP Server - GitHub Resource Definitions
3
+ *
4
+ * Resources: github/status, github/insights, github/milestones, milestones/{number}
5
+ */
6
+
7
+ import { ICON_GITHUB, ICON_ANALYTICS, ICON_MILESTONE } from '../../constants/icons.js'
8
+ import type { InternalResourceDef, ResourceContext, ResourceResult } from './shared.js'
9
+
10
+ /**
11
+ * Get GitHub resource definitions
12
+ */
13
+ export function getGitHubResourceDefinitions(): InternalResourceDef[] {
14
+ return [
15
+ {
16
+ uri: 'memory://github/status',
17
+ name: 'GitHub Status',
18
+ title: 'GitHub Repository Status',
19
+ description:
20
+ 'Compact GitHub status: repository, branch, CI, issues, PRs, Kanban summary',
21
+ mimeType: 'application/json',
22
+ icons: [ICON_GITHUB],
23
+ annotations: {
24
+ audience: ['assistant'],
25
+ priority: 0.7,
26
+ },
27
+ handler: async (_uri: string, context: ResourceContext): Promise<ResourceResult> => {
28
+ const lastModified = new Date().toISOString()
29
+
30
+ if (!context.github) {
31
+ return {
32
+ data: {
33
+ error: 'GitHub integration not available',
34
+ hint: 'Set GITHUB_TOKEN and GITHUB_REPO_PATH environment variables.',
35
+ },
36
+ annotations: { lastModified },
37
+ }
38
+ }
39
+
40
+ const repoInfo = await context.github.getRepoInfo()
41
+ const owner = repoInfo.owner
42
+ const repo = repoInfo.repo
43
+
44
+ if (!owner || !repo) {
45
+ return {
46
+ data: {
47
+ error: 'Could not detect repository',
48
+ hint: 'Set GITHUB_REPO_PATH to your git repository.',
49
+ branch: repoInfo.branch,
50
+ },
51
+ annotations: { lastModified },
52
+ }
53
+ }
54
+
55
+ // Get current commit
56
+ let commit: string | null = null
57
+ try {
58
+ const repoContext = await context.github.getRepoContext()
59
+ commit = repoContext.commit
60
+ } catch {
61
+ // Ignore
62
+ }
63
+
64
+ // Get open issues (limited for token efficiency)
65
+ const issues = await context.github.getIssues(owner, repo, 'open', 5)
66
+ const openIssues = issues.map((i) => ({
67
+ number: i.number,
68
+ title: i.title.slice(0, 50),
69
+ }))
70
+
71
+ // Get open PRs (limited for token efficiency)
72
+ const prs = await context.github.getPullRequests(owner, repo, 'open', 5)
73
+ const openPrs = prs.map((pr) => ({
74
+ number: pr.number,
75
+ title: pr.title.slice(0, 50),
76
+ state: pr.state,
77
+ }))
78
+
79
+ // Get CI status from latest workflow run
80
+ const workflowRuns = await context.github.getWorkflowRuns(owner, repo, 5)
81
+ let ciStatus: 'passing' | 'failing' | 'pending' | 'cancelled' | 'unknown' =
82
+ 'unknown'
83
+ let latestRun: { name: string; conclusion: string | null; headSha: string } | null =
84
+ null
85
+
86
+ if (workflowRuns.length > 0) {
87
+ const latestCompleted = workflowRuns.find((r) => r.status === 'completed')
88
+ const latest = workflowRuns[0]
89
+
90
+ latestRun = {
91
+ name: latest?.name ?? 'Unknown',
92
+ conclusion: latest?.conclusion ?? null,
93
+ headSha: latest?.headSha?.slice(0, 7) ?? '',
94
+ }
95
+
96
+ if (latestCompleted) {
97
+ switch (latestCompleted.conclusion) {
98
+ case 'success':
99
+ ciStatus = 'passing'
100
+ break
101
+ case 'failure':
102
+ ciStatus = 'failing'
103
+ break
104
+ case 'cancelled':
105
+ ciStatus = 'cancelled'
106
+ break
107
+ default:
108
+ ciStatus = 'unknown'
109
+ }
110
+ } else if (workflowRuns.some((r) => r.status !== 'completed')) {
111
+ ciStatus = 'pending'
112
+ }
113
+ }
114
+
115
+ // Get Kanban summary if project 1 exists
116
+ let kanbanSummary: Record<string, number> | null = null
117
+ try {
118
+ const kanban = await context.github.getProjectKanban(owner, 1, repo)
119
+ if (kanban) {
120
+ kanbanSummary = {}
121
+ for (const col of kanban.columns) {
122
+ kanbanSummary[col.status] = col.items.length
123
+ }
124
+ }
125
+ } catch {
126
+ // Kanban not available
127
+ }
128
+
129
+ // Get milestone summary
130
+ let milestoneSummary:
131
+ | {
132
+ number: number
133
+ title: string
134
+ state: string
135
+ openIssues: number
136
+ closedIssues: number
137
+ completionPercentage: number
138
+ dueOn: string | null
139
+ }[]
140
+ | null = null
141
+ try {
142
+ const milestones = await context.github.getMilestones(owner, repo, 'open', 5)
143
+ if (milestones.length > 0) {
144
+ milestoneSummary = milestones.map((ms) => {
145
+ const total = ms.openIssues + ms.closedIssues
146
+ const pct = total > 0 ? Math.round((ms.closedIssues / total) * 100) : 0
147
+ return {
148
+ number: ms.number,
149
+ title: ms.title,
150
+ state: ms.state,
151
+ openIssues: ms.openIssues,
152
+ closedIssues: ms.closedIssues,
153
+ completionPercentage: pct,
154
+ dueOn: ms.dueOn,
155
+ }
156
+ })
157
+ }
158
+ } catch {
159
+ // Milestones not available
160
+ }
161
+
162
+ return {
163
+ data: {
164
+ repository: `${owner}/${repo}`,
165
+ branch: repoInfo.branch,
166
+ commit: commit?.slice(0, 7) ?? null,
167
+ ci: {
168
+ status: ciStatus,
169
+ latestRun,
170
+ },
171
+ issues: {
172
+ openCount: issues.length,
173
+ items: openIssues,
174
+ },
175
+ pullRequests: {
176
+ openCount: prs.length,
177
+ items: openPrs,
178
+ },
179
+ kanbanSummary,
180
+ milestones: milestoneSummary,
181
+ },
182
+ annotations: { lastModified },
183
+ }
184
+ },
185
+ },
186
+ // Repository insights resource
187
+ {
188
+ uri: 'memory://github/insights',
189
+ name: 'Repository Insights',
190
+ title: 'Repository Stars & Traffic Summary',
191
+ description: 'Compact repo insights: stars, forks, 14-day traffic totals (~150 tokens)',
192
+ mimeType: 'application/json',
193
+ icons: [ICON_ANALYTICS],
194
+ annotations: {
195
+ audience: ['assistant'],
196
+ priority: 0.4,
197
+ },
198
+ handler: async (_uri: string, context: ResourceContext): Promise<ResourceResult> => {
199
+ const lastModified = new Date().toISOString()
200
+
201
+ if (!context.github) {
202
+ return {
203
+ data: {
204
+ error: 'GitHub integration not available',
205
+ hint: 'Set GITHUB_TOKEN and GITHUB_REPO_PATH environment variables.',
206
+ },
207
+ annotations: { lastModified },
208
+ }
209
+ }
210
+
211
+ const repoInfo = await context.github.getRepoInfo()
212
+ const owner = repoInfo.owner
213
+ const repo = repoInfo.repo
214
+
215
+ if (!owner || !repo) {
216
+ return {
217
+ data: {
218
+ error: 'Could not detect repository',
219
+ hint: 'Set GITHUB_REPO_PATH to your git repository.',
220
+ },
221
+ annotations: { lastModified },
222
+ }
223
+ }
224
+
225
+ const stats = await context.github.getRepoStats(owner, repo)
226
+
227
+ let traffic: { clones14d: number; views14d: number } | null = null
228
+ try {
229
+ const trafficData = await context.github.getTrafficData(owner, repo)
230
+ if (trafficData) {
231
+ traffic = {
232
+ clones14d: trafficData.clones.total,
233
+ views14d: trafficData.views.total,
234
+ }
235
+ }
236
+ } catch {
237
+ // Traffic data requires push access
238
+ }
239
+
240
+ return {
241
+ data: {
242
+ repository: `${owner}/${repo}`,
243
+ stars: stats?.stars ?? null,
244
+ forks: stats?.forks ?? null,
245
+ watchers: stats?.watchers ?? null,
246
+ ...(traffic ?? {}),
247
+ hint: !traffic
248
+ ? 'Traffic data requires push access to the repository.'
249
+ : undefined,
250
+ },
251
+ annotations: { lastModified },
252
+ }
253
+ },
254
+ },
255
+ // Milestone resources
256
+ {
257
+ uri: 'memory://github/milestones',
258
+ name: 'GitHub Milestones',
259
+ title: 'GitHub Repository Milestones',
260
+ description:
261
+ 'Open GitHub milestones with completion percentages, due dates, and issue counts',
262
+ mimeType: 'application/json',
263
+ icons: [ICON_MILESTONE],
264
+ annotations: {
265
+ audience: ['assistant'],
266
+ priority: 0.6,
267
+ },
268
+ handler: async (_uri: string, context: ResourceContext) => {
269
+ if (!context.github) {
270
+ return {
271
+ error: 'GitHub integration not available',
272
+ hint: 'Set GITHUB_TOKEN and GITHUB_REPO_PATH environment variables.',
273
+ }
274
+ }
275
+
276
+ const repoInfo = await context.github.getRepoInfo()
277
+ const owner = repoInfo.owner
278
+ const repo = repoInfo.repo
279
+
280
+ if (!owner || !repo) {
281
+ return {
282
+ error: 'Could not detect repository',
283
+ hint: 'Set GITHUB_REPO_PATH to your git repository.',
284
+ }
285
+ }
286
+
287
+ const milestones = await context.github.getMilestones(owner, repo, 'open', 20)
288
+ const milestonesWithProgress = milestones.map((ms) => {
289
+ const total = ms.openIssues + ms.closedIssues
290
+ const completionPercentage =
291
+ total > 0 ? Math.round((ms.closedIssues / total) * 100) : 0
292
+ return { ...ms, completionPercentage }
293
+ })
294
+
295
+ return {
296
+ repository: `${owner}/${repo}`,
297
+ milestones: milestonesWithProgress,
298
+ count: milestonesWithProgress.length,
299
+ hint: 'Use get_github_milestones tool for state filtering. Use memory://milestones/{number} for detail.',
300
+ }
301
+ },
302
+ },
303
+ {
304
+ uri: 'memory://milestones/{number}',
305
+ name: 'Milestone Detail',
306
+ title: 'GitHub Milestone Detail',
307
+ description:
308
+ 'Detailed view of a single GitHub milestone with completion progress and issue counts. Use get_github_issues with the milestone filter for individual issue details.',
309
+ mimeType: 'application/json',
310
+ icons: [ICON_MILESTONE],
311
+ annotations: {
312
+ audience: ['assistant'],
313
+ priority: 0.5,
314
+ },
315
+ handler: async (uri: string, context: ResourceContext) => {
316
+ const match = /memory:\/\/milestones\/(\d+)/.exec(uri)
317
+ const milestoneNumber = match?.[1] ? parseInt(match[1], 10) : null
318
+
319
+ if (milestoneNumber === null) {
320
+ return { error: 'Invalid milestone number' }
321
+ }
322
+
323
+ if (!context.github) {
324
+ return {
325
+ error: 'GitHub integration not available',
326
+ hint: 'Set GITHUB_TOKEN and GITHUB_REPO_PATH environment variables.',
327
+ }
328
+ }
329
+
330
+ const repoInfo = await context.github.getRepoInfo()
331
+ const owner = repoInfo.owner
332
+ const repo = repoInfo.repo
333
+
334
+ if (!owner || !repo) {
335
+ return {
336
+ error: 'Could not detect repository',
337
+ hint: 'Set GITHUB_REPO_PATH to your git repository.',
338
+ }
339
+ }
340
+
341
+ const milestone = await context.github.getMilestone(owner, repo, milestoneNumber)
342
+ if (!milestone) {
343
+ return { error: `Milestone #${String(milestoneNumber)} not found` }
344
+ }
345
+
346
+ const total = milestone.openIssues + milestone.closedIssues
347
+ const completionPercentage =
348
+ total > 0 ? Math.round((milestone.closedIssues / total) * 100) : 0
349
+
350
+ return {
351
+ repository: `${owner}/${repo}`,
352
+ milestone: { ...milestone, completionPercentage },
353
+ hint: 'Use get_github_issues tool to list issues associated with this milestone.',
354
+ }
355
+ },
356
+ },
357
+ ]
358
+ }
@@ -0,0 +1,254 @@
1
+ /**
2
+ * Memory Journal MCP Server - Graph Resource Definitions
3
+ *
4
+ * Resources: graph/recent, graph/actions, actions/recent
5
+ */
6
+
7
+ import { ICON_GRAPH, ICON_GITHUB } from '../../constants/icons.js'
8
+ import type { InternalResourceDef, ResourceContext } from './shared.js'
9
+ import { execQuery, transformEntryRow } from './shared.js'
10
+
11
+ /**
12
+ * Get graph resource definitions
13
+ */
14
+ export function getGraphResourceDefinitions(): InternalResourceDef[] {
15
+ return [
16
+ {
17
+ uri: 'memory://graph/recent',
18
+ name: 'Recent Relationship Graph',
19
+ title: 'Live Mermaid Diagram',
20
+ description: 'Live Mermaid diagram of recent relationships',
21
+ mimeType: 'text/plain',
22
+ icons: [ICON_GRAPH],
23
+ annotations: {
24
+ audience: ['user', 'assistant'],
25
+ priority: 0.5,
26
+ },
27
+ handler: (_uri: string, context: ResourceContext) => {
28
+ const relationships = execQuery(
29
+ context.db,
30
+ `
31
+ SELECT
32
+ r.id, r.from_entry_id, r.to_entry_id, r.relationship_type, r.description,
33
+ e1.content as from_content,
34
+ e2.content as to_content
35
+ FROM relationships r
36
+ JOIN memory_journal e1 ON r.from_entry_id = e1.id
37
+ JOIN memory_journal e2 ON r.to_entry_id = e2.id
38
+ WHERE e1.deleted_at IS NULL AND e2.deleted_at IS NULL
39
+ ORDER BY r.created_at DESC
40
+ LIMIT 20
41
+ `
42
+ ) as {
43
+ from_entry_id: number
44
+ to_entry_id: number
45
+ relationship_type: string
46
+ from_content: string
47
+ to_content: string
48
+ }[]
49
+
50
+ if (relationships.length === 0) {
51
+ return {
52
+ format: 'mermaid',
53
+ diagram: 'graph TD\n NoData[No relationships found]',
54
+ message:
55
+ 'No entry relationships exist yet. Use link_entries tool to create relationships.',
56
+ }
57
+ }
58
+
59
+ // Build Mermaid graph
60
+ const lines: string[] = ['graph TD']
61
+ const seenNodes = new Set<number>()
62
+
63
+ // Relationship type to arrow style mapping
64
+ const arrowStyles: Record<string, string> = {
65
+ references: '-->',
66
+ evolves_from: '-->',
67
+ depends_on: '-->',
68
+ implements: '==>',
69
+ resolved: '==>',
70
+ clarifies: '-..->',
71
+ caused: '-.->',
72
+ related_to: '<-->',
73
+ response_to: '<-->',
74
+ blocked_by: '--x',
75
+ }
76
+
77
+ for (const rel of relationships) {
78
+ if (!seenNodes.has(rel.from_entry_id)) {
79
+ const label = rel.from_content
80
+ .slice(0, 30)
81
+ .replace(/[\]"'`[]]/g, ' ')
82
+ .trim()
83
+ lines.push(
84
+ ` E${String(rel.from_entry_id)}["#${String(rel.from_entry_id)}: ${label}..."]`
85
+ )
86
+ seenNodes.add(rel.from_entry_id)
87
+ }
88
+ if (!seenNodes.has(rel.to_entry_id)) {
89
+ const label = rel.to_content
90
+ .slice(0, 30)
91
+ .replace(/[\]"'`[]]/g, ' ')
92
+ .trim()
93
+ lines.push(
94
+ ` E${String(rel.to_entry_id)}["#${String(rel.to_entry_id)}: ${label}..."]`
95
+ )
96
+ seenNodes.add(rel.to_entry_id)
97
+ }
98
+
99
+ const arrow = arrowStyles[rel.relationship_type] ?? '-->'
100
+ lines.push(
101
+ ` E${String(rel.from_entry_id)} ${arrow}|${rel.relationship_type}| E${String(rel.to_entry_id)}`
102
+ )
103
+ }
104
+
105
+ return {
106
+ format: 'mermaid',
107
+ diagram: lines.join('\n'),
108
+ relationshipCount: relationships.length,
109
+ nodeCount: seenNodes.size,
110
+ }
111
+ },
112
+ },
113
+ {
114
+ uri: 'memory://graph/actions',
115
+ name: 'Actions Graph',
116
+ title: 'CI/CD Narrative Graph',
117
+ description:
118
+ 'CI/CD narrative graph: commits → runs → failures → entries → fixes → deployments',
119
+ mimeType: 'text/plain',
120
+ icons: [ICON_GITHUB],
121
+ annotations: {
122
+ audience: ['user', 'assistant'],
123
+ priority: 0.5,
124
+ },
125
+ handler: async (_uri: string, context: ResourceContext) => {
126
+ if (!context.github) {
127
+ return {
128
+ format: 'mermaid',
129
+ diagram: 'graph LR\n NoGitHub[GitHub integration not available]',
130
+ message:
131
+ 'GitHub integration not configured. Set GITHUB_TOKEN and GITHUB_REPO_PATH.',
132
+ }
133
+ }
134
+
135
+ const repoInfo = await context.github.getRepoInfo()
136
+ if (!repoInfo.owner || !repoInfo.repo) {
137
+ return {
138
+ format: 'mermaid',
139
+ diagram: 'graph LR\n NoRepo[Repository not detected]',
140
+ message:
141
+ 'Could not detect repository. Set GITHUB_REPO_PATH in your config.',
142
+ }
143
+ }
144
+
145
+ const workflowRuns = await context.github.getWorkflowRuns(
146
+ repoInfo.owner,
147
+ repoInfo.repo,
148
+ 10
149
+ )
150
+
151
+ if (workflowRuns.length === 0) {
152
+ return {
153
+ format: 'mermaid',
154
+ diagram: 'graph LR\n NoRuns[No workflow runs found]',
155
+ message: 'No GitHub Actions workflow runs found for this repository.',
156
+ }
157
+ }
158
+
159
+ const lines: string[] = ['graph LR']
160
+
161
+ const statusStyles: Record<string, string> = {
162
+ success: ':::success',
163
+ failure: ':::failure',
164
+ cancelled: ':::cancelled',
165
+ skipped: ':::skipped',
166
+ }
167
+
168
+ lines.push(' classDef success fill:#28a745,color:#fff')
169
+ lines.push(' classDef failure fill:#dc3545,color:#fff')
170
+ lines.push(' classDef cancelled fill:#6c757d,color:#fff')
171
+ lines.push(' classDef skipped fill:#ffc107,color:#000')
172
+
173
+ for (const run of workflowRuns) {
174
+ const shortSha = run.headSha.slice(0, 7)
175
+ const nodeId = `R${String(run.id)}`
176
+ const commitId = `C${shortSha}`
177
+ const style = statusStyles[run.conclusion ?? 'skipped'] ?? ''
178
+ const statusIcon =
179
+ run.conclusion === 'success'
180
+ ? '✓'
181
+ : run.conclusion === 'failure'
182
+ ? '✗'
183
+ : '○'
184
+
185
+ lines.push(` ${commitId}["${shortSha}"]`)
186
+ lines.push(` ${nodeId}["${statusIcon} ${run.name}"]${style}`)
187
+ lines.push(` ${commitId} --> ${nodeId}`)
188
+ }
189
+
190
+ return {
191
+ format: 'mermaid',
192
+ diagram: lines.join('\n'),
193
+ workflowRunCount: workflowRuns.length,
194
+ repository: `${repoInfo.owner}/${repoInfo.repo}`,
195
+ }
196
+ },
197
+ },
198
+ {
199
+ uri: 'memory://actions/recent',
200
+ name: 'Recent Actions',
201
+ title: 'Recent Workflow Runs',
202
+ description: 'Recent workflow runs with CI status',
203
+ mimeType: 'application/json',
204
+ icons: [ICON_GITHUB],
205
+ annotations: {
206
+ audience: ['assistant'],
207
+ priority: 0.5,
208
+ },
209
+ handler: async (_uri: string, context: ResourceContext) => {
210
+ if (context.github) {
211
+ try {
212
+ const repoInfo = await context.github.getRepoInfo()
213
+ if (repoInfo.owner && repoInfo.repo) {
214
+ const runs = await context.github.getWorkflowRuns(
215
+ repoInfo.owner,
216
+ repoInfo.repo,
217
+ 10
218
+ )
219
+
220
+ const entries = runs.map((run) => ({
221
+ id: -1 * run.id,
222
+ entryType: 'tool_output',
223
+ content: `Workflow: ${run.name}\nStatus: ${run.status}\nConclusion: ${run.conclusion || 'pending'}\nBranch: ${run.headBranch}\nURL: ${run.url}`,
224
+ timestamp: run.createdAt,
225
+ isPersonal: false,
226
+ significanceType: null,
227
+ workflowRunId: run.id,
228
+ workflowName: run.name,
229
+ workflowStatus: run.conclusion || run.status,
230
+ }))
231
+
232
+ return { entries, count: entries.length, source: 'github_api' }
233
+ }
234
+ } catch {
235
+ // Fallback to DB if GitHub fails
236
+ }
237
+ }
238
+
239
+ const rows = execQuery(
240
+ context.db,
241
+ `
242
+ SELECT * FROM memory_journal
243
+ WHERE workflow_run_id IS NOT NULL
244
+ AND deleted_at IS NULL
245
+ ORDER BY timestamp DESC
246
+ LIMIT 10
247
+ `
248
+ )
249
+ const entries = rows.map(transformEntryRow)
250
+ return { entries, count: entries.length, source: 'database' }
251
+ },
252
+ },
253
+ ]
254
+ }