obsidian-accomplishments-mcp 0.1.9 → 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.
- package/README.md +154 -182
- package/dist/index.js +207 -38
- package/dist/index.js.map +1 -1
- package/dist/integration.test.d.ts +8 -0
- package/dist/integration.test.d.ts.map +1 -0
- package/dist/integration.test.js +979 -0
- package/dist/integration.test.js.map +1 -0
- package/dist/models/types.d.ts +1 -2
- package/dist/models/types.d.ts.map +1 -1
- package/dist/models/types.js.map +1 -1
- package/dist/models/v2-types.d.ts +460 -0
- package/dist/models/v2-types.d.ts.map +1 -0
- package/dist/models/v2-types.js +137 -0
- package/dist/models/v2-types.js.map +1 -0
- package/dist/models/v2-types.test.d.ts +5 -0
- package/dist/models/v2-types.test.d.ts.map +1 -0
- package/dist/models/v2-types.test.js +133 -0
- package/dist/models/v2-types.test.js.map +1 -0
- package/dist/parsers/canvas-parser.d.ts +1 -1
- package/dist/parsers/canvas-parser.d.ts.map +1 -1
- package/dist/parsers/canvas-parser.js +1 -1
- package/dist/parsers/canvas-parser.js.map +1 -1
- package/dist/parsers/markdown-parser.js +9 -9
- package/dist/parsers/markdown-parser.js.map +1 -1
- package/dist/services/v2/archive-manager.d.ts +96 -0
- package/dist/services/v2/archive-manager.d.ts.map +1 -0
- package/dist/services/v2/archive-manager.js +281 -0
- package/dist/services/v2/archive-manager.js.map +1 -0
- package/dist/services/v2/canvas-manager.d.ts +155 -0
- package/dist/services/v2/canvas-manager.d.ts.map +1 -0
- package/dist/services/v2/canvas-manager.js +540 -0
- package/dist/services/v2/canvas-manager.js.map +1 -0
- package/dist/services/v2/canvas-manager.test.d.ts +5 -0
- package/dist/services/v2/canvas-manager.test.d.ts.map +1 -0
- package/dist/services/v2/canvas-manager.test.js +327 -0
- package/dist/services/v2/canvas-manager.test.js.map +1 -0
- package/dist/services/v2/cascade-manager.d.ts +54 -0
- package/dist/services/v2/cascade-manager.d.ts.map +1 -0
- package/dist/services/v2/cascade-manager.js +220 -0
- package/dist/services/v2/cascade-manager.js.map +1 -0
- package/dist/services/v2/cycle-detector.d.ts +76 -0
- package/dist/services/v2/cycle-detector.d.ts.map +1 -0
- package/dist/services/v2/cycle-detector.js +183 -0
- package/dist/services/v2/cycle-detector.js.map +1 -0
- package/dist/services/v2/cycle-detector.test.d.ts +7 -0
- package/dist/services/v2/cycle-detector.test.d.ts.map +1 -0
- package/dist/services/v2/cycle-detector.test.js +125 -0
- package/dist/services/v2/cycle-detector.test.js.map +1 -0
- package/dist/services/v2/entity-parser.d.ts +54 -0
- package/dist/services/v2/entity-parser.d.ts.map +1 -0
- package/dist/services/v2/entity-parser.js +418 -0
- package/dist/services/v2/entity-parser.js.map +1 -0
- package/dist/services/v2/entity-parser.test.d.ts +5 -0
- package/dist/services/v2/entity-parser.test.d.ts.map +1 -0
- package/dist/services/v2/entity-parser.test.js +637 -0
- package/dist/services/v2/entity-parser.test.js.map +1 -0
- package/dist/services/v2/entity-serializer.d.ts +94 -0
- package/dist/services/v2/entity-serializer.d.ts.map +1 -0
- package/dist/services/v2/entity-serializer.js +583 -0
- package/dist/services/v2/entity-serializer.js.map +1 -0
- package/dist/services/v2/entity-serializer.test.d.ts +5 -0
- package/dist/services/v2/entity-serializer.test.d.ts.map +1 -0
- package/dist/services/v2/entity-serializer.test.js +241 -0
- package/dist/services/v2/entity-serializer.test.js.map +1 -0
- package/dist/services/v2/entity-validator.d.ts +65 -0
- package/dist/services/v2/entity-validator.d.ts.map +1 -0
- package/dist/services/v2/entity-validator.js +573 -0
- package/dist/services/v2/entity-validator.js.map +1 -0
- package/dist/services/v2/entity-validator.test.d.ts +5 -0
- package/dist/services/v2/entity-validator.test.d.ts.map +1 -0
- package/dist/services/v2/entity-validator.test.js +519 -0
- package/dist/services/v2/entity-validator.test.js.map +1 -0
- package/dist/services/v2/file-manager.d.ts +73 -0
- package/dist/services/v2/file-manager.d.ts.map +1 -0
- package/dist/services/v2/file-manager.js +310 -0
- package/dist/services/v2/file-manager.js.map +1 -0
- package/dist/services/v2/file-manager.test.d.ts +5 -0
- package/dist/services/v2/file-manager.test.d.ts.map +1 -0
- package/dist/services/v2/file-manager.test.js +339 -0
- package/dist/services/v2/file-manager.test.js.map +1 -0
- package/dist/services/v2/index-manager.d.ts +68 -0
- package/dist/services/v2/index-manager.d.ts.map +1 -0
- package/dist/services/v2/index-manager.js +228 -0
- package/dist/services/v2/index-manager.js.map +1 -0
- package/dist/services/v2/index-manager.test.d.ts +5 -0
- package/dist/services/v2/index-manager.test.d.ts.map +1 -0
- package/dist/services/v2/index-manager.test.js +386 -0
- package/dist/services/v2/index-manager.test.js.map +1 -0
- package/dist/services/v2/index-service.d.ts +82 -0
- package/dist/services/v2/index-service.d.ts.map +1 -0
- package/dist/services/v2/index-service.js +274 -0
- package/dist/services/v2/index-service.js.map +1 -0
- package/dist/services/v2/index-service.test.d.ts +5 -0
- package/dist/services/v2/index-service.test.d.ts.map +1 -0
- package/dist/services/v2/index-service.test.js +117 -0
- package/dist/services/v2/index-service.test.js.map +1 -0
- package/dist/services/v2/lifecycle-manager.d.ts +59 -0
- package/dist/services/v2/lifecycle-manager.d.ts.map +1 -0
- package/dist/services/v2/lifecycle-manager.js +310 -0
- package/dist/services/v2/lifecycle-manager.js.map +1 -0
- package/dist/services/v2/lifecycle-manager.test.d.ts +5 -0
- package/dist/services/v2/lifecycle-manager.test.d.ts.map +1 -0
- package/dist/services/v2/lifecycle-manager.test.js +141 -0
- package/dist/services/v2/lifecycle-manager.test.js.map +1 -0
- package/dist/services/v2/path-resolver.d.ts +64 -0
- package/dist/services/v2/path-resolver.d.ts.map +1 -0
- package/dist/services/v2/path-resolver.js +174 -0
- package/dist/services/v2/path-resolver.js.map +1 -0
- package/dist/services/v2/progress-computer.d.ts +46 -0
- package/dist/services/v2/progress-computer.d.ts.map +1 -0
- package/dist/services/v2/progress-computer.js +200 -0
- package/dist/services/v2/progress-computer.js.map +1 -0
- package/dist/services/v2/search-service.d.ts +68 -0
- package/dist/services/v2/search-service.d.ts.map +1 -0
- package/dist/services/v2/search-service.js +194 -0
- package/dist/services/v2/search-service.js.map +1 -0
- package/dist/services/v2/transitive-dependency-remover.d.ts +54 -0
- package/dist/services/v2/transitive-dependency-remover.d.ts.map +1 -0
- package/dist/services/v2/transitive-dependency-remover.js +156 -0
- package/dist/services/v2/transitive-dependency-remover.js.map +1 -0
- package/dist/services/v2/transitive-dependency-remover.test.d.ts +7 -0
- package/dist/services/v2/transitive-dependency-remover.test.d.ts.map +1 -0
- package/dist/services/v2/transitive-dependency-remover.test.js +119 -0
- package/dist/services/v2/transitive-dependency-remover.test.js.map +1 -0
- package/dist/services/v2/v2-runtime.d.ts +374 -0
- package/dist/services/v2/v2-runtime.d.ts.map +1 -0
- package/dist/services/v2/v2-runtime.js +1908 -0
- package/dist/services/v2/v2-runtime.js.map +1 -0
- package/dist/services/v2/v2-runtime.test.d.ts +5 -0
- package/dist/services/v2/v2-runtime.test.d.ts.map +1 -0
- package/dist/services/v2/v2-runtime.test.js +658 -0
- package/dist/services/v2/v2-runtime.test.js.map +1 -0
- package/dist/services/v2/workstream-normalizer.d.ts +59 -0
- package/dist/services/v2/workstream-normalizer.d.ts.map +1 -0
- package/dist/services/v2/workstream-normalizer.js +137 -0
- package/dist/services/v2/workstream-normalizer.js.map +1 -0
- package/dist/services/v2/workstream-normalizer.test.d.ts +7 -0
- package/dist/services/v2/workstream-normalizer.test.d.ts.map +1 -0
- package/dist/services/v2/workstream-normalizer.test.js +130 -0
- package/dist/services/v2/workstream-normalizer.test.js.map +1 -0
- package/dist/test-runner.d.ts +4 -1
- package/dist/test-runner.d.ts.map +1 -1
- package/dist/test-runner.js +44 -249
- package/dist/test-runner.js.map +1 -1
- package/dist/tools/batch-operations-tools.d.ts +54 -0
- package/dist/tools/batch-operations-tools.d.ts.map +1 -0
- package/dist/tools/batch-operations-tools.js +370 -0
- package/dist/tools/batch-operations-tools.js.map +1 -0
- package/dist/tools/decision-document-tools.d.ts +78 -0
- package/dist/tools/decision-document-tools.d.ts.map +1 -0
- package/dist/tools/decision-document-tools.js +260 -0
- package/dist/tools/decision-document-tools.js.map +1 -0
- package/dist/tools/entity-management-tools.d.ts +79 -0
- package/dist/tools/entity-management-tools.d.ts.map +1 -0
- package/dist/tools/entity-management-tools.js +851 -0
- package/dist/tools/entity-management-tools.js.map +1 -0
- package/dist/tools/entity-management-tools.test.d.ts +5 -0
- package/dist/tools/entity-management-tools.test.d.ts.map +1 -0
- package/dist/tools/entity-management-tools.test.js +530 -0
- package/dist/tools/entity-management-tools.test.js.map +1 -0
- package/dist/tools/index.d.ts +15 -271
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +510 -47
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/index.test.d.ts +8 -0
- package/dist/tools/index.test.d.ts.map +1 -0
- package/dist/tools/index.test.js +429 -0
- package/dist/tools/index.test.js.map +1 -0
- package/dist/tools/project-understanding-tools.d.ts +75 -0
- package/dist/tools/project-understanding-tools.d.ts.map +1 -0
- package/dist/tools/project-understanding-tools.js +751 -0
- package/dist/tools/project-understanding-tools.js.map +1 -0
- package/dist/tools/search-navigation-tools.d.ts +77 -0
- package/dist/tools/search-navigation-tools.d.ts.map +1 -0
- package/dist/tools/search-navigation-tools.js +379 -0
- package/dist/tools/search-navigation-tools.js.map +1 -0
- package/dist/tools/tool-types.d.ts +703 -0
- package/dist/tools/tool-types.d.ts.map +1 -0
- package/dist/tools/tool-types.js +7 -0
- package/dist/tools/tool-types.js.map +1 -0
- package/dist/utils/config.d.ts +0 -4
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +2 -19
- package/dist/utils/config.js.map +1 -1
- package/package.json +16 -1
- package/dist/services/accomplishment-service.d.ts +0 -33
- package/dist/services/accomplishment-service.d.ts.map +0 -1
- package/dist/services/accomplishment-service.js +0 -293
- package/dist/services/accomplishment-service.js.map +0 -1
- package/dist/services/canvas-service.d.ts +0 -96
- package/dist/services/canvas-service.d.ts.map +0 -1
- package/dist/services/canvas-service.js +0 -231
- package/dist/services/canvas-service.js.map +0 -1
- package/dist/services/context-doc-service.d.ts +0 -70
- package/dist/services/context-doc-service.d.ts.map +0 -1
- package/dist/services/context-doc-service.js +0 -229
- package/dist/services/context-doc-service.js.map +0 -1
- package/dist/services/dependency-service.d.ts +0 -22
- package/dist/services/dependency-service.d.ts.map +0 -1
- package/dist/services/dependency-service.js +0 -99
- package/dist/services/dependency-service.js.map +0 -1
- package/dist/services/status-indicator-service.d.ts +0 -40
- package/dist/services/status-indicator-service.d.ts.map +0 -1
- package/dist/services/status-indicator-service.js +0 -173
- package/dist/services/status-indicator-service.js.map +0 -1
- package/dist/services/task-service.d.ts +0 -32
- package/dist/services/task-service.d.ts.map +0 -1
- package/dist/services/task-service.js +0 -152
- package/dist/services/task-service.js.map +0 -1
- package/dist/test-real-vault.d.ts +0 -6
- package/dist/test-real-vault.d.ts.map +0 -1
- package/dist/test-real-vault.js +0 -30
- package/dist/test-real-vault.js.map +0 -1
- package/dist/tools/batch-operations.d.ts +0 -246
- package/dist/tools/batch-operations.d.ts.map +0 -1
- package/dist/tools/batch-operations.js +0 -235
- package/dist/tools/batch-operations.js.map +0 -1
- package/dist/tools/get-accomplishment.d.ts +0 -26
- package/dist/tools/get-accomplishment.d.ts.map +0 -1
- package/dist/tools/get-accomplishment.js +0 -53
- package/dist/tools/get-accomplishment.js.map +0 -1
- package/dist/tools/get-accomplishments-graph.d.ts +0 -26
- package/dist/tools/get-accomplishments-graph.d.ts.map +0 -1
- package/dist/tools/get-accomplishments-graph.js +0 -137
- package/dist/tools/get-accomplishments-graph.js.map +0 -1
- package/dist/tools/get-blocked-items.d.ts +0 -15
- package/dist/tools/get-blocked-items.d.ts.map +0 -1
- package/dist/tools/get-blocked-items.js +0 -73
- package/dist/tools/get-blocked-items.js.map +0 -1
- package/dist/tools/get-current-work.d.ts +0 -15
- package/dist/tools/get-current-work.d.ts.map +0 -1
- package/dist/tools/get-current-work.js +0 -68
- package/dist/tools/get-current-work.js.map +0 -1
- package/dist/tools/get-project-status.d.ts +0 -26
- package/dist/tools/get-project-status.d.ts.map +0 -1
- package/dist/tools/get-project-status.js +0 -98
- package/dist/tools/get-project-status.js.map +0 -1
- package/dist/tools/get-ready-to-start.d.ts +0 -15
- package/dist/tools/get-ready-to-start.d.ts.map +0 -1
- package/dist/tools/get-ready-to-start.js +0 -47
- package/dist/tools/get-ready-to-start.js.map +0 -1
- package/dist/tools/list-accomplishments.d.ts +0 -34
- package/dist/tools/list-accomplishments.d.ts.map +0 -1
- package/dist/tools/list-accomplishments.js +0 -34
- package/dist/tools/list-accomplishments.js.map +0 -1
- package/dist/tools/manage-accomplishment.d.ts +0 -147
- package/dist/tools/manage-accomplishment.d.ts.map +0 -1
- package/dist/tools/manage-accomplishment.js +0 -153
- package/dist/tools/manage-accomplishment.js.map +0 -1
- package/dist/tools/manage-dependency.d.ts +0 -41
- package/dist/tools/manage-dependency.d.ts.map +0 -1
- package/dist/tools/manage-dependency.js +0 -66
- package/dist/tools/manage-dependency.js.map +0 -1
- package/dist/tools/manage-task.d.ts +0 -119
- package/dist/tools/manage-task.d.ts.map +0 -1
- package/dist/tools/manage-task.js +0 -126
- package/dist/tools/manage-task.js.map +0 -1
- package/dist/tools/reconcile-canvas.d.ts +0 -33
- package/dist/tools/reconcile-canvas.d.ts.map +0 -1
- package/dist/tools/reconcile-canvas.js +0 -41
- package/dist/tools/reconcile-canvas.js.map +0 -1
- package/dist/tools/set-work-focus.d.ts +0 -48
- package/dist/tools/set-work-focus.d.ts.map +0 -1
- package/dist/tools/set-work-focus.js +0 -78
- package/dist/tools/set-work-focus.js.map +0 -1
- package/dist/tools/sync-dependencies.d.ts +0 -33
- package/dist/tools/sync-dependencies.d.ts.map +0 -1
- package/dist/tools/sync-dependencies.js +0 -144
- 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
|