@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,46 +1,46 @@
|
|
|
1
|
-
import { describe, it, expect, beforeAll, afterAll, vi, beforeEach } from
|
|
2
|
-
import fs from
|
|
3
|
-
import path from
|
|
4
|
-
import os from
|
|
5
|
-
import { fileURLToPath } from
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll, vi, beforeEach } from 'vitest';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
6
|
const currentFilePath = fileURLToPath(import.meta.url);
|
|
7
7
|
const sourceDir = path.dirname(currentFilePath);
|
|
8
8
|
// Mock McpServer and StdioServerTransport
|
|
9
|
-
vi.mock(
|
|
9
|
+
vi.mock('@modelcontextprotocol/sdk/server/mcp.js', () => ({
|
|
10
10
|
McpServer: vi.fn().mockImplementation(() => ({
|
|
11
11
|
registerResource: vi.fn(),
|
|
12
12
|
registerPrompt: vi.fn(),
|
|
13
13
|
connect: vi.fn().mockResolvedValue(undefined),
|
|
14
14
|
})),
|
|
15
15
|
}));
|
|
16
|
-
vi.mock(
|
|
16
|
+
vi.mock('@modelcontextprotocol/sdk/server/stdio.js', () => ({
|
|
17
17
|
StdioServerTransport: vi.fn().mockImplementation(() => ({})),
|
|
18
18
|
}));
|
|
19
19
|
// Mock the registration functions
|
|
20
|
-
vi.mock(
|
|
20
|
+
vi.mock('./tools/project.js', () => ({
|
|
21
21
|
registerProjectTools: vi.fn(),
|
|
22
22
|
}));
|
|
23
|
-
vi.mock(
|
|
23
|
+
vi.mock('./tools/task.js', () => ({
|
|
24
24
|
registerTaskTools: vi.fn(),
|
|
25
25
|
}));
|
|
26
|
-
vi.mock(
|
|
26
|
+
vi.mock('./tools/roadmap.js', () => ({
|
|
27
27
|
registerRoadmapTools: vi.fn(),
|
|
28
28
|
}));
|
|
29
|
-
vi.mock(
|
|
29
|
+
vi.mock('./resources/governance.js', () => ({
|
|
30
30
|
registerGovernanceResources: vi.fn(),
|
|
31
31
|
}));
|
|
32
|
-
vi.mock(
|
|
32
|
+
vi.mock('./resources/designs.js', () => ({
|
|
33
33
|
registerDesignFilesResources: vi.fn(),
|
|
34
34
|
}));
|
|
35
|
-
vi.mock(
|
|
35
|
+
vi.mock('./prompts/governance.js', () => ({
|
|
36
36
|
registerGovernancePrompts: vi.fn(),
|
|
37
37
|
}));
|
|
38
|
-
describe(
|
|
38
|
+
describe('index module', () => {
|
|
39
39
|
let tempDir;
|
|
40
40
|
let testProjectDir;
|
|
41
41
|
beforeAll(async () => {
|
|
42
|
-
tempDir = await fs.mkdtemp(path.join(os.tmpdir(),
|
|
43
|
-
testProjectDir = path.join(tempDir,
|
|
42
|
+
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'projitive-index-test-'));
|
|
43
|
+
testProjectDir = path.join(tempDir, 'test-project');
|
|
44
44
|
await fs.mkdir(testProjectDir);
|
|
45
45
|
});
|
|
46
46
|
afterAll(async () => {
|
|
@@ -49,62 +49,62 @@ describe("index module", () => {
|
|
|
49
49
|
beforeEach(() => {
|
|
50
50
|
vi.clearAllMocks();
|
|
51
51
|
});
|
|
52
|
-
it(
|
|
53
|
-
const indexPath = path.join(sourceDir,
|
|
52
|
+
it('should export the main server entry point', async () => {
|
|
53
|
+
const indexPath = path.join(sourceDir, 'index.ts');
|
|
54
54
|
const exists = await fs.access(indexPath).then(() => true).catch(() => false);
|
|
55
55
|
expect(exists).toBe(true);
|
|
56
56
|
});
|
|
57
|
-
it(
|
|
58
|
-
const indexPath = path.join(sourceDir,
|
|
59
|
-
const content = await fs.readFile(indexPath,
|
|
60
|
-
expect(content.startsWith(
|
|
61
|
-
expect(content).toContain(
|
|
62
|
-
expect(content).toContain(
|
|
63
|
-
expect(content).toContain(
|
|
64
|
-
expect(content).toContain(
|
|
65
|
-
expect(content).toContain(
|
|
57
|
+
it('should have the correct shebang and imports', async () => {
|
|
58
|
+
const indexPath = path.join(sourceDir, 'index.ts');
|
|
59
|
+
const content = await fs.readFile(indexPath, 'utf-8');
|
|
60
|
+
expect(content.startsWith('#!/usr/bin/env node')).toBe(true);
|
|
61
|
+
expect(content).toContain('McpServer');
|
|
62
|
+
expect(content).toContain('StdioServerTransport');
|
|
63
|
+
expect(content).toContain('registerTools');
|
|
64
|
+
expect(content).toContain('registerResources');
|
|
65
|
+
expect(content).toContain('registerPrompts');
|
|
66
66
|
});
|
|
67
|
-
it(
|
|
68
|
-
const indexPath = path.join(sourceDir,
|
|
69
|
-
const content = await fs.readFile(indexPath,
|
|
70
|
-
expect(content).toContain(
|
|
71
|
-
expect(content).toContain('
|
|
67
|
+
it('should define PROJITIVE_SPEC_VERSION as 1.1.0', async () => {
|
|
68
|
+
const indexPath = path.join(sourceDir, 'index.ts');
|
|
69
|
+
const content = await fs.readFile(indexPath, 'utf-8');
|
|
70
|
+
expect(content).toContain('PROJITIVE_SPEC_VERSION');
|
|
71
|
+
expect(content).toContain("'1.1.0'");
|
|
72
72
|
});
|
|
73
|
-
it(
|
|
74
|
-
const indexPath = path.join(sourceDir,
|
|
75
|
-
const content = await fs.readFile(indexPath,
|
|
76
|
-
expect(content).toContain(
|
|
77
|
-
expect(content).toContain(
|
|
78
|
-
expect(content).toContain(
|
|
73
|
+
it('should have main function with server startup', async () => {
|
|
74
|
+
const indexPath = path.join(sourceDir, 'index.ts');
|
|
75
|
+
const content = await fs.readFile(indexPath, 'utf-8');
|
|
76
|
+
expect(content).toContain('async function main');
|
|
77
|
+
expect(content).toContain('server.connect');
|
|
78
|
+
expect(content).toContain('StdioServerTransport');
|
|
79
79
|
});
|
|
80
|
-
it(
|
|
81
|
-
const indexPath = path.join(sourceDir,
|
|
82
|
-
const content = await fs.readFile(indexPath,
|
|
83
|
-
expect(content).toContain(
|
|
84
|
-
expect(content).toContain(
|
|
85
|
-
expect(content).toContain(
|
|
80
|
+
it('should register all tool categories', async () => {
|
|
81
|
+
const indexPath = path.join(sourceDir, 'index.ts');
|
|
82
|
+
const content = await fs.readFile(indexPath, 'utf-8');
|
|
83
|
+
expect(content).toContain('registerTools(server)');
|
|
84
|
+
expect(content).toContain('registerResources(server, repoRoot)');
|
|
85
|
+
expect(content).toContain('registerPrompts(server)');
|
|
86
86
|
});
|
|
87
|
-
it(
|
|
88
|
-
const indexPath = path.join(sourceDir,
|
|
89
|
-
const content = await fs.readFile(indexPath,
|
|
90
|
-
expect(content).toContain(
|
|
91
|
-
expect(content).toContain("console.error(
|
|
92
|
-
expect(content).toContain(
|
|
87
|
+
it('should have proper error handling in main', async () => {
|
|
88
|
+
const indexPath = path.join(sourceDir, 'index.ts');
|
|
89
|
+
const content = await fs.readFile(indexPath, 'utf-8');
|
|
90
|
+
expect(content).toContain('void main().catch');
|
|
91
|
+
expect(content).toContain("console.error('Server error:', error)");
|
|
92
|
+
expect(content).toContain('process.exit(1)');
|
|
93
93
|
});
|
|
94
|
-
it(
|
|
95
|
-
const indexPath = path.join(sourceDir,
|
|
96
|
-
const content = await fs.readFile(indexPath,
|
|
97
|
-
expect(content).toContain("console.error(
|
|
98
|
-
expect(content).toContain(
|
|
99
|
-
expect(content).toContain(
|
|
100
|
-
expect(content).toContain(
|
|
101
|
-
expect(content).toContain(
|
|
94
|
+
it('should log server startup information', async () => {
|
|
95
|
+
const indexPath = path.join(sourceDir, 'index.ts');
|
|
96
|
+
const content = await fs.readFile(indexPath, 'utf-8');
|
|
97
|
+
expect(content).toContain("console.error('[projitive-mcp] starting server')");
|
|
98
|
+
expect(content).toContain('console.error(`[projitive-mcp] version=');
|
|
99
|
+
expect(content).toContain('spec=');
|
|
100
|
+
expect(content).toContain('transport=stdio');
|
|
101
|
+
expect(content).toContain('pid=');
|
|
102
102
|
});
|
|
103
|
-
it(
|
|
104
|
-
const indexPath = path.join(sourceDir,
|
|
105
|
-
const content = await fs.readFile(indexPath,
|
|
106
|
-
expect(content).toContain("name:
|
|
107
|
-
expect(content).toContain(
|
|
108
|
-
expect(content).toContain(
|
|
103
|
+
it('should define MCP server with correct metadata', async () => {
|
|
104
|
+
const indexPath = path.join(sourceDir, 'index.ts');
|
|
105
|
+
const content = await fs.readFile(indexPath, 'utf-8');
|
|
106
|
+
expect(content).toContain("name: 'projitive'");
|
|
107
|
+
expect(content).toContain('version: MCP_RUNTIME_VERSION');
|
|
108
|
+
expect(content).toContain('description:');
|
|
109
109
|
});
|
|
110
110
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Governance workflow prompts
|
|
2
|
-
import { registerQuickStartPrompt } from
|
|
3
|
-
import { registerTaskDiscoveryPrompt } from
|
|
4
|
-
import { registerTaskExecutionPrompt } from
|
|
2
|
+
import { registerQuickStartPrompt } from './quickStart.js';
|
|
3
|
+
import { registerTaskDiscoveryPrompt } from './taskDiscovery.js';
|
|
4
|
+
import { registerTaskExecutionPrompt } from './taskExecution.js';
|
|
5
5
|
export function registerPrompts(server) {
|
|
6
6
|
registerQuickStartPrompt(server);
|
|
7
7
|
registerTaskDiscoveryPrompt(server);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
vi.mock('./quickStart.js', () => ({
|
|
3
|
+
registerQuickStartPrompt: vi.fn(),
|
|
4
|
+
}));
|
|
5
|
+
vi.mock('./taskDiscovery.js', () => ({
|
|
6
|
+
registerTaskDiscoveryPrompt: vi.fn(),
|
|
7
|
+
}));
|
|
8
|
+
vi.mock('./taskExecution.js', () => ({
|
|
9
|
+
registerTaskExecutionPrompt: vi.fn(),
|
|
10
|
+
}));
|
|
11
|
+
import { registerQuickStartPrompt } from './quickStart.js';
|
|
12
|
+
import { registerTaskDiscoveryPrompt } from './taskDiscovery.js';
|
|
13
|
+
import { registerTaskExecutionPrompt } from './taskExecution.js';
|
|
14
|
+
import { registerPrompts } from './index.js';
|
|
15
|
+
describe('prompts index module', () => {
|
|
16
|
+
it('registers all prompt categories', () => {
|
|
17
|
+
const server = {};
|
|
18
|
+
registerPrompts(server);
|
|
19
|
+
expect(registerQuickStartPrompt).toHaveBeenCalledWith(server);
|
|
20
|
+
expect(registerTaskDiscoveryPrompt).toHaveBeenCalledWith(server);
|
|
21
|
+
expect(registerTaskExecutionPrompt).toHaveBeenCalledWith(server);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { z } from
|
|
1
|
+
import { z } from 'zod';
|
|
2
2
|
function asUserPrompt(text) {
|
|
3
3
|
return {
|
|
4
4
|
messages: [
|
|
5
5
|
{
|
|
6
|
-
role:
|
|
6
|
+
role: 'user',
|
|
7
7
|
content: {
|
|
8
|
-
type:
|
|
8
|
+
type: 'text',
|
|
9
9
|
text,
|
|
10
10
|
},
|
|
11
11
|
},
|
|
@@ -13,108 +13,108 @@ function asUserPrompt(text) {
|
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
15
|
export function registerQuickStartPrompt(server) {
|
|
16
|
-
server.registerPrompt(
|
|
17
|
-
title:
|
|
18
|
-
description:
|
|
16
|
+
server.registerPrompt('quickStart', {
|
|
17
|
+
title: 'Quick Start',
|
|
18
|
+
description: 'Complete workflow to discover project, load context, and start task execution',
|
|
19
19
|
argsSchema: {
|
|
20
20
|
projectPath: z.string().optional(),
|
|
21
21
|
},
|
|
22
22
|
}, async ({ projectPath }) => {
|
|
23
23
|
const text = [
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
'# How to Govern a Project',
|
|
25
|
+
'',
|
|
26
|
+
'You are a Projitive governance assistant. Here is the complete workflow:',
|
|
27
|
+
'',
|
|
28
|
+
'## Step 1: Discover the Project',
|
|
29
|
+
'',
|
|
30
30
|
projectPath
|
|
31
31
|
? [
|
|
32
32
|
`- Known project path: "${projectPath}"`,
|
|
33
|
-
|
|
34
|
-
].join(
|
|
33
|
+
'- Call `projectContext(projectPath="' + projectPath + '")` directly to load project overview',
|
|
34
|
+
].join('\n')
|
|
35
35
|
: [
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
].join(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
].join(
|
|
36
|
+
'- Unknown project path:',
|
|
37
|
+
' 1. Call `projectScan()` to discover all governance roots',
|
|
38
|
+
' 2. Select a target project',
|
|
39
|
+
' 3. Call `projectLocate(inputPath="<selected-path>")` to lock governance root',
|
|
40
|
+
' 4. Call `projectContext(projectPath="<project-path>")` to load project overview',
|
|
41
|
+
].join('\n'),
|
|
42
|
+
'',
|
|
43
|
+
'## Step 2: Understand the Project',
|
|
44
|
+
'',
|
|
45
|
+
'After loading project context, read these resources:',
|
|
46
|
+
'- projitive://governance/workspace - Project overview',
|
|
47
|
+
'- projitive://governance/tasks - Current task pool',
|
|
48
|
+
'- projitive://governance/roadmap - Project roadmap',
|
|
49
|
+
'- projitive://designs/* - Design documents',
|
|
50
|
+
'',
|
|
51
|
+
'## Step 3: Discover Tasks',
|
|
52
|
+
'',
|
|
53
|
+
'Two ways to discover tasks:',
|
|
54
|
+
'',
|
|
55
|
+
'### Option A: Auto-select (Recommended)',
|
|
56
|
+
'Call `taskNext()` to get highest-priority actionable task.',
|
|
57
|
+
'If no actionable tasks are returned and roadmap has active goals, analyze roadmap context and create 1-3 TODO tasks via `taskCreate()`.',
|
|
58
|
+
'Then call `taskNext()` again to re-rank.',
|
|
59
|
+
'',
|
|
60
|
+
'### Option B: Manual select',
|
|
61
|
+
'1. Call `taskList()` to list all tasks',
|
|
62
|
+
'2. Select a task based on status and priority',
|
|
63
|
+
'3. Call `taskContext(projectPath="...", taskId="...")` for details',
|
|
64
|
+
'',
|
|
65
|
+
'## Step 4: Execute the Task',
|
|
66
|
+
'',
|
|
67
|
+
'After getting task context:',
|
|
68
|
+
'1. Read evidence links in Suggested Read Order',
|
|
69
|
+
'2. Understand task requirements and acceptance criteria',
|
|
70
|
+
'3. Write governance source via tools (`taskCreate` / `taskUpdate` / `roadmapCreate` / `roadmapUpdate`) instead of editing tasks.md/roadmap.md directly',
|
|
71
|
+
'4. Update docs (`designs/` / `reports/`) as required by evidence',
|
|
72
|
+
'5. taskCreate/taskUpdate/roadmapCreate/roadmapUpdate will auto-sync corresponding markdown views',
|
|
73
|
+
'6. Update task status:',
|
|
74
|
+
' - TODO → IN_PROGRESS (when starting execution)',
|
|
75
|
+
' - IN_PROGRESS → DONE (when completed)',
|
|
76
|
+
' - IN_PROGRESS → BLOCKED (when blocked)',
|
|
77
|
+
'7. Re-run taskContext() to verify changes',
|
|
78
|
+
'',
|
|
79
|
+
'## Autonomous Operating Loop',
|
|
80
|
+
'',
|
|
81
|
+
'Keep this loop until no high-value actionable work remains:',
|
|
82
|
+
'1. Discover: `taskNext()`',
|
|
83
|
+
'2. Execute: update governance store + docs + report evidence',
|
|
84
|
+
'3. Verify: `taskContext()`',
|
|
85
|
+
'4. Re-prioritize: `taskNext()`',
|
|
86
|
+
'',
|
|
87
|
+
'Stop and re-discover only when:',
|
|
88
|
+
'- Current task is BLOCKED with a clear blocker description and unblock condition',
|
|
89
|
+
'- Acceptance criteria are met and status is DONE',
|
|
90
|
+
'- Project has no actionable tasks and requires roadmap-driven task creation',
|
|
91
|
+
'',
|
|
92
|
+
'## Special Cases',
|
|
93
|
+
'',
|
|
94
|
+
'### Case 1: No .projitive directory',
|
|
95
|
+
'Call `projectInit(projectPath="<project-dir>")` to initialize governance structure.',
|
|
96
|
+
'',
|
|
97
|
+
'### Case 2: No actionable tasks',
|
|
98
|
+
'1. Check if .projitive database is missing',
|
|
99
|
+
'2. If roadmap has active goals, split milestones into 1-3 executable TODO tasks',
|
|
100
|
+
'3. Apply task creation gate before adding each task:',
|
|
101
|
+
' - Clear outcome: one-sentence done condition',
|
|
102
|
+
' - Verifiable evidence: at least one report/designs/readme link target',
|
|
103
|
+
' - Small slice: should be completable in one focused execution cycle',
|
|
104
|
+
' - Traceability: include at least one roadmapRefs item when applicable',
|
|
105
|
+
' - Distinct scope: avoid overlap with existing DONE/BLOCKED tasks',
|
|
106
|
+
'4. Prefer unblocking tasks that unlock multiple follow-up tasks',
|
|
107
|
+
'5. Re-run `taskNext()` to pick the new tasks',
|
|
108
|
+
'6. If still no tasks, read design documents in projitive://designs/ and create TODO tasks via `taskCreate()`',
|
|
109
|
+
'',
|
|
110
|
+
'## Hard Rules',
|
|
111
|
+
'',
|
|
112
|
+
'- **NEVER modify TASK/ROADMAP IDs** - Keep them immutable once assigned',
|
|
113
|
+
'- **Every status transition must have report evidence** - Create execution reports in reports/ directory',
|
|
114
|
+
'- **.projitive governance store is source of truth** - tasks.md/roadmap.md are generated views and may be overwritten',
|
|
115
|
+
'- **Prefer tool writes over manual table/view edits** - Use taskCreate/taskUpdate/roadmapCreate/roadmapUpdate',
|
|
116
|
+
'- **Always verify after updates** - Re-run taskContext() to confirm reference consistency',
|
|
117
|
+
].join('\n');
|
|
118
118
|
return asUserPrompt(text);
|
|
119
119
|
});
|
|
120
120
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { registerQuickStartPrompt } from './quickStart.js';
|
|
3
|
+
describe('quickStart prompt', () => {
|
|
4
|
+
it('registers the prompt and renders known project guidance', async () => {
|
|
5
|
+
const server = { registerPrompt: vi.fn() };
|
|
6
|
+
registerQuickStartPrompt(server);
|
|
7
|
+
const handler = server.registerPrompt.mock.calls[0][2];
|
|
8
|
+
const result = await handler({ projectPath: '/workspace/app' });
|
|
9
|
+
const text = result.messages[0].content.text;
|
|
10
|
+
expect(server.registerPrompt.mock.calls[0][0]).toBe('quickStart');
|
|
11
|
+
expect(text).toContain('Known project path: "/workspace/app"');
|
|
12
|
+
expect(text).toContain('taskCreate');
|
|
13
|
+
expect(text).toContain('.projitive governance store is source of truth');
|
|
14
|
+
});
|
|
15
|
+
it('renders discovery workflow when project path is unknown', async () => {
|
|
16
|
+
const server = { registerPrompt: vi.fn() };
|
|
17
|
+
registerQuickStartPrompt(server);
|
|
18
|
+
const handler = server.registerPrompt.mock.calls[0][2];
|
|
19
|
+
const result = await handler({});
|
|
20
|
+
const text = result.messages[0].content.text;
|
|
21
|
+
expect(text).toContain('Call `projectScan()` to discover all governance roots');
|
|
22
|
+
expect(text).toContain('Call `projectLocate(inputPath="<selected-path>")` to lock governance root');
|
|
23
|
+
});
|
|
24
|
+
});
|