@projitive/mcp 2.1.1 → 2.1.3
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 +5 -1
- package/output/source/common/linter.js +2 -4
- package/output/source/common/response.js +27 -2
- package/output/source/common/response.test.js +13 -1
- package/output/source/prompts/quickStart.js +8 -3
- package/output/source/prompts/taskDiscovery.js +17 -2
- package/output/source/prompts/taskDiscovery.test.js +4 -0
- package/output/source/prompts/taskExecution.js +19 -5
- package/output/source/prompts/taskExecution.test.js +6 -1
- package/output/source/tools/project.js +316 -45
- package/output/source/tools/project.test.js +50 -5
- package/output/source/tools/task.js +83 -55
- package/output/source/tools/task.test.js +42 -32
- package/output/source/types.js +2 -1
- package/package.json +5 -1
|
@@ -12,7 +12,9 @@ export const TASK_RESEARCH_DIR = 'designs/research';
|
|
|
12
12
|
export const TASK_RESEARCH_FILE_SUFFIX = '.implementation-research.md';
|
|
13
13
|
export const CORE_DESIGN_DOCS_DIR = 'designs/core';
|
|
14
14
|
export const CORE_ARCHITECTURE_DOC_FILE = `${CORE_DESIGN_DOCS_DIR}/architecture.md`;
|
|
15
|
-
export const
|
|
15
|
+
export const CORE_CODE_STYLE_DOC_FILE = `${CORE_DESIGN_DOCS_DIR}/code-style.md`;
|
|
16
|
+
export const CORE_UI_STYLE_DOC_FILE = `${CORE_DESIGN_DOCS_DIR}/ui-style.md`;
|
|
17
|
+
export const CORE_STYLE_DOC_FILE = CORE_UI_STYLE_DOC_FILE;
|
|
16
18
|
function taskStatusGuidance(task) {
|
|
17
19
|
if (task.status === 'TODO') {
|
|
18
20
|
return [
|
|
@@ -220,7 +222,8 @@ const DEFAULT_NO_TASK_DISCOVERY_GUIDANCE = [
|
|
|
220
222
|
'- Prefer slices that unlock multiple downstream tasks before isolated refactors or low-impact cleanups.',
|
|
221
223
|
'- Skip duplicate scope: do not create tasks that overlap existing TODO/IN_PROGRESS/BLOCKED task intent.',
|
|
222
224
|
'- Use quality gates for discovery candidates: user value, delivery risk reduction, or measurable throughput improvement.',
|
|
223
|
-
'- Review and update
|
|
225
|
+
'- Review and update core governance docs under designs/core/ (architecture.md, code-style.md, ui-style.md) if they are missing or outdated.',
|
|
226
|
+
'- If a task changes module boundaries, engineering conventions, or UI patterns, create follow-up TODO work or update the matching core doc before closing the loop.',
|
|
224
227
|
'- Keep each discovery round small (1-3 tasks), then rerun taskNext immediately for re-ranking and execution.',
|
|
225
228
|
];
|
|
226
229
|
const DEFAULT_TASK_CONTEXT_READING_GUIDANCE = [
|
|
@@ -232,14 +235,6 @@ const DEFAULT_TASK_CONTEXT_READING_GUIDANCE = [
|
|
|
232
235
|
'- Read process guides under templates/docs/project guidelines to align with local governance rules.',
|
|
233
236
|
'- If available, read docs/ architecture or migration guides before major structural changes.',
|
|
234
237
|
];
|
|
235
|
-
export async function resolveNoTaskDiscoveryGuidance(governanceDir) {
|
|
236
|
-
void governanceDir;
|
|
237
|
-
return DEFAULT_NO_TASK_DISCOVERY_GUIDANCE;
|
|
238
|
-
}
|
|
239
|
-
export async function resolveTaskContextReadingGuidance(governanceDir) {
|
|
240
|
-
void governanceDir;
|
|
241
|
-
return DEFAULT_TASK_CONTEXT_READING_GUIDANCE;
|
|
242
|
-
}
|
|
243
238
|
async function readRoadmapIds(governanceDir) {
|
|
244
239
|
const dbPath = path.join(governanceDir, '.projitive');
|
|
245
240
|
try {
|
|
@@ -285,9 +280,6 @@ function normalizeTaskLink(link) {
|
|
|
285
280
|
const withoutDotPrefix = slashNormalized.replace(/^\.\//, '');
|
|
286
281
|
return withoutDotPrefix.replace(/^\/+/, '');
|
|
287
282
|
}
|
|
288
|
-
function resolveTaskLinkPath(projectPath, link) {
|
|
289
|
-
return path.join(projectPath, link);
|
|
290
|
-
}
|
|
291
283
|
function taskResearchBriefRelativePath(taskId) {
|
|
292
284
|
return `${TASK_RESEARCH_DIR}/${taskId}${TASK_RESEARCH_FILE_SUFFIX}`;
|
|
293
285
|
}
|
|
@@ -317,9 +309,8 @@ function renderTaskResearchBriefTemplate(task) {
|
|
|
317
309
|
];
|
|
318
310
|
}
|
|
319
311
|
async function inspectTaskResearchBrief(governanceDir, task) {
|
|
320
|
-
const projectPath = toProjectPath(governanceDir);
|
|
321
312
|
const relativePath = taskResearchBriefRelativePath(task.id);
|
|
322
|
-
const absolutePath =
|
|
313
|
+
const absolutePath = path.join(governanceDir, relativePath);
|
|
323
314
|
const exists = await fs.access(absolutePath).then(() => true).catch(() => false);
|
|
324
315
|
return { relativePath, absolutePath, exists, ready: exists };
|
|
325
316
|
}
|
|
@@ -327,44 +318,56 @@ function collectTaskResearchBriefLintSuggestions(state) {
|
|
|
327
318
|
if (!state.exists) {
|
|
328
319
|
return [{
|
|
329
320
|
code: TASK_LINT_CODES.RESEARCH_BRIEF_MISSING,
|
|
330
|
-
message: `Pre-execution research brief missing: ${state.relativePath}.`,
|
|
331
|
-
fixHint: 'Create the file and fill required sections before implementation.',
|
|
321
|
+
message: `Pre-execution research brief missing under governanceDir: ${state.relativePath}.`,
|
|
322
|
+
fixHint: 'Create the file under governanceDir and fill required sections before implementation.',
|
|
332
323
|
}];
|
|
333
324
|
}
|
|
334
325
|
return [];
|
|
335
326
|
}
|
|
336
|
-
function inspectProjectContextDocsFromArtifacts(files) {
|
|
327
|
+
export function inspectProjectContextDocsFromArtifacts(files) {
|
|
337
328
|
const markdownFiles = files
|
|
338
329
|
.map((item) => item.replace(/\\/g, '/'))
|
|
339
330
|
.filter((item) => item.toLowerCase().endsWith('.md'));
|
|
340
331
|
const architectureDocSuffix = `/${CORE_ARCHITECTURE_DOC_FILE}`.toLowerCase();
|
|
341
|
-
const
|
|
332
|
+
const codeStyleDocSuffix = `/${CORE_CODE_STYLE_DOC_FILE}`.toLowerCase();
|
|
333
|
+
const uiStyleDocSuffix = `/${CORE_UI_STYLE_DOC_FILE}`.toLowerCase();
|
|
342
334
|
const architectureDocs = markdownFiles.filter((item) => item.toLowerCase().endsWith(architectureDocSuffix));
|
|
343
|
-
const
|
|
335
|
+
const codeStyleDocs = markdownFiles.filter((item) => item.toLowerCase().endsWith(codeStyleDocSuffix));
|
|
336
|
+
const uiStyleDocs = markdownFiles.filter((item) => item.toLowerCase().endsWith(uiStyleDocSuffix));
|
|
344
337
|
const missingArchitectureDocs = architectureDocs.length === 0;
|
|
345
|
-
const
|
|
338
|
+
const missingCodeStyleDocs = codeStyleDocs.length === 0;
|
|
339
|
+
const missingUiStyleDocs = uiStyleDocs.length === 0;
|
|
346
340
|
return {
|
|
347
341
|
architectureDocs,
|
|
348
|
-
|
|
342
|
+
codeStyleDocs,
|
|
343
|
+
uiStyleDocs,
|
|
349
344
|
missingArchitectureDocs,
|
|
350
|
-
|
|
351
|
-
|
|
345
|
+
missingCodeStyleDocs,
|
|
346
|
+
missingUiStyleDocs,
|
|
347
|
+
ready: !missingArchitectureDocs && !missingCodeStyleDocs && !missingUiStyleDocs,
|
|
352
348
|
};
|
|
353
349
|
}
|
|
354
|
-
function collectProjectContextDocsLintSuggestions(state) {
|
|
350
|
+
export function collectProjectContextDocsLintSuggestions(state) {
|
|
355
351
|
const suggestions = [];
|
|
356
352
|
if (state.missingArchitectureDocs) {
|
|
357
353
|
suggestions.push({
|
|
358
354
|
code: PROJECT_LINT_CODES.ARCHITECTURE_DOC_MISSING,
|
|
359
355
|
message: 'Project context is missing architecture design documentation.',
|
|
360
|
-
fixHint: `Add required file: ${CORE_ARCHITECTURE_DOC_FILE}.`,
|
|
356
|
+
fixHint: `Add required file under governanceDir: ${CORE_ARCHITECTURE_DOC_FILE}.`,
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
if (state.missingCodeStyleDocs) {
|
|
360
|
+
suggestions.push({
|
|
361
|
+
code: PROJECT_LINT_CODES.CODE_STYLE_DOC_MISSING,
|
|
362
|
+
message: 'Project context is missing code style documentation.',
|
|
363
|
+
fixHint: `Add required file under governanceDir: ${CORE_CODE_STYLE_DOC_FILE}.`,
|
|
361
364
|
});
|
|
362
365
|
}
|
|
363
|
-
if (state.
|
|
366
|
+
if (state.missingUiStyleDocs) {
|
|
364
367
|
suggestions.push({
|
|
365
|
-
code: PROJECT_LINT_CODES.
|
|
366
|
-
message: 'Project context is missing
|
|
367
|
-
fixHint: `Add required file: ${
|
|
368
|
+
code: PROJECT_LINT_CODES.UI_STYLE_DOC_MISSING,
|
|
369
|
+
message: 'Project context is missing UI style documentation.',
|
|
370
|
+
fixHint: `Add required file under governanceDir: ${CORE_UI_STYLE_DOC_FILE}.`,
|
|
368
371
|
});
|
|
369
372
|
}
|
|
370
373
|
return suggestions;
|
|
@@ -1000,7 +1003,7 @@ export function registerTaskTools(server) {
|
|
|
1000
1003
|
}));
|
|
1001
1004
|
const preferredProject = projectSnapshots[0];
|
|
1002
1005
|
const preferredRoadmapRef = preferredProject?.roadmapIds[0] ?? 'ROADMAP-0001';
|
|
1003
|
-
const noTaskDiscoveryGuidance =
|
|
1006
|
+
const noTaskDiscoveryGuidance = DEFAULT_NO_TASK_DISCOVERY_GUIDANCE;
|
|
1004
1007
|
return { isEmpty: true, roots, depth, projects, projectSnapshots, preferredProject, preferredRoadmapRef, noTaskDiscoveryGuidance };
|
|
1005
1008
|
}
|
|
1006
1009
|
const selected = rankedCandidates[0];
|
|
@@ -1107,15 +1110,20 @@ export function registerTaskTools(server) {
|
|
|
1107
1110
|
return [
|
|
1108
1111
|
...(!data.projectContextDocsState.ready
|
|
1109
1112
|
? [
|
|
1110
|
-
'- Project context docs are incomplete. Complete missing project architecture
|
|
1113
|
+
'- Project context docs are incomplete. Complete missing project architecture, code style, and UI style docs before deep implementation.',
|
|
1114
|
+
`- Rerun projectInit to backfill missing governance artifacts first: projectInit(projectPath="${toProjectPath(data.selected.governanceDir)}")`,
|
|
1111
1115
|
...(data.projectContextDocsState.missingArchitectureDocs
|
|
1112
|
-
? [`- Missing architecture design doc: create required file ${CORE_ARCHITECTURE_DOC_FILE}.`]
|
|
1116
|
+
? [`- Missing architecture design doc: create required file under governanceDir: ${CORE_ARCHITECTURE_DOC_FILE}.`]
|
|
1117
|
+
: []),
|
|
1118
|
+
...(data.projectContextDocsState.missingCodeStyleDocs
|
|
1119
|
+
? [`- Missing code style doc: create required file under governanceDir: ${CORE_CODE_STYLE_DOC_FILE}.`]
|
|
1113
1120
|
: []),
|
|
1114
|
-
...(data.projectContextDocsState.
|
|
1115
|
-
? [`- Missing
|
|
1121
|
+
...(data.projectContextDocsState.missingUiStyleDocs
|
|
1122
|
+
? [`- Missing UI style doc: create required file under governanceDir: ${CORE_UI_STYLE_DOC_FILE}.`]
|
|
1116
1123
|
: []),
|
|
1117
1124
|
]
|
|
1118
1125
|
: []),
|
|
1126
|
+
'- Governance state updates MUST go through tools; NEVER directly edit tasks.md/roadmap.md generated views.',
|
|
1119
1127
|
'- Start immediately with Suggested Read Order and execute the selected task.',
|
|
1120
1128
|
'- Update markdown artifacts directly while keeping TASK/ROADMAP IDs unchanged.',
|
|
1121
1129
|
'- Re-run `taskContext` for the selectedTaskId after edits to verify evidence consistency.',
|
|
@@ -1163,7 +1171,7 @@ export function registerTaskTools(server) {
|
|
|
1163
1171
|
throw new ToolExecutionError(`Task not found: ${taskId}`, ['run `taskList` to discover available IDs', 'retry with an existing task ID'], `taskList(projectPath="${toProjectPath(governanceDir)}")`);
|
|
1164
1172
|
}
|
|
1165
1173
|
const researchBriefState = await inspectTaskResearchBrief(governanceDir, task);
|
|
1166
|
-
const contextReadingGuidance =
|
|
1174
|
+
const contextReadingGuidance = DEFAULT_TASK_CONTEXT_READING_GUIDANCE;
|
|
1167
1175
|
const taskLocation = (await findTextReferences(markdownPath, taskId))[0];
|
|
1168
1176
|
const artifacts = await discoverGovernanceArtifacts(governanceDir);
|
|
1169
1177
|
const fileCandidates = candidateFilesFromArtifacts(artifacts);
|
|
@@ -1192,8 +1200,9 @@ export function registerTaskTools(server) {
|
|
|
1192
1200
|
`- roadmapRefs: ${task.roadmapRefs.join(', ') || '(none)'}`,
|
|
1193
1201
|
`- researchBriefPath: ${researchBriefState.relativePath}`,
|
|
1194
1202
|
`- researchBriefStatus: ${researchBriefState.ready ? 'READY' : 'MISSING'}`,
|
|
1195
|
-
`-
|
|
1196
|
-
`-
|
|
1203
|
+
`- projectArchitectureDocsStatus: ${projectContextDocsState.missingArchitectureDocs ? 'MISSING' : 'READY'}`,
|
|
1204
|
+
`- codeStyleDocsStatus: ${projectContextDocsState.missingCodeStyleDocs ? 'MISSING' : 'READY'}`,
|
|
1205
|
+
`- uiStyleDocsStatus: ${projectContextDocsState.missingUiStyleDocs ? 'MISSING' : 'READY'}`,
|
|
1197
1206
|
`- taskLocation: ${taskLocation ? `${taskLocation.filePath}#L${taskLocation.line}` : markdownPath}`,
|
|
1198
1207
|
];
|
|
1199
1208
|
if (task.subState && task.status === 'IN_PROGRESS') {
|
|
@@ -1235,11 +1244,15 @@ export function registerTaskTools(server) {
|
|
|
1235
1244
|
`- architecture docs: ${projectContextDocsState.architectureDocs.length > 0 ? 'found' : 'missing'}`,
|
|
1236
1245
|
...(projectContextDocsState.architectureDocs.length > 0
|
|
1237
1246
|
? projectContextDocsState.architectureDocs.map((item) => `- architecture: ${item}`)
|
|
1238
|
-
: [`- architecture: add required file ${CORE_ARCHITECTURE_DOC_FILE}.`]),
|
|
1239
|
-
`-
|
|
1240
|
-
...(projectContextDocsState.
|
|
1241
|
-
? projectContextDocsState.
|
|
1242
|
-
: [`- style: add required file ${
|
|
1247
|
+
: [`- architecture: add required file under governanceDir: ${CORE_ARCHITECTURE_DOC_FILE}.`]),
|
|
1248
|
+
`- code style docs: ${projectContextDocsState.codeStyleDocs.length > 0 ? 'found' : 'missing'}`,
|
|
1249
|
+
...(projectContextDocsState.codeStyleDocs.length > 0
|
|
1250
|
+
? projectContextDocsState.codeStyleDocs.map((item) => `- code-style: ${item}`)
|
|
1251
|
+
: [`- code-style: add required file under governanceDir: ${CORE_CODE_STYLE_DOC_FILE}.`]),
|
|
1252
|
+
`- UI style docs: ${projectContextDocsState.uiStyleDocs.length > 0 ? 'found' : 'missing'}`,
|
|
1253
|
+
...(projectContextDocsState.uiStyleDocs.length > 0
|
|
1254
|
+
? projectContextDocsState.uiStyleDocs.map((item) => `- ui-style: ${item}`)
|
|
1255
|
+
: [`- ui-style: add required file under governanceDir: ${CORE_UI_STYLE_DOC_FILE}.`]),
|
|
1243
1256
|
'',
|
|
1244
1257
|
'### Related Artifacts',
|
|
1245
1258
|
...(relatedArtifacts.length > 0 ? relatedArtifacts.map((file) => `- ${file}`) : ['- (none)']),
|
|
@@ -1252,31 +1265,35 @@ export function registerTaskTools(server) {
|
|
|
1252
1265
|
'### Suggested Read Order',
|
|
1253
1266
|
...suggestedReadOrder.map((item, index) => `${index + 1}. ${item}`),
|
|
1254
1267
|
],
|
|
1255
|
-
guidance: ({ researchBriefState, projectContextDocsState, contextReadingGuidance, task }) => [
|
|
1268
|
+
guidance: ({ normalizedProjectPath, researchBriefState, projectContextDocsState, contextReadingGuidance, task }) => [
|
|
1256
1269
|
...(!researchBriefState.ready
|
|
1257
1270
|
? [
|
|
1258
1271
|
'- Pre-execution gate is NOT satisfied. Complete research brief first, then proceed with implementation.',
|
|
1259
|
-
`- Create or update ${researchBriefState.relativePath} with design guidelines + code architecture findings before code changes.`,
|
|
1272
|
+
`- Create or update ${researchBriefState.relativePath} under governanceDir with design guidelines + code architecture findings before code changes.`,
|
|
1260
1273
|
'- Include exact file/line locations in the brief (for example path/to/file.ts#L120).',
|
|
1261
1274
|
'- Re-run taskContext after writing the brief and confirm researchBriefStatus becomes READY.',
|
|
1262
1275
|
]
|
|
1263
1276
|
: [
|
|
1264
1277
|
'- Pre-execution gate satisfied. Read the research brief first, then continue implementation.',
|
|
1265
|
-
`- Must read ${researchBriefState.relativePath} before any task execution changes.`,
|
|
1278
|
+
`- Must read ${researchBriefState.relativePath} under governanceDir before any task execution changes.`,
|
|
1266
1279
|
]),
|
|
1267
1280
|
...(!projectContextDocsState.ready
|
|
1268
1281
|
? [
|
|
1269
|
-
'- Project context docs gate is NOT satisfied. Complete missing project architecture
|
|
1282
|
+
'- Project context docs gate is NOT satisfied. Complete missing project architecture, code style, and UI style docs first.',
|
|
1283
|
+
`- Rerun projectInit to repair missing governance artifacts first: projectInit(projectPath="${normalizedProjectPath}")`,
|
|
1270
1284
|
...(projectContextDocsState.missingArchitectureDocs
|
|
1271
|
-
? [`- Missing architecture design doc. Add required file ${CORE_ARCHITECTURE_DOC_FILE} and include architecture boundaries and module responsibilities.`]
|
|
1285
|
+
? [`- Missing architecture design doc. Add required file under governanceDir: ${CORE_ARCHITECTURE_DOC_FILE} and include architecture boundaries and module responsibilities.`]
|
|
1286
|
+
: []),
|
|
1287
|
+
...(projectContextDocsState.missingCodeStyleDocs
|
|
1288
|
+
? [`- Missing code style doc. Add required file under governanceDir: ${CORE_CODE_STYLE_DOC_FILE} and include naming, module boundaries, testing expectations, and review rules.`]
|
|
1272
1289
|
: []),
|
|
1273
|
-
...(projectContextDocsState.
|
|
1274
|
-
? [`- Missing
|
|
1290
|
+
...(projectContextDocsState.missingUiStyleDocs
|
|
1291
|
+
? [`- Missing UI style doc. Add required file under governanceDir: ${CORE_UI_STYLE_DOC_FILE} and include style language, tokens/themes, accessibility expectations, and UI consistency rules.`]
|
|
1275
1292
|
: []),
|
|
1276
|
-
'- Re-run taskContext and confirm
|
|
1293
|
+
'- Re-run taskContext and confirm projectArchitectureDocsStatus/codeStyleDocsStatus/uiStyleDocsStatus are READY.',
|
|
1277
1294
|
]
|
|
1278
1295
|
: [
|
|
1279
|
-
'- Project context docs gate satisfied. Architecture
|
|
1296
|
+
'- Project context docs gate satisfied. Architecture, code style, and UI style docs are available for execution alignment.',
|
|
1280
1297
|
]),
|
|
1281
1298
|
'- Read the files in Suggested Read Order.',
|
|
1282
1299
|
'',
|
|
@@ -1285,7 +1302,8 @@ export function registerTaskTools(server) {
|
|
|
1285
1302
|
'',
|
|
1286
1303
|
'- Verify whether current status and evidence are consistent.',
|
|
1287
1304
|
...taskStatusGuidance(task),
|
|
1288
|
-
'- If updates are needed, use tool writes for governance store (`taskUpdate` / `roadmapUpdate`) and keep TASK IDs unchanged.',
|
|
1305
|
+
'- If updates are needed, use tool writes for governance store (`taskCreate` / `taskUpdate` / `roadmapCreate` / `roadmapUpdate`) and keep TASK IDs unchanged.',
|
|
1306
|
+
'- NEVER directly edit tasks.md/roadmap.md; they are generated views and will be overwritten.',
|
|
1289
1307
|
'- After editing, re-run `taskContext` to verify references and context consistency.',
|
|
1290
1308
|
],
|
|
1291
1309
|
suggestions: ({ task, researchBriefState, projectContextDocsState }) => [
|
|
@@ -1414,11 +1432,21 @@ export function registerTaskTools(server) {
|
|
|
1414
1432
|
guidance: ({ updates, originalStatus }) => [
|
|
1415
1433
|
'Task updated successfully and tasks.md has been synced. Run `taskContext` to verify the changes.',
|
|
1416
1434
|
...(updates.status === 'IN_PROGRESS' && originalStatus === 'TODO'
|
|
1417
|
-
? ['- Ensure pre-execution research brief exists before deep implementation.']
|
|
1435
|
+
? ['- Ensure pre-execution research brief exists under governanceDir before deep implementation.']
|
|
1418
1436
|
: []),
|
|
1419
1437
|
...(updates.status === 'DONE'
|
|
1420
|
-
? [
|
|
1438
|
+
? [
|
|
1439
|
+
'- Verify evidence links are attached and reflect completed work.',
|
|
1440
|
+
'- Core docs review checklist (required):',
|
|
1441
|
+
'- [ ] architecture.md reviewed (designs/core/architecture.md)',
|
|
1442
|
+
'- [ ] code-style.md reviewed (designs/core/code-style.md)',
|
|
1443
|
+
'- [ ] ui-style.md reviewed (designs/core/ui-style.md)',
|
|
1444
|
+
'- Re-check whether this task changed module boundaries or system behavior, and update architecture.md if needed.',
|
|
1445
|
+
'- Re-check whether this task introduced or normalized engineering conventions, and update code-style.md if needed.',
|
|
1446
|
+
'- Re-check whether this task changed UI patterns, tokens, accessibility expectations, or interaction rules, and update ui-style.md if needed.',
|
|
1447
|
+
]
|
|
1421
1448
|
: []),
|
|
1449
|
+
'- Governance state changes must use task/roadmap tools only; never manually edit tasks.md/roadmap.md views.',
|
|
1422
1450
|
'.projitive governance store is source of truth; tasks.md is a generated view and may be overwritten.',
|
|
1423
1451
|
],
|
|
1424
1452
|
suggestions: async ({ previewTask, governanceDir }) => [
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { collectTaskLintSuggestions, isValidTaskId, normalizeTask, rankActionableTaskCandidates,
|
|
2
|
+
import { collectTaskLintSuggestions, isValidTaskId, normalizeTask, rankActionableTaskCandidates, renderTaskSeedTemplate, renderTasksMarkdown, loadTasksDocument, registerTaskTools, saveTasks, taskPriority, toTaskUpdatedAtMs, validateTransition, } from './task.js';
|
|
3
3
|
import fs from 'node:fs/promises';
|
|
4
4
|
import os from 'node:os';
|
|
5
5
|
import path from 'node:path';
|
|
@@ -159,15 +159,6 @@ describe('tasks module', () => {
|
|
|
159
159
|
const markdown = lines.join('\n');
|
|
160
160
|
expect(markdown).toContain('- roadmapRefs: ROADMAP-0099');
|
|
161
161
|
});
|
|
162
|
-
it('uses default no-task guidance when hook file is absent', async () => {
|
|
163
|
-
const guidance = await resolveNoTaskDiscoveryGuidance('/path/that/does/not/exist');
|
|
164
|
-
expect(guidance.length).toBeGreaterThan(3);
|
|
165
|
-
});
|
|
166
|
-
it('returns same default no-task guidance regardless of path', async () => {
|
|
167
|
-
const guidanceA = await resolveNoTaskDiscoveryGuidance('/path/that/does/not/exist');
|
|
168
|
-
const guidanceB = await resolveNoTaskDiscoveryGuidance('/another/path');
|
|
169
|
-
expect(guidanceA).toEqual(guidanceB);
|
|
170
|
-
});
|
|
171
162
|
it('loads and saves tasks from governance store and keeps newest-first order', async () => {
|
|
172
163
|
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'projitive-mcp-task-'));
|
|
173
164
|
const governanceDir = path.join(root, '.projitive');
|
|
@@ -284,11 +275,14 @@ describe('tasks module', () => {
|
|
|
284
275
|
expect(contextResult.content[0].text).toContain('### Pre-Execution Research Brief');
|
|
285
276
|
expect(contextResult.content[0].text).toContain('researchBriefStatus: MISSING');
|
|
286
277
|
expect(contextResult.content[0].text).toContain('designs/research/TASK-0001.implementation-research.md');
|
|
287
|
-
expect(contextResult.content[0].text).toContain('
|
|
288
|
-
expect(contextResult.content[0].text).toContain('
|
|
278
|
+
expect(contextResult.content[0].text).toContain('projectArchitectureDocsStatus: MISSING');
|
|
279
|
+
expect(contextResult.content[0].text).toContain('codeStyleDocsStatus: MISSING');
|
|
280
|
+
expect(contextResult.content[0].text).toContain('uiStyleDocsStatus: MISSING');
|
|
289
281
|
expect(contextResult.content[0].text).toContain('Project context docs gate is NOT satisfied');
|
|
290
282
|
expect(contextResult.content[0].text).toContain('PROJECT_ARCHITECTURE_DOC_MISSING');
|
|
291
|
-
expect(contextResult.content[0].text).toContain('
|
|
283
|
+
expect(contextResult.content[0].text).toContain('PROJECT_CODE_STYLE_DOC_MISSING');
|
|
284
|
+
expect(contextResult.content[0].text).toContain('PROJECT_UI_STYLE_DOC_MISSING');
|
|
285
|
+
expect(contextResult.content[0].text).toContain('projectInit(projectPath="');
|
|
292
286
|
await fs.rm(projectRoot, { recursive: true, force: true });
|
|
293
287
|
});
|
|
294
288
|
it('taskUpdate allows TODO -> IN_PROGRESS when research brief is missing, with lint guidance', async () => {
|
|
@@ -313,14 +307,14 @@ describe('tasks module', () => {
|
|
|
313
307
|
await fs.rm(projectRoot, { recursive: true, force: true });
|
|
314
308
|
});
|
|
315
309
|
it('taskUpdate allows TODO -> IN_PROGRESS when research brief is ready', async () => {
|
|
316
|
-
const { projectRoot, dbPath } = await createGovernanceWorkspace();
|
|
310
|
+
const { projectRoot, governanceDir, dbPath } = await createGovernanceWorkspace();
|
|
317
311
|
await replaceRoadmapsInStore(dbPath, [
|
|
318
312
|
{ id: 'ROADMAP-0001', title: 'Bootstrap', status: 'active', updatedAt: '2026-03-14T00:00:00.000Z' },
|
|
319
313
|
]);
|
|
320
314
|
await saveTasks(dbPath, [
|
|
321
315
|
normalizeTask({ id: 'TASK-0001', title: 'gate test pass', status: 'TODO', roadmapRefs: ['ROADMAP-0001'] }),
|
|
322
316
|
]);
|
|
323
|
-
const researchDir = path.join(
|
|
317
|
+
const researchDir = path.join(governanceDir, 'designs', 'research');
|
|
324
318
|
await fs.mkdir(researchDir, { recursive: true });
|
|
325
319
|
await fs.writeFile(path.join(researchDir, 'TASK-0001.implementation-research.md'), [
|
|
326
320
|
'# TASK-0001 Implementation Research Brief',
|
|
@@ -353,19 +347,22 @@ describe('tasks module', () => {
|
|
|
353
347
|
]);
|
|
354
348
|
await fs.mkdir(path.join(projectRoot, 'designs'), { recursive: true });
|
|
355
349
|
await fs.writeFile(path.join(projectRoot, 'designs', 'architecture.md'), '# Architecture\n', 'utf-8');
|
|
356
|
-
await fs.writeFile(path.join(projectRoot, 'designs', 'style
|
|
350
|
+
await fs.writeFile(path.join(projectRoot, 'designs', 'code-style.md'), '# Code Style\n', 'utf-8');
|
|
351
|
+
await fs.writeFile(path.join(projectRoot, 'designs', 'ui-style.md'), '# UI Style\n', 'utf-8');
|
|
357
352
|
const mockServer = { registerTool: vi.fn() };
|
|
358
353
|
registerTaskTools(mockServer);
|
|
359
354
|
const taskContext = getToolHandler(mockServer, 'taskContext');
|
|
360
355
|
const contextResult = await taskContext({ projectPath: projectRoot, taskId: 'TASK-0001' });
|
|
361
|
-
expect(contextResult.content[0].text).toContain('
|
|
362
|
-
expect(contextResult.content[0].text).toContain('
|
|
356
|
+
expect(contextResult.content[0].text).toContain('projectArchitectureDocsStatus: MISSING');
|
|
357
|
+
expect(contextResult.content[0].text).toContain('codeStyleDocsStatus: MISSING');
|
|
358
|
+
expect(contextResult.content[0].text).toContain('uiStyleDocsStatus: MISSING');
|
|
363
359
|
expect(contextResult.content[0].text).toContain('designs/core/architecture.md');
|
|
364
360
|
expect(contextResult.content[0].text).toContain('PROJECT_ARCHITECTURE_DOC_MISSING');
|
|
365
|
-
expect(contextResult.content[0].text).toContain('
|
|
361
|
+
expect(contextResult.content[0].text).toContain('PROJECT_CODE_STYLE_DOC_MISSING');
|
|
362
|
+
expect(contextResult.content[0].text).toContain('PROJECT_UI_STYLE_DOC_MISSING');
|
|
366
363
|
await fs.rm(projectRoot, { recursive: true, force: true });
|
|
367
364
|
});
|
|
368
|
-
it('taskContext marks project core docs ready when architecture/style docs exist under designs/core', async () => {
|
|
365
|
+
it('taskContext marks project core docs ready when architecture/code-style/ui-style docs exist under designs/core', async () => {
|
|
369
366
|
const { projectRoot, governanceDir, dbPath } = await createGovernanceWorkspace();
|
|
370
367
|
await replaceRoadmapsInStore(dbPath, [
|
|
371
368
|
{ id: 'ROADMAP-0001', title: 'Roadmap', status: 'active', updatedAt: '2026-03-14T00:00:00.000Z' },
|
|
@@ -376,13 +373,15 @@ describe('tasks module', () => {
|
|
|
376
373
|
const coreDir = path.join(governanceDir, 'designs', 'core');
|
|
377
374
|
await fs.mkdir(coreDir, { recursive: true });
|
|
378
375
|
await fs.writeFile(path.join(coreDir, 'architecture.md'), '# Architecture\n', 'utf-8');
|
|
379
|
-
await fs.writeFile(path.join(coreDir, 'style
|
|
376
|
+
await fs.writeFile(path.join(coreDir, 'code-style.md'), '# Code Style\n', 'utf-8');
|
|
377
|
+
await fs.writeFile(path.join(coreDir, 'ui-style.md'), '# UI Style\n', 'utf-8');
|
|
380
378
|
const mockServer = { registerTool: vi.fn() };
|
|
381
379
|
registerTaskTools(mockServer);
|
|
382
380
|
const taskContext = getToolHandler(mockServer, 'taskContext');
|
|
383
381
|
const contextResult = await taskContext({ projectPath: projectRoot, taskId: 'TASK-0001' });
|
|
384
|
-
expect(contextResult.content[0].text).toContain('
|
|
385
|
-
expect(contextResult.content[0].text).toContain('
|
|
382
|
+
expect(contextResult.content[0].text).toContain('projectArchitectureDocsStatus: READY');
|
|
383
|
+
expect(contextResult.content[0].text).toContain('codeStyleDocsStatus: READY');
|
|
384
|
+
expect(contextResult.content[0].text).toContain('uiStyleDocsStatus: READY');
|
|
386
385
|
expect(contextResult.content[0].text).toContain('Project context docs gate satisfied');
|
|
387
386
|
await fs.rm(projectRoot, { recursive: true, force: true });
|
|
388
387
|
});
|
|
@@ -397,15 +396,19 @@ describe('tasks module', () => {
|
|
|
397
396
|
const coreDir = path.join(governanceDir, 'designs', 'core');
|
|
398
397
|
await fs.mkdir(coreDir, { recursive: true });
|
|
399
398
|
await fs.writeFile(path.join(coreDir, 'system-architecture.md'), '# Architecture\n', 'utf-8');
|
|
400
|
-
await fs.writeFile(path.join(coreDir, '
|
|
399
|
+
await fs.writeFile(path.join(coreDir, 'engineering-style.md'), '# Code Style\n', 'utf-8');
|
|
400
|
+
await fs.writeFile(path.join(coreDir, 'visual-style.md'), '# UI Style\n', 'utf-8');
|
|
401
401
|
const mockServer = { registerTool: vi.fn() };
|
|
402
402
|
registerTaskTools(mockServer);
|
|
403
403
|
const taskContext = getToolHandler(mockServer, 'taskContext');
|
|
404
404
|
const contextResult = await taskContext({ projectPath: projectRoot, taskId: 'TASK-0001' });
|
|
405
|
-
expect(contextResult.content[0].text).toContain('
|
|
406
|
-
expect(contextResult.content[0].text).toContain('
|
|
407
|
-
expect(contextResult.content[0].text).toContain('
|
|
408
|
-
expect(contextResult.content[0].text).toContain('add required file designs/core/
|
|
405
|
+
expect(contextResult.content[0].text).toContain('projectArchitectureDocsStatus: MISSING');
|
|
406
|
+
expect(contextResult.content[0].text).toContain('codeStyleDocsStatus: MISSING');
|
|
407
|
+
expect(contextResult.content[0].text).toContain('uiStyleDocsStatus: MISSING');
|
|
408
|
+
expect(contextResult.content[0].text).toContain('add required file under governanceDir: designs/core/architecture.md');
|
|
409
|
+
expect(contextResult.content[0].text).toContain('add required file under governanceDir: designs/core/code-style.md');
|
|
410
|
+
expect(contextResult.content[0].text).toContain('add required file under governanceDir: designs/core/ui-style.md');
|
|
411
|
+
expect(contextResult.content[0].text).toContain('projectInit(projectPath="');
|
|
409
412
|
await fs.rm(projectRoot, { recursive: true, force: true });
|
|
410
413
|
});
|
|
411
414
|
it('taskUpdate allows IN_PROGRESS -> DONE with lint guidance when conformance fails', async () => {
|
|
@@ -423,7 +426,7 @@ describe('tasks module', () => {
|
|
|
423
426
|
links: [],
|
|
424
427
|
}),
|
|
425
428
|
]);
|
|
426
|
-
const researchDir = path.join(
|
|
429
|
+
const researchDir = path.join(governanceDir, 'designs', 'research');
|
|
427
430
|
await fs.mkdir(researchDir, { recursive: true });
|
|
428
431
|
await fs.writeFile(path.join(researchDir, 'TASK-0001.implementation-research.md'), [
|
|
429
432
|
'# TASK-0001 Implementation Research Brief',
|
|
@@ -437,7 +440,8 @@ describe('tasks module', () => {
|
|
|
437
440
|
const coreDir = path.join(governanceDir, 'designs', 'core');
|
|
438
441
|
await fs.mkdir(coreDir, { recursive: true });
|
|
439
442
|
await fs.writeFile(path.join(coreDir, 'architecture.md'), '# Architecture\n', 'utf-8');
|
|
440
|
-
await fs.writeFile(path.join(coreDir, 'style
|
|
443
|
+
await fs.writeFile(path.join(coreDir, 'code-style.md'), '# Code Style\n', 'utf-8');
|
|
444
|
+
await fs.writeFile(path.join(coreDir, 'ui-style.md'), '# UI Style\n', 'utf-8');
|
|
441
445
|
const mockServer = { registerTool: vi.fn() };
|
|
442
446
|
registerTaskTools(mockServer);
|
|
443
447
|
const taskUpdate = getToolHandler(mockServer, 'taskUpdate');
|
|
@@ -449,6 +453,10 @@ describe('tasks module', () => {
|
|
|
449
453
|
expect(result.isError).toBeUndefined();
|
|
450
454
|
expect(result.content[0].text).toContain('newStatus: DONE');
|
|
451
455
|
expect(result.content[0].text).toContain('TASK_DONE_LINKS_MISSING');
|
|
456
|
+
expect(result.content[0].text).toContain('Core docs review checklist (required):');
|
|
457
|
+
expect(result.content[0].text).toContain('- [ ] architecture.md reviewed (designs/core/architecture.md)');
|
|
458
|
+
expect(result.content[0].text).toContain('- [ ] code-style.md reviewed (designs/core/code-style.md)');
|
|
459
|
+
expect(result.content[0].text).toContain('- [ ] ui-style.md reviewed (designs/core/ui-style.md)');
|
|
452
460
|
await fs.rm(projectRoot, { recursive: true, force: true });
|
|
453
461
|
});
|
|
454
462
|
it('taskUpdate allows IN_PROGRESS -> DONE when conformance re-check passes', async () => {
|
|
@@ -467,7 +475,7 @@ describe('tasks module', () => {
|
|
|
467
475
|
links: ['README.md'],
|
|
468
476
|
}),
|
|
469
477
|
]);
|
|
470
|
-
const researchDir = path.join(
|
|
478
|
+
const researchDir = path.join(governanceDir, 'designs', 'research');
|
|
471
479
|
await fs.mkdir(researchDir, { recursive: true });
|
|
472
480
|
await fs.writeFile(path.join(researchDir, 'TASK-0001.implementation-research.md'), [
|
|
473
481
|
'# TASK-0001 Implementation Research Brief',
|
|
@@ -481,7 +489,8 @@ describe('tasks module', () => {
|
|
|
481
489
|
const coreDir = path.join(governanceDir, 'designs', 'core');
|
|
482
490
|
await fs.mkdir(coreDir, { recursive: true });
|
|
483
491
|
await fs.writeFile(path.join(coreDir, 'architecture.md'), '# Architecture\n', 'utf-8');
|
|
484
|
-
await fs.writeFile(path.join(coreDir, 'style
|
|
492
|
+
await fs.writeFile(path.join(coreDir, 'code-style.md'), '# Code Style\n', 'utf-8');
|
|
493
|
+
await fs.writeFile(path.join(coreDir, 'ui-style.md'), '# UI Style\n', 'utf-8');
|
|
485
494
|
const mockServer = { registerTool: vi.fn() };
|
|
486
495
|
registerTaskTools(mockServer);
|
|
487
496
|
const taskUpdate = getToolHandler(mockServer, 'taskUpdate');
|
|
@@ -492,6 +501,7 @@ describe('tasks module', () => {
|
|
|
492
501
|
});
|
|
493
502
|
expect(result.isError).toBeUndefined();
|
|
494
503
|
expect(result.content[0].text).toContain('newStatus: DONE');
|
|
504
|
+
expect(result.content[0].text).toContain('Core docs review checklist (required):');
|
|
495
505
|
await fs.rm(projectRoot, { recursive: true, force: true });
|
|
496
506
|
});
|
|
497
507
|
});
|
package/output/source/types.js
CHANGED
|
@@ -50,5 +50,6 @@ export const TASK_LINT_CODES = {
|
|
|
50
50
|
};
|
|
51
51
|
export const PROJECT_LINT_CODES = {
|
|
52
52
|
ARCHITECTURE_DOC_MISSING: 'PROJECT_ARCHITECTURE_DOC_MISSING',
|
|
53
|
-
|
|
53
|
+
CODE_STYLE_DOC_MISSING: 'PROJECT_CODE_STYLE_DOC_MISSING',
|
|
54
|
+
UI_STYLE_DOC_MISSING: 'PROJECT_UI_STYLE_DOC_MISSING',
|
|
54
55
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@projitive/mcp",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.3",
|
|
4
4
|
"description": "Projitive MCP Server for project and task discovery/update",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"author": "",
|
|
@@ -12,6 +12,10 @@
|
|
|
12
12
|
"publishConfig": {
|
|
13
13
|
"access": "public"
|
|
14
14
|
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/yinxulai/projitive"
|
|
18
|
+
},
|
|
15
19
|
"scripts": {
|
|
16
20
|
"test": "vitest run",
|
|
17
21
|
"test:coverage": "vitest run --coverage",
|