@projitive/mcp 1.0.8 → 1.1.2
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 +4 -1
- package/output/source/{helpers/catch → common}/catch.js +6 -6
- package/output/source/{helpers/catch → common}/catch.test.js +6 -6
- package/output/source/common/errors.js +120 -0
- package/output/source/{helpers/files → common}/files.js +1 -1
- package/output/source/{helpers/files → common}/files.test.js +1 -1
- package/output/source/common/index.js +9 -0
- package/output/source/{helpers/linter/codes.js → common/linter.js} +13 -0
- package/output/source/{helpers/markdown → common}/markdown.test.js +1 -1
- package/output/source/{helpers/response → common}/response.test.js +1 -1
- package/output/source/common/types.js +7 -0
- package/output/source/common/utils.js +39 -0
- package/output/source/index.js +8 -196
- package/output/source/index.test.js +110 -0
- package/output/source/prompts/index.js +9 -0
- package/output/source/prompts/quickStart.js +94 -0
- package/output/source/prompts/taskDiscovery.js +190 -0
- package/output/source/prompts/taskExecution.js +161 -0
- package/output/source/resources/designs.js +108 -0
- package/output/source/resources/designs.test.js +154 -0
- package/output/source/resources/governance.js +40 -0
- package/output/source/resources/index.js +6 -0
- package/output/source/resources/readme.test.js +167 -0
- package/output/source/{reports.js → resources/reports.js} +5 -3
- package/output/source/resources/reports.test.js +149 -0
- package/output/source/tools/index.js +8 -0
- package/output/source/{projitive.js → tools/project.js} +4 -6
- package/output/source/tools/project.test.js +322 -0
- package/output/source/{roadmap.js → tools/roadmap.js} +4 -7
- package/output/source/tools/roadmap.test.js +103 -0
- package/output/source/{tasks.js → tools/task.js} +470 -23
- package/output/source/tools/task.test.js +473 -0
- package/output/source/types.js +56 -0
- package/package.json +4 -1
- package/output/source/design-context.js +0 -515
- package/output/source/designs.js +0 -38
- package/output/source/helpers/artifacts/index.js +0 -1
- package/output/source/helpers/catch/index.js +0 -1
- package/output/source/helpers/files/index.js +0 -1
- package/output/source/helpers/index.js +0 -6
- package/output/source/helpers/linter/index.js +0 -2
- package/output/source/helpers/linter/linter.js +0 -6
- package/output/source/helpers/markdown/index.js +0 -1
- package/output/source/helpers/response/index.js +0 -1
- package/output/source/projitive.test.js +0 -111
- package/output/source/roadmap.test.js +0 -11
- package/output/source/tasks.test.js +0 -152
- /package/output/source/{helpers/artifacts → common}/artifacts.js +0 -0
- /package/output/source/{helpers/artifacts → common}/artifacts.test.js +0 -0
- /package/output/source/{helpers/linter → common}/linter.test.js +0 -0
- /package/output/source/{helpers/markdown → common}/markdown.js +0 -0
- /package/output/source/{helpers/response → common}/response.js +0 -0
- /package/output/source/{readme.js → resources/readme.js} +0 -0
package/output/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@projitive/mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "Projitive MCP Server for project and task discovery/update",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"author": "",
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
16
|
"test": "vitest run",
|
|
17
|
+
"test:coverage": "vitest run --coverage",
|
|
18
|
+
"benchmark": "vitest bench --run",
|
|
17
19
|
"lint": "tsc -p tsconfig.json --noEmit",
|
|
18
20
|
"build": "rm -rf output && tsc -p tsconfig.json",
|
|
19
21
|
"prepublishOnly": "npm run build",
|
|
@@ -28,6 +30,7 @@
|
|
|
28
30
|
},
|
|
29
31
|
"devDependencies": {
|
|
30
32
|
"@types/node": "^24.3.0",
|
|
33
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
31
34
|
"tsx": "^4.20.5",
|
|
32
35
|
"typescript": "^5.9.2",
|
|
33
36
|
"vitest": "^3.2.4"
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Helper function: check if value is PromiseLike
|
|
2
2
|
function isPromiseLike(value) {
|
|
3
3
|
return value != null && typeof value === 'object' && 'then' in value && typeof value.then === 'function';
|
|
4
4
|
}
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
* isError
|
|
6
|
+
* Construct success result object
|
|
7
|
+
* isError always returns false
|
|
8
8
|
*/
|
|
9
9
|
function createSuccess(value) {
|
|
10
10
|
return {
|
|
@@ -14,8 +14,8 @@ function createSuccess(value) {
|
|
|
14
14
|
};
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
18
|
-
* isError
|
|
17
|
+
* Construct failure result object
|
|
18
|
+
* isError always returns true
|
|
19
19
|
*/
|
|
20
20
|
function createFailure(error) {
|
|
21
21
|
return {
|
|
@@ -43,6 +43,6 @@ export async function catchIt(input) {
|
|
|
43
43
|
catch (error) {
|
|
44
44
|
return createFailure(error);
|
|
45
45
|
}
|
|
46
|
-
//
|
|
46
|
+
// Theoretically shouldn't reach here, fallback for type safety
|
|
47
47
|
return createFailure(new Error('Unexpected input type'));
|
|
48
48
|
}
|
|
@@ -1,39 +1,39 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
2
|
import { catchIt } from './catch.js';
|
|
3
3
|
describe('catchIt', () => {
|
|
4
|
-
it('
|
|
4
|
+
it('sync function should return value, error undefined, isError false', async () => {
|
|
5
5
|
const result = await catchIt(() => 123);
|
|
6
6
|
expect(result.value).toBe(123);
|
|
7
7
|
expect(result.error).toBeUndefined();
|
|
8
8
|
expect(result.isError()).toBe(false);
|
|
9
9
|
});
|
|
10
|
-
it('
|
|
10
|
+
it('async function should return value, error undefined, isError false', async () => {
|
|
11
11
|
const result = await catchIt(async () => 456);
|
|
12
12
|
expect(result.value).toBe(456);
|
|
13
13
|
expect(result.error).toBeUndefined();
|
|
14
14
|
expect(result.isError()).toBe(false);
|
|
15
15
|
});
|
|
16
|
-
it('
|
|
16
|
+
it('sync throw should return error, value undefined, isError true', async () => {
|
|
17
17
|
const error = new Error('fail');
|
|
18
18
|
const result = await catchIt(() => { throw error; });
|
|
19
19
|
expect(result.value).toBeUndefined();
|
|
20
20
|
expect(result.error).toBe(error);
|
|
21
21
|
expect(result.isError()).toBe(true);
|
|
22
22
|
});
|
|
23
|
-
it('
|
|
23
|
+
it('async throw should return error, value undefined, isError true', async () => {
|
|
24
24
|
const error = new Error('fail-async');
|
|
25
25
|
const result = await catchIt(() => Promise.reject(error));
|
|
26
26
|
expect(result.value).toBeUndefined();
|
|
27
27
|
expect(result.error).toBe(error);
|
|
28
28
|
expect(result.isError()).toBe(true);
|
|
29
29
|
});
|
|
30
|
-
it('PromiseLike resolve
|
|
30
|
+
it('PromiseLike resolve should return value, error undefined, isError false', async () => {
|
|
31
31
|
const result = await catchIt(Promise.resolve('ok'));
|
|
32
32
|
expect(result.value).toBe('ok');
|
|
33
33
|
expect(result.error).toBeUndefined();
|
|
34
34
|
expect(result.isError()).toBe(false);
|
|
35
35
|
});
|
|
36
|
-
it('PromiseLike reject
|
|
36
|
+
it('PromiseLike reject should return error, value undefined, isError true', async () => {
|
|
37
37
|
const error = new Error('promise-fail');
|
|
38
38
|
const result = await catchIt(Promise.reject(error));
|
|
39
39
|
expect(result.value).toBeUndefined();
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// Projitive unified error type definitions
|
|
2
|
+
export class ProjitiveError extends Error {
|
|
3
|
+
code;
|
|
4
|
+
details;
|
|
5
|
+
constructor(message, code, details) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.code = code;
|
|
8
|
+
this.details = details;
|
|
9
|
+
this.name = "ProjitiveError";
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
// Project related errors
|
|
13
|
+
export class ProjectError extends ProjitiveError {
|
|
14
|
+
constructor(message, code, details) {
|
|
15
|
+
super(message, code, details);
|
|
16
|
+
this.name = "ProjectError";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export class ProjectNotFoundError extends ProjectError {
|
|
20
|
+
constructor(inputPath) {
|
|
21
|
+
super(`Project not found at path: ${inputPath}`, "PROJECT_NOT_FOUND", {
|
|
22
|
+
inputPath,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export class GovernanceRootNotFoundError extends ProjectError {
|
|
27
|
+
constructor(projectPath) {
|
|
28
|
+
super(`Governance root not found for project: ${projectPath}`, "GOVERNANCE_ROOT_NOT_FOUND", { projectPath });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// Task related errors
|
|
32
|
+
export class TaskError extends ProjitiveError {
|
|
33
|
+
constructor(message, code, details) {
|
|
34
|
+
super(message, code, details);
|
|
35
|
+
this.name = "TaskError";
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export class TaskNotFoundError extends TaskError {
|
|
39
|
+
constructor(taskId) {
|
|
40
|
+
super(`Task not found: ${taskId}`, "TASK_NOT_FOUND", { taskId });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export class InvalidTaskIdError extends TaskError {
|
|
44
|
+
constructor(taskId) {
|
|
45
|
+
super(`Invalid task ID: ${taskId}`, "INVALID_TASK_ID", { taskId });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export class TaskValidationError extends TaskError {
|
|
49
|
+
errors;
|
|
50
|
+
constructor(taskId, errors) {
|
|
51
|
+
super(`Task validation failed for ${taskId}: ${errors.join(", ")}`, "TASK_VALIDATION_FAILED", { taskId, errors });
|
|
52
|
+
this.errors = errors;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// File operation errors
|
|
56
|
+
export class FileError extends ProjitiveError {
|
|
57
|
+
filePath;
|
|
58
|
+
constructor(message, filePath, code, details) {
|
|
59
|
+
super(message, code || "FILE_ERROR", { filePath, ...details });
|
|
60
|
+
this.filePath = filePath;
|
|
61
|
+
this.name = "FileError";
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export class FileNotFoundError extends FileError {
|
|
65
|
+
constructor(filePath) {
|
|
66
|
+
super(`File not found: ${filePath}`, filePath, "FILE_NOT_FOUND");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export class FileReadError extends FileError {
|
|
70
|
+
constructor(filePath, cause) {
|
|
71
|
+
super(`Failed to read file: ${filePath}`, filePath, "FILE_READ_ERROR", {
|
|
72
|
+
cause: cause?.message,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export class FileWriteError extends FileError {
|
|
77
|
+
constructor(filePath, cause) {
|
|
78
|
+
super(`Failed to write file: ${filePath}`, filePath, "FILE_WRITE_ERROR", {
|
|
79
|
+
cause: cause?.message,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Validation errors
|
|
84
|
+
export class ValidationError extends ProjitiveError {
|
|
85
|
+
errors;
|
|
86
|
+
constructor(message, errors = [], code) {
|
|
87
|
+
super(message, code || "VALIDATION_FAILED", { errors });
|
|
88
|
+
this.errors = errors;
|
|
89
|
+
this.name = "ValidationError";
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
export class ConfidenceScoreError extends ValidationError {
|
|
93
|
+
score;
|
|
94
|
+
constructor(message, score, errors = []) {
|
|
95
|
+
super(message, errors, "CONFIDENCE_SCORE_ERROR");
|
|
96
|
+
this.score = score;
|
|
97
|
+
this.score = score;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// MCP related errors
|
|
101
|
+
export class MCPError extends ProjitiveError {
|
|
102
|
+
constructor(message, code, details) {
|
|
103
|
+
super(message, code, details);
|
|
104
|
+
this.name = "MCPError";
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
export class ResourceNotFoundError extends MCPError {
|
|
108
|
+
constructor(resourceUri) {
|
|
109
|
+
super(`Resource not found: ${resourceUri}`, "RESOURCE_NOT_FOUND", {
|
|
110
|
+
resourceUri,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
export class PromptNotFoundError extends MCPError {
|
|
115
|
+
constructor(promptName) {
|
|
116
|
+
super(`Prompt not found: ${promptName}`, "PROMPT_NOT_FOUND", {
|
|
117
|
+
promptName,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { catchIt } from "
|
|
3
|
+
import { catchIt } from "./catch.js";
|
|
4
4
|
const FILE_ARTIFACTS = ["README.md", "roadmap.md", "tasks.md"];
|
|
5
5
|
const DIRECTORY_ARTIFACTS = ["designs", "reports", "hooks"];
|
|
6
6
|
async function fileLineCount(filePath) {
|
|
@@ -2,7 +2,7 @@ import fs from "node:fs/promises";
|
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { afterEach, describe, expect, it } from "vitest";
|
|
5
|
-
import { discoverGovernanceArtifacts } from
|
|
5
|
+
import { discoverGovernanceArtifacts } from './files.js';
|
|
6
6
|
const tempPaths = [];
|
|
7
7
|
async function createTempDir() {
|
|
8
8
|
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "projitive-mcp-test-"));
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from "./errors.js";
|
|
2
|
+
export * from "./types.js";
|
|
3
|
+
export * from "./utils.js";
|
|
4
|
+
export * from "./markdown.js";
|
|
5
|
+
export * from "./files.js";
|
|
6
|
+
export * from "./response.js";
|
|
7
|
+
export * from "./catch.js";
|
|
8
|
+
export * from "./artifacts.js";
|
|
9
|
+
export * from "./linter.js";
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
export function renderLintSuggestions(suggestions) {
|
|
2
|
+
return suggestions.map((item) => {
|
|
3
|
+
const suffix = item.fixHint ? ` ${item.fixHint}` : "";
|
|
4
|
+
return `- [${item.code}] ${item.message}${suffix}`;
|
|
5
|
+
});
|
|
6
|
+
}
|
|
1
7
|
export const TASK_LINT_CODES = {
|
|
2
8
|
DUPLICATE_ID: "TASK_DUPLICATE_ID",
|
|
3
9
|
IN_PROGRESS_OWNER_EMPTY: "TASK_IN_PROGRESS_OWNER_EMPTY",
|
|
@@ -11,6 +17,13 @@ export const TASK_LINT_CODES = {
|
|
|
11
17
|
FILTER_EMPTY: "TASK_FILTER_EMPTY",
|
|
12
18
|
CONTEXT_HOOK_HEAD_MISSING: "TASK_CONTEXT_HOOK_HEAD_MISSING",
|
|
13
19
|
CONTEXT_HOOK_FOOTER_MISSING: "TASK_CONTEXT_HOOK_FOOTER_MISSING",
|
|
20
|
+
// Spec v1.1.0 - Blocker Categorization
|
|
21
|
+
BLOCKED_WITHOUT_BLOCKER: "TASK_BLOCKED_WITHOUT_BLOCKER",
|
|
22
|
+
BLOCKER_TYPE_INVALID: "TASK_BLOCKER_TYPE_INVALID",
|
|
23
|
+
BLOCKER_DESCRIPTION_EMPTY: "TASK_BLOCKER_DESCRIPTION_EMPTY",
|
|
24
|
+
IN_PROGRESS_WITHOUT_SUBSTATE: "TASK_IN_PROGRESS_WITHOUT_SUBSTATE",
|
|
25
|
+
SUBSTATE_PHASE_INVALID: "TASK_SUBSTATE_PHASE_INVALID",
|
|
26
|
+
SUBSTATE_CONFIDENCE_INVALID: "TASK_SUBSTATE_CONFIDENCE_INVALID",
|
|
14
27
|
};
|
|
15
28
|
export const ROADMAP_LINT_CODES = {
|
|
16
29
|
IDS_EMPTY: "ROADMAP_IDS_EMPTY",
|
|
@@ -2,7 +2,7 @@ import fs from "node:fs/promises";
|
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { afterEach, describe, expect, it } from "vitest";
|
|
5
|
-
import { findTextReferences, readMarkdownSections } from
|
|
5
|
+
import { findTextReferences, readMarkdownSections } from './markdown.js';
|
|
6
6
|
const tempPaths = [];
|
|
7
7
|
async function createTempDir() {
|
|
8
8
|
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "projitive-mcp-test-"));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { asText, nextCallSection, renderErrorMarkdown, renderToolResponseMarkdown, summarySection, } from
|
|
2
|
+
import { asText, nextCallSection, renderErrorMarkdown, renderToolResponseMarkdown, summarySection, } from './response.js';
|
|
3
3
|
describe("response helpers", () => {
|
|
4
4
|
it("wraps markdown text as MCP text content", () => {
|
|
5
5
|
const result = asText("# hello");
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Common utility functions
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
/**
|
|
5
|
+
* Safely read Markdown file content, return fallback if file doesn't exist or is empty
|
|
6
|
+
*/
|
|
7
|
+
export async function readMarkdownOrFallback(relativePath, fallbackTitle, repoRoot = process.cwd()) {
|
|
8
|
+
const absolutePath = path.resolve(repoRoot, relativePath);
|
|
9
|
+
try {
|
|
10
|
+
const content = await fs.readFile(absolutePath, "utf-8");
|
|
11
|
+
if (content.trim().length > 0) {
|
|
12
|
+
return content;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
if (error.code !== "ENOENT") {
|
|
17
|
+
console.error(`Failed to read file: ${absolutePath}`, error);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return [
|
|
21
|
+
`# ${fallbackTitle}`,
|
|
22
|
+
"",
|
|
23
|
+
`- file: ${relativePath}`,
|
|
24
|
+
"- status: missing-or-empty",
|
|
25
|
+
"- next: create this file or ensure it has readable markdown content",
|
|
26
|
+
].join("\n");
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Capitalize first letter
|
|
30
|
+
*/
|
|
31
|
+
export function capitalizeFirstLetter(str) {
|
|
32
|
+
return str.split(/[-_]/).map(part => part.charAt(0).toUpperCase() + part.slice(1)).join('');
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Format title
|
|
36
|
+
*/
|
|
37
|
+
export function formatTitle(str) {
|
|
38
|
+
return str.split(/[-_]/).map(part => part.charAt(0).toUpperCase() + part.slice(1)).join(' ');
|
|
39
|
+
}
|
package/output/source/index.js
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import fs from "node:fs/promises";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
import process from "node:process";
|
|
5
4
|
import { fileURLToPath } from "node:url";
|
|
6
5
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
7
6
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
8
|
-
import { z } from "zod";
|
|
9
7
|
import packageJson from "../package.json" with { type: "json" };
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
const PROJITIVE_SPEC_VERSION = "1.0.0";
|
|
8
|
+
import { registerTools } from "./tools/index.js";
|
|
9
|
+
import { registerPrompts } from "./prompts/index.js";
|
|
10
|
+
import { registerResources } from "./resources/index.js";
|
|
11
|
+
const PROJITIVE_SPEC_VERSION = "1.1.0";
|
|
15
12
|
const currentFilePath = fileURLToPath(import.meta.url);
|
|
16
13
|
const sourceDir = path.dirname(currentFilePath);
|
|
17
14
|
const repoRoot = path.resolve(sourceDir, "..", "..", "..");
|
|
@@ -23,195 +20,10 @@ const server = new McpServer({
|
|
|
23
20
|
version: MCP_RUNTIME_VERSION,
|
|
24
21
|
description: "Semantic Projitive MCP for project/task discovery and agent guidance with markdown-first outputs",
|
|
25
22
|
});
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const absolutePath = resolveRepoFile(relativePath);
|
|
31
|
-
const content = await fs.readFile(absolutePath, "utf-8").catch(() => undefined);
|
|
32
|
-
if (typeof content === "string" && content.trim().length > 0) {
|
|
33
|
-
return content;
|
|
34
|
-
}
|
|
35
|
-
return [
|
|
36
|
-
`# ${fallbackTitle}`,
|
|
37
|
-
"",
|
|
38
|
-
`- file: ${relativePath}`,
|
|
39
|
-
"- status: missing-or-empty",
|
|
40
|
-
"- next: create this file or ensure it has readable markdown content",
|
|
41
|
-
].join("\n");
|
|
42
|
-
}
|
|
43
|
-
function renderMethodCatalogMarkdown() {
|
|
44
|
-
return [
|
|
45
|
-
"# MCP Method Catalog",
|
|
46
|
-
"",
|
|
47
|
-
"## Start Here",
|
|
48
|
-
"- Unknown project path: `projectScan` -> `projectLocate` -> `projectContext` -> `taskNext`.",
|
|
49
|
-
"- Known project path: `projectContext` -> `taskNext` (or `taskList`) -> `taskContext`.",
|
|
50
|
-
"- Need to bootstrap governance: call `projectInit(projectPath=\"<project-dir>\")` only when `.projitive` is missing.",
|
|
51
|
-
"",
|
|
52
|
-
"## Methods",
|
|
53
|
-
"| Order | Group | Method | Agent Use |",
|
|
54
|
-
"|---|---|---|---|",
|
|
55
|
-
"| 1 | Project | projectScan | discover governance roots when project is unknown |",
|
|
56
|
-
"| 2 | Project | projectLocate | lock nearest governance root from any path |",
|
|
57
|
-
"| 3 | Project | projectContext | load project summary before task decisions |",
|
|
58
|
-
"| 4 | Task | taskNext | auto-pick highest-priority actionable task |",
|
|
59
|
-
"| 5 | Task | taskList | list/filter tasks for manual selection |",
|
|
60
|
-
"| 6 | Task | taskContext | inspect one task with evidence and read order |",
|
|
61
|
-
"| 7 | Roadmap | roadmapList | inspect roadmap-task linkage |",
|
|
62
|
-
"| 8 | Roadmap | roadmapContext | inspect one roadmap with references |",
|
|
63
|
-
"| 9 | Project | projectNext | rank actionable projects across workspace |",
|
|
64
|
-
"| 10 | Project | projectInit | bootstrap governance files if missing |"
|
|
65
|
-
].join("\n");
|
|
66
|
-
}
|
|
67
|
-
function registerGovernanceResources() {
|
|
68
|
-
server.registerResource("governanceWorkspace", "projitive://governance/workspace", {
|
|
69
|
-
title: "Governance Workspace",
|
|
70
|
-
description: "Primary governance README under .projitive",
|
|
71
|
-
mimeType: "text/markdown",
|
|
72
|
-
}, async () => ({
|
|
73
|
-
contents: [
|
|
74
|
-
{
|
|
75
|
-
uri: "projitive://governance/workspace",
|
|
76
|
-
text: await readMarkdownOrFallback(".projitive/README.md", "Governance Workspace"),
|
|
77
|
-
},
|
|
78
|
-
],
|
|
79
|
-
}));
|
|
80
|
-
server.registerResource("governanceTasks", "projitive://governance/tasks", {
|
|
81
|
-
title: "Governance Tasks",
|
|
82
|
-
description: "Current task pool and status under .projitive/tasks.md",
|
|
83
|
-
mimeType: "text/markdown",
|
|
84
|
-
}, async () => ({
|
|
85
|
-
contents: [
|
|
86
|
-
{
|
|
87
|
-
uri: "projitive://governance/tasks",
|
|
88
|
-
text: await readMarkdownOrFallback(".projitive/tasks.md", "Governance Tasks"),
|
|
89
|
-
},
|
|
90
|
-
],
|
|
91
|
-
}));
|
|
92
|
-
server.registerResource("governanceRoadmap", "projitive://governance/roadmap", {
|
|
93
|
-
title: "Governance Roadmap",
|
|
94
|
-
description: "Current roadmap under .projitive/roadmap.md",
|
|
95
|
-
mimeType: "text/markdown",
|
|
96
|
-
}, async () => ({
|
|
97
|
-
contents: [
|
|
98
|
-
{
|
|
99
|
-
uri: "projitive://governance/roadmap",
|
|
100
|
-
text: await readMarkdownOrFallback(".projitive/roadmap.md", "Governance Roadmap"),
|
|
101
|
-
},
|
|
102
|
-
],
|
|
103
|
-
}));
|
|
104
|
-
server.registerResource("mcpMethodCatalog", "projitive://mcp/method-catalog", {
|
|
105
|
-
title: "MCP Method Catalog",
|
|
106
|
-
description: "Method naming and purpose map for agent routing",
|
|
107
|
-
mimeType: "text/markdown",
|
|
108
|
-
}, async () => ({
|
|
109
|
-
contents: [
|
|
110
|
-
{
|
|
111
|
-
uri: "projitive://mcp/method-catalog",
|
|
112
|
-
text: renderMethodCatalogMarkdown(),
|
|
113
|
-
},
|
|
114
|
-
],
|
|
115
|
-
}));
|
|
116
|
-
}
|
|
117
|
-
function asUserPrompt(text) {
|
|
118
|
-
return {
|
|
119
|
-
messages: [
|
|
120
|
-
{
|
|
121
|
-
role: "user",
|
|
122
|
-
content: {
|
|
123
|
-
type: "text",
|
|
124
|
-
text,
|
|
125
|
-
},
|
|
126
|
-
},
|
|
127
|
-
],
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
function registerGovernancePrompts() {
|
|
131
|
-
server.registerPrompt("executeTaskWorkflow", {
|
|
132
|
-
title: "Execute Task Workflow",
|
|
133
|
-
description: "Primary execution prompt: select one task, execute, and verify evidence consistency",
|
|
134
|
-
argsSchema: {
|
|
135
|
-
projectPath: z.string().optional(),
|
|
136
|
-
taskId: z.string().optional(),
|
|
137
|
-
},
|
|
138
|
-
}, async ({ projectPath, taskId }) => {
|
|
139
|
-
const taskEntry = taskId && projectPath
|
|
140
|
-
? `1) Run taskContext(projectPath=\"${projectPath}\", taskId=\"${taskId}\").`
|
|
141
|
-
: "1) Run taskNext().";
|
|
142
|
-
const text = [
|
|
143
|
-
"You are executing Projitive governance workflow in agent-first mode.",
|
|
144
|
-
"",
|
|
145
|
-
"Fast path:",
|
|
146
|
-
taskEntry,
|
|
147
|
-
"2) Follow Suggested Read Order and identify execution blockers.",
|
|
148
|
-
"3) Edit governance markdown only (tasks/designs/reports/roadmap).",
|
|
149
|
-
"4) Re-run taskContext for the selected task and verify references.",
|
|
150
|
-
"",
|
|
151
|
-
"Fallbacks:",
|
|
152
|
-
"- If `.projitive` is missing for a known project, run `projectInit(projectPath=\"<project-dir>\")` first.",
|
|
153
|
-
"- If taskNext returns no actionable task, follow its no-task checklist and create 1-3 TODO tasks.",
|
|
154
|
-
"- If project is unknown, run projectScan -> projectLocate -> projectContext before task tools.",
|
|
155
|
-
"",
|
|
156
|
-
"Hard rules:",
|
|
157
|
-
"- Keep TASK/ROADMAP IDs immutable.",
|
|
158
|
-
"- Every status transition must have report evidence.",
|
|
159
|
-
"- Do not introduce non-governance file edits unless task scope requires.",
|
|
160
|
-
].join("\n");
|
|
161
|
-
return asUserPrompt(text);
|
|
162
|
-
});
|
|
163
|
-
server.registerPrompt("updateTaskStatusWithEvidence", {
|
|
164
|
-
title: "Update Task Status With Evidence",
|
|
165
|
-
description: "Safe status transition playbook with mandatory evidence backfill",
|
|
166
|
-
argsSchema: {
|
|
167
|
-
projectPath: z.string(),
|
|
168
|
-
taskId: z.string(),
|
|
169
|
-
targetStatus: z.enum(["TODO", "IN_PROGRESS", "BLOCKED", "DONE"]),
|
|
170
|
-
},
|
|
171
|
-
}, async ({ projectPath, taskId, targetStatus }) => {
|
|
172
|
-
const text = [
|
|
173
|
-
"Perform a safe task status update with evidence alignment.",
|
|
174
|
-
"",
|
|
175
|
-
`1) Run taskContext(projectPath=\"${projectPath}\", taskId=\"${taskId}\").`,
|
|
176
|
-
`2) Confirm transition to ${targetStatus} is valid.`,
|
|
177
|
-
"3) Update tasks.md status and updatedAt.",
|
|
178
|
-
"4) Add or update a report under reports/ with concrete evidence.",
|
|
179
|
-
"5) Re-run taskContext and confirm status/evidence/reference consistency.",
|
|
180
|
-
"6) If lint remains, fix and re-run taskContext once more.",
|
|
181
|
-
"",
|
|
182
|
-
"Checklist:",
|
|
183
|
-
"- Transition is valid per status machine.",
|
|
184
|
-
"- links/roadmapRefs remain parseable and consistent.",
|
|
185
|
-
"- Only `hooks/task_no_actionable.md` is used as global background hook for no-task discovery.",
|
|
186
|
-
].join("\n");
|
|
187
|
-
return asUserPrompt(text);
|
|
188
|
-
});
|
|
189
|
-
server.registerPrompt("triageProjectGovernance", {
|
|
190
|
-
title: "Triage Project Governance",
|
|
191
|
-
description: "Discovery-first triage prompt to pick project and next executable task",
|
|
192
|
-
argsSchema: {},
|
|
193
|
-
}, async () => {
|
|
194
|
-
const text = [
|
|
195
|
-
"Triage governance and pick one execution target quickly.",
|
|
196
|
-
"",
|
|
197
|
-
"0) If known project has no `.projitive`, run projectInit(projectPath=<project-dir>) first.",
|
|
198
|
-
"1) If project path is unknown, run projectScan() and pick one discovered project.",
|
|
199
|
-
"2) Run projectNext() to rank projects.",
|
|
200
|
-
"3) Run projectContext(projectPath=<selectedProject>).",
|
|
201
|
-
"4) Run taskNext() for best actionable task.",
|
|
202
|
-
"5) If manual filtering is needed, run taskList(projectPath=<selectedProject>, status=IN_PROGRESS).",
|
|
203
|
-
"6) Continue with taskContext(projectPath=<selectedProject>, taskId=<selectedTaskId>).",
|
|
204
|
-
].join("\n");
|
|
205
|
-
return asUserPrompt(text);
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
registerProjectTools(server);
|
|
209
|
-
registerTaskTools(server);
|
|
210
|
-
registerRoadmapTools(server);
|
|
211
|
-
registerGovernanceResources();
|
|
212
|
-
registerGovernancePrompts();
|
|
213
|
-
registerDesignContextResources(server);
|
|
214
|
-
registerDesignContextPrompts(server);
|
|
23
|
+
// 注册所有模块
|
|
24
|
+
registerTools(server);
|
|
25
|
+
registerPrompts(server);
|
|
26
|
+
registerResources(server, repoRoot);
|
|
215
27
|
async function main() {
|
|
216
28
|
console.error(`[projitive-mcp] starting server`);
|
|
217
29
|
console.error(`[projitive-mcp] version=${MCP_RUNTIME_VERSION} spec=${PROJITIVE_SPEC_VERSION} transport=stdio pid=${process.pid}`);
|