@projitive/mcp 2.0.4 → 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 +1 -1
- package/output/source/common/errors.test.js +59 -0
- package/output/source/common/files.js +20 -9
- package/output/source/common/index.js +1 -0
- package/output/source/common/linter.js +3 -1
- package/output/source/common/response.js +51 -67
- package/output/source/common/tool.js +43 -0
- package/output/source/common/utils.test.js +48 -0
- package/output/source/index.runtime.test.js +57 -0
- package/output/source/prompts/index.test.js +23 -0
- package/output/source/prompts/quickStart.test.js +24 -0
- package/output/source/prompts/taskDiscovery.test.js +24 -0
- package/output/source/prompts/taskExecution.js +17 -1
- package/output/source/prompts/taskExecution.test.js +27 -0
- package/output/source/resources/designs.resources.test.js +52 -0
- package/output/source/resources/governance.test.js +35 -0
- package/output/source/resources/index.test.js +18 -0
- package/output/source/tools/index.test.js +23 -0
- package/output/source/tools/project.js +210 -257
- package/output/source/tools/project.test.js +136 -4
- package/output/source/tools/roadmap.js +182 -216
- package/output/source/tools/roadmap.test.js +187 -0
- package/output/source/tools/task.js +598 -508
- package/output/source/tools/task.test.js +323 -2
- package/output/source/types.js +6 -0
- package/package.json +1 -1
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
import { candidateFilesFromArtifacts, discoverGovernanceArtifacts, ROADMAP_LINT_CODES, renderLintSuggestions, findTextReferences, ensureStore, loadRoadmapsFromStore, upsertRoadmapInStore, getStoreVersion, getMarkdownViewState, markMarkdownViewBuilt, } from '../common/index.js';
|
|
5
|
-
import { asText, evidenceSection, guidanceSection, lintSection, nextCallSection, renderErrorMarkdown, renderToolResponseMarkdown, summarySection, } from '../common/index.js';
|
|
4
|
+
import { candidateFilesFromArtifacts, discoverGovernanceArtifacts, ROADMAP_LINT_CODES, renderLintSuggestions, findTextReferences, ensureStore, loadRoadmapsFromStore, upsertRoadmapInStore, getStoreVersion, getMarkdownViewState, markMarkdownViewBuilt, createGovernedTool, ToolExecutionError, } from '../common/index.js';
|
|
6
5
|
import { resolveGovernanceDir, toProjectPath } from './project.js';
|
|
7
6
|
import { loadTasks, loadTasksDocument } from './task.js';
|
|
8
7
|
export const ROADMAP_ID_REGEX = /^ROADMAP-(\d+)$/;
|
|
@@ -33,11 +32,7 @@ function sortMilestonesNewestFirst(milestones) {
|
|
|
33
32
|
if (Number.isFinite(updatedAtDelta) && updatedAtDelta !== 0) {
|
|
34
33
|
return updatedAtDelta;
|
|
35
34
|
}
|
|
36
|
-
|
|
37
|
-
if (idDelta !== 0) {
|
|
38
|
-
return idDelta;
|
|
39
|
-
}
|
|
40
|
-
return b.id.localeCompare(a.id);
|
|
35
|
+
return toRoadmapIdNumericSuffix(b.id) - toRoadmapIdNumericSuffix(a.id);
|
|
41
36
|
});
|
|
42
37
|
}
|
|
43
38
|
function normalizeMilestone(raw) {
|
|
@@ -101,8 +96,7 @@ export async function loadRoadmapDocumentWithOptions(inputPath, forceViewSync) {
|
|
|
101
96
|
const governanceDir = await resolveGovernanceDir(inputPath);
|
|
102
97
|
const { roadmapPath, markdownPath } = resolveRoadmapArtifactPaths(governanceDir);
|
|
103
98
|
await ensureStore(roadmapPath);
|
|
104
|
-
const
|
|
105
|
-
const normalizedMilestones = normalizeAndSortMilestones(milestones);
|
|
99
|
+
const normalizedMilestones = normalizeAndSortMilestones(await loadRoadmapsFromStore(roadmapPath));
|
|
106
100
|
const markdown = renderRoadmapMarkdown(normalizedMilestones);
|
|
107
101
|
await syncRoadmapMarkdownView(roadmapPath, markdownPath, markdown, forceViewSync);
|
|
108
102
|
return {
|
|
@@ -167,108 +161,100 @@ export function isValidRoadmapId(id) {
|
|
|
167
161
|
return toRoadmapIdNumericSuffix(id) > 0;
|
|
168
162
|
}
|
|
169
163
|
export function registerRoadmapTools(server) {
|
|
170
|
-
server.registerTool(
|
|
164
|
+
server.registerTool(...createGovernedTool({
|
|
165
|
+
name: 'roadmapList',
|
|
171
166
|
title: 'Roadmap List',
|
|
172
167
|
description: 'List roadmap IDs and task linkage for planning or traceability',
|
|
173
168
|
inputSchema: {
|
|
174
169
|
projectPath: z.string(),
|
|
175
170
|
},
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
});
|
|
207
|
-
return asText(markdown);
|
|
208
|
-
});
|
|
209
|
-
server.registerTool('roadmapContext', {
|
|
171
|
+
async execute({ projectPath }) {
|
|
172
|
+
const governanceDir = await resolveGovernanceDir(projectPath);
|
|
173
|
+
const normalizedProjectPath = toProjectPath(governanceDir);
|
|
174
|
+
const { milestones, markdownPath: roadmapViewPath } = await loadRoadmapDocument(governanceDir);
|
|
175
|
+
const roadmapIds = milestones.map((item) => item.id);
|
|
176
|
+
const { tasks, markdownPath: tasksViewPath } = await loadTasksDocument(governanceDir);
|
|
177
|
+
return { normalizedProjectPath, governanceDir, roadmapIds, roadmapViewPath, tasksViewPath, tasks };
|
|
178
|
+
},
|
|
179
|
+
summary: ({ normalizedProjectPath, governanceDir, tasksViewPath, roadmapViewPath, roadmapIds }) => [
|
|
180
|
+
`- projectPath: ${normalizedProjectPath}`,
|
|
181
|
+
`- governanceDir: ${governanceDir}`,
|
|
182
|
+
`- tasksView: ${tasksViewPath}`,
|
|
183
|
+
`- roadmapView: ${roadmapViewPath}`,
|
|
184
|
+
`- roadmapCount: ${roadmapIds.length}`,
|
|
185
|
+
],
|
|
186
|
+
evidence: ({ roadmapIds, tasks }) => [
|
|
187
|
+
'- roadmaps:',
|
|
188
|
+
...roadmapIds.map((id) => {
|
|
189
|
+
const linkedTasks = tasks.filter((task) => task.roadmapRefs.includes(id));
|
|
190
|
+
return `- ${id} | linkedTasks=${linkedTasks.length}`;
|
|
191
|
+
}),
|
|
192
|
+
],
|
|
193
|
+
guidance: () => ['- Pick one roadmap ID and call `roadmapContext`.'],
|
|
194
|
+
suggestions: ({ roadmapIds, tasks }) => collectRoadmapLintSuggestions(roadmapIds, tasks),
|
|
195
|
+
nextCall: ({ roadmapIds, normalizedProjectPath }) => roadmapIds[0]
|
|
196
|
+
? `roadmapContext(projectPath="${normalizedProjectPath}", roadmapId="${roadmapIds[0]}")`
|
|
197
|
+
: undefined,
|
|
198
|
+
}));
|
|
199
|
+
server.registerTool(...createGovernedTool({
|
|
200
|
+
name: 'roadmapContext',
|
|
210
201
|
title: 'Roadmap Context',
|
|
211
202
|
description: 'Inspect one roadmap with linked tasks and reference locations',
|
|
212
203
|
inputSchema: {
|
|
213
204
|
projectPath: z.string(),
|
|
214
205
|
roadmapId: z.string(),
|
|
215
206
|
},
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
],
|
|
268
|
-
});
|
|
269
|
-
return asText(markdown);
|
|
270
|
-
});
|
|
271
|
-
server.registerTool('roadmapCreate', {
|
|
207
|
+
async execute({ projectPath, roadmapId }) {
|
|
208
|
+
if (!isValidRoadmapId(roadmapId)) {
|
|
209
|
+
throw new ToolExecutionError(`Invalid roadmap ID format: ${roadmapId}`, ['expected format: ROADMAP-1 or ROADMAP-0001', 'retry with a valid roadmap ID'], `roadmapContext(projectPath="${projectPath}", roadmapId="ROADMAP-0001")`);
|
|
210
|
+
}
|
|
211
|
+
const governanceDir = await resolveGovernanceDir(projectPath);
|
|
212
|
+
const normalizedProjectPath = toProjectPath(governanceDir);
|
|
213
|
+
const { markdownPath: roadmapViewPath } = resolveRoadmapArtifactPaths(governanceDir);
|
|
214
|
+
const artifacts = await discoverGovernanceArtifacts(governanceDir);
|
|
215
|
+
const fileCandidates = candidateFilesFromArtifacts(artifacts);
|
|
216
|
+
const referenceLocations = (await Promise.all(fileCandidates.map((file) => findTextReferences(file, roadmapId)))).flat();
|
|
217
|
+
const { tasks, markdownPath: tasksViewPath } = await loadTasksDocument(governanceDir);
|
|
218
|
+
const relatedTasks = tasks.filter((task) => task.roadmapRefs.includes(roadmapId));
|
|
219
|
+
const roadmapIds = await loadRoadmapIds(governanceDir);
|
|
220
|
+
return { normalizedProjectPath, governanceDir, roadmapId, roadmapViewPath, tasksViewPath, relatedTasks, referenceLocations, roadmapIds, tasks };
|
|
221
|
+
},
|
|
222
|
+
summary: ({ normalizedProjectPath, governanceDir, tasksViewPath, roadmapViewPath, roadmapId, relatedTasks, referenceLocations }) => [
|
|
223
|
+
`- projectPath: ${normalizedProjectPath}`,
|
|
224
|
+
`- governanceDir: ${governanceDir}`,
|
|
225
|
+
`- tasksView: ${tasksViewPath}`,
|
|
226
|
+
`- roadmapView: ${roadmapViewPath}`,
|
|
227
|
+
`- roadmapId: ${roadmapId}`,
|
|
228
|
+
`- relatedTasks: ${relatedTasks.length}`,
|
|
229
|
+
`- references: ${referenceLocations.length}`,
|
|
230
|
+
],
|
|
231
|
+
evidence: ({ relatedTasks, referenceLocations }) => [
|
|
232
|
+
'### Related Tasks',
|
|
233
|
+
...relatedTasks.map((task) => `- ${task.id} | ${task.status} | ${task.title}`),
|
|
234
|
+
'',
|
|
235
|
+
'### Reference Locations',
|
|
236
|
+
...referenceLocations.map((item) => `- ${item.filePath}#L${item.line}: ${item.text}`),
|
|
237
|
+
],
|
|
238
|
+
guidance: () => [
|
|
239
|
+
'- Read roadmap references first, then related tasks.',
|
|
240
|
+
'- Keep ROADMAP/TASK IDs unchanged while updating markdown files.',
|
|
241
|
+
'- Re-run `roadmapContext` after edits to confirm references remain consistent.',
|
|
242
|
+
],
|
|
243
|
+
suggestions: ({ roadmapIds, tasks, relatedTasks, roadmapId }) => {
|
|
244
|
+
const items = collectRoadmapLintSuggestionItems(roadmapIds, tasks);
|
|
245
|
+
if (relatedTasks.length === 0) {
|
|
246
|
+
items.push({
|
|
247
|
+
code: ROADMAP_LINT_CODES.CONTEXT_RELATED_TASKS_EMPTY,
|
|
248
|
+
message: `relatedTasks=0 for ${roadmapId}.`,
|
|
249
|
+
fixHint: 'Batch bind task roadmapRefs to improve execution traceability.',
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
return renderLintSuggestions(items);
|
|
253
|
+
},
|
|
254
|
+
nextCall: ({ normalizedProjectPath, roadmapId }) => `roadmapContext(projectPath="${normalizedProjectPath}", roadmapId="${roadmapId}")`,
|
|
255
|
+
}));
|
|
256
|
+
server.registerTool(...createGovernedTool({
|
|
257
|
+
name: 'roadmapCreate',
|
|
272
258
|
title: 'Roadmap Create',
|
|
273
259
|
description: 'Create one roadmap milestone in governance store',
|
|
274
260
|
inputSchema: {
|
|
@@ -278,66 +264,56 @@ export function registerRoadmapTools(server) {
|
|
|
278
264
|
status: z.enum(['active', 'done']).optional(),
|
|
279
265
|
time: z.string().optional(),
|
|
280
266
|
},
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
})
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
'Milestone created successfully and roadmap.md has been synced.',
|
|
332
|
-
'Re-run roadmapContext to verify linked task traceability.',
|
|
333
|
-
]),
|
|
334
|
-
lintSection(lintSuggestions),
|
|
335
|
-
nextCallSection(`roadmapContext(projectPath="${normalizedProjectPath}", roadmapId="${created.id}")`),
|
|
336
|
-
],
|
|
337
|
-
});
|
|
338
|
-
return asText(markdown);
|
|
339
|
-
});
|
|
340
|
-
server.registerTool('roadmapUpdate', {
|
|
267
|
+
async execute({ projectPath, roadmapId, title, status, time }) {
|
|
268
|
+
if (roadmapId && !isValidRoadmapId(roadmapId)) {
|
|
269
|
+
throw new ToolExecutionError(`Invalid roadmap ID format: ${roadmapId}`, ['expected format: ROADMAP-1 or ROADMAP-0001', 'omit roadmapId to auto-generate next ID'], `roadmapCreate(projectPath="${projectPath}", title="Define milestone", time="2026-Q2")`);
|
|
270
|
+
}
|
|
271
|
+
const governanceDir = await resolveGovernanceDir(projectPath);
|
|
272
|
+
const normalizedProjectPath = toProjectPath(governanceDir);
|
|
273
|
+
const doc = await loadRoadmapDocument(governanceDir);
|
|
274
|
+
const { markdownPath: tasksViewPath } = await loadTasksDocument(governanceDir);
|
|
275
|
+
const finalRoadmapId = roadmapId ?? nextRoadmapId(doc.milestones);
|
|
276
|
+
const duplicated = doc.milestones.some((item) => item.id === finalRoadmapId);
|
|
277
|
+
if (duplicated) {
|
|
278
|
+
throw new ToolExecutionError(`Roadmap milestone already exists: ${finalRoadmapId}`, ['roadmap IDs must be unique', 'use roadmapUpdate for existing milestone'], `roadmapUpdate(projectPath="${normalizedProjectPath}", roadmapId="${finalRoadmapId}", updates={...})`);
|
|
279
|
+
}
|
|
280
|
+
const created = normalizeMilestone({
|
|
281
|
+
id: finalRoadmapId,
|
|
282
|
+
title,
|
|
283
|
+
status: status ?? 'active',
|
|
284
|
+
time,
|
|
285
|
+
updatedAt: nowIso(),
|
|
286
|
+
});
|
|
287
|
+
await upsertRoadmapInStore(doc.roadmapPath, created);
|
|
288
|
+
const refreshed = await loadRoadmapDocumentWithOptions(governanceDir, true);
|
|
289
|
+
const { tasks } = await loadTasks(governanceDir);
|
|
290
|
+
return { normalizedProjectPath, governanceDir, tasksViewPath, roadmapViewPath: refreshed.markdownPath, created, refreshed, tasks };
|
|
291
|
+
},
|
|
292
|
+
summary: ({ normalizedProjectPath, governanceDir, tasksViewPath, roadmapViewPath, created }) => [
|
|
293
|
+
`- projectPath: ${normalizedProjectPath}`,
|
|
294
|
+
`- governanceDir: ${governanceDir}`,
|
|
295
|
+
`- tasksView: ${tasksViewPath}`,
|
|
296
|
+
`- roadmapView: ${roadmapViewPath}`,
|
|
297
|
+
`- roadmapId: ${created.id}`,
|
|
298
|
+
`- status: ${created.status}`,
|
|
299
|
+
`- updatedAt: ${created.updatedAt}`,
|
|
300
|
+
],
|
|
301
|
+
evidence: ({ created, refreshed }) => [
|
|
302
|
+
'### Created Milestone',
|
|
303
|
+
`- ${created.id} | ${created.status} | ${created.title}${created.time ? ` | time=${created.time}` : ''}`,
|
|
304
|
+
'',
|
|
305
|
+
'### Roadmap Count',
|
|
306
|
+
`- total: ${refreshed.milestones.length}`,
|
|
307
|
+
],
|
|
308
|
+
guidance: () => [
|
|
309
|
+
'Milestone created successfully and roadmap.md has been synced.',
|
|
310
|
+
'Re-run roadmapContext to verify linked task traceability.',
|
|
311
|
+
],
|
|
312
|
+
suggestions: ({ refreshed, tasks }) => collectRoadmapLintSuggestions(refreshed.milestones.map((item) => item.id), tasks),
|
|
313
|
+
nextCall: ({ normalizedProjectPath, created }) => `roadmapContext(projectPath="${normalizedProjectPath}", roadmapId="${created.id}")`,
|
|
314
|
+
}));
|
|
315
|
+
server.registerTool(...createGovernedTool({
|
|
316
|
+
name: 'roadmapUpdate',
|
|
341
317
|
title: 'Roadmap Update',
|
|
342
318
|
description: 'Update one roadmap milestone fields incrementally in governance store',
|
|
343
319
|
inputSchema: {
|
|
@@ -349,61 +325,51 @@ export function registerRoadmapTools(server) {
|
|
|
349
325
|
time: z.string().optional(),
|
|
350
326
|
}),
|
|
351
327
|
},
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
328
|
+
async execute({ projectPath, roadmapId, updates }) {
|
|
329
|
+
if (!isValidRoadmapId(roadmapId)) {
|
|
330
|
+
throw new ToolExecutionError(`Invalid roadmap ID format: ${roadmapId}`, ['expected format: ROADMAP-1 or ROADMAP-0001', 'retry with a valid roadmap ID'], `roadmapUpdate(projectPath="${projectPath}", roadmapId="ROADMAP-0001", updates={...})`);
|
|
331
|
+
}
|
|
332
|
+
const governanceDir = await resolveGovernanceDir(projectPath);
|
|
333
|
+
const normalizedProjectPath = toProjectPath(governanceDir);
|
|
334
|
+
const doc = await loadRoadmapDocument(governanceDir);
|
|
335
|
+
const { markdownPath: tasksViewPath } = await loadTasksDocument(governanceDir);
|
|
336
|
+
const existing = doc.milestones.find((item) => item.id === roadmapId);
|
|
337
|
+
if (!existing) {
|
|
338
|
+
throw new ToolExecutionError(`Roadmap milestone not found: ${roadmapId}`, ['run roadmapList to discover existing roadmap IDs', 'retry with an existing roadmap ID'], `roadmapList(projectPath="${toProjectPath(governanceDir)}")`);
|
|
339
|
+
}
|
|
340
|
+
const updated = {
|
|
341
|
+
...existing,
|
|
342
|
+
title: updates.title ?? existing.title,
|
|
343
|
+
status: updates.status ?? existing.status,
|
|
344
|
+
time: updates.time ?? existing.time,
|
|
345
|
+
updatedAt: nowIso(),
|
|
368
346
|
};
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
]),
|
|
398
|
-
guidanceSection([
|
|
399
|
-
'Milestone updated successfully and roadmap.md has been synced.',
|
|
400
|
-
'Re-run roadmapContext to verify linked task traceability.',
|
|
401
|
-
'.projitive governance store is source of truth; roadmap.md is a generated view and may be overwritten.',
|
|
402
|
-
]),
|
|
403
|
-
lintSection([]),
|
|
404
|
-
nextCallSection(`roadmapContext(projectPath="${toProjectPath(governanceDir)}", roadmapId="${roadmapId}")`),
|
|
405
|
-
],
|
|
406
|
-
});
|
|
407
|
-
return asText(markdown);
|
|
408
|
-
});
|
|
347
|
+
await upsertRoadmapInStore(doc.roadmapPath, updated);
|
|
348
|
+
const refreshed = await loadRoadmapDocumentWithOptions(governanceDir, true);
|
|
349
|
+
return { normalizedProjectPath, governanceDir, tasksViewPath, roadmapViewPath: refreshed.markdownPath, roadmapId, updated, refreshed };
|
|
350
|
+
},
|
|
351
|
+
summary: ({ normalizedProjectPath, governanceDir, tasksViewPath, roadmapViewPath, roadmapId, updated }) => [
|
|
352
|
+
`- projectPath: ${normalizedProjectPath}`,
|
|
353
|
+
`- governanceDir: ${governanceDir}`,
|
|
354
|
+
`- tasksView: ${tasksViewPath}`,
|
|
355
|
+
`- roadmapView: ${roadmapViewPath}`,
|
|
356
|
+
`- roadmapId: ${roadmapId}`,
|
|
357
|
+
`- newStatus: ${updated.status}`,
|
|
358
|
+
`- updatedAt: ${updated.updatedAt}`,
|
|
359
|
+
],
|
|
360
|
+
evidence: ({ updated, refreshed }) => [
|
|
361
|
+
'### Updated Milestone',
|
|
362
|
+
`- ${updated.id} | ${updated.status} | ${updated.title}${updated.time ? ` | time=${updated.time}` : ''}`,
|
|
363
|
+
'',
|
|
364
|
+
'### Roadmap Count',
|
|
365
|
+
`- total: ${refreshed.milestones.length}`,
|
|
366
|
+
],
|
|
367
|
+
guidance: () => [
|
|
368
|
+
'Milestone updated successfully and roadmap.md has been synced.',
|
|
369
|
+
'Re-run roadmapContext to verify linked task traceability.',
|
|
370
|
+
'.projitive governance store is source of truth; roadmap.md is a generated view and may be overwritten.',
|
|
371
|
+
],
|
|
372
|
+
suggestions: () => [],
|
|
373
|
+
nextCall: ({ normalizedProjectPath, roadmapId }) => `roadmapContext(projectPath="${normalizedProjectPath}", roadmapId="${roadmapId}")`,
|
|
374
|
+
}));
|
|
409
375
|
}
|