@projitive/mcp 2.0.3 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/output/package.json +8 -2
- package/output/source/common/artifacts.js +1 -1
- package/output/source/common/artifacts.test.js +11 -11
- package/output/source/common/errors.js +19 -19
- package/output/source/common/errors.test.js +59 -0
- package/output/source/common/files.js +30 -19
- package/output/source/common/files.test.js +14 -14
- package/output/source/common/index.js +11 -10
- package/output/source/common/linter.js +29 -27
- package/output/source/common/linter.test.js +9 -9
- package/output/source/common/markdown.js +3 -3
- package/output/source/common/markdown.test.js +15 -15
- package/output/source/common/response.js +91 -107
- package/output/source/common/response.test.js +30 -30
- package/output/source/common/store.js +40 -40
- package/output/source/common/store.test.js +72 -72
- package/output/source/common/tool.js +43 -0
- package/output/source/common/types.js +3 -3
- package/output/source/common/utils.js +8 -8
- package/output/source/common/utils.test.js +48 -0
- package/output/source/index.js +16 -16
- package/output/source/index.runtime.test.js +57 -0
- package/output/source/index.test.js +64 -64
- package/output/source/prompts/index.js +3 -3
- package/output/source/prompts/index.test.js +23 -0
- package/output/source/prompts/quickStart.js +96 -96
- package/output/source/prompts/quickStart.test.js +24 -0
- package/output/source/prompts/taskDiscovery.js +184 -184
- package/output/source/prompts/taskDiscovery.test.js +24 -0
- package/output/source/prompts/taskExecution.js +164 -148
- package/output/source/prompts/taskExecution.test.js +27 -0
- package/output/source/resources/designs.js +26 -26
- package/output/source/resources/designs.resources.test.js +52 -0
- package/output/source/resources/designs.test.js +88 -88
- package/output/source/resources/governance.js +19 -19
- package/output/source/resources/governance.test.js +35 -0
- package/output/source/resources/index.js +2 -2
- package/output/source/resources/index.test.js +18 -0
- package/output/source/resources/readme.js +7 -7
- package/output/source/resources/readme.test.js +113 -113
- package/output/source/resources/reports.js +10 -10
- package/output/source/resources/reports.test.js +83 -83
- package/output/source/tools/index.js +3 -3
- package/output/source/tools/index.test.js +23 -0
- package/output/source/tools/project.js +330 -377
- package/output/source/tools/project.test.js +308 -175
- package/output/source/tools/roadmap.js +236 -255
- package/output/source/tools/roadmap.test.js +241 -46
- package/output/source/tools/task.js +770 -652
- package/output/source/tools/task.test.js +433 -105
- package/output/source/types.js +28 -22
- package/package.json +8 -2
|
@@ -1,153 +1,153 @@
|
|
|
1
|
-
import { describe, expect, it } from
|
|
2
|
-
import { parseDesignMetadata, validateDesignMetadata } from
|
|
3
|
-
describe(
|
|
4
|
-
describe(
|
|
5
|
-
it(
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { parseDesignMetadata, validateDesignMetadata } from './designs.js';
|
|
3
|
+
describe('designs module', () => {
|
|
4
|
+
describe('parseDesignMetadata', () => {
|
|
5
|
+
it('parses task metadata from markdown', () => {
|
|
6
6
|
const markdown = [
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
].join(
|
|
7
|
+
'# Design Document',
|
|
8
|
+
'',
|
|
9
|
+
'**Task:** TASK-0001',
|
|
10
|
+
'**Owner:** ai-copilot',
|
|
11
|
+
'**Status:** Draft',
|
|
12
|
+
'**Last Updated:** 2026-02-22',
|
|
13
|
+
'',
|
|
14
|
+
'Some content here',
|
|
15
|
+
].join('\n');
|
|
16
16
|
const metadata = parseDesignMetadata(markdown);
|
|
17
|
-
expect(metadata.task).toBe(
|
|
18
|
-
expect(metadata.owner).toBe(
|
|
19
|
-
expect(metadata.status).toBe(
|
|
20
|
-
expect(metadata.lastUpdated).toBe(
|
|
17
|
+
expect(metadata.task).toBe('TASK-0001');
|
|
18
|
+
expect(metadata.owner).toBe('ai-copilot');
|
|
19
|
+
expect(metadata.status).toBe('Draft');
|
|
20
|
+
expect(metadata.lastUpdated).toBe('2026-02-22');
|
|
21
21
|
});
|
|
22
|
-
it(
|
|
22
|
+
it('parses roadmap metadata from markdown', () => {
|
|
23
23
|
const markdown = [
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
].join(
|
|
24
|
+
'# Design Document',
|
|
25
|
+
'',
|
|
26
|
+
'**Roadmap:** ROADMAP-0001',
|
|
27
|
+
'',
|
|
28
|
+
'Some content here',
|
|
29
|
+
].join('\n');
|
|
30
30
|
const metadata = parseDesignMetadata(markdown);
|
|
31
|
-
expect(metadata.roadmap).toBe(
|
|
31
|
+
expect(metadata.roadmap).toBe('ROADMAP-0001');
|
|
32
32
|
});
|
|
33
|
-
it(
|
|
33
|
+
it('returns empty object for markdown without metadata', () => {
|
|
34
34
|
const markdown = [
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
].join(
|
|
35
|
+
'# Simple Design',
|
|
36
|
+
'',
|
|
37
|
+
'No metadata here',
|
|
38
|
+
].join('\n');
|
|
39
39
|
const metadata = parseDesignMetadata(markdown);
|
|
40
40
|
expect(metadata).toEqual({});
|
|
41
41
|
});
|
|
42
|
-
it(
|
|
43
|
-
const metadata = parseDesignMetadata(
|
|
42
|
+
it('handles empty string', () => {
|
|
43
|
+
const metadata = parseDesignMetadata('');
|
|
44
44
|
expect(metadata).toEqual({});
|
|
45
45
|
});
|
|
46
|
-
it(
|
|
46
|
+
it('handles malformed metadata lines', () => {
|
|
47
47
|
const markdown = [
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
].join(
|
|
48
|
+
'# Report',
|
|
49
|
+
'',
|
|
50
|
+
'Task without colon',
|
|
51
|
+
'Not a metadata line',
|
|
52
|
+
':',
|
|
53
|
+
'Task:',
|
|
54
|
+
].join('\n');
|
|
55
55
|
const metadata = parseDesignMetadata(markdown);
|
|
56
56
|
expect(metadata).toBeDefined();
|
|
57
57
|
});
|
|
58
|
-
it(
|
|
58
|
+
it('parses metadata in different formats', () => {
|
|
59
59
|
const markdown = [
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
].join(
|
|
60
|
+
'Task: TASK-0001',
|
|
61
|
+
'task: TASK-0002',
|
|
62
|
+
'TASK: TASK-0003',
|
|
63
|
+
' task : TASK-0004 ',
|
|
64
|
+
].join('\n');
|
|
65
65
|
const metadata = parseDesignMetadata(markdown);
|
|
66
66
|
expect(metadata.task).toBeDefined();
|
|
67
67
|
});
|
|
68
68
|
});
|
|
69
|
-
describe(
|
|
70
|
-
it(
|
|
69
|
+
describe('validateDesignMetadata', () => {
|
|
70
|
+
it('validates correct task metadata', () => {
|
|
71
71
|
const metadata = {
|
|
72
|
-
task:
|
|
73
|
-
owner:
|
|
74
|
-
status:
|
|
75
|
-
lastUpdated:
|
|
72
|
+
task: 'TASK-0001',
|
|
73
|
+
owner: 'ai-copilot',
|
|
74
|
+
status: 'Draft',
|
|
75
|
+
lastUpdated: '2026-02-22',
|
|
76
76
|
};
|
|
77
77
|
const result = validateDesignMetadata(metadata);
|
|
78
78
|
expect(result.ok).toBe(true);
|
|
79
79
|
expect(result.errors).toEqual([]);
|
|
80
80
|
});
|
|
81
|
-
it(
|
|
81
|
+
it('rejects missing task metadata', () => {
|
|
82
82
|
const metadata = {
|
|
83
|
-
owner:
|
|
83
|
+
owner: 'ai-copilot',
|
|
84
84
|
};
|
|
85
85
|
const result = validateDesignMetadata(metadata);
|
|
86
86
|
expect(result.ok).toBe(false);
|
|
87
|
-
expect(result.errors).toContain(
|
|
87
|
+
expect(result.errors).toContain('Missing Task metadata');
|
|
88
88
|
});
|
|
89
|
-
it(
|
|
89
|
+
it('rejects invalid task ID format', () => {
|
|
90
90
|
const metadata = {
|
|
91
|
-
task:
|
|
91
|
+
task: 'invalid-format',
|
|
92
92
|
};
|
|
93
93
|
const result = validateDesignMetadata(metadata);
|
|
94
94
|
expect(result.ok).toBe(false);
|
|
95
|
-
expect(result.errors.some((e) => e.includes(
|
|
95
|
+
expect(result.errors.some((e) => e.includes('Invalid Task'))).toBe(true);
|
|
96
96
|
});
|
|
97
|
-
it(
|
|
97
|
+
it('validates optional roadmap metadata', () => {
|
|
98
98
|
const metadata = {
|
|
99
|
-
task:
|
|
100
|
-
roadmap:
|
|
99
|
+
task: 'TASK-0001',
|
|
100
|
+
roadmap: 'ROADMAP-0001',
|
|
101
101
|
};
|
|
102
102
|
const result = validateDesignMetadata(metadata);
|
|
103
103
|
expect(result.ok).toBe(true);
|
|
104
104
|
});
|
|
105
|
-
it(
|
|
105
|
+
it('rejects invalid roadmap ID format', () => {
|
|
106
106
|
const metadata = {
|
|
107
|
-
task:
|
|
108
|
-
roadmap:
|
|
107
|
+
task: 'TASK-0001',
|
|
108
|
+
roadmap: 'invalid-roadmap',
|
|
109
109
|
};
|
|
110
110
|
const result = validateDesignMetadata(metadata);
|
|
111
111
|
expect(result.ok).toBe(false);
|
|
112
|
-
expect(result.errors.some((e) => e.includes(
|
|
112
|
+
expect(result.errors.some((e) => e.includes('Invalid Roadmap'))).toBe(true);
|
|
113
113
|
});
|
|
114
|
-
it(
|
|
114
|
+
it('handles empty metadata object', () => {
|
|
115
115
|
const metadata = {};
|
|
116
116
|
const result = validateDesignMetadata(metadata);
|
|
117
117
|
expect(result.ok).toBe(false);
|
|
118
118
|
expect(result.errors.length).toBeGreaterThan(0);
|
|
119
119
|
});
|
|
120
|
-
it(
|
|
120
|
+
it('collects multiple validation errors', () => {
|
|
121
121
|
const metadata = {
|
|
122
|
-
task:
|
|
123
|
-
roadmap:
|
|
122
|
+
task: 'invalid-task',
|
|
123
|
+
roadmap: 'invalid-roadmap',
|
|
124
124
|
};
|
|
125
125
|
const result = validateDesignMetadata(metadata);
|
|
126
126
|
expect(result.ok).toBe(false);
|
|
127
127
|
expect(result.errors.length).toBeGreaterThan(1);
|
|
128
128
|
});
|
|
129
129
|
});
|
|
130
|
-
describe(
|
|
131
|
-
it(
|
|
130
|
+
describe('integration', () => {
|
|
131
|
+
it('parses and validates complete design metadata', () => {
|
|
132
132
|
const markdown = [
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
].join(
|
|
133
|
+
'# Design Completion Report',
|
|
134
|
+
'',
|
|
135
|
+
'**Task:** TASK-0001',
|
|
136
|
+
'**Roadmap:** ROADMAP-0002',
|
|
137
|
+
'**Owner:** ai-copilot',
|
|
138
|
+
'**Status:** Completed',
|
|
139
|
+
'**Last Updated:** 2026-02-22',
|
|
140
|
+
'',
|
|
141
|
+
'## Summary',
|
|
142
|
+
'Design completed successfully',
|
|
143
|
+
].join('\n');
|
|
144
144
|
const metadata = parseDesignMetadata(markdown);
|
|
145
145
|
const validation = validateDesignMetadata(metadata);
|
|
146
|
-
expect(metadata.task).toBe(
|
|
147
|
-
expect(metadata.roadmap).toBe(
|
|
148
|
-
expect(metadata.owner).toBe(
|
|
149
|
-
expect(metadata.status).toBe(
|
|
150
|
-
expect(metadata.lastUpdated).toBe(
|
|
146
|
+
expect(metadata.task).toBe('TASK-0001');
|
|
147
|
+
expect(metadata.roadmap).toBe('ROADMAP-0002');
|
|
148
|
+
expect(metadata.owner).toBe('ai-copilot');
|
|
149
|
+
expect(metadata.status).toBe('Completed');
|
|
150
|
+
expect(metadata.lastUpdated).toBe('2026-02-22');
|
|
151
151
|
expect(validation.ok).toBe(true);
|
|
152
152
|
});
|
|
153
153
|
});
|
|
@@ -1,39 +1,39 @@
|
|
|
1
1
|
// Governance resource management
|
|
2
|
-
import { readMarkdownOrFallback } from
|
|
2
|
+
import { readMarkdownOrFallback } from '../common/utils.js';
|
|
3
3
|
export function registerGovernanceResources(server, repoRoot) {
|
|
4
|
-
server.registerResource(
|
|
5
|
-
title:
|
|
6
|
-
description:
|
|
7
|
-
mimeType:
|
|
4
|
+
server.registerResource('governanceWorkspace', 'projitive://governance/workspace', {
|
|
5
|
+
title: 'Governance Workspace',
|
|
6
|
+
description: 'Primary governance README under .projitive',
|
|
7
|
+
mimeType: 'text/markdown',
|
|
8
8
|
}, async () => ({
|
|
9
9
|
contents: [
|
|
10
10
|
{
|
|
11
|
-
uri:
|
|
12
|
-
text: await readMarkdownOrFallback(
|
|
11
|
+
uri: 'projitive://governance/workspace',
|
|
12
|
+
text: await readMarkdownOrFallback('.projitive/README.md', 'Governance Workspace', repoRoot),
|
|
13
13
|
},
|
|
14
14
|
],
|
|
15
15
|
}));
|
|
16
|
-
server.registerResource(
|
|
17
|
-
title:
|
|
18
|
-
description:
|
|
19
|
-
mimeType:
|
|
16
|
+
server.registerResource('governanceTasks', 'projitive://governance/tasks', {
|
|
17
|
+
title: 'Governance Tasks',
|
|
18
|
+
description: 'Current task pool markdown view generated from .projitive governance store',
|
|
19
|
+
mimeType: 'text/markdown',
|
|
20
20
|
}, async () => ({
|
|
21
21
|
contents: [
|
|
22
22
|
{
|
|
23
|
-
uri:
|
|
24
|
-
text: await readMarkdownOrFallback(
|
|
23
|
+
uri: 'projitive://governance/tasks',
|
|
24
|
+
text: await readMarkdownOrFallback('.projitive/tasks.md', 'Governance Tasks', repoRoot),
|
|
25
25
|
},
|
|
26
26
|
],
|
|
27
27
|
}));
|
|
28
|
-
server.registerResource(
|
|
29
|
-
title:
|
|
30
|
-
description:
|
|
31
|
-
mimeType:
|
|
28
|
+
server.registerResource('governanceRoadmap', 'projitive://governance/roadmap', {
|
|
29
|
+
title: 'Governance Roadmap',
|
|
30
|
+
description: 'Current roadmap markdown view generated from .projitive governance store',
|
|
31
|
+
mimeType: 'text/markdown',
|
|
32
32
|
}, async () => ({
|
|
33
33
|
contents: [
|
|
34
34
|
{
|
|
35
|
-
uri:
|
|
36
|
-
text: await readMarkdownOrFallback(
|
|
35
|
+
uri: 'projitive://governance/roadmap',
|
|
36
|
+
text: await readMarkdownOrFallback('.projitive/roadmap.md', 'Governance Roadmap', repoRoot),
|
|
37
37
|
},
|
|
38
38
|
],
|
|
39
39
|
}));
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
5
|
+
import { registerGovernanceResources } from './governance.js';
|
|
6
|
+
const tempPaths = [];
|
|
7
|
+
async function createTempDir() {
|
|
8
|
+
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'projitive-governance-resource-test-'));
|
|
9
|
+
tempPaths.push(dir);
|
|
10
|
+
return dir;
|
|
11
|
+
}
|
|
12
|
+
afterEach(async () => {
|
|
13
|
+
await Promise.all(tempPaths.splice(0).map((dir) => fs.rm(dir, { recursive: true, force: true })));
|
|
14
|
+
vi.restoreAllMocks();
|
|
15
|
+
});
|
|
16
|
+
describe('governance resources', () => {
|
|
17
|
+
it('registers workspace, tasks, and roadmap resources from markdown files', async () => {
|
|
18
|
+
const root = await createTempDir();
|
|
19
|
+
const governanceDir = path.join(root, '.projitive');
|
|
20
|
+
await fs.mkdir(governanceDir, { recursive: true });
|
|
21
|
+
await fs.writeFile(path.join(governanceDir, 'README.md'), '# Workspace\n', 'utf-8');
|
|
22
|
+
await fs.writeFile(path.join(governanceDir, 'tasks.md'), '# Tasks\n', 'utf-8');
|
|
23
|
+
await fs.writeFile(path.join(governanceDir, 'roadmap.md'), '# Roadmap\n', 'utf-8');
|
|
24
|
+
const server = { registerResource: vi.fn() };
|
|
25
|
+
registerGovernanceResources(server, root);
|
|
26
|
+
const calls = server.registerResource.mock.calls;
|
|
27
|
+
expect(calls).toHaveLength(3);
|
|
28
|
+
const workspaceHandler = calls[0][3];
|
|
29
|
+
const tasksHandler = calls[1][3];
|
|
30
|
+
const roadmapHandler = calls[2][3];
|
|
31
|
+
await expect(workspaceHandler()).resolves.toMatchObject({ contents: [{ uri: 'projitive://governance/workspace', text: '# Workspace\n' }] });
|
|
32
|
+
await expect(tasksHandler()).resolves.toMatchObject({ contents: [{ uri: 'projitive://governance/tasks', text: '# Tasks\n' }] });
|
|
33
|
+
await expect(roadmapHandler()).resolves.toMatchObject({ contents: [{ uri: 'projitive://governance/roadmap', text: '# Roadmap\n' }] });
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { registerGovernanceResources } from
|
|
2
|
-
import { registerDesignFilesResources } from
|
|
1
|
+
import { registerGovernanceResources } from './governance.js';
|
|
2
|
+
import { registerDesignFilesResources } from './designs.js';
|
|
3
3
|
export function registerResources(server, repoRoot) {
|
|
4
4
|
registerGovernanceResources(server, repoRoot);
|
|
5
5
|
registerDesignFilesResources(server, repoRoot);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
vi.mock('./governance.js', () => ({
|
|
3
|
+
registerGovernanceResources: vi.fn(),
|
|
4
|
+
}));
|
|
5
|
+
vi.mock('./designs.js', () => ({
|
|
6
|
+
registerDesignFilesResources: vi.fn(),
|
|
7
|
+
}));
|
|
8
|
+
import { registerGovernanceResources } from './governance.js';
|
|
9
|
+
import { registerDesignFilesResources } from './designs.js';
|
|
10
|
+
import { registerResources } from './index.js';
|
|
11
|
+
describe('resources index module', () => {
|
|
12
|
+
it('registers governance and design resources', () => {
|
|
13
|
+
const server = {};
|
|
14
|
+
registerResources(server, '/workspace/repo');
|
|
15
|
+
expect(registerGovernanceResources).toHaveBeenCalledWith(server, '/workspace/repo');
|
|
16
|
+
expect(registerDesignFilesResources).toHaveBeenCalledWith(server, '/workspace/repo');
|
|
17
|
+
});
|
|
18
|
+
});
|
|
@@ -8,18 +8,18 @@ export function parseRequiredReading(markdown) {
|
|
|
8
8
|
inSection = true;
|
|
9
9
|
continue;
|
|
10
10
|
}
|
|
11
|
-
if (inSection && trimmed.startsWith(
|
|
11
|
+
if (inSection && trimmed.startsWith('## ')) {
|
|
12
12
|
break;
|
|
13
13
|
}
|
|
14
|
-
if (!inSection || !trimmed.startsWith(
|
|
14
|
+
if (!inSection || !trimmed.startsWith('- ')) {
|
|
15
15
|
continue;
|
|
16
16
|
}
|
|
17
|
-
const payload = trimmed.replace(/^-\s+/,
|
|
18
|
-
if (payload.startsWith(
|
|
19
|
-
result.push({ source:
|
|
17
|
+
const payload = trimmed.replace(/^-\s+/, '');
|
|
18
|
+
if (payload.startsWith('Local:')) {
|
|
19
|
+
result.push({ source: 'Local', value: payload.replace('Local:', '').trim() });
|
|
20
20
|
}
|
|
21
|
-
if (payload.startsWith(
|
|
22
|
-
result.push({ source:
|
|
21
|
+
if (payload.startsWith('External:')) {
|
|
22
|
+
result.push({ source: 'External', value: payload.replace('External:', '').trim() });
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
return result;
|