@smartmemory/compose 0.1.7-beta → 0.1.8-beta
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/README.md +32 -5
- package/bin/compose.js +167 -5
- package/dist/assets/{_baseUniq-D-avYfn5.js → _baseUniq-3jW4HAOf.js} +1 -1
- package/dist/assets/{arc-BC4dfQ-X.js → arc-DzzDimyd.js} +1 -1
- package/dist/assets/{architectureDiagram-Q4EWVU46-BZmFXnGI.js → architectureDiagram-Q4EWVU46-CtAgwORz.js} +1 -1
- package/dist/assets/{blockDiagram-DXYQGD6D-DlfWSuux.js → blockDiagram-DXYQGD6D-Bryby0c_.js} +1 -1
- package/dist/assets/{c4Diagram-AHTNJAMY-Y__uJrRx.js → c4Diagram-AHTNJAMY-C7N9RTJ8.js} +1 -1
- package/dist/assets/channel-DDkv7DUd.js +1 -0
- package/dist/assets/{chunk-4BX2VUAB-BfMePfTp.js → chunk-4BX2VUAB-wijkFgZY.js} +1 -1
- package/dist/assets/{chunk-4TB4RGXK-BdlMSdEA.js → chunk-4TB4RGXK-zdSZGRS2.js} +1 -1
- package/dist/assets/{chunk-55IACEB6-vrQHZTdv.js → chunk-55IACEB6-6zqzTZQQ.js} +1 -1
- package/dist/assets/{chunk-EDXVE4YY-B8wioVlW.js → chunk-EDXVE4YY-frd1Vwf-.js} +1 -1
- package/dist/assets/{chunk-FMBD7UC4-Cd6Hrux2.js → chunk-FMBD7UC4-CdkRK5Hx.js} +1 -1
- package/dist/assets/{chunk-OYMX7WX6-CfrhdQXY.js → chunk-OYMX7WX6-C6bMB0cf.js} +1 -1
- package/dist/assets/{chunk-QZHKN3VN-B9JQerOU.js → chunk-QZHKN3VN-4vsxN3jq.js} +1 -1
- package/dist/assets/{chunk-YZCP3GAM-DFN9X99H.js → chunk-YZCP3GAM-DbNARKip.js} +1 -1
- package/dist/assets/classDiagram-6PBFFD2Q-J6ZTeCbW.js +1 -0
- package/dist/assets/classDiagram-v2-HSJHXN6E-J6ZTeCbW.js +1 -0
- package/dist/assets/clone-5MVZ89iV.js +1 -0
- package/dist/assets/{cose-bilkent-S5V4N54A-BAn0ap_E.js → cose-bilkent-S5V4N54A-BpXeV7Vj.js} +1 -1
- package/dist/assets/{dagre-KV5264BT-DyxnVq1g.js → dagre-KV5264BT-DQLu_W8r.js} +1 -1
- package/dist/assets/{diagram-5BDNPKRD-XCrzqski.js → diagram-5BDNPKRD-skaOoe5A.js} +1 -1
- package/dist/assets/{diagram-G4DWMVQ6-MBCAXft_.js → diagram-G4DWMVQ6-DezlfFH4.js} +1 -1
- package/dist/assets/{diagram-MMDJMWI5-DbtB2yS6.js → diagram-MMDJMWI5-BUu-v-wT.js} +1 -1
- package/dist/assets/{diagram-TYMM5635-Bb5NzX61.js → diagram-TYMM5635-CziQ6LPs.js} +1 -1
- package/dist/assets/{erDiagram-SMLLAGMA-CpIeCOh2.js → erDiagram-SMLLAGMA-BsAyOVTI.js} +1 -1
- package/dist/assets/{flowDiagram-DWJPFMVM-CHyoKnhW.js → flowDiagram-DWJPFMVM-CbYWJOLq.js} +1 -1
- package/dist/assets/{ganttDiagram-T4ZO3ILL-DErKteO_.js → ganttDiagram-T4ZO3ILL-CAwgDkLl.js} +1 -1
- package/dist/assets/{gitGraphDiagram-UUTBAWPF-KFVAtj2F.js → gitGraphDiagram-UUTBAWPF-DK4RlkjO.js} +1 -1
- package/dist/assets/{graph-CRnO_ifT.js → graph-orv1XHGx.js} +1 -1
- package/dist/assets/{index-DkRKLuNr.js → index-Ceywghsu.js} +143 -143
- package/dist/assets/{infoDiagram-42DDH7IO-BZFnuSp5.js → infoDiagram-42DDH7IO-DQyA75sK.js} +1 -1
- package/dist/assets/{ishikawaDiagram-UXIWVN3A-4Xe2Szde.js → ishikawaDiagram-UXIWVN3A-C-F_5q4k.js} +1 -1
- package/dist/assets/{journeyDiagram-VCZTEJTY-CZRByfS-.js → journeyDiagram-VCZTEJTY-Bj8UIvK-.js} +1 -1
- package/dist/assets/{kanban-definition-6JOO6SKY-B95sk6Fk.js → kanban-definition-6JOO6SKY-DZYr8Dp1.js} +1 -1
- package/dist/assets/{layout-BqNQzxWT.js → layout-CBaTKjpX.js} +1 -1
- package/dist/assets/{linear-CUh7qb64.js → linear-j1sI_SiN.js} +1 -1
- package/dist/assets/{min-wXgOS3ig.js → min-DtJISjld.js} +1 -1
- package/dist/assets/{mindmap-definition-QFDTVHPH-DB6iaAbO.js → mindmap-definition-QFDTVHPH-Bulb64RS.js} +1 -1
- package/dist/assets/{pieDiagram-DEJITSTG-CHkZHrTW.js → pieDiagram-DEJITSTG-D11keQxr.js} +1 -1
- package/dist/assets/{quadrantDiagram-34T5L4WZ-DoTEO8e3.js → quadrantDiagram-34T5L4WZ-BEcWQiEG.js} +1 -1
- package/dist/assets/{requirementDiagram-MS252O5E-Dn8peXYp.js → requirementDiagram-MS252O5E-Cbp23uDf.js} +1 -1
- package/dist/assets/{sankeyDiagram-XADWPNL6-DRXs6Ipb.js → sankeyDiagram-XADWPNL6-Dae1hMc5.js} +1 -1
- package/dist/assets/{sequenceDiagram-FGHM5R23-wBBYZ0aq.js → sequenceDiagram-FGHM5R23-C16abORi.js} +1 -1
- package/dist/assets/{stateDiagram-FHFEXIEX-DPlBNGmf.js → stateDiagram-FHFEXIEX-CbEtfhbx.js} +1 -1
- package/dist/assets/stateDiagram-v2-QKLJ7IA2-CyY84hEA.js +1 -0
- package/dist/assets/{timeline-definition-GMOUNBTQ-CbbyTlHk.js → timeline-definition-GMOUNBTQ-BV7JTNMI.js} +1 -1
- package/dist/assets/{vennDiagram-DHZGUBPP-Bj4GaFfj.js → vennDiagram-DHZGUBPP-DBZiT48j.js} +1 -1
- package/dist/assets/{wardley-RL74JXVD-RtNzq8KU.js → wardley-RL74JXVD-Cc8uoiL3.js} +37 -37
- package/dist/assets/{wardleyDiagram-NUSXRM2D-CDfE3zSj.js → wardleyDiagram-NUSXRM2D-DEYcWGo5.js} +1 -1
- package/dist/assets/{xychartDiagram-5P7HB3ND-CZXHHYD5.js → xychartDiagram-5P7HB3ND-bFhLXv2b.js} +1 -1
- package/dist/index.html +1 -1
- package/lib/build.js +193 -19
- package/lib/completion-writer.js +7 -4
- package/lib/deps.js +17 -6
- package/lib/feature-events.js +3 -0
- package/lib/feature-writer.js +34 -22
- package/lib/followup-writer.js +556 -0
- package/lib/mcp-enforcement.js +173 -0
- package/lib/migrate-roadmap.js +4 -1
- package/lib/project-paths.js +36 -0
- package/lib/review-lenses.js +23 -8
- package/lib/review-normalize.js +42 -3
- package/lib/roadmap-drift.js +54 -0
- package/lib/roadmap-gen.js +297 -27
- package/lib/roadmap-preservers.js +353 -0
- package/lib/step-prompt.js +15 -0
- package/lib/triage.js +2 -1
- package/lib/version-check.js +110 -0
- package/package.json +1 -1
- package/server/compose-mcp-tools.js +16 -2
- package/server/compose-mcp.js +24 -1
- package/server/vision-routes.js +51 -2
- package/templates/ROADMAP.md +6 -0
- package/dist/assets/channel-LRG9kHqJ.js +0 -1
- package/dist/assets/classDiagram-6PBFFD2Q-BC9a6pDE.js +0 -1
- package/dist/assets/classDiagram-v2-HSJHXN6E-BC9a6pDE.js +0 -1
- package/dist/assets/clone-dRxgFrBv.js +0 -1
- package/dist/assets/stateDiagram-v2-QKLJ7IA2-BW0ezXb4.js +0 -1
package/lib/feature-writer.js
CHANGED
|
@@ -24,6 +24,7 @@ const _listFeatures = listFeatures;
|
|
|
24
24
|
import { writeRoadmap } from './roadmap-gen.js';
|
|
25
25
|
import { appendEvent, readEvents } from './feature-events.js';
|
|
26
26
|
import { checkOrInsert } from './idempotency.js';
|
|
27
|
+
import { loadFeaturesDir } from './project-paths.js';
|
|
27
28
|
|
|
28
29
|
// ---------------------------------------------------------------------------
|
|
29
30
|
// Status / transition policy
|
|
@@ -98,8 +99,9 @@ export async function addRoadmapEntry(cwd, args) {
|
|
|
98
99
|
throw new Error(`feature-writer: invalid status "${status}"`);
|
|
99
100
|
}
|
|
100
101
|
|
|
102
|
+
const featuresDir = loadFeaturesDir(cwd);
|
|
101
103
|
return maybeIdempotent({ ...args, cwd }, () => {
|
|
102
|
-
const existing = readFeature(cwd, args.code);
|
|
104
|
+
const existing = readFeature(cwd, args.code, featuresDir);
|
|
103
105
|
if (existing) {
|
|
104
106
|
throw new Error(`feature-writer: feature "${args.code}" already exists`);
|
|
105
107
|
}
|
|
@@ -116,14 +118,14 @@ export async function addRoadmapEntry(cwd, args) {
|
|
|
116
118
|
if (args.complexity) feature.complexity = args.complexity;
|
|
117
119
|
feature.position = args.position !== undefined
|
|
118
120
|
? args.position
|
|
119
|
-
: nextPositionInPhase(cwd, args.phase);
|
|
121
|
+
: nextPositionInPhase(cwd, args.phase, featuresDir);
|
|
120
122
|
if (args.parent) feature.parent = args.parent;
|
|
121
123
|
if (args.tags && args.tags.length) feature.tags = args.tags;
|
|
122
124
|
|
|
123
|
-
writeFeature(cwd, feature);
|
|
125
|
+
writeFeature(cwd, feature, featuresDir);
|
|
124
126
|
let roadmapPath;
|
|
125
127
|
try {
|
|
126
|
-
roadmapPath = writeRoadmap(cwd);
|
|
128
|
+
roadmapPath = writeRoadmap(cwd, { featuresDir });
|
|
127
129
|
} catch (err) {
|
|
128
130
|
throw partialWriteError(
|
|
129
131
|
`add_roadmap_entry: feature.json for "${args.code}" was written but ROADMAP.md regeneration failed. ` +
|
|
@@ -151,8 +153,8 @@ export async function addRoadmapEntry(cwd, args) {
|
|
|
151
153
|
|
|
152
154
|
// Default position for a new feature: max existing position in the same
|
|
153
155
|
// phase, plus 1. Falls back to 1 when the phase is empty.
|
|
154
|
-
function nextPositionInPhase(cwd, phase) {
|
|
155
|
-
const peers = _listFeatures(cwd).filter(f => f.phase === phase);
|
|
156
|
+
function nextPositionInPhase(cwd, phase, featuresDir) {
|
|
157
|
+
const peers = _listFeatures(cwd, featuresDir).filter(f => f.phase === phase);
|
|
156
158
|
if (peers.length === 0) return 1;
|
|
157
159
|
const maxPos = peers.reduce((m, f) => {
|
|
158
160
|
const p = typeof f.position === 'number' ? f.position : 0;
|
|
@@ -205,8 +207,9 @@ export async function setFeatureStatus(cwd, args) {
|
|
|
205
207
|
throw new Error(`feature-writer: invalid status "${args.status}" — must be one of ${[...STATUSES].join(', ')}`);
|
|
206
208
|
}
|
|
207
209
|
|
|
210
|
+
const featuresDir = loadFeaturesDir(cwd);
|
|
208
211
|
return maybeIdempotent({ ...args, cwd }, () => {
|
|
209
|
-
const feature = readFeature(cwd, args.code);
|
|
212
|
+
const feature = readFeature(cwd, args.code, featuresDir);
|
|
210
213
|
if (!feature) {
|
|
211
214
|
throw new Error(`feature-writer: feature "${args.code}" not found`);
|
|
212
215
|
}
|
|
@@ -229,9 +232,9 @@ export async function setFeatureStatus(cwd, args) {
|
|
|
229
232
|
|
|
230
233
|
const updates = { status: to };
|
|
231
234
|
if (args.commit_sha) updates.commit_sha = args.commit_sha;
|
|
232
|
-
updateFeature(cwd, args.code, updates);
|
|
235
|
+
updateFeature(cwd, args.code, updates, featuresDir);
|
|
233
236
|
try {
|
|
234
|
-
writeRoadmap(cwd);
|
|
237
|
+
writeRoadmap(cwd, { featuresDir });
|
|
235
238
|
} catch (err) {
|
|
236
239
|
throw partialWriteError(
|
|
237
240
|
`set_feature_status: feature.json for "${args.code}" was updated (${from} → ${to}) but ROADMAP.md regeneration failed. ` +
|
|
@@ -269,12 +272,17 @@ export async function setFeatureStatus(cwd, args) {
|
|
|
269
272
|
*/
|
|
270
273
|
export function roadmapDiff(cwd, args = {}) {
|
|
271
274
|
const since = args.since ?? '24h';
|
|
272
|
-
const
|
|
275
|
+
const rawEvents = readEvents(cwd, {
|
|
273
276
|
since,
|
|
274
277
|
code: args.feature_code,
|
|
275
278
|
tool: args.tool,
|
|
276
279
|
});
|
|
277
280
|
|
|
281
|
+
// Filter out internal reconciliation events that aren't user-driven mutations.
|
|
282
|
+
// `roadmap_drift` events fire when a curated phase override diverges from
|
|
283
|
+
// the auto-rollup; they're observability output, not roadmap changes.
|
|
284
|
+
const events = rawEvents.filter(e => e.tool !== 'roadmap_drift');
|
|
285
|
+
|
|
278
286
|
const added = [];
|
|
279
287
|
const status_changed = [];
|
|
280
288
|
for (const e of events) {
|
|
@@ -336,14 +344,14 @@ function validateRepoPath(cwd, path) {
|
|
|
336
344
|
return normalized;
|
|
337
345
|
}
|
|
338
346
|
|
|
339
|
-
function rejectCanonicalArtifact(featureCode, normalizedPath) {
|
|
340
|
-
// Reject paths like
|
|
347
|
+
function rejectCanonicalArtifact(featuresDir, featureCode, normalizedPath) {
|
|
348
|
+
// Reject paths like <featuresDir>/<CODE>/design.md, prd.md, etc.
|
|
341
349
|
const file = basename(normalizedPath);
|
|
342
350
|
if (!CANONICAL_ARTIFACT_NAMES.has(file)) return;
|
|
343
351
|
// The canonical files live under the feature folder. If this path points
|
|
344
352
|
// inside the feature's own folder, refuse — those are auto-discovered.
|
|
345
353
|
const parent = dirname(normalizedPath);
|
|
346
|
-
if (parent.endsWith(
|
|
354
|
+
if (parent.endsWith(`${featuresDir}/${featureCode}`)) {
|
|
347
355
|
throw new Error(
|
|
348
356
|
`feature-writer: "${file}" inside the feature folder is a canonical artifact; ` +
|
|
349
357
|
`it is auto-discovered by assess_feature_artifacts and should not be linked explicitly.`
|
|
@@ -371,10 +379,11 @@ export async function linkArtifact(cwd, args) {
|
|
|
371
379
|
throw new Error('feature-writer: artifact_type is required (non-empty string)');
|
|
372
380
|
}
|
|
373
381
|
const normalizedPath = validateRepoPath(cwd, args.path);
|
|
374
|
-
|
|
382
|
+
const featuresDir = loadFeaturesDir(cwd);
|
|
383
|
+
rejectCanonicalArtifact(featuresDir, args.feature_code, normalizedPath);
|
|
375
384
|
|
|
376
385
|
return maybeIdempotent({ ...args, cwd }, () => {
|
|
377
|
-
const feature = readFeature(cwd, args.feature_code);
|
|
386
|
+
const feature = readFeature(cwd, args.feature_code, featuresDir);
|
|
378
387
|
if (!feature) {
|
|
379
388
|
throw new Error(`feature-writer: feature "${args.feature_code}" not found`);
|
|
380
389
|
}
|
|
@@ -399,7 +408,7 @@ export async function linkArtifact(cwd, args) {
|
|
|
399
408
|
if (matchIdx !== -1) artifacts[matchIdx] = entry;
|
|
400
409
|
else artifacts.push(entry);
|
|
401
410
|
|
|
402
|
-
updateFeature(cwd, args.feature_code, { artifacts });
|
|
411
|
+
updateFeature(cwd, args.feature_code, { artifacts }, featuresDir);
|
|
403
412
|
|
|
404
413
|
safeAppendEvent(cwd, {
|
|
405
414
|
tool: 'link_artifact',
|
|
@@ -443,8 +452,9 @@ export async function linkFeatures(cwd, args) {
|
|
|
443
452
|
);
|
|
444
453
|
}
|
|
445
454
|
|
|
455
|
+
const featuresDir = loadFeaturesDir(cwd);
|
|
446
456
|
return maybeIdempotent({ ...args, cwd }, () => {
|
|
447
|
-
const feature = readFeature(cwd, args.from_code);
|
|
457
|
+
const feature = readFeature(cwd, args.from_code, featuresDir);
|
|
448
458
|
if (!feature) {
|
|
449
459
|
throw new Error(`feature-writer: feature "${args.from_code}" not found`);
|
|
450
460
|
}
|
|
@@ -464,7 +474,7 @@ export async function linkFeatures(cwd, args) {
|
|
|
464
474
|
if (matchIdx !== -1) links[matchIdx] = entry;
|
|
465
475
|
else links.push(entry);
|
|
466
476
|
|
|
467
|
-
updateFeature(cwd, args.from_code, { links });
|
|
477
|
+
updateFeature(cwd, args.from_code, { links }, featuresDir);
|
|
468
478
|
|
|
469
479
|
safeAppendEvent(cwd, {
|
|
470
480
|
tool: 'link_features',
|
|
@@ -498,7 +508,8 @@ export async function linkFeatures(cwd, args) {
|
|
|
498
508
|
*/
|
|
499
509
|
export async function getFeatureArtifacts(cwd, args) {
|
|
500
510
|
validateCode(args.feature_code);
|
|
501
|
-
const
|
|
511
|
+
const featuresDir = loadFeaturesDir(cwd);
|
|
512
|
+
const feature = readFeature(cwd, args.feature_code, featuresDir);
|
|
502
513
|
if (!feature) {
|
|
503
514
|
throw new Error(`feature-writer: feature "${args.feature_code}" not found`);
|
|
504
515
|
}
|
|
@@ -514,7 +525,7 @@ export async function getFeatureArtifacts(cwd, args) {
|
|
|
514
525
|
let canonical = null;
|
|
515
526
|
try {
|
|
516
527
|
const { ArtifactManager } = await import('../server/artifact-manager.js');
|
|
517
|
-
const featureRoot = resolve(realCwd,
|
|
528
|
+
const featureRoot = resolve(realCwd, featuresDir);
|
|
518
529
|
if (existsSync(featureRoot)) {
|
|
519
530
|
const manager = new ArtifactManager(featureRoot);
|
|
520
531
|
canonical = manager.assess(args.feature_code);
|
|
@@ -549,10 +560,11 @@ export function getFeatureLinks(cwd, args) {
|
|
|
549
560
|
}
|
|
550
561
|
const kind = args.kind;
|
|
551
562
|
|
|
563
|
+
const featuresDir = loadFeaturesDir(cwd);
|
|
552
564
|
const out = { feature_code: args.feature_code };
|
|
553
565
|
|
|
554
566
|
if (direction === 'outgoing' || direction === 'both') {
|
|
555
|
-
const feature = readFeature(cwd, args.feature_code);
|
|
567
|
+
const feature = readFeature(cwd, args.feature_code, featuresDir);
|
|
556
568
|
if (!feature) {
|
|
557
569
|
throw new Error(`feature-writer: feature "${args.feature_code}" not found`);
|
|
558
570
|
}
|
|
@@ -562,7 +574,7 @@ export function getFeatureLinks(cwd, args) {
|
|
|
562
574
|
}
|
|
563
575
|
|
|
564
576
|
if (direction === 'incoming' || direction === 'both') {
|
|
565
|
-
const all = _listFeatures(cwd);
|
|
577
|
+
const all = _listFeatures(cwd, featuresDir);
|
|
566
578
|
const incoming = [];
|
|
567
579
|
for (const f of all) {
|
|
568
580
|
if (f.code === args.feature_code) continue;
|