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