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,751 @@
1
+ /**
2
+ * Project Understanding Tools
3
+ *
4
+ * Category 3: Project Understanding
5
+ * - get_project_overview: High-level project status
6
+ * - get_workstream_status: Workstream-specific status
7
+ * - analyze_project_state: Deep analysis with blockers and suggestions
8
+ */
9
+ // =============================================================================
10
+ // Get Project Overview
11
+ // =============================================================================
12
+ /**
13
+ * Get high-level project status across all workstreams.
14
+ * Enhanced to support workstream filtering and grouping (consolidates get_workstream_status).
15
+ */
16
+ export async function getProjectOverview(input, deps) {
17
+ const { include_completed, include_archived, workstream: filterWorkstream, group_by } = input;
18
+ // Get all entities (optionally filtered by workstream)
19
+ const entities = await deps.getAllEntities({
20
+ includeCompleted: include_completed,
21
+ includeArchived: include_archived,
22
+ workstream: filterWorkstream,
23
+ });
24
+ // Initialize counters
25
+ const summary = {
26
+ milestones: { total: 0, completed: 0, in_progress: 0, blocked: 0 },
27
+ stories: { total: 0, completed: 0, in_progress: 0, blocked: 0 },
28
+ tasks: { total: 0, completed: 0, in_progress: 0, blocked: 0 },
29
+ decisions: { total: 0, pending: 0, decided: 0 },
30
+ documents: { total: 0, draft: 0, approved: 0 },
31
+ };
32
+ const workstreams = {};
33
+ let pendingDecisions = 0;
34
+ let readyForImplementation = 0;
35
+ // Process entities
36
+ for (const entity of entities) {
37
+ // Update type-specific counters
38
+ switch (entity.type) {
39
+ case 'milestone':
40
+ summary.milestones.total++;
41
+ if (entity.status === 'Completed')
42
+ summary.milestones.completed++;
43
+ else if (entity.status === 'In Progress')
44
+ summary.milestones.in_progress++;
45
+ else if (entity.status === 'Blocked')
46
+ summary.milestones.blocked++;
47
+ break;
48
+ case 'story':
49
+ summary.stories.total++;
50
+ if (entity.status === 'Completed')
51
+ summary.stories.completed++;
52
+ else if (entity.status === 'In Progress')
53
+ summary.stories.in_progress++;
54
+ else if (entity.status === 'Blocked')
55
+ summary.stories.blocked++;
56
+ break;
57
+ case 'task':
58
+ summary.tasks.total++;
59
+ if (entity.status === 'Completed')
60
+ summary.tasks.completed++;
61
+ else if (entity.status === 'In Progress')
62
+ summary.tasks.in_progress++;
63
+ else if (entity.status === 'Blocked')
64
+ summary.tasks.blocked++;
65
+ break;
66
+ case 'decision':
67
+ summary.decisions.total++;
68
+ if (entity.status === 'Pending') {
69
+ summary.decisions.pending++;
70
+ pendingDecisions++;
71
+ }
72
+ else if (entity.status === 'Decided') {
73
+ summary.decisions.decided++;
74
+ }
75
+ break;
76
+ case 'document':
77
+ summary.documents.total++;
78
+ if (entity.status === 'Draft')
79
+ summary.documents.draft++;
80
+ else if (entity.status === 'Approved') {
81
+ summary.documents.approved++;
82
+ readyForImplementation++;
83
+ }
84
+ break;
85
+ }
86
+ // Update workstream stats
87
+ const ws = entity.workstream;
88
+ if (!workstreams[ws]) {
89
+ workstreams[ws] = { health: 'healthy', progress_percent: 0, blocked_count: 0 };
90
+ }
91
+ if (entity.status === 'Blocked') {
92
+ workstreams[ws].blocked_count++;
93
+ }
94
+ }
95
+ // Calculate workstream health and progress
96
+ for (const ws of Object.keys(workstreams)) {
97
+ const wsEntities = entities.filter((e) => e.workstream === ws);
98
+ const completed = wsEntities.filter((e) => e.status === 'Completed' || e.status === 'Decided' || e.status === 'Approved').length;
99
+ workstreams[ws].progress_percent = wsEntities.length > 0
100
+ ? Math.round((completed / wsEntities.length) * 100)
101
+ : 0;
102
+ workstreams[ws].health = workstreams[ws].blocked_count > 2
103
+ ? 'blocked'
104
+ : workstreams[ws].blocked_count > 0
105
+ ? 'at_risk'
106
+ : 'healthy';
107
+ }
108
+ const result = {
109
+ summary,
110
+ workstreams,
111
+ pending_decisions: pendingDecisions,
112
+ ready_for_implementation: readyForImplementation,
113
+ };
114
+ // If workstream filter is specified, add detailed workstream info
115
+ if (filterWorkstream) {
116
+ result.workstream_detail = await buildWorkstreamDetail(filterWorkstream, entities, group_by || 'status', deps);
117
+ }
118
+ return result;
119
+ }
120
+ /**
121
+ * Build detailed workstream information (extracted from getWorkstreamStatus).
122
+ */
123
+ async function buildWorkstreamDetail(workstream, entities, group_by, deps) {
124
+ // Build summary
125
+ const byStatus = {};
126
+ const byType = {};
127
+ let blockedCount = 0;
128
+ let crossWorkstreamDeps = 0;
129
+ for (const entity of entities) {
130
+ // Count by status
131
+ byStatus[entity.status] = (byStatus[entity.status] || 0) + 1;
132
+ // Count by type
133
+ byType[entity.type] = (byType[entity.type] || 0) + 1;
134
+ // Count blocked
135
+ if (entity.status === 'Blocked') {
136
+ blockedCount++;
137
+ }
138
+ // Check for cross-workstream dependencies
139
+ const blockers = await deps.getBlockers(entity.id);
140
+ for (const blocker of blockers) {
141
+ if (blocker.workstream !== workstream) {
142
+ crossWorkstreamDeps++;
143
+ }
144
+ }
145
+ }
146
+ // Group entities
147
+ const groupMap = new Map();
148
+ for (const entity of entities) {
149
+ let key;
150
+ switch (group_by) {
151
+ case 'type':
152
+ key = entity.type;
153
+ break;
154
+ case 'priority':
155
+ key = entity.priority || 'none';
156
+ break;
157
+ default:
158
+ key = entity.status;
159
+ }
160
+ if (!groupMap.has(key)) {
161
+ groupMap.set(key, []);
162
+ }
163
+ groupMap.get(key).push(deps.toEntitySummary(entity));
164
+ }
165
+ const groups = Array.from(groupMap.entries()).map(([group_key, entities]) => ({
166
+ group_key,
167
+ entities,
168
+ }));
169
+ // Find cross-workstream blocking relationships
170
+ const blockingOther = [];
171
+ const blockedByOther = [];
172
+ for (const entity of entities) {
173
+ const blockedBy = await deps.getBlockedBy(entity.id);
174
+ for (const blocked of blockedBy) {
175
+ if (blocked.workstream !== workstream) {
176
+ blockingOther.push(deps.toEntitySummary(entity));
177
+ break;
178
+ }
179
+ }
180
+ const blockers = await deps.getBlockers(entity.id);
181
+ for (const blocker of blockers) {
182
+ if (blocker.workstream !== workstream) {
183
+ blockedByOther.push(deps.toEntitySummary(entity));
184
+ break;
185
+ }
186
+ }
187
+ }
188
+ return {
189
+ workstream,
190
+ summary: {
191
+ total: entities.length,
192
+ by_status: byStatus,
193
+ by_type: byType,
194
+ blocked_count: blockedCount,
195
+ cross_workstream_dependencies: crossWorkstreamDeps,
196
+ },
197
+ groups,
198
+ blocking_other_workstreams: blockingOther,
199
+ blocked_by_other_workstreams: blockedByOther,
200
+ };
201
+ }
202
+ // =============================================================================
203
+ // Get Workstream Status (DEPRECATED)
204
+ // =============================================================================
205
+ /**
206
+ * Get detailed status for a specific workstream.
207
+ *
208
+ * @deprecated Use `getProjectOverview` with `workstream` filter instead.
209
+ * Example: `getProjectOverview({ workstream: 'auth', group_by: 'status' })`
210
+ */
211
+ export async function getWorkstreamStatus(input, deps) {
212
+ const { workstream, include_completed, group_by = 'status' } = input;
213
+ // Get entities for this workstream
214
+ const entities = await deps.getAllEntities({
215
+ workstream,
216
+ includeCompleted: include_completed,
217
+ });
218
+ // Build summary
219
+ const byStatus = {};
220
+ const byType = {};
221
+ let blockedCount = 0;
222
+ let crossWorkstreamDeps = 0;
223
+ for (const entity of entities) {
224
+ // Count by status
225
+ byStatus[entity.status] = (byStatus[entity.status] || 0) + 1;
226
+ // Count by type
227
+ byType[entity.type] = (byType[entity.type] || 0) + 1;
228
+ // Count blocked
229
+ if (entity.status === 'Blocked') {
230
+ blockedCount++;
231
+ }
232
+ // Check for cross-workstream dependencies
233
+ const blockers = await deps.getBlockers(entity.id);
234
+ for (const blocker of blockers) {
235
+ if (blocker.workstream !== workstream) {
236
+ crossWorkstreamDeps++;
237
+ }
238
+ }
239
+ }
240
+ // Group entities
241
+ const groupMap = new Map();
242
+ for (const entity of entities) {
243
+ let key;
244
+ switch (group_by) {
245
+ case 'type':
246
+ key = entity.type;
247
+ break;
248
+ case 'priority':
249
+ key = entity.priority || 'none';
250
+ break;
251
+ default:
252
+ key = entity.status;
253
+ }
254
+ if (!groupMap.has(key)) {
255
+ groupMap.set(key, []);
256
+ }
257
+ groupMap.get(key).push(deps.toEntitySummary(entity));
258
+ }
259
+ const groups = Array.from(groupMap.entries()).map(([group_key, entities]) => ({
260
+ group_key,
261
+ entities,
262
+ }));
263
+ // Find cross-workstream blocking relationships
264
+ const blockingOther = [];
265
+ const blockedByOther = [];
266
+ for (const entity of entities) {
267
+ const blockedBy = await deps.getBlockedBy(entity.id);
268
+ for (const blocked of blockedBy) {
269
+ if (blocked.workstream !== workstream) {
270
+ blockingOther.push(deps.toEntitySummary(entity));
271
+ break;
272
+ }
273
+ }
274
+ const blockers = await deps.getBlockers(entity.id);
275
+ for (const blocker of blockers) {
276
+ if (blocker.workstream !== workstream) {
277
+ blockedByOther.push(deps.toEntitySummary(entity));
278
+ break;
279
+ }
280
+ }
281
+ }
282
+ return {
283
+ workstream,
284
+ summary: {
285
+ total: entities.length,
286
+ by_status: byStatus,
287
+ by_type: byType,
288
+ blocked_count: blockedCount,
289
+ cross_workstream_dependencies: crossWorkstreamDeps,
290
+ },
291
+ groups,
292
+ blocking_other_workstreams: blockingOther,
293
+ blocked_by_other_workstreams: blockedByOther,
294
+ };
295
+ }
296
+ // =============================================================================
297
+ // Analyze Project State
298
+ // =============================================================================
299
+ /**
300
+ * Deep analysis of project state with blockers and suggested actions.
301
+ */
302
+ export async function analyzeProjectState(input, deps) {
303
+ const { workstream, focus = 'both', depth = 'summary' } = input;
304
+ // Get all entities
305
+ const entities = await deps.getAllEntities({
306
+ workstream,
307
+ includeCompleted: false,
308
+ });
309
+ // Calculate health per workstream
310
+ const workstreamHealth = {};
311
+ const workstreamEntities = new Map();
312
+ for (const entity of entities) {
313
+ const ws = entity.workstream;
314
+ if (!workstreamEntities.has(ws)) {
315
+ workstreamEntities.set(ws, []);
316
+ }
317
+ workstreamEntities.get(ws).push(entity);
318
+ }
319
+ for (const [ws, wsEntities] of workstreamEntities) {
320
+ const completed = wsEntities.filter((e) => e.status === 'Completed' || e.status === 'Decided' || e.status === 'Approved').length;
321
+ const blocked = wsEntities.filter((e) => e.status === 'Blocked').length;
322
+ const progress = wsEntities.length > 0 ? Math.round((completed / wsEntities.length) * 100) : 0;
323
+ workstreamHealth[ws] = {
324
+ status: blocked > 2 ? 'blocked' : blocked > 0 ? 'at_risk' : 'healthy',
325
+ progress,
326
+ blocker_count: blocked,
327
+ };
328
+ }
329
+ // Determine overall health
330
+ const blockedWorkstreams = Object.values(workstreamHealth).filter((w) => w.status === 'blocked').length;
331
+ const atRiskWorkstreams = Object.values(workstreamHealth).filter((w) => w.status === 'at_risk').length;
332
+ const overallHealth = blockedWorkstreams > 0 ? 'blocked' : atRiskWorkstreams > 0 ? 'at_risk' : 'healthy';
333
+ // Find blockers
334
+ const criticalPath = [];
335
+ const pendingDecisions = [];
336
+ const incompleteSpecs = [];
337
+ const externalDeps = [];
338
+ const staleItems = [];
339
+ const now = new Date();
340
+ const oneWeekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
341
+ for (const entity of entities) {
342
+ // Check for pending decisions
343
+ if (entity.type === 'decision' && entity.status === 'Pending') {
344
+ pendingDecisions.push(deps.toEntitySummary(entity));
345
+ }
346
+ // Check for incomplete specs
347
+ if (entity.type === 'document' && entity.status === 'Draft') {
348
+ incompleteSpecs.push(deps.toEntitySummary(entity));
349
+ }
350
+ // Check for stale items (not updated in a week)
351
+ const lastUpdated = deps.getLastUpdated(entity);
352
+ if (lastUpdated < oneWeekAgo && entity.status !== 'Completed') {
353
+ staleItems.push(deps.toEntitySummary(entity));
354
+ }
355
+ // Check for blocked items and their impact
356
+ if (entity.status === 'Blocked') {
357
+ const blockedBy = await deps.getBlockedBy(entity.id);
358
+ const workstreamsAffected = new Set();
359
+ for (const blocked of blockedBy) {
360
+ workstreamsAffected.add(blocked.workstream);
361
+ }
362
+ criticalPath.push({
363
+ blocker: deps.toEntitySummary(entity),
364
+ impact: {
365
+ directly_blocks: blockedBy.map((e) => e.id),
366
+ cascade_blocks: [], // Would need deeper analysis
367
+ total_blocked: blockedBy.length,
368
+ workstreams_affected: Array.from(workstreamsAffected),
369
+ },
370
+ suggested_resolution: `Resolve ${entity.type} "${entity.title}" to unblock ${blockedBy.length} items`,
371
+ days_blocked: Math.floor((now.getTime() - deps.getLastUpdated(entity).getTime()) / (24 * 60 * 60 * 1000)),
372
+ });
373
+ }
374
+ }
375
+ // Generate suggested actions
376
+ const suggestedActions = [];
377
+ if (pendingDecisions.length > 0) {
378
+ suggestedActions.push({
379
+ priority: 1,
380
+ action: `Resolve ${pendingDecisions.length} pending decision(s)`,
381
+ reason: 'Pending decisions may be blocking implementation work',
382
+ effort: pendingDecisions.length > 3 ? 'high' : 'medium',
383
+ owner_hint: 'Project lead or decision makers',
384
+ });
385
+ }
386
+ if (incompleteSpecs.length > 0) {
387
+ suggestedActions.push({
388
+ priority: 2,
389
+ action: `Complete ${incompleteSpecs.length} draft document(s)`,
390
+ reason: 'Draft documents need review before implementation',
391
+ effort: 'medium',
392
+ owner_hint: 'Document owners',
393
+ });
394
+ }
395
+ if (staleItems.length > 0) {
396
+ suggestedActions.push({
397
+ priority: 3,
398
+ action: `Review ${staleItems.length} stale item(s)`,
399
+ reason: 'Items not updated in over a week may need attention',
400
+ effort: 'low',
401
+ owner_hint: 'Item assignees',
402
+ });
403
+ }
404
+ // Calculate stats
405
+ const completedThisWeek = entities.filter((e) => {
406
+ const updated = deps.getLastUpdated(e);
407
+ return updated >= oneWeekAgo && e.status === 'Completed';
408
+ }).length;
409
+ return {
410
+ health: {
411
+ overall: overallHealth,
412
+ workstreams: workstreamHealth,
413
+ },
414
+ blockers: {
415
+ critical_path: criticalPath,
416
+ by_type: {
417
+ pending_decisions: pendingDecisions,
418
+ incomplete_specs: incompleteSpecs,
419
+ external_dependencies: externalDeps,
420
+ },
421
+ stale_items: staleItems,
422
+ },
423
+ suggested_actions: suggestedActions,
424
+ stats: {
425
+ decisions_pending: pendingDecisions.length,
426
+ specs_ready: entities.filter((e) => e.type === 'document' && e.status === 'Approved').length,
427
+ items_blocked: criticalPath.length,
428
+ items_completed_this_week: completedThisWeek,
429
+ },
430
+ };
431
+ }
432
+ /**
433
+ * Get feature coverage analysis showing implementation, documentation, and testing status.
434
+ */
435
+ export async function getFeatureCoverage(input, deps) {
436
+ const { phase, tier, include_tests, summary_only, feature_ids, fields } = input;
437
+ // Get all features with optional filtering
438
+ let features = await deps.getAllFeatures({
439
+ tier,
440
+ phase,
441
+ includeDeferred: true, // Include deferred to show full picture
442
+ });
443
+ // Filter to specific feature IDs if provided
444
+ if (feature_ids && feature_ids.length > 0) {
445
+ const featureIdSet = new Set(feature_ids);
446
+ features = features.filter(f => featureIdSet.has(f.id));
447
+ }
448
+ // Get all documents for documentation coverage
449
+ const allDocs = await deps.getAllDocuments();
450
+ const docsByFeature = new Map();
451
+ // Build map of documents that document each feature
452
+ for (const doc of allDocs) {
453
+ if (doc.documents && doc.documents.length > 0) {
454
+ for (const featureId of doc.documents) {
455
+ if (!docsByFeature.has(featureId)) {
456
+ docsByFeature.set(featureId, []);
457
+ }
458
+ docsByFeature.get(featureId).push(doc.id);
459
+ }
460
+ }
461
+ }
462
+ // Process each feature
463
+ const coverageItems = [];
464
+ const missingImplementation = [];
465
+ const missingDocs = [];
466
+ const missingTests = [];
467
+ let implementedCount = 0;
468
+ let documentedCount = 0;
469
+ let testedCount = 0;
470
+ for (const feature of features) {
471
+ // Get implementing entities
472
+ const implementedBy = feature.implemented_by || [];
473
+ const milestones = [];
474
+ const stories = [];
475
+ for (const implId of implementedBy) {
476
+ const entity = await deps.getEntity(implId);
477
+ if (entity?.type === 'milestone') {
478
+ milestones.push(implId);
479
+ }
480
+ else if (entity?.type === 'story') {
481
+ stories.push(implId);
482
+ }
483
+ }
484
+ // Calculate implementation progress
485
+ let progressPercent = 0;
486
+ if (implementedBy.length > 0) {
487
+ let completedCount = 0;
488
+ for (const implId of implementedBy) {
489
+ const entity = await deps.getEntity(implId);
490
+ if (entity && (entity.status === 'Completed' || entity.status === 'Complete')) {
491
+ completedCount++;
492
+ }
493
+ }
494
+ progressPercent = Math.round((completedCount / implementedBy.length) * 100);
495
+ }
496
+ // Get documentation
497
+ const documentingDocs = docsByFeature.get(feature.id) || [];
498
+ const specs = [];
499
+ const guides = [];
500
+ for (const docId of documentingDocs) {
501
+ const doc = await deps.getEntity(docId);
502
+ if (doc && doc.type === 'document') {
503
+ const docEntity = doc;
504
+ // Specs: spec, adr, vision, research
505
+ if (docEntity.doc_type === 'spec' || docEntity.doc_type === 'adr' ||
506
+ docEntity.doc_type === 'vision' || docEntity.doc_type === 'research') {
507
+ specs.push(docId);
508
+ }
509
+ else if (docEntity.doc_type === 'guide') {
510
+ // Guides: guide
511
+ guides.push(docId);
512
+ }
513
+ }
514
+ }
515
+ // Determine documentation coverage
516
+ let docCoverage = 'none';
517
+ if (specs.length > 0 && guides.length > 0) {
518
+ docCoverage = 'full';
519
+ }
520
+ else if (specs.length > 0 || guides.length > 0) {
521
+ docCoverage = 'partial';
522
+ }
523
+ // Track gaps
524
+ const hasImplementation = implementedBy.length > 0;
525
+ const hasDocs = documentingDocs.length > 0;
526
+ if (!hasImplementation && feature.status !== 'Deferred') {
527
+ missingImplementation.push(feature.id);
528
+ }
529
+ else if (hasImplementation) {
530
+ implementedCount++;
531
+ }
532
+ if (!hasDocs && feature.status !== 'Deferred') {
533
+ missingDocs.push(feature.id);
534
+ }
535
+ else if (hasDocs) {
536
+ documentedCount++;
537
+ }
538
+ // Build coverage item
539
+ const coverageItem = {
540
+ id: feature.id,
541
+ title: feature.title,
542
+ tier: feature.tier || 'OSS',
543
+ phase: feature.phase || 'MVP',
544
+ status: feature.status,
545
+ implementation: {
546
+ milestones,
547
+ stories,
548
+ progress_percent: progressPercent,
549
+ },
550
+ documentation: {
551
+ specs,
552
+ guides,
553
+ coverage: docCoverage,
554
+ },
555
+ };
556
+ // Add testing info if requested
557
+ if (include_tests) {
558
+ // For now, we check if there are test references in the feature content
559
+ // This could be enhanced to scan actual test files
560
+ const hasTests = feature.test_refs && feature.test_refs.length > 0;
561
+ coverageItem.testing = {
562
+ test_refs: feature.test_refs || [],
563
+ has_tests: hasTests || false,
564
+ };
565
+ if (!hasTests && feature.status !== 'Deferred') {
566
+ missingTests.push(feature.id);
567
+ }
568
+ else if (hasTests) {
569
+ testedCount++;
570
+ }
571
+ }
572
+ coverageItems.push(coverageItem);
573
+ }
574
+ // Build summary
575
+ const summary = {
576
+ total: features.length,
577
+ implemented: implementedCount,
578
+ documented: documentedCount,
579
+ tested: testedCount,
580
+ gaps: {
581
+ missing_implementation: missingImplementation,
582
+ missing_docs: missingDocs,
583
+ missing_tests: missingTests,
584
+ },
585
+ };
586
+ // Return summary only if requested
587
+ if (summary_only) {
588
+ return { summary };
589
+ }
590
+ // Filter fields if specified
591
+ let filteredItems = coverageItems;
592
+ if (fields && fields.length > 0) {
593
+ const fieldSet = new Set(fields);
594
+ filteredItems = coverageItems.map(item => {
595
+ const filtered = {};
596
+ if (fieldSet.has('id'))
597
+ filtered.id = item.id;
598
+ if (fieldSet.has('title'))
599
+ filtered.title = item.title;
600
+ if (fieldSet.has('tier'))
601
+ filtered.tier = item.tier;
602
+ if (fieldSet.has('phase'))
603
+ filtered.phase = item.phase;
604
+ if (fieldSet.has('status'))
605
+ filtered.status = item.status;
606
+ if (fieldSet.has('implementation'))
607
+ filtered.implementation = item.implementation;
608
+ if (fieldSet.has('documentation'))
609
+ filtered.documentation = item.documentation;
610
+ if (fieldSet.has('testing') && item.testing)
611
+ filtered.testing = item.testing;
612
+ return filtered;
613
+ });
614
+ }
615
+ return {
616
+ features: filteredItems,
617
+ summary,
618
+ };
619
+ }
620
+ // =============================================================================
621
+ // Get Schema
622
+ // =============================================================================
623
+ /** Schema definitions for all entity types */
624
+ const ENTITY_SCHEMAS = [
625
+ {
626
+ type: 'milestone',
627
+ id_pattern: 'M-XXX',
628
+ fields: {
629
+ id: { type: 'MilestoneId', required: true, description: 'Unique identifier (M-XXX format)' },
630
+ title: { type: 'string', required: true, description: 'Milestone title' },
631
+ status: { type: 'enum', required: true, values: ['Planned', 'In Progress', 'Complete', 'Blocked', 'Deferred'] },
632
+ workstream: { type: 'string', required: true, description: 'Workstream this milestone belongs to' },
633
+ priority: { type: 'enum', values: ['P0', 'P1', 'P2', 'P3'], description: 'Priority level' },
634
+ objective: { type: 'string', description: 'Milestone objective/goal' },
635
+ depends_on: { type: 'EntityId[]', relationship: { target_types: ['milestone', 'decision'], inverse: 'blocks' } },
636
+ implements: { type: 'FeatureId[]', relationship: { target_types: ['feature'], inverse: 'implemented_by', auto_sync: true } },
637
+ },
638
+ statuses: ['Planned', 'In Progress', 'Complete', 'Blocked', 'Deferred'],
639
+ status_transitions: {
640
+ 'Planned': ['In Progress', 'Blocked', 'Deferred'],
641
+ 'In Progress': ['Complete', 'Blocked', 'Deferred'],
642
+ 'Blocked': ['In Progress', 'Deferred'],
643
+ 'Complete': [],
644
+ 'Deferred': ['Planned'],
645
+ },
646
+ },
647
+ {
648
+ type: 'story',
649
+ id_pattern: 'S-XXX',
650
+ fields: {
651
+ id: { type: 'StoryId', required: true, description: 'Unique identifier (S-XXX format)' },
652
+ title: { type: 'string', required: true, description: 'Story title' },
653
+ status: { type: 'enum', required: true, values: ['Planned', 'In Progress', 'Complete', 'Blocked', 'Deferred'] },
654
+ workstream: { type: 'string', required: true, description: 'Workstream this story belongs to' },
655
+ parent: { type: 'MilestoneId', required: true, relationship: { target_types: ['milestone'], inverse: 'children' } },
656
+ priority: { type: 'enum', values: ['P0', 'P1', 'P2', 'P3'], description: 'Priority level' },
657
+ effort: { type: 'enum', values: ['XS', 'S', 'M', 'L', 'XL'], description: 'Effort estimate' },
658
+ outcome: { type: 'string', description: 'Expected outcome' },
659
+ acceptance_criteria: { type: 'string[]', description: 'List of acceptance criteria' },
660
+ depends_on: { type: 'EntityId[]', relationship: { target_types: ['story', 'decision', 'document'], inverse: 'blocks' } },
661
+ implements: { type: 'FeatureId[]', relationship: { target_types: ['feature'], inverse: 'implemented_by', auto_sync: true } },
662
+ },
663
+ statuses: ['Planned', 'In Progress', 'Complete', 'Blocked', 'Deferred'],
664
+ },
665
+ {
666
+ type: 'task',
667
+ id_pattern: 'T-XXX',
668
+ fields: {
669
+ id: { type: 'TaskId', required: true, description: 'Unique identifier (T-XXX format)' },
670
+ title: { type: 'string', required: true, description: 'Task title' },
671
+ status: { type: 'enum', required: true, values: ['Pending', 'In Progress', 'Completed', 'Blocked', 'Deferred'] },
672
+ workstream: { type: 'string', description: 'Workstream (inherited from parent story)' },
673
+ parent: { type: 'StoryId', required: true, relationship: { target_types: ['story'], inverse: 'children' } },
674
+ goal: { type: 'string', description: 'Task goal' },
675
+ description: { type: 'string', description: 'Task description' },
676
+ technical_notes: { type: 'string', description: 'Technical implementation notes' },
677
+ depends_on: { type: 'EntityId[]', relationship: { target_types: ['task', 'decision'], inverse: 'blocks' } },
678
+ },
679
+ statuses: ['Pending', 'In Progress', 'Completed', 'Blocked', 'Deferred'],
680
+ },
681
+ {
682
+ type: 'decision',
683
+ id_pattern: 'DEC-XXX',
684
+ fields: {
685
+ id: { type: 'DecisionId', required: true, description: 'Unique identifier (DEC-XXX format)' },
686
+ title: { type: 'string', required: true, description: 'Decision title' },
687
+ status: { type: 'enum', required: true, values: ['Proposed', 'Accepted', 'Rejected', 'Superseded'] },
688
+ workstream: { type: 'string', required: true, description: 'Workstream this decision affects' },
689
+ context: { type: 'string', description: 'Context/background for the decision' },
690
+ decision: { type: 'string', description: 'The actual decision made' },
691
+ rationale: { type: 'string', description: 'Reasoning behind the decision' },
692
+ affects: { type: 'FeatureId[]', relationship: { target_types: ['feature'], inverse: 'decided_by', auto_sync: true } },
693
+ blocks: { type: 'EntityId[]', relationship: { target_types: ['document', 'story', 'task'], inverse: 'depends_on' } },
694
+ supersedes: { type: 'DecisionId', relationship: { target_types: ['decision'], inverse: 'superseded_by' } },
695
+ },
696
+ statuses: ['Proposed', 'Accepted', 'Rejected', 'Superseded'],
697
+ },
698
+ {
699
+ type: 'document',
700
+ id_pattern: 'DOC-XXX',
701
+ fields: {
702
+ id: { type: 'DocumentId', required: true, description: 'Unique identifier (DOC-XXX format)' },
703
+ title: { type: 'string', required: true, description: 'Document title' },
704
+ status: { type: 'enum', required: true, values: ['Draft', 'Review', 'Published', 'Archived'] },
705
+ workstream: { type: 'string', required: true, description: 'Workstream this document belongs to' },
706
+ content: { type: 'string', description: 'Document content (markdown)' },
707
+ documents: { type: 'FeatureId[]', relationship: { target_types: ['feature'], inverse: 'documented_by', auto_sync: true } },
708
+ implemented_by: { type: 'EntityId[]', relationship: { target_types: ['story', 'task'], inverse: 'implements' } },
709
+ },
710
+ statuses: ['Draft', 'Review', 'Published', 'Archived'],
711
+ },
712
+ {
713
+ type: 'feature',
714
+ id_pattern: 'F-XXX',
715
+ fields: {
716
+ id: { type: 'FeatureId', required: true, description: 'Unique identifier (F-XXX format)' },
717
+ title: { type: 'string', required: true, description: 'Feature title' },
718
+ status: { type: 'enum', required: true, values: ['Planned', 'In Progress', 'Complete', 'Deferred'] },
719
+ workstream: { type: 'string', description: 'Primary workstream' },
720
+ user_story: { type: 'string', required: true, description: 'User story format: "As a... I want... so that..."' },
721
+ tier: { type: 'enum', required: true, values: ['OSS', 'Premium'], default: 'OSS', description: 'Feature tier' },
722
+ phase: { type: 'enum', required: true, values: ['MVP', '0', '1', '2', '3', '4', '5'], default: 'MVP', description: 'Implementation phase' },
723
+ implemented_by: { type: '(MilestoneId|StoryId)[]', relationship: { target_types: ['milestone', 'story'], inverse: 'implements', auto_sync: true } },
724
+ documented_by: { type: 'DocumentId[]', relationship: { target_types: ['document'], inverse: 'documents', auto_sync: true } },
725
+ decided_by: { type: 'DecisionId[]', relationship: { target_types: ['decision'], inverse: 'affects', auto_sync: true } },
726
+ test_refs: { type: 'string[]', description: 'Test file references' },
727
+ },
728
+ statuses: ['Planned', 'In Progress', 'Complete', 'Deferred'],
729
+ },
730
+ ];
731
+ /**
732
+ * Get entity schema information.
733
+ * Returns field definitions, valid values, and relationship info.
734
+ */
735
+ export function getSchema(input) {
736
+ const { entity_type, relationships_only } = input;
737
+ let schemas = ENTITY_SCHEMAS;
738
+ // Filter by entity type if specified
739
+ if (entity_type) {
740
+ schemas = schemas.filter(s => s.type === entity_type);
741
+ }
742
+ // Filter to relationships only if requested
743
+ if (relationships_only) {
744
+ schemas = schemas.map(schema => ({
745
+ ...schema,
746
+ fields: Object.fromEntries(Object.entries(schema.fields).filter(([_, def]) => def.relationship)),
747
+ }));
748
+ }
749
+ return { schemas };
750
+ }
751
+ //# sourceMappingURL=project-understanding-tools.js.map