obsidian-accomplishments-mcp 0.1.10 → 0.1.11

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 (269) hide show
  1. package/README.md +154 -182
  2. package/dist/index.js +207 -38
  3. package/dist/index.js.map +1 -1
  4. package/dist/integration.test.d.ts +8 -0
  5. package/dist/integration.test.d.ts.map +1 -0
  6. package/dist/integration.test.js +979 -0
  7. package/dist/integration.test.js.map +1 -0
  8. package/dist/models/types.d.ts +1 -2
  9. package/dist/models/types.d.ts.map +1 -1
  10. package/dist/models/types.js.map +1 -1
  11. package/dist/models/v2-types.d.ts +460 -0
  12. package/dist/models/v2-types.d.ts.map +1 -0
  13. package/dist/models/v2-types.js +137 -0
  14. package/dist/models/v2-types.js.map +1 -0
  15. package/dist/models/v2-types.test.d.ts +5 -0
  16. package/dist/models/v2-types.test.d.ts.map +1 -0
  17. package/dist/models/v2-types.test.js +133 -0
  18. package/dist/models/v2-types.test.js.map +1 -0
  19. package/dist/parsers/canvas-parser.d.ts +1 -1
  20. package/dist/parsers/canvas-parser.d.ts.map +1 -1
  21. package/dist/parsers/canvas-parser.js +1 -1
  22. package/dist/parsers/canvas-parser.js.map +1 -1
  23. package/dist/parsers/markdown-parser.js +9 -9
  24. package/dist/parsers/markdown-parser.js.map +1 -1
  25. package/dist/services/v2/archive-manager.d.ts +96 -0
  26. package/dist/services/v2/archive-manager.d.ts.map +1 -0
  27. package/dist/services/v2/archive-manager.js +281 -0
  28. package/dist/services/v2/archive-manager.js.map +1 -0
  29. package/dist/services/v2/canvas-manager.d.ts +155 -0
  30. package/dist/services/v2/canvas-manager.d.ts.map +1 -0
  31. package/dist/services/v2/canvas-manager.js +540 -0
  32. package/dist/services/v2/canvas-manager.js.map +1 -0
  33. package/dist/services/v2/canvas-manager.test.d.ts +5 -0
  34. package/dist/services/v2/canvas-manager.test.d.ts.map +1 -0
  35. package/dist/services/v2/canvas-manager.test.js +327 -0
  36. package/dist/services/v2/canvas-manager.test.js.map +1 -0
  37. package/dist/services/v2/cascade-manager.d.ts +54 -0
  38. package/dist/services/v2/cascade-manager.d.ts.map +1 -0
  39. package/dist/services/v2/cascade-manager.js +220 -0
  40. package/dist/services/v2/cascade-manager.js.map +1 -0
  41. package/dist/services/v2/cycle-detector.d.ts +76 -0
  42. package/dist/services/v2/cycle-detector.d.ts.map +1 -0
  43. package/dist/services/v2/cycle-detector.js +183 -0
  44. package/dist/services/v2/cycle-detector.js.map +1 -0
  45. package/dist/services/v2/cycle-detector.test.d.ts +7 -0
  46. package/dist/services/v2/cycle-detector.test.d.ts.map +1 -0
  47. package/dist/services/v2/cycle-detector.test.js +125 -0
  48. package/dist/services/v2/cycle-detector.test.js.map +1 -0
  49. package/dist/services/v2/entity-parser.d.ts +54 -0
  50. package/dist/services/v2/entity-parser.d.ts.map +1 -0
  51. package/dist/services/v2/entity-parser.js +418 -0
  52. package/dist/services/v2/entity-parser.js.map +1 -0
  53. package/dist/services/v2/entity-parser.test.d.ts +5 -0
  54. package/dist/services/v2/entity-parser.test.d.ts.map +1 -0
  55. package/dist/services/v2/entity-parser.test.js +637 -0
  56. package/dist/services/v2/entity-parser.test.js.map +1 -0
  57. package/dist/services/v2/entity-serializer.d.ts +94 -0
  58. package/dist/services/v2/entity-serializer.d.ts.map +1 -0
  59. package/dist/services/v2/entity-serializer.js +583 -0
  60. package/dist/services/v2/entity-serializer.js.map +1 -0
  61. package/dist/services/v2/entity-serializer.test.d.ts +5 -0
  62. package/dist/services/v2/entity-serializer.test.d.ts.map +1 -0
  63. package/dist/services/v2/entity-serializer.test.js +241 -0
  64. package/dist/services/v2/entity-serializer.test.js.map +1 -0
  65. package/dist/services/v2/entity-validator.d.ts +65 -0
  66. package/dist/services/v2/entity-validator.d.ts.map +1 -0
  67. package/dist/services/v2/entity-validator.js +573 -0
  68. package/dist/services/v2/entity-validator.js.map +1 -0
  69. package/dist/services/v2/entity-validator.test.d.ts +5 -0
  70. package/dist/services/v2/entity-validator.test.d.ts.map +1 -0
  71. package/dist/services/v2/entity-validator.test.js +519 -0
  72. package/dist/services/v2/entity-validator.test.js.map +1 -0
  73. package/dist/services/v2/file-manager.d.ts +73 -0
  74. package/dist/services/v2/file-manager.d.ts.map +1 -0
  75. package/dist/services/v2/file-manager.js +310 -0
  76. package/dist/services/v2/file-manager.js.map +1 -0
  77. package/dist/services/v2/file-manager.test.d.ts +5 -0
  78. package/dist/services/v2/file-manager.test.d.ts.map +1 -0
  79. package/dist/services/v2/file-manager.test.js +339 -0
  80. package/dist/services/v2/file-manager.test.js.map +1 -0
  81. package/dist/services/v2/index-manager.d.ts +68 -0
  82. package/dist/services/v2/index-manager.d.ts.map +1 -0
  83. package/dist/services/v2/index-manager.js +228 -0
  84. package/dist/services/v2/index-manager.js.map +1 -0
  85. package/dist/services/v2/index-manager.test.d.ts +5 -0
  86. package/dist/services/v2/index-manager.test.d.ts.map +1 -0
  87. package/dist/services/v2/index-manager.test.js +386 -0
  88. package/dist/services/v2/index-manager.test.js.map +1 -0
  89. package/dist/services/v2/index-service.d.ts +82 -0
  90. package/dist/services/v2/index-service.d.ts.map +1 -0
  91. package/dist/services/v2/index-service.js +274 -0
  92. package/dist/services/v2/index-service.js.map +1 -0
  93. package/dist/services/v2/index-service.test.d.ts +5 -0
  94. package/dist/services/v2/index-service.test.d.ts.map +1 -0
  95. package/dist/services/v2/index-service.test.js +117 -0
  96. package/dist/services/v2/index-service.test.js.map +1 -0
  97. package/dist/services/v2/lifecycle-manager.d.ts +59 -0
  98. package/dist/services/v2/lifecycle-manager.d.ts.map +1 -0
  99. package/dist/services/v2/lifecycle-manager.js +310 -0
  100. package/dist/services/v2/lifecycle-manager.js.map +1 -0
  101. package/dist/services/v2/lifecycle-manager.test.d.ts +5 -0
  102. package/dist/services/v2/lifecycle-manager.test.d.ts.map +1 -0
  103. package/dist/services/v2/lifecycle-manager.test.js +141 -0
  104. package/dist/services/v2/lifecycle-manager.test.js.map +1 -0
  105. package/dist/services/v2/path-resolver.d.ts +64 -0
  106. package/dist/services/v2/path-resolver.d.ts.map +1 -0
  107. package/dist/services/v2/path-resolver.js +174 -0
  108. package/dist/services/v2/path-resolver.js.map +1 -0
  109. package/dist/services/v2/progress-computer.d.ts +46 -0
  110. package/dist/services/v2/progress-computer.d.ts.map +1 -0
  111. package/dist/services/v2/progress-computer.js +200 -0
  112. package/dist/services/v2/progress-computer.js.map +1 -0
  113. package/dist/services/v2/search-service.d.ts +68 -0
  114. package/dist/services/v2/search-service.d.ts.map +1 -0
  115. package/dist/services/v2/search-service.js +194 -0
  116. package/dist/services/v2/search-service.js.map +1 -0
  117. package/dist/services/v2/transitive-dependency-remover.d.ts +54 -0
  118. package/dist/services/v2/transitive-dependency-remover.d.ts.map +1 -0
  119. package/dist/services/v2/transitive-dependency-remover.js +156 -0
  120. package/dist/services/v2/transitive-dependency-remover.js.map +1 -0
  121. package/dist/services/v2/transitive-dependency-remover.test.d.ts +7 -0
  122. package/dist/services/v2/transitive-dependency-remover.test.d.ts.map +1 -0
  123. package/dist/services/v2/transitive-dependency-remover.test.js +119 -0
  124. package/dist/services/v2/transitive-dependency-remover.test.js.map +1 -0
  125. package/dist/services/v2/v2-runtime.d.ts +374 -0
  126. package/dist/services/v2/v2-runtime.d.ts.map +1 -0
  127. package/dist/services/v2/v2-runtime.js +1908 -0
  128. package/dist/services/v2/v2-runtime.js.map +1 -0
  129. package/dist/services/v2/v2-runtime.test.d.ts +5 -0
  130. package/dist/services/v2/v2-runtime.test.d.ts.map +1 -0
  131. package/dist/services/v2/v2-runtime.test.js +658 -0
  132. package/dist/services/v2/v2-runtime.test.js.map +1 -0
  133. package/dist/services/v2/workstream-normalizer.d.ts +59 -0
  134. package/dist/services/v2/workstream-normalizer.d.ts.map +1 -0
  135. package/dist/services/v2/workstream-normalizer.js +137 -0
  136. package/dist/services/v2/workstream-normalizer.js.map +1 -0
  137. package/dist/services/v2/workstream-normalizer.test.d.ts +7 -0
  138. package/dist/services/v2/workstream-normalizer.test.d.ts.map +1 -0
  139. package/dist/services/v2/workstream-normalizer.test.js +130 -0
  140. package/dist/services/v2/workstream-normalizer.test.js.map +1 -0
  141. package/dist/test-runner.d.ts +4 -1
  142. package/dist/test-runner.d.ts.map +1 -1
  143. package/dist/test-runner.js +44 -249
  144. package/dist/test-runner.js.map +1 -1
  145. package/dist/tools/batch-operations-tools.d.ts +54 -0
  146. package/dist/tools/batch-operations-tools.d.ts.map +1 -0
  147. package/dist/tools/batch-operations-tools.js +370 -0
  148. package/dist/tools/batch-operations-tools.js.map +1 -0
  149. package/dist/tools/decision-document-tools.d.ts +78 -0
  150. package/dist/tools/decision-document-tools.d.ts.map +1 -0
  151. package/dist/tools/decision-document-tools.js +260 -0
  152. package/dist/tools/decision-document-tools.js.map +1 -0
  153. package/dist/tools/entity-management-tools.d.ts +79 -0
  154. package/dist/tools/entity-management-tools.d.ts.map +1 -0
  155. package/dist/tools/entity-management-tools.js +851 -0
  156. package/dist/tools/entity-management-tools.js.map +1 -0
  157. package/dist/tools/entity-management-tools.test.d.ts +5 -0
  158. package/dist/tools/entity-management-tools.test.d.ts.map +1 -0
  159. package/dist/tools/entity-management-tools.test.js +530 -0
  160. package/dist/tools/entity-management-tools.test.js.map +1 -0
  161. package/dist/tools/index.d.ts +15 -331
  162. package/dist/tools/index.d.ts.map +1 -1
  163. package/dist/tools/index.js +510 -47
  164. package/dist/tools/index.js.map +1 -1
  165. package/dist/tools/index.test.d.ts +8 -0
  166. package/dist/tools/index.test.d.ts.map +1 -0
  167. package/dist/tools/index.test.js +429 -0
  168. package/dist/tools/index.test.js.map +1 -0
  169. package/dist/tools/project-understanding-tools.d.ts +75 -0
  170. package/dist/tools/project-understanding-tools.d.ts.map +1 -0
  171. package/dist/tools/project-understanding-tools.js +751 -0
  172. package/dist/tools/project-understanding-tools.js.map +1 -0
  173. package/dist/tools/search-navigation-tools.d.ts +77 -0
  174. package/dist/tools/search-navigation-tools.d.ts.map +1 -0
  175. package/dist/tools/search-navigation-tools.js +379 -0
  176. package/dist/tools/search-navigation-tools.js.map +1 -0
  177. package/dist/tools/tool-types.d.ts +703 -0
  178. package/dist/tools/tool-types.d.ts.map +1 -0
  179. package/dist/tools/tool-types.js +7 -0
  180. package/dist/tools/tool-types.js.map +1 -0
  181. package/dist/utils/config.d.ts +0 -4
  182. package/dist/utils/config.d.ts.map +1 -1
  183. package/dist/utils/config.js +2 -19
  184. package/dist/utils/config.js.map +1 -1
  185. package/package.json +16 -1
  186. package/dist/services/accomplishment-service.d.ts +0 -33
  187. package/dist/services/accomplishment-service.d.ts.map +0 -1
  188. package/dist/services/accomplishment-service.js +0 -296
  189. package/dist/services/accomplishment-service.js.map +0 -1
  190. package/dist/services/canvas-service.d.ts +0 -96
  191. package/dist/services/canvas-service.d.ts.map +0 -1
  192. package/dist/services/canvas-service.js +0 -231
  193. package/dist/services/canvas-service.js.map +0 -1
  194. package/dist/services/context-doc-service.d.ts +0 -70
  195. package/dist/services/context-doc-service.d.ts.map +0 -1
  196. package/dist/services/context-doc-service.js +0 -229
  197. package/dist/services/context-doc-service.js.map +0 -1
  198. package/dist/services/dependency-service.d.ts +0 -22
  199. package/dist/services/dependency-service.d.ts.map +0 -1
  200. package/dist/services/dependency-service.js +0 -99
  201. package/dist/services/dependency-service.js.map +0 -1
  202. package/dist/services/status-indicator-service.d.ts +0 -40
  203. package/dist/services/status-indicator-service.d.ts.map +0 -1
  204. package/dist/services/status-indicator-service.js +0 -173
  205. package/dist/services/status-indicator-service.js.map +0 -1
  206. package/dist/services/task-service.d.ts +0 -32
  207. package/dist/services/task-service.d.ts.map +0 -1
  208. package/dist/services/task-service.js +0 -152
  209. package/dist/services/task-service.js.map +0 -1
  210. package/dist/test-real-vault.d.ts +0 -6
  211. package/dist/test-real-vault.d.ts.map +0 -1
  212. package/dist/test-real-vault.js +0 -30
  213. package/dist/test-real-vault.js.map +0 -1
  214. package/dist/tools/batch-operations.d.ts +0 -246
  215. package/dist/tools/batch-operations.d.ts.map +0 -1
  216. package/dist/tools/batch-operations.js +0 -235
  217. package/dist/tools/batch-operations.js.map +0 -1
  218. package/dist/tools/get-accomplishment.d.ts +0 -42
  219. package/dist/tools/get-accomplishment.d.ts.map +0 -1
  220. package/dist/tools/get-accomplishment.js +0 -93
  221. package/dist/tools/get-accomplishment.js.map +0 -1
  222. package/dist/tools/get-accomplishments-graph.d.ts +0 -26
  223. package/dist/tools/get-accomplishments-graph.d.ts.map +0 -1
  224. package/dist/tools/get-accomplishments-graph.js +0 -137
  225. package/dist/tools/get-accomplishments-graph.js.map +0 -1
  226. package/dist/tools/get-blocked-items.d.ts +0 -15
  227. package/dist/tools/get-blocked-items.d.ts.map +0 -1
  228. package/dist/tools/get-blocked-items.js +0 -73
  229. package/dist/tools/get-blocked-items.js.map +0 -1
  230. package/dist/tools/get-current-work.d.ts +0 -15
  231. package/dist/tools/get-current-work.d.ts.map +0 -1
  232. package/dist/tools/get-current-work.js +0 -68
  233. package/dist/tools/get-current-work.js.map +0 -1
  234. package/dist/tools/get-project-status.d.ts +0 -26
  235. package/dist/tools/get-project-status.d.ts.map +0 -1
  236. package/dist/tools/get-project-status.js +0 -98
  237. package/dist/tools/get-project-status.js.map +0 -1
  238. package/dist/tools/get-ready-to-start.d.ts +0 -15
  239. package/dist/tools/get-ready-to-start.d.ts.map +0 -1
  240. package/dist/tools/get-ready-to-start.js +0 -47
  241. package/dist/tools/get-ready-to-start.js.map +0 -1
  242. package/dist/tools/list-accomplishments.d.ts +0 -42
  243. package/dist/tools/list-accomplishments.d.ts.map +0 -1
  244. package/dist/tools/list-accomplishments.js +0 -40
  245. package/dist/tools/list-accomplishments.js.map +0 -1
  246. package/dist/tools/manage-accomplishment.d.ts +0 -147
  247. package/dist/tools/manage-accomplishment.d.ts.map +0 -1
  248. package/dist/tools/manage-accomplishment.js +0 -153
  249. package/dist/tools/manage-accomplishment.js.map +0 -1
  250. package/dist/tools/manage-dependency.d.ts +0 -41
  251. package/dist/tools/manage-dependency.d.ts.map +0 -1
  252. package/dist/tools/manage-dependency.js +0 -66
  253. package/dist/tools/manage-dependency.js.map +0 -1
  254. package/dist/tools/manage-task.d.ts +0 -119
  255. package/dist/tools/manage-task.d.ts.map +0 -1
  256. package/dist/tools/manage-task.js +0 -126
  257. package/dist/tools/manage-task.js.map +0 -1
  258. package/dist/tools/reconcile-canvas.d.ts +0 -33
  259. package/dist/tools/reconcile-canvas.d.ts.map +0 -1
  260. package/dist/tools/reconcile-canvas.js +0 -41
  261. package/dist/tools/reconcile-canvas.js.map +0 -1
  262. package/dist/tools/set-work-focus.d.ts +0 -48
  263. package/dist/tools/set-work-focus.d.ts.map +0 -1
  264. package/dist/tools/set-work-focus.js +0 -78
  265. package/dist/tools/set-work-focus.js.map +0 -1
  266. package/dist/tools/sync-dependencies.d.ts +0 -33
  267. package/dist/tools/sync-dependencies.d.ts.map +0 -1
  268. package/dist/tools/sync-dependencies.js +0 -144
  269. package/dist/tools/sync-dependencies.js.map +0 -1
@@ -0,0 +1,658 @@
1
+ /**
2
+ * Tests for V2 Runtime
3
+ */
4
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
5
+ import { V2Runtime, getV2Runtime, resetV2Runtime } from './v2-runtime.js';
6
+ import * as fs from 'fs/promises';
7
+ import * as path from 'path';
8
+ import * as os from 'os';
9
+ describe('V2Runtime', () => {
10
+ let tempDir;
11
+ let config;
12
+ let runtime;
13
+ beforeEach(async () => {
14
+ // Reset singleton
15
+ resetV2Runtime();
16
+ // Create a unique temp directory for each test
17
+ tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'v2-runtime-test-'));
18
+ // Create entity folders
19
+ const entitiesFolder = 'accomplishments';
20
+ await fs.mkdir(path.join(tempDir, entitiesFolder, 'milestones'), { recursive: true });
21
+ await fs.mkdir(path.join(tempDir, entitiesFolder, 'stories'), { recursive: true });
22
+ await fs.mkdir(path.join(tempDir, entitiesFolder, 'tasks'), { recursive: true });
23
+ await fs.mkdir(path.join(tempDir, entitiesFolder, 'decisions'), { recursive: true });
24
+ await fs.mkdir(path.join(tempDir, entitiesFolder, 'documents'), { recursive: true });
25
+ await fs.mkdir(path.join(tempDir, entitiesFolder, 'archive'), { recursive: true });
26
+ config = {
27
+ vaultPath: tempDir,
28
+ entitiesFolder,
29
+ archiveFolder: `${entitiesFolder}/archive`,
30
+ canvasFolder: entitiesFolder,
31
+ defaultCanvas: 'canvas.canvas',
32
+ workspaces: {},
33
+ };
34
+ runtime = new V2Runtime(config);
35
+ });
36
+ afterEach(async () => {
37
+ // Clean up temp directory
38
+ try {
39
+ await fs.rm(tempDir, { recursive: true, force: true });
40
+ }
41
+ catch {
42
+ // Ignore cleanup errors
43
+ }
44
+ resetV2Runtime();
45
+ });
46
+ // Helper to create a milestone file
47
+ async function createMilestoneFile(id, title, workstream = 'engineering') {
48
+ const content = `---
49
+ id: ${id}
50
+ title: ${title}
51
+ workstream: ${workstream}
52
+ status: In Progress
53
+ archived: false
54
+ ---
55
+ # ${title}
56
+
57
+ ## Objective
58
+ Test objective.`;
59
+ await fs.writeFile(path.join(tempDir, config.entitiesFolder, 'milestones', `${id} ${title}.md`), content, 'utf-8');
60
+ }
61
+ // Helper to create a story file
62
+ async function createStoryFile(id, title, parent, workstream = 'engineering') {
63
+ const content = `---
64
+ id: ${id}
65
+ title: ${title}
66
+ workstream: ${workstream}
67
+ status: In Progress
68
+ parent: ${parent}
69
+ archived: false
70
+ ---
71
+ # ${title}
72
+
73
+ ## Outcome
74
+ Test outcome.`;
75
+ await fs.writeFile(path.join(tempDir, config.entitiesFolder, 'stories', `${id} ${title}.md`), content, 'utf-8');
76
+ }
77
+ // Helper to create a task file
78
+ async function createTaskFile(id, title, parent, workstream = 'engineering') {
79
+ const content = `---
80
+ id: ${id}
81
+ title: ${title}
82
+ workstream: ${workstream}
83
+ status: In Progress
84
+ parent: ${parent}
85
+ goal: Test goal
86
+ archived: false
87
+ ---
88
+ # ${title}
89
+
90
+ ## Description
91
+ Test description.`;
92
+ await fs.writeFile(path.join(tempDir, config.entitiesFolder, 'tasks', `${id} ${title}.md`), content, 'utf-8');
93
+ }
94
+ // Helper to create a decision file
95
+ async function createDecisionFile(id, title, workstream = 'engineering') {
96
+ const content = `---
97
+ id: ${id}
98
+ title: ${title}
99
+ workstream: ${workstream}
100
+ status: Decided
101
+ archived: false
102
+ ---
103
+ # ${title}
104
+
105
+ ## Context
106
+ Test context.`;
107
+ await fs.writeFile(path.join(tempDir, config.entitiesFolder, 'decisions', `${id} ${title}.md`), content, 'utf-8');
108
+ }
109
+ // Helper to create a document file
110
+ async function createDocumentFile(id, title, workstream = 'engineering') {
111
+ const content = `---
112
+ id: ${id}
113
+ title: ${title}
114
+ workstream: ${workstream}
115
+ status: Draft
116
+ doc_type: spec
117
+ archived: false
118
+ ---
119
+ # ${title}
120
+
121
+ ## Content
122
+ Test content.`;
123
+ await fs.writeFile(path.join(tempDir, config.entitiesFolder, 'documents', `${id} ${title}.md`), content, 'utf-8');
124
+ }
125
+ describe('initialize', () => {
126
+ it('should initialize and scan vault', async () => {
127
+ await createMilestoneFile('M-001', 'Test Milestone');
128
+ await createStoryFile('S-001', 'Test Story', 'M-001');
129
+ await runtime.initialize();
130
+ const milestone = await runtime.getEntity('M-001');
131
+ expect(milestone).not.toBeNull();
132
+ expect(milestone?.title).toBe('Test Milestone');
133
+ const story = await runtime.getEntity('S-001');
134
+ expect(story).not.toBeNull();
135
+ expect(story?.title).toBe('Test Story');
136
+ });
137
+ it('should handle empty vault', async () => {
138
+ await runtime.initialize();
139
+ const entities = await runtime.getAllEntities();
140
+ expect(entities).toEqual([]);
141
+ });
142
+ it('should detect duplicate IDs', async () => {
143
+ // Create two files with the same ID
144
+ await createMilestoneFile('M-001', 'First Milestone');
145
+ const content = `---
146
+ id: M-001
147
+ title: Duplicate Milestone
148
+ workstream: engineering
149
+ status: In Progress
150
+ ---
151
+ # Duplicate`;
152
+ await fs.writeFile(path.join(tempDir, config.entitiesFolder, 'milestones', 'M-001 Duplicate.md'), content, 'utf-8');
153
+ await runtime.initialize();
154
+ expect(runtime.hasDuplicateIds()).toBe(true);
155
+ const duplicates = runtime.getDuplicateIds();
156
+ expect(duplicates.has('M-001')).toBe(true);
157
+ });
158
+ });
159
+ describe('getEntity', () => {
160
+ it('should return entity by ID', async () => {
161
+ await createMilestoneFile('M-001', 'Test Milestone');
162
+ await runtime.initialize();
163
+ const entity = await runtime.getEntity('M-001');
164
+ expect(entity).not.toBeNull();
165
+ expect(entity?.id).toBe('M-001');
166
+ expect(entity?.type).toBe('milestone');
167
+ });
168
+ it('should return null for non-existent entity', async () => {
169
+ await runtime.initialize();
170
+ const entity = await runtime.getEntity('M-999');
171
+ expect(entity).toBeNull();
172
+ });
173
+ });
174
+ describe('getAllEntities', () => {
175
+ it('should return all entities', async () => {
176
+ await createMilestoneFile('M-001', 'Milestone 1');
177
+ await createMilestoneFile('M-002', 'Milestone 2');
178
+ await createStoryFile('S-001', 'Story 1', 'M-001');
179
+ await runtime.initialize();
180
+ const entities = await runtime.getAllEntities({ includeCompleted: true });
181
+ expect(entities.length).toBe(3);
182
+ });
183
+ it('should filter by type', async () => {
184
+ await createMilestoneFile('M-001', 'Milestone');
185
+ await createStoryFile('S-001', 'Story', 'M-001');
186
+ await runtime.initialize();
187
+ const milestones = await runtime.getAllEntities({ types: ['milestone'], includeCompleted: true });
188
+ expect(milestones.length).toBe(1);
189
+ expect(milestones[0].type).toBe('milestone');
190
+ });
191
+ it('should filter by workstream', async () => {
192
+ await createMilestoneFile('M-001', 'Engineering Milestone', 'engineering');
193
+ await createMilestoneFile('M-002', 'Design Milestone', 'design');
194
+ await runtime.initialize();
195
+ const entities = await runtime.getAllEntities({ workstream: 'engineering', includeCompleted: true });
196
+ expect(entities.length).toBe(1);
197
+ expect(entities[0].id).toBe('M-001');
198
+ });
199
+ it('should exclude archived by default', async () => {
200
+ await createMilestoneFile('M-001', 'Active Milestone');
201
+ const archivedContent = `---
202
+ id: M-002
203
+ title: Archived Milestone
204
+ workstream: engineering
205
+ status: Completed
206
+ archived: true
207
+ ---
208
+ # Archived`;
209
+ await fs.writeFile(path.join(tempDir, config.entitiesFolder, 'milestones', 'M-002 Archived.md'), archivedContent, 'utf-8');
210
+ await runtime.initialize();
211
+ const entities = await runtime.getAllEntities({ includeCompleted: true });
212
+ expect(entities.length).toBe(1);
213
+ expect(entities[0].id).toBe('M-001');
214
+ });
215
+ it('should include archived when requested', async () => {
216
+ await createMilestoneFile('M-001', 'Active Milestone');
217
+ const archivedContent = `---
218
+ id: M-002
219
+ title: Archived Milestone
220
+ workstream: engineering
221
+ status: Completed
222
+ archived: true
223
+ ---
224
+ # Archived`;
225
+ await fs.writeFile(path.join(tempDir, config.entitiesFolder, 'milestones', 'M-002 Archived.md'), archivedContent, 'utf-8');
226
+ await runtime.initialize();
227
+ const entities = await runtime.getAllEntities({ includeArchived: true, includeCompleted: true });
228
+ expect(entities.length).toBe(2);
229
+ });
230
+ });
231
+ describe('getNextId', () => {
232
+ it('should generate sequential IDs for milestones', async () => {
233
+ await runtime.initialize();
234
+ // First call returns M-001 (no existing milestones)
235
+ const id1 = await runtime.getNextId('milestone');
236
+ expect(id1).toBe('M-001');
237
+ // Without writing an entity, calling again returns the same ID
238
+ // because the vault scan finds no existing entities
239
+ const id2 = await runtime.getNextId('milestone');
240
+ expect(id2).toBe('M-001');
241
+ });
242
+ it('should generate sequential IDs for stories', async () => {
243
+ await runtime.initialize();
244
+ // First call returns S-001 (no existing stories)
245
+ const id1 = await runtime.getNextId('story');
246
+ expect(id1).toBe('S-001');
247
+ // Without writing an entity, calling again returns the same ID
248
+ const id2 = await runtime.getNextId('story');
249
+ expect(id2).toBe('S-001');
250
+ });
251
+ it('should generate sequential IDs for tasks', async () => {
252
+ await runtime.initialize();
253
+ const id1 = await runtime.getNextId('task');
254
+ expect(id1).toBe('T-001');
255
+ });
256
+ it('should generate sequential IDs for decisions', async () => {
257
+ await runtime.initialize();
258
+ const id1 = await runtime.getNextId('decision');
259
+ expect(id1).toBe('DEC-001');
260
+ });
261
+ it('should generate sequential IDs for documents', async () => {
262
+ await runtime.initialize();
263
+ const id1 = await runtime.getNextId('document');
264
+ expect(id1).toBe('DOC-001');
265
+ });
266
+ it('should continue from highest existing ID', async () => {
267
+ await createMilestoneFile('M-005', 'Existing Milestone');
268
+ await runtime.initialize();
269
+ const nextId = await runtime.getNextId('milestone');
270
+ expect(nextId).toBe('M-006');
271
+ });
272
+ });
273
+ describe('entityExists', () => {
274
+ it('should return true for existing entity', async () => {
275
+ await createMilestoneFile('M-001', 'Test');
276
+ await runtime.initialize();
277
+ expect(runtime.entityExists('M-001')).toBe(true);
278
+ });
279
+ it('should return false for non-existent entity', async () => {
280
+ await runtime.initialize();
281
+ expect(runtime.entityExists('M-999')).toBe(false);
282
+ });
283
+ });
284
+ describe('getEntityTypeFromCache', () => {
285
+ it('should return entity type', async () => {
286
+ await createMilestoneFile('M-001', 'Test');
287
+ await createStoryFile('S-001', 'Story', 'M-001');
288
+ await runtime.initialize();
289
+ expect(runtime.getEntityTypeFromCache('M-001')).toBe('milestone');
290
+ expect(runtime.getEntityTypeFromCache('S-001')).toBe('story');
291
+ });
292
+ it('should return null for non-existent entity', async () => {
293
+ await runtime.initialize();
294
+ expect(runtime.getEntityTypeFromCache('M-999')).toBeNull();
295
+ });
296
+ });
297
+ describe('writeEntity', () => {
298
+ it('should write entity to file', async () => {
299
+ await runtime.initialize();
300
+ const milestone = {
301
+ id: 'M-001',
302
+ type: 'milestone',
303
+ title: 'New Milestone',
304
+ workstream: 'engineering',
305
+ status: 'In Progress',
306
+ archived: false,
307
+ created_at: new Date().toISOString(),
308
+ updated_at: new Date().toISOString(),
309
+ canvas_source: '',
310
+ cssclasses: [],
311
+ vault_path: '',
312
+ priority: 'High',
313
+ depends_on: [],
314
+ };
315
+ await runtime.writeEntity(milestone);
316
+ const retrieved = await runtime.getEntity('M-001');
317
+ expect(retrieved).not.toBeNull();
318
+ expect(retrieved?.title).toBe('New Milestone');
319
+ });
320
+ });
321
+ describe('getChildren', () => {
322
+ it('should return children of milestone', async () => {
323
+ await createMilestoneFile('M-001', 'Parent Milestone');
324
+ await createStoryFile('S-001', 'Child Story 1', 'M-001');
325
+ await createStoryFile('S-002', 'Child Story 2', 'M-001');
326
+ await runtime.initialize();
327
+ const children = await runtime.getChildren('M-001');
328
+ expect(children.length).toBe(2);
329
+ expect(children.map(c => c.id)).toContain('S-001');
330
+ expect(children.map(c => c.id)).toContain('S-002');
331
+ });
332
+ it('should return children of story', async () => {
333
+ await createMilestoneFile('M-001', 'Milestone');
334
+ await createStoryFile('S-001', 'Story', 'M-001');
335
+ await createTaskFile('T-001', 'Task 1', 'S-001');
336
+ await createTaskFile('T-002', 'Task 2', 'S-001');
337
+ await runtime.initialize();
338
+ const children = await runtime.getChildren('S-001');
339
+ expect(children.length).toBe(2);
340
+ });
341
+ it('should return empty array for entity without children', async () => {
342
+ await createMilestoneFile('M-001', 'Milestone');
343
+ await runtime.initialize();
344
+ const children = await runtime.getChildren('M-001');
345
+ expect(children).toEqual([]);
346
+ });
347
+ });
348
+ describe('getParent', () => {
349
+ it('should return parent of story', async () => {
350
+ await createMilestoneFile('M-001', 'Parent');
351
+ await createStoryFile('S-001', 'Child', 'M-001');
352
+ await runtime.initialize();
353
+ const parent = await runtime.getParent('S-001');
354
+ expect(parent).not.toBeNull();
355
+ expect(parent?.id).toBe('M-001');
356
+ });
357
+ it('should return parent of task', async () => {
358
+ await createMilestoneFile('M-001', 'Milestone');
359
+ await createStoryFile('S-001', 'Story', 'M-001');
360
+ await createTaskFile('T-001', 'Task', 'S-001');
361
+ await runtime.initialize();
362
+ const parent = await runtime.getParent('T-001');
363
+ expect(parent).not.toBeNull();
364
+ expect(parent?.id).toBe('S-001');
365
+ });
366
+ it('should return null for milestone (no parent)', async () => {
367
+ await createMilestoneFile('M-001', 'Milestone');
368
+ await runtime.initialize();
369
+ const parent = await runtime.getParent('M-001');
370
+ expect(parent).toBeNull();
371
+ });
372
+ it('should return null for non-existent entity', async () => {
373
+ await runtime.initialize();
374
+ const parent = await runtime.getParent('M-999');
375
+ expect(parent).toBeNull();
376
+ });
377
+ });
378
+ describe('getSiblings', () => {
379
+ it('should return siblings of story', async () => {
380
+ await createMilestoneFile('M-001', 'Milestone');
381
+ await createStoryFile('S-001', 'Story 1', 'M-001');
382
+ await createStoryFile('S-002', 'Story 2', 'M-001');
383
+ await createStoryFile('S-003', 'Story 3', 'M-001');
384
+ await runtime.initialize();
385
+ const siblings = await runtime.getSiblings('S-001');
386
+ expect(siblings.length).toBe(2);
387
+ expect(siblings.map(s => s.id)).toContain('S-002');
388
+ expect(siblings.map(s => s.id)).toContain('S-003');
389
+ expect(siblings.map(s => s.id)).not.toContain('S-001');
390
+ });
391
+ it('should return same-type entities for top-level entities', async () => {
392
+ await createMilestoneFile('M-001', 'Milestone 1');
393
+ await createMilestoneFile('M-002', 'Milestone 2');
394
+ await runtime.initialize();
395
+ const siblings = await runtime.getSiblings('M-001');
396
+ expect(siblings.length).toBe(1);
397
+ expect(siblings[0].id).toBe('M-002');
398
+ });
399
+ });
400
+ describe('toEntitySummary', () => {
401
+ it('should convert entity to summary', async () => {
402
+ await createMilestoneFile('M-001', 'Test Milestone');
403
+ await runtime.initialize();
404
+ const entity = await runtime.getEntity('M-001');
405
+ const summary = runtime.toEntitySummary(entity);
406
+ expect(summary.id).toBe('M-001');
407
+ expect(summary.type).toBe('milestone');
408
+ expect(summary.title).toBe('Test Milestone');
409
+ expect(summary.status).toBe('In Progress');
410
+ expect(summary.workstream).toBe('engineering');
411
+ });
412
+ });
413
+ describe('toEntityFull', () => {
414
+ it('should convert entity to full representation', async () => {
415
+ await createMilestoneFile('M-001', 'Test Milestone');
416
+ await createStoryFile('S-001', 'Child Story', 'M-001');
417
+ await runtime.initialize();
418
+ const entity = await runtime.getEntity('M-001');
419
+ const full = await runtime.toEntityFull(entity);
420
+ expect(full.id).toBe('M-001');
421
+ expect(full.children_count).toBe(1);
422
+ expect(full.children?.length).toBe(1);
423
+ expect(full.children?.[0].id).toBe('S-001');
424
+ });
425
+ });
426
+ describe('searchEntities', () => {
427
+ it('should search entities by query', async () => {
428
+ await createMilestoneFile('M-001', 'Authentication Feature');
429
+ await createMilestoneFile('M-002', 'Database Migration');
430
+ await runtime.initialize();
431
+ const results = await runtime.searchEntities('authentication');
432
+ expect(results.length).toBeGreaterThan(0);
433
+ expect(results[0].entity.id).toBe('M-001');
434
+ });
435
+ it('should filter search by type', async () => {
436
+ await createMilestoneFile('M-001', 'Auth Milestone');
437
+ await createStoryFile('S-001', 'Auth Story', 'M-001');
438
+ await runtime.initialize();
439
+ const results = await runtime.searchEntities('auth', { types: ['story'] });
440
+ expect(results.every(r => r.entity.type === 'story')).toBe(true);
441
+ });
442
+ });
443
+ describe('getTaskProgress', () => {
444
+ it('should return task progress for story', async () => {
445
+ await createMilestoneFile('M-001', 'Milestone');
446
+ await createStoryFile('S-001', 'Story', 'M-001');
447
+ await createTaskFile('T-001', 'Task 1', 'S-001');
448
+ await createTaskFile('T-002', 'Task 2', 'S-001');
449
+ await runtime.initialize();
450
+ const progress = await runtime.getTaskProgress('S-001');
451
+ expect(progress.total).toBe(2);
452
+ expect(progress.completed).toBe(0);
453
+ });
454
+ });
455
+ describe('getAllDecisions', () => {
456
+ it('should return all decisions', async () => {
457
+ await createDecisionFile('DEC-001', 'Decision 1');
458
+ await createDecisionFile('DEC-002', 'Decision 2');
459
+ await runtime.initialize();
460
+ const decisions = await runtime.getAllDecisions();
461
+ expect(decisions.length).toBe(2);
462
+ });
463
+ it('should filter by workstream', async () => {
464
+ await createDecisionFile('DEC-001', 'Engineering Decision', 'engineering');
465
+ await createDecisionFile('DEC-002', 'Design Decision', 'design');
466
+ await runtime.initialize();
467
+ const decisions = await runtime.getAllDecisions({ workstream: 'engineering' });
468
+ expect(decisions.length).toBe(1);
469
+ expect(decisions[0].id).toBe('DEC-001');
470
+ });
471
+ });
472
+ describe('getAllDocuments', () => {
473
+ it('should return all documents', async () => {
474
+ await createDocumentFile('DOC-001', 'Document 1');
475
+ await createDocumentFile('DOC-002', 'Document 2');
476
+ await runtime.initialize();
477
+ const documents = await runtime.getAllDocuments();
478
+ expect(documents.length).toBe(2);
479
+ });
480
+ });
481
+ describe('getAllStories', () => {
482
+ it('should return all stories', async () => {
483
+ await createMilestoneFile('M-001', 'Milestone');
484
+ await createStoryFile('S-001', 'Story 1', 'M-001');
485
+ await createStoryFile('S-002', 'Story 2', 'M-001');
486
+ await runtime.initialize();
487
+ const stories = await runtime.getAllStories();
488
+ expect(stories.length).toBe(2);
489
+ });
490
+ });
491
+ describe('createDecision', () => {
492
+ it('should create a new decision', async () => {
493
+ await runtime.initialize();
494
+ const decision = await runtime.createDecision({
495
+ title: 'Use TypeScript',
496
+ context: 'Need to choose a language',
497
+ decision: 'Use TypeScript',
498
+ rationale: 'Type safety',
499
+ workstream: 'engineering',
500
+ decided_by: 'tech-lead',
501
+ });
502
+ expect(decision.id).toBe('DEC-001');
503
+ expect(decision.title).toBe('Use TypeScript');
504
+ expect(decision.status).toBe('Decided');
505
+ });
506
+ });
507
+ describe('updateDocument', () => {
508
+ it('should update document', async () => {
509
+ await createDocumentFile('DOC-001', 'Original Title');
510
+ await runtime.initialize();
511
+ const updated = await runtime.updateDocument('DOC-001', {
512
+ title: 'Updated Title',
513
+ });
514
+ expect(updated.title).toBe('Updated Title');
515
+ });
516
+ it('should throw for non-existent document', async () => {
517
+ await runtime.initialize();
518
+ await expect(runtime.updateDocument('DOC-999', { title: 'Test' }))
519
+ .rejects.toThrow('Document not found');
520
+ });
521
+ });
522
+ describe('generateId', () => {
523
+ it('should generate decision ID', async () => {
524
+ await runtime.initialize();
525
+ const id = await runtime.generateId('decision');
526
+ expect(id).toBe('DEC-001');
527
+ });
528
+ it('should generate document ID', async () => {
529
+ await runtime.initialize();
530
+ const id = await runtime.generateId('document');
531
+ expect(id).toBe('DOC-001');
532
+ });
533
+ });
534
+ describe('hasOpenTodos', () => {
535
+ it('should detect open TODOs in entity content fields', async () => {
536
+ // hasOpenTodos checks getEntityContent() which returns field values
537
+ // For tasks: [goal, description, technical_notes, notes]
538
+ // So we need to put TODO in one of those fields
539
+ const content = `---
540
+ id: T-001
541
+ title: Task with TODO
542
+ workstream: engineering
543
+ status: In Progress
544
+ parent: S-001
545
+ goal: Complete the feature - TODO add error handling
546
+ ---
547
+ # Task`;
548
+ await fs.writeFile(path.join(tempDir, config.entitiesFolder, 'tasks', 'T-001 Task.md'), content, 'utf-8');
549
+ await runtime.initialize();
550
+ const hasTodos = await runtime.hasOpenTodos('T-001');
551
+ expect(hasTodos).toBe(true);
552
+ });
553
+ it('should return false when no TODOs', async () => {
554
+ await createTaskFile('T-001', 'Task', 'S-001');
555
+ await runtime.initialize();
556
+ const hasTodos = await runtime.hasOpenTodos('T-001');
557
+ expect(hasTodos).toBe(false);
558
+ });
559
+ });
560
+ describe('getAcceptanceCriteria', () => {
561
+ it('should return acceptance criteria for story', async () => {
562
+ const content = `---
563
+ id: S-001
564
+ title: Story with AC
565
+ workstream: engineering
566
+ status: In Progress
567
+ parent: M-001
568
+ acceptance_criteria:
569
+ - User can log in
570
+ - User can log out
571
+ ---
572
+ # Story`;
573
+ await fs.writeFile(path.join(tempDir, config.entitiesFolder, 'stories', 'S-001 Story.md'), content, 'utf-8');
574
+ await runtime.initialize();
575
+ const criteria = await runtime.getAcceptanceCriteria('S-001');
576
+ expect(criteria).toEqual(['User can log in', 'User can log out']);
577
+ });
578
+ it('should return empty array for non-story', async () => {
579
+ await createMilestoneFile('M-001', 'Milestone');
580
+ await runtime.initialize();
581
+ const criteria = await runtime.getAcceptanceCriteria('M-001');
582
+ expect(criteria).toEqual([]);
583
+ });
584
+ });
585
+ describe('searchContent', () => {
586
+ it('should find pattern in entity content', async () => {
587
+ const content = `---
588
+ id: T-001
589
+ title: Task
590
+ workstream: engineering
591
+ status: In Progress
592
+ parent: S-001
593
+ goal: Implement authentication
594
+ ---
595
+ # Task
596
+
597
+ ## Description
598
+ This task involves implementing OAuth2 authentication.`;
599
+ await fs.writeFile(path.join(tempDir, config.entitiesFolder, 'tasks', 'T-001 Task.md'), content, 'utf-8');
600
+ await runtime.initialize();
601
+ const found = await runtime.searchContent('T-001', 'oauth2');
602
+ expect(found).toBe(true);
603
+ });
604
+ it('should return false when pattern not found', async () => {
605
+ await createTaskFile('T-001', 'Task', 'S-001');
606
+ await runtime.initialize();
607
+ const found = await runtime.searchContent('T-001', 'nonexistent');
608
+ expect(found).toBe(false);
609
+ });
610
+ });
611
+ describe('getV2Runtime singleton', () => {
612
+ it('should return same instance', async () => {
613
+ const runtime1 = await getV2Runtime(config);
614
+ const runtime2 = await getV2Runtime(config);
615
+ expect(runtime1).toBe(runtime2);
616
+ });
617
+ it('should reset singleton', async () => {
618
+ const runtime1 = await getV2Runtime(config);
619
+ resetV2Runtime();
620
+ const runtime2 = await getV2Runtime(config);
621
+ expect(runtime1).not.toBe(runtime2);
622
+ });
623
+ });
624
+ describe('dependency providers', () => {
625
+ it('should provide entity management deps', async () => {
626
+ await runtime.initialize();
627
+ const deps = runtime.getEntityManagementDeps();
628
+ expect(deps.getEntity).toBeDefined();
629
+ expect(deps.getNextId).toBeDefined();
630
+ expect(deps.writeEntity).toBeDefined();
631
+ });
632
+ it('should provide batch operations deps', async () => {
633
+ await runtime.initialize();
634
+ const deps = runtime.getBatchOperationsDeps();
635
+ expect(deps.createEntity).toBeDefined();
636
+ expect(deps.getEntity).toBeDefined();
637
+ });
638
+ it('should provide project understanding deps', async () => {
639
+ await runtime.initialize();
640
+ const deps = runtime.getProjectUnderstandingDeps();
641
+ expect(deps.getAllEntities).toBeDefined();
642
+ expect(deps.toEntitySummary).toBeDefined();
643
+ });
644
+ it('should provide search navigation deps', async () => {
645
+ await runtime.initialize();
646
+ const deps = runtime.getSearchNavigationDeps();
647
+ expect(deps.searchEntities).toBeDefined();
648
+ expect(deps.getEntity).toBeDefined();
649
+ });
650
+ it('should provide decision document deps', async () => {
651
+ await runtime.initialize();
652
+ const deps = runtime.getDecisionDocumentDeps();
653
+ expect(deps.createDecision).toBeDefined();
654
+ expect(deps.getAllDecisions).toBeDefined();
655
+ });
656
+ });
657
+ });
658
+ //# sourceMappingURL=v2-runtime.test.js.map