@smartmemory/compose 0.1.44-beta → 0.2.1-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.
Files changed (77) hide show
  1. package/README.md +1 -1
  2. package/bin/compose.js +71 -35
  3. package/dist/assets/App-VU2lfA8m.js +770 -0
  4. package/dist/assets/{arc-N74_SuiS.js → arc-CIeqpX37.js} +1 -1
  5. package/dist/assets/{architectureDiagram-3BPJPVTR-DHOb5xas.js → architectureDiagram-3BPJPVTR-itmOSZLE.js} +1 -1
  6. package/dist/assets/{blockDiagram-GPEHLZMM-DR9b_xXC.js → blockDiagram-GPEHLZMM-N7MotI_5.js} +8 -8
  7. package/dist/assets/{c4Diagram-AAUBKEIU-EXIx4J1v.js → c4Diagram-AAUBKEIU-DRKW39LH.js} +1 -1
  8. package/dist/assets/channel-DugSMLKi.js +1 -0
  9. package/dist/assets/{chunk-2J33WTMH-CyZqa6ub.js → chunk-2J33WTMH-CF6iSwEb.js} +1 -1
  10. package/dist/assets/{chunk-4BX2VUAB-SjPmvNaj.js → chunk-4BX2VUAB-BTe-QE0R.js} +1 -1
  11. package/dist/assets/{chunk-55IACEB6-Cnu3mdms.js → chunk-55IACEB6-E2hHEsl9.js} +1 -1
  12. package/dist/assets/{chunk-727SXJPM-DNj5i6fj.js → chunk-727SXJPM-CBRmkSvh.js} +1 -1
  13. package/dist/assets/{chunk-AQP2D5EJ-BIVskOlI.js → chunk-AQP2D5EJ-BdtQ63fN.js} +1 -1
  14. package/dist/assets/{chunk-FMBD7UC4-BWlLU-hh.js → chunk-FMBD7UC4-DfYQ2YmB.js} +1 -1
  15. package/dist/assets/{chunk-ND2GUHAM-DQEknadH.js → chunk-ND2GUHAM-CDrOVOW5.js} +1 -1
  16. package/dist/assets/{chunk-QZHKN3VN-CUYnhnAB.js → chunk-QZHKN3VN-DwjqJ9xB.js} +1 -1
  17. package/dist/assets/classDiagram-4FO5ZUOK-D2RRwp7J.js +1 -0
  18. package/dist/assets/classDiagram-v2-Q7XG4LA2-D2RRwp7J.js +1 -0
  19. package/dist/assets/{cose-bilkent-S5V4N54A-X3n23b12.js → cose-bilkent-S5V4N54A-MHpsrtBZ.js} +1 -1
  20. package/dist/assets/{dagre-BM42HDAG-C0SrhQ_X.js → dagre-BM42HDAG-DaPz_mPt.js} +1 -1
  21. package/dist/assets/{diagram-2AECGRRQ-Bc3qx6pJ.js → diagram-2AECGRRQ-DIdstuOm.js} +1 -1
  22. package/dist/assets/{diagram-5GNKFQAL-UiCrD06F.js → diagram-5GNKFQAL-DbkTGVES.js} +1 -1
  23. package/dist/assets/{diagram-KO2AKTUF-B9Vn5KyO.js → diagram-KO2AKTUF-BPalYJed.js} +1 -1
  24. package/dist/assets/{diagram-LMA3HP47-DLOYeLM3.js → diagram-LMA3HP47-vnySSoyd.js} +1 -1
  25. package/dist/assets/{diagram-OG6HWLK6-CXjh2miZ.js → diagram-OG6HWLK6-Dv3BUJft.js} +1 -1
  26. package/dist/assets/{erDiagram-TEJ5UH35-EmDzXNsM.js → erDiagram-TEJ5UH35-B3OLgtKK.js} +1 -1
  27. package/dist/assets/{flowDiagram-I6XJVG4X-vk6E_ebo.js → flowDiagram-I6XJVG4X-DdpxVf-5.js} +1 -1
  28. package/dist/assets/{ganttDiagram-6RSMTGT7-DYYSAjNx.js → ganttDiagram-6RSMTGT7-QALT_Lj9.js} +4 -4
  29. package/dist/assets/{gitGraphDiagram-PVQCEYII-CWPZVbhV.js → gitGraphDiagram-PVQCEYII-nITcPPED.js} +1 -1
  30. package/dist/assets/{graph-uO5hwVZK.js → graph-DnLKqSPg.js} +2 -2
  31. package/dist/assets/{index-BYYTTzUT.js → index-CLb8RFcn.js} +3 -3
  32. package/dist/assets/index-jqUffYBL.css +1 -0
  33. package/dist/assets/{infoDiagram-5YYISTIA-Dsu-eeJm.js → infoDiagram-5YYISTIA-CjlRce3x.js} +1 -1
  34. package/dist/assets/{ishikawaDiagram-YF4QCWOH-BP1SP8WA.js → ishikawaDiagram-YF4QCWOH-OyKVgxOz.js} +1 -1
  35. package/dist/assets/{journeyDiagram-JHISSGLW-DkE5By_R.js → journeyDiagram-JHISSGLW-3FaFyfLR.js} +1 -1
  36. package/dist/assets/{kanban-definition-UN3LZRKU-Cf_230xs.js → kanban-definition-UN3LZRKU-DUPnRo3q.js} +1 -1
  37. package/dist/assets/{linear-B-paxRBQ.js → linear-BeL8i3rv.js} +1 -1
  38. package/dist/assets/{mindmap-definition-RKZ34NQL-DAp6uJ_b.js → mindmap-definition-RKZ34NQL-C0CwWNdR.js} +1 -1
  39. package/dist/assets/mobile-qvdJ5p0m.js +17 -0
  40. package/dist/assets/{pieDiagram-4H26LBE5-CbYY5KL0.js → pieDiagram-4H26LBE5-DaU2jPjX.js} +1 -1
  41. package/dist/assets/{quadrantDiagram-W4KKPZXB-D5S4_ac5.js → quadrantDiagram-W4KKPZXB-HFtjZSAT.js} +1 -1
  42. package/dist/assets/{requirementDiagram-4Y6WPE33-BrPWCnHz.js → requirementDiagram-4Y6WPE33-CX_Mz3gv.js} +1 -1
  43. package/dist/assets/{sankeyDiagram-5OEKKPKP-CP8j1mcl.js → sankeyDiagram-5OEKKPKP-BR2_eTy9.js} +1 -1
  44. package/dist/assets/{sequenceDiagram-3UESZ5HK-c8DuhvUj.js → sequenceDiagram-3UESZ5HK-CtHp0Qnp.js} +1 -1
  45. package/dist/assets/{stateDiagram-AJRCARHV-KO9G1Jrm.js → stateDiagram-AJRCARHV-DmiEmD6G.js} +1 -1
  46. package/dist/assets/stateDiagram-v2-BHNVJYJU-7rdO1Tgp.js +1 -0
  47. package/dist/assets/{timeline-definition-PNZ67QCA-Cs2HLlbG.js → timeline-definition-PNZ67QCA-GSHqrJ3A.js} +1 -1
  48. package/dist/assets/{vennDiagram-CIIHVFJN-rcSRidqI.js → vennDiagram-CIIHVFJN-CNxhQnCU.js} +1 -1
  49. package/dist/assets/{wardley-L42UT6IY-BsajGfii.js → wardley-L42UT6IY-Bf-gQIFY.js} +1 -1
  50. package/dist/assets/{wardleyDiagram-YWT4CUSO-CSWALc_m.js → wardleyDiagram-YWT4CUSO-RGxoapr7.js} +1 -1
  51. package/dist/assets/{xychartDiagram-2RQKCTM6-jC4Q0GvG.js → xychartDiagram-2RQKCTM6-1_H1qVde.js} +1 -1
  52. package/dist/index.html +3 -3
  53. package/lib/build.js +3 -2
  54. package/lib/feature-code.js +14 -4
  55. package/lib/feature-json.js +33 -2
  56. package/lib/feature-validator.js +135 -11
  57. package/lib/feature-writer.js +83 -3
  58. package/lib/migrate-roadmap.js +16 -2
  59. package/lib/project-paths.js +16 -0
  60. package/lib/roadmap-config.js +50 -0
  61. package/lib/roadmap-gen.js +46 -31
  62. package/lib/roadmap-heading.js +85 -0
  63. package/lib/roadmap-parser.js +69 -18
  64. package/lib/roadmap-preservers.js +60 -19
  65. package/lib/roadmap-roundtrip.js +137 -0
  66. package/lib/vision-writer.js +42 -14
  67. package/lib/xref-sync.js +160 -0
  68. package/package.json +1 -1
  69. package/server/compose-mcp.js +2 -1
  70. package/server/vision-store.js +1 -1
  71. package/dist/assets/App-CdP799CF.js +0 -768
  72. package/dist/assets/channel-yPY0IE15.js +0 -1
  73. package/dist/assets/classDiagram-4FO5ZUOK-SGKYXTP4.js +0 -1
  74. package/dist/assets/classDiagram-v2-Q7XG4LA2-SGKYXTP4.js +0 -1
  75. package/dist/assets/index-Dh2rRpBR.css +0 -1
  76. package/dist/assets/mobile-BwduHUEq.js +0 -17
  77. package/dist/assets/stateDiagram-v2-BHNVJYJU-eVyb8_R4.js +0 -1
package/README.md CHANGED
@@ -32,7 +32,7 @@ compose build TODO-1
32
32
 
33
33
  ## Quick install
34
34
 
35
- Prerequisites: Node.js 18+ and `stratum-mcp` on PATH (`pip install stratum`). Codex steps additionally need the OpenAI `codex` CLI. Full prereqs in [docs/install.md](docs/install.md).
35
+ Prerequisites: Node.js 18+ and `stratum-mcp` on PATH (`pip install stratum-mcp`, requires Python 3.11+). Codex steps additionally need the OpenAI `codex` CLI. Full prereqs in [docs/install.md](docs/install.md).
36
36
 
37
37
  The package is published to npm as `@smartmemory/compose`. Pick one install style:
38
38
 
package/bin/compose.js CHANGED
@@ -119,6 +119,7 @@ if (!cmd || cmd === '--help' || cmd === '-h') {
119
119
  console.log(' roadmap generate Regenerate ROADMAP.md from feature.json files')
120
120
  console.log(' roadmap migrate Extract ROADMAP.md entries into feature.json files')
121
121
  console.log(' roadmap check Verify feature.json and ROADMAP.md are in sync')
122
+ console.log(' roadmap xref-sync Pull-reconcile feature.json external links to live state')
122
123
  console.log(' items List vision items from local state (no server)')
123
124
  console.log(' items show <id> Show detail for a specific vision item')
124
125
  console.log(' triage Analyze a feature and recommend build profile')
@@ -1036,22 +1037,47 @@ _This is a seed design doc created by \`compose feature\`. The \`compose build\`
1036
1037
  if (cmd === 'roadmap') {
1037
1038
  const subcmd = args[0]
1038
1039
 
1039
- // compose roadmap generate — regenerate ROADMAP.md from feature.json files
1040
+ // compose roadmap generate — regenerate ROADMAP.md from feature.json files,
1041
+ // converging to a fixed point before finishing.
1040
1042
  if (subcmd === 'generate' || subcmd === 'gen') {
1041
1043
  const { writeRoadmap } = await import('../lib/roadmap-gen.js')
1044
+ const { checkRoundtrip } = await import('../lib/roadmap-roundtrip.js')
1045
+ const { listFeatures } = await import('../lib/feature-json.js')
1046
+ const { loadExternalPrefixes } = await import('../lib/project-paths.js')
1047
+ const { isNarrativeOwned } = await import('../lib/roadmap-config.js')
1042
1048
  const { root: cwd } = resolveCwdWithWorkspace(args)
1049
+ // Narrative-owned workspaces (#39): ROADMAP.md is hand-authored, not a render
1050
+ // of feature.json. writeRoadmap already no-ops, but the canonicalization pass
1051
+ // below would still overwrite the file (or crash if it's absent) — skip the
1052
+ // whole generate path here so the hand-authored file is never touched.
1053
+ if (isNarrativeOwned(cwd)) {
1054
+ console.log('narrative-owned workspace (roadmap.narrative=true) — ROADMAP.md is hand-authored; generate skipped.')
1055
+ process.exit(0)
1056
+ }
1043
1057
  const path = writeRoadmap(cwd)
1044
- console.log(`Generated ${path} from feature.json files`)
1058
+ const externalPrefixes = loadExternalPrefixes(cwd)
1059
+ // checkRoundtrip's now:'0000-00-00' is only used to detect/canonicalize
1060
+ // structural non-convergence — once the file has headings, readPreamble
1061
+ // preserves the existing preamble date verbatim, so no sentinel date leaks.
1062
+ const rt = checkRoundtrip(readFileSync(path, 'utf-8'), listFeatures(cwd), { now: '0000-00-00', externalPrefixes })
1063
+ if (!rt.fixedPoint) {
1064
+ writeFileSync(path, rt.canonical)
1065
+ console.log(`Generated ${path} (canonicalized over ${rt.passes} passes)`)
1066
+ } else {
1067
+ console.log(`Generated ${path} from feature.json files`)
1068
+ }
1045
1069
  process.exit(0)
1046
1070
  }
1047
1071
 
1048
1072
  // compose roadmap migrate — extract ROADMAP.md entries into feature.json files
1049
1073
  if (subcmd === 'migrate') {
1050
1074
  const { migrateRoadmap } = await import('../lib/migrate-roadmap.js')
1075
+ const { loadExternalPrefixes } = await import('../lib/project-paths.js')
1051
1076
  const { root: cwd } = resolveCwdWithWorkspace(args)
1052
1077
  const dryRun = args.includes('--dry-run')
1053
1078
  const overwrite = args.includes('--overwrite')
1054
- const result = migrateRoadmap(cwd, { dryRun, overwrite })
1079
+ const externalPrefixes = loadExternalPrefixes(cwd)
1080
+ const result = migrateRoadmap(cwd, { dryRun, overwrite, externalPrefixes })
1055
1081
  if (!dryRun) {
1056
1082
  console.log(`Created: ${result.created.length} feature.json files`)
1057
1083
  if (result.created.length > 0) console.log(` ${result.created.join(', ')}`)
@@ -1059,6 +1085,9 @@ if (cmd === 'roadmap') {
1059
1085
  if (result.updated.length > 0) console.log(` ${result.updated.join(', ')}`)
1060
1086
  console.log(`Skipped: ${result.skipped.length} (already exist, use --overwrite to replace)`)
1061
1087
  if (result.skipped.length > 0) console.log(` ${result.skipped.join(', ')}`)
1088
+ const ext = result.skippedExternal ?? []
1089
+ console.log(`Skipped (external, cross-project refs): ${ext.length}`)
1090
+ if (ext.length > 0) console.log(` ${ext.join(', ')}`)
1062
1091
  }
1063
1092
  process.exit(0)
1064
1093
  }
@@ -1066,49 +1095,56 @@ if (cmd === 'roadmap') {
1066
1095
  // compose roadmap check — verify feature.json ↔ ROADMAP.md consistency
1067
1096
  if (subcmd === 'check') {
1068
1097
  const { listFeatures } = await import('../lib/feature-json.js')
1069
- const { parseRoadmap } = await import('../lib/roadmap-parser.js')
1098
+ const { checkRoundtrip, describeLossyDiff } = await import('../lib/roadmap-roundtrip.js')
1099
+ const { loadExternalPrefixes } = await import('../lib/project-paths.js')
1070
1100
  const { root: cwd } = resolveCwdWithWorkspace(args)
1071
1101
  const roadmapPath = join(cwd, 'ROADMAP.md')
1072
1102
  if (!existsSync(roadmapPath)) {
1073
1103
  console.error('No ROADMAP.md found. Run: compose roadmap generate')
1074
1104
  process.exit(1)
1075
1105
  }
1076
- const features = listFeatures(cwd)
1077
- const roadmapEntries = parseRoadmap(readFileSync(roadmapPath, 'utf-8'))
1078
- const roadmapCodes = new Set(roadmapEntries.filter(e => !e.code.startsWith('_anon_')).map(e => e.code))
1079
- const featureCodes = new Set(features.map(f => f.code))
1080
-
1081
- let clean = true
1082
- // Features in feature.json but missing from ROADMAP.md
1083
- for (const f of features) {
1084
- if (!roadmapCodes.has(f.code)) {
1085
- console.log(`MISSING from ROADMAP.md: ${f.code}`)
1086
- clean = false
1087
- }
1106
+ // Narrative-owned workspaces (#39): ROADMAP.md is hand-authored, not a render
1107
+ // of feature.json the roundtrip would always report false drift. The file
1108
+ // must still EXIST (checked above); we only skip the drift comparison.
1109
+ const { isNarrativeOwned } = await import('../lib/roadmap-config.js')
1110
+ if (isNarrativeOwned(cwd)) {
1111
+ console.log('narrative-owned workspace (roadmap.narrative=true) — ROADMAP.md is hand-authored; roundtrip check skipped.')
1112
+ process.exit(0)
1088
1113
  }
1089
- // Features in ROADMAP.md but missing feature.json
1090
- for (const e of roadmapEntries) {
1091
- if (e.code.startsWith('_anon_')) continue
1092
- if (!featureCodes.has(e.code)) {
1093
- console.log(`NO feature.json: ${e.code}`)
1094
- clean = false
1095
- }
1114
+ const externalPrefixes = loadExternalPrefixes(cwd)
1115
+ const rt = checkRoundtrip(readFileSync(roadmapPath, 'utf-8'), listFeatures(cwd), { now: '0000-00-00', externalPrefixes })
1116
+ if (rt.fixedPoint && rt.lossless) {
1117
+ console.log('feature.json and ROADMAP.md are in sync (fixed point, lossless).')
1118
+ process.exit(0)
1096
1119
  }
1097
- // Status mismatches
1098
- const roadmapMap = new Map(roadmapEntries.map(e => [e.code, e]))
1099
- for (const f of features) {
1100
- const rm = roadmapMap.get(f.code)
1101
- if (rm && rm.status !== f.status) {
1102
- console.log(`STATUS MISMATCH: ${f.code} — feature.json=${f.status}, ROADMAP.md=${rm.status}`)
1103
- clean = false
1104
- }
1120
+ if (!rt.fixedPoint) {
1121
+ const d = rt.diffs.find(x => x.kind === 'FIXED_POINT_DIVERGENCE')
1122
+ console.log(`NOT A FIXED POINT: ${d?.detail ?? 'ROADMAP.md changes on regen'}`)
1123
+ }
1124
+ for (const d of rt.diffs.filter(x => x.kind.startsWith('LOSSLESS_'))) {
1125
+ console.log(describeLossyDiff(d))
1105
1126
  }
1127
+ console.log('\nRun `compose roadmap generate` to regenerate ROADMAP.md from feature.json.')
1128
+ process.exit(1)
1129
+ }
1106
1130
 
1107
- if (clean) {
1108
- console.log('feature.json and ROADMAP.md are in sync.')
1131
+ // compose roadmap xref-sync — pull-reconcile feature.json external links'
1132
+ // expect= to live target state (COMP-ROADMAP-XREF-SYNC v1). Never writes external.
1133
+ if (subcmd === 'xref-sync') {
1134
+ const { syncExternalRefs } = await import('../lib/xref-sync.js')
1135
+ const { root: cwd } = resolveCwdWithWorkspace(args)
1136
+ const dryRun = args.includes('--dry-run')
1137
+ const res = await syncExternalRefs(cwd, { dryRun })
1138
+ const verb = dryRun ? 'would update' : 'updated'
1139
+ if (res.synced.length === 0) {
1140
+ console.log(`No external-link drift to reconcile (${res.scanned} resolvable link(s) checked, ${res.unchanged} already in sync).`)
1109
1141
  } else {
1110
- console.log('\nRun `compose roadmap generate` to regenerate ROADMAP.md from feature.json.')
1111
- process.exit(1)
1142
+ console.log(`${dryRun ? 'Would reconcile' : 'Reconciled'} ${res.synced.length} external link(s):`)
1143
+ for (const s of res.synced) console.log(` ${s.code} ${s.provider} ${s.target}: expect ${s.from} → ${s.to} (${verb})`)
1144
+ }
1145
+ if (res.skipped.length > 0) {
1146
+ console.log(`\nSkipped ${res.skipped.length} unresolved link(s):`)
1147
+ for (const s of res.skipped) console.log(` ${s.code} ${s.provider} ${s.target}: ${s.reason}`)
1112
1148
  }
1113
1149
  process.exit(0)
1114
1150
  }