@smartmemory/compose 0.1.43-beta → 0.2.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/bin/compose.js +179 -35
- package/dist/assets/App-VU2lfA8m.js +770 -0
- package/dist/assets/{arc-N74_SuiS.js → arc-CIeqpX37.js} +1 -1
- package/dist/assets/{architectureDiagram-3BPJPVTR-DHOb5xas.js → architectureDiagram-3BPJPVTR-itmOSZLE.js} +1 -1
- package/dist/assets/{blockDiagram-GPEHLZMM-DR9b_xXC.js → blockDiagram-GPEHLZMM-N7MotI_5.js} +8 -8
- package/dist/assets/{c4Diagram-AAUBKEIU-EXIx4J1v.js → c4Diagram-AAUBKEIU-DRKW39LH.js} +1 -1
- package/dist/assets/channel-DugSMLKi.js +1 -0
- package/dist/assets/{chunk-2J33WTMH-CyZqa6ub.js → chunk-2J33WTMH-CF6iSwEb.js} +1 -1
- package/dist/assets/{chunk-4BX2VUAB-SjPmvNaj.js → chunk-4BX2VUAB-BTe-QE0R.js} +1 -1
- package/dist/assets/{chunk-55IACEB6-Cnu3mdms.js → chunk-55IACEB6-E2hHEsl9.js} +1 -1
- package/dist/assets/{chunk-727SXJPM-DNj5i6fj.js → chunk-727SXJPM-CBRmkSvh.js} +1 -1
- package/dist/assets/{chunk-AQP2D5EJ-BIVskOlI.js → chunk-AQP2D5EJ-BdtQ63fN.js} +1 -1
- package/dist/assets/{chunk-FMBD7UC4-BWlLU-hh.js → chunk-FMBD7UC4-DfYQ2YmB.js} +1 -1
- package/dist/assets/{chunk-ND2GUHAM-DQEknadH.js → chunk-ND2GUHAM-CDrOVOW5.js} +1 -1
- package/dist/assets/{chunk-QZHKN3VN-CUYnhnAB.js → chunk-QZHKN3VN-DwjqJ9xB.js} +1 -1
- package/dist/assets/classDiagram-4FO5ZUOK-D2RRwp7J.js +1 -0
- package/dist/assets/classDiagram-v2-Q7XG4LA2-D2RRwp7J.js +1 -0
- package/dist/assets/{cose-bilkent-S5V4N54A-X3n23b12.js → cose-bilkent-S5V4N54A-MHpsrtBZ.js} +1 -1
- package/dist/assets/{dagre-BM42HDAG-C0SrhQ_X.js → dagre-BM42HDAG-DaPz_mPt.js} +1 -1
- package/dist/assets/{diagram-2AECGRRQ-Bc3qx6pJ.js → diagram-2AECGRRQ-DIdstuOm.js} +1 -1
- package/dist/assets/{diagram-5GNKFQAL-UiCrD06F.js → diagram-5GNKFQAL-DbkTGVES.js} +1 -1
- package/dist/assets/{diagram-KO2AKTUF-B9Vn5KyO.js → diagram-KO2AKTUF-BPalYJed.js} +1 -1
- package/dist/assets/{diagram-LMA3HP47-DLOYeLM3.js → diagram-LMA3HP47-vnySSoyd.js} +1 -1
- package/dist/assets/{diagram-OG6HWLK6-CXjh2miZ.js → diagram-OG6HWLK6-Dv3BUJft.js} +1 -1
- package/dist/assets/{erDiagram-TEJ5UH35-EmDzXNsM.js → erDiagram-TEJ5UH35-B3OLgtKK.js} +1 -1
- package/dist/assets/{flowDiagram-I6XJVG4X-vk6E_ebo.js → flowDiagram-I6XJVG4X-DdpxVf-5.js} +1 -1
- package/dist/assets/{ganttDiagram-6RSMTGT7-DYYSAjNx.js → ganttDiagram-6RSMTGT7-QALT_Lj9.js} +4 -4
- package/dist/assets/{gitGraphDiagram-PVQCEYII-CWPZVbhV.js → gitGraphDiagram-PVQCEYII-nITcPPED.js} +1 -1
- package/dist/assets/{graph-uO5hwVZK.js → graph-DnLKqSPg.js} +2 -2
- package/dist/assets/{index-BYYTTzUT.js → index-CLb8RFcn.js} +3 -3
- package/dist/assets/index-jqUffYBL.css +1 -0
- package/dist/assets/{infoDiagram-5YYISTIA-Dsu-eeJm.js → infoDiagram-5YYISTIA-CjlRce3x.js} +1 -1
- package/dist/assets/{ishikawaDiagram-YF4QCWOH-BP1SP8WA.js → ishikawaDiagram-YF4QCWOH-OyKVgxOz.js} +1 -1
- package/dist/assets/{journeyDiagram-JHISSGLW-DkE5By_R.js → journeyDiagram-JHISSGLW-3FaFyfLR.js} +1 -1
- package/dist/assets/{kanban-definition-UN3LZRKU-Cf_230xs.js → kanban-definition-UN3LZRKU-DUPnRo3q.js} +1 -1
- package/dist/assets/{linear-B-paxRBQ.js → linear-BeL8i3rv.js} +1 -1
- package/dist/assets/{mindmap-definition-RKZ34NQL-DAp6uJ_b.js → mindmap-definition-RKZ34NQL-C0CwWNdR.js} +1 -1
- package/dist/assets/mobile-qvdJ5p0m.js +17 -0
- package/dist/assets/{pieDiagram-4H26LBE5-CbYY5KL0.js → pieDiagram-4H26LBE5-DaU2jPjX.js} +1 -1
- package/dist/assets/{quadrantDiagram-W4KKPZXB-D5S4_ac5.js → quadrantDiagram-W4KKPZXB-HFtjZSAT.js} +1 -1
- package/dist/assets/{requirementDiagram-4Y6WPE33-BrPWCnHz.js → requirementDiagram-4Y6WPE33-CX_Mz3gv.js} +1 -1
- package/dist/assets/{sankeyDiagram-5OEKKPKP-CP8j1mcl.js → sankeyDiagram-5OEKKPKP-BR2_eTy9.js} +1 -1
- package/dist/assets/{sequenceDiagram-3UESZ5HK-c8DuhvUj.js → sequenceDiagram-3UESZ5HK-CtHp0Qnp.js} +1 -1
- package/dist/assets/{stateDiagram-AJRCARHV-KO9G1Jrm.js → stateDiagram-AJRCARHV-DmiEmD6G.js} +1 -1
- package/dist/assets/stateDiagram-v2-BHNVJYJU-7rdO1Tgp.js +1 -0
- package/dist/assets/{timeline-definition-PNZ67QCA-Cs2HLlbG.js → timeline-definition-PNZ67QCA-GSHqrJ3A.js} +1 -1
- package/dist/assets/{vennDiagram-CIIHVFJN-rcSRidqI.js → vennDiagram-CIIHVFJN-CNxhQnCU.js} +1 -1
- package/dist/assets/{wardley-L42UT6IY-BsajGfii.js → wardley-L42UT6IY-Bf-gQIFY.js} +1 -1
- package/dist/assets/{wardleyDiagram-YWT4CUSO-CSWALc_m.js → wardleyDiagram-YWT4CUSO-RGxoapr7.js} +1 -1
- package/dist/assets/{xychartDiagram-2RQKCTM6-jC4Q0GvG.js → xychartDiagram-2RQKCTM6-1_H1qVde.js} +1 -1
- package/dist/index.html +3 -3
- package/lib/build.js +3 -2
- package/lib/feature-code.js +14 -4
- package/lib/feature-json.js +33 -2
- package/lib/feature-validator.js +135 -11
- package/lib/feature-writer.js +83 -3
- package/lib/migrate-roadmap.js +16 -2
- package/lib/project-paths.js +16 -0
- package/lib/roadmap-config.js +50 -0
- package/lib/roadmap-gen.js +46 -31
- package/lib/roadmap-heading.js +85 -0
- package/lib/roadmap-parser.js +69 -18
- package/lib/roadmap-preservers.js +60 -19
- package/lib/roadmap-roundtrip.js +137 -0
- package/lib/vision-writer.js +42 -14
- package/lib/xref-sync.js +160 -0
- package/package.json +1 -1
- package/server/compose-mcp.js +2 -1
- package/server/vision-store.js +1 -1
- package/dist/assets/App-CdP799CF.js +0 -768
- package/dist/assets/channel-yPY0IE15.js +0 -1
- package/dist/assets/classDiagram-4FO5ZUOK-SGKYXTP4.js +0 -1
- package/dist/assets/classDiagram-v2-Q7XG4LA2-SGKYXTP4.js +0 -1
- package/dist/assets/index-Dh2rRpBR.css +0 -1
- package/dist/assets/mobile-BwduHUEq.js +0 -17
- package/dist/assets/stateDiagram-v2-BHNVJYJU-eVyb8_R4.js +0 -1
package/bin/compose.js
CHANGED
|
@@ -119,6 +119,9 @@ 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')
|
|
123
|
+
console.log(' items List vision items from local state (no server)')
|
|
124
|
+
console.log(' items show <id> Show detail for a specific vision item')
|
|
122
125
|
console.log(' triage Analyze a feature and recommend build profile')
|
|
123
126
|
console.log(' qa-scope Show affected routes from a feature\'s changed files')
|
|
124
127
|
console.log(' init Initialize Compose in the current project')
|
|
@@ -1034,22 +1037,47 @@ _This is a seed design doc created by \`compose feature\`. The \`compose build\`
|
|
|
1034
1037
|
if (cmd === 'roadmap') {
|
|
1035
1038
|
const subcmd = args[0]
|
|
1036
1039
|
|
|
1037
|
-
// 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.
|
|
1038
1042
|
if (subcmd === 'generate' || subcmd === 'gen') {
|
|
1039
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')
|
|
1040
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
|
+
}
|
|
1041
1057
|
const path = writeRoadmap(cwd)
|
|
1042
|
-
|
|
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
|
+
}
|
|
1043
1069
|
process.exit(0)
|
|
1044
1070
|
}
|
|
1045
1071
|
|
|
1046
1072
|
// compose roadmap migrate — extract ROADMAP.md entries into feature.json files
|
|
1047
1073
|
if (subcmd === 'migrate') {
|
|
1048
1074
|
const { migrateRoadmap } = await import('../lib/migrate-roadmap.js')
|
|
1075
|
+
const { loadExternalPrefixes } = await import('../lib/project-paths.js')
|
|
1049
1076
|
const { root: cwd } = resolveCwdWithWorkspace(args)
|
|
1050
1077
|
const dryRun = args.includes('--dry-run')
|
|
1051
1078
|
const overwrite = args.includes('--overwrite')
|
|
1052
|
-
const
|
|
1079
|
+
const externalPrefixes = loadExternalPrefixes(cwd)
|
|
1080
|
+
const result = migrateRoadmap(cwd, { dryRun, overwrite, externalPrefixes })
|
|
1053
1081
|
if (!dryRun) {
|
|
1054
1082
|
console.log(`Created: ${result.created.length} feature.json files`)
|
|
1055
1083
|
if (result.created.length > 0) console.log(` ${result.created.join(', ')}`)
|
|
@@ -1057,6 +1085,9 @@ if (cmd === 'roadmap') {
|
|
|
1057
1085
|
if (result.updated.length > 0) console.log(` ${result.updated.join(', ')}`)
|
|
1058
1086
|
console.log(`Skipped: ${result.skipped.length} (already exist, use --overwrite to replace)`)
|
|
1059
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(', ')}`)
|
|
1060
1091
|
}
|
|
1061
1092
|
process.exit(0)
|
|
1062
1093
|
}
|
|
@@ -1064,49 +1095,56 @@ if (cmd === 'roadmap') {
|
|
|
1064
1095
|
// compose roadmap check — verify feature.json ↔ ROADMAP.md consistency
|
|
1065
1096
|
if (subcmd === 'check') {
|
|
1066
1097
|
const { listFeatures } = await import('../lib/feature-json.js')
|
|
1067
|
-
const {
|
|
1098
|
+
const { checkRoundtrip, describeLossyDiff } = await import('../lib/roadmap-roundtrip.js')
|
|
1099
|
+
const { loadExternalPrefixes } = await import('../lib/project-paths.js')
|
|
1068
1100
|
const { root: cwd } = resolveCwdWithWorkspace(args)
|
|
1069
1101
|
const roadmapPath = join(cwd, 'ROADMAP.md')
|
|
1070
1102
|
if (!existsSync(roadmapPath)) {
|
|
1071
1103
|
console.error('No ROADMAP.md found. Run: compose roadmap generate')
|
|
1072
1104
|
process.exit(1)
|
|
1073
1105
|
}
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
const
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
for (const f of features) {
|
|
1082
|
-
if (!roadmapCodes.has(f.code)) {
|
|
1083
|
-
console.log(`MISSING from ROADMAP.md: ${f.code}`)
|
|
1084
|
-
clean = false
|
|
1085
|
-
}
|
|
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)
|
|
1086
1113
|
}
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
clean = false
|
|
1093
|
-
}
|
|
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)
|
|
1094
1119
|
}
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
const rm = roadmapMap.get(f.code)
|
|
1099
|
-
if (rm && rm.status !== f.status) {
|
|
1100
|
-
console.log(`STATUS MISMATCH: ${f.code} — feature.json=${f.status}, ROADMAP.md=${rm.status}`)
|
|
1101
|
-
clean = false
|
|
1102
|
-
}
|
|
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'}`)
|
|
1103
1123
|
}
|
|
1124
|
+
for (const d of rt.diffs.filter(x => x.kind.startsWith('LOSSLESS_'))) {
|
|
1125
|
+
console.log(describeLossyDiff(d))
|
|
1126
|
+
}
|
|
1127
|
+
console.log('\nRun `compose roadmap generate` to regenerate ROADMAP.md from feature.json.')
|
|
1128
|
+
process.exit(1)
|
|
1129
|
+
}
|
|
1104
1130
|
|
|
1105
|
-
|
|
1106
|
-
|
|
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).`)
|
|
1107
1141
|
} else {
|
|
1108
|
-
console.log(
|
|
1109
|
-
|
|
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}`)
|
|
1110
1148
|
}
|
|
1111
1149
|
process.exit(0)
|
|
1112
1150
|
}
|
|
@@ -2692,6 +2730,112 @@ if (cmd === 'build') {
|
|
|
2692
2730
|
process.exit(1)
|
|
2693
2731
|
}
|
|
2694
2732
|
|
|
2733
|
+
} else if (cmd === 'items') {
|
|
2734
|
+
// ---------------------------------------------------------------------------
|
|
2735
|
+
// compose items — read vision-state.json directly (no server required)
|
|
2736
|
+
// ---------------------------------------------------------------------------
|
|
2737
|
+
const itemsSub = args[0] && !args[0].startsWith('-') ? args[0] : 'list'
|
|
2738
|
+
const jsonFlag = args.includes('--json')
|
|
2739
|
+
|
|
2740
|
+
const { root: itemsCwd } = resolveCwdWithWorkspace(args)
|
|
2741
|
+
const vsPath = join(itemsCwd, '.compose', 'data', 'vision-state.json')
|
|
2742
|
+
|
|
2743
|
+
if (!existsSync(vsPath)) {
|
|
2744
|
+
console.error('No vision state found at .compose/data/vision-state.json')
|
|
2745
|
+
console.error('Run `compose start` at least once to generate it, then you can use `compose items` offline.')
|
|
2746
|
+
process.exit(1)
|
|
2747
|
+
}
|
|
2748
|
+
|
|
2749
|
+
let visionState
|
|
2750
|
+
try {
|
|
2751
|
+
visionState = JSON.parse(readFileSync(vsPath, 'utf-8'))
|
|
2752
|
+
} catch (err) {
|
|
2753
|
+
console.error(`Failed to read vision state: ${err.message}`)
|
|
2754
|
+
process.exit(1)
|
|
2755
|
+
}
|
|
2756
|
+
|
|
2757
|
+
const allItems = visionState.items || []
|
|
2758
|
+
|
|
2759
|
+
if (itemsSub === 'list') {
|
|
2760
|
+
if (jsonFlag) {
|
|
2761
|
+
console.log(JSON.stringify(allItems, null, 2))
|
|
2762
|
+
process.exit(0)
|
|
2763
|
+
}
|
|
2764
|
+
|
|
2765
|
+
if (allItems.length === 0) {
|
|
2766
|
+
console.log('No items found.')
|
|
2767
|
+
process.exit(0)
|
|
2768
|
+
}
|
|
2769
|
+
|
|
2770
|
+
// Table output: id (truncated), title, status, type
|
|
2771
|
+
const header = { id: 'ID', title: 'TITLE', status: 'STATUS', type: 'TYPE' }
|
|
2772
|
+
const rows = allItems.map(it => ({
|
|
2773
|
+
id: (it.id || '').slice(0, 8),
|
|
2774
|
+
title: (it.title || '(untitled)').slice(0, 50),
|
|
2775
|
+
status: it.status || '-',
|
|
2776
|
+
type: it.type || '-',
|
|
2777
|
+
}))
|
|
2778
|
+
|
|
2779
|
+
// Compute column widths
|
|
2780
|
+
const cols = ['id', 'title', 'status', 'type']
|
|
2781
|
+
const widths = {}
|
|
2782
|
+
for (const c of cols) {
|
|
2783
|
+
widths[c] = Math.max(header[c].length, ...rows.map(r => r[c].length))
|
|
2784
|
+
}
|
|
2785
|
+
|
|
2786
|
+
const pad = (s, w) => s + ' '.repeat(Math.max(0, w - s.length))
|
|
2787
|
+
const line = cols.map(c => pad(header[c], widths[c])).join(' ')
|
|
2788
|
+
console.log(line)
|
|
2789
|
+
console.log(cols.map(c => '-'.repeat(widths[c])).join(' '))
|
|
2790
|
+
for (const r of rows) {
|
|
2791
|
+
console.log(cols.map(c => pad(r[c], widths[c])).join(' '))
|
|
2792
|
+
}
|
|
2793
|
+
process.exit(0)
|
|
2794
|
+
|
|
2795
|
+
} else if (itemsSub === 'show') {
|
|
2796
|
+
const positionalArgs = args.filter(a => !a.startsWith('-'))
|
|
2797
|
+
const showId = positionalArgs[1]
|
|
2798
|
+
if (!showId) {
|
|
2799
|
+
console.error('Usage: compose items show <id>')
|
|
2800
|
+
process.exit(1)
|
|
2801
|
+
}
|
|
2802
|
+
|
|
2803
|
+
const matches = allItems.filter(it => it.id === showId || it.id.startsWith(showId))
|
|
2804
|
+
if (matches.length > 1) {
|
|
2805
|
+
console.error(`Ambiguous ID prefix '${showId}' matches ${matches.length} items:`)
|
|
2806
|
+
for (const m of matches) console.error(` ${(m.id || '').slice(0, 8)} ${m.title || '(untitled)'}`)
|
|
2807
|
+
process.exit(1)
|
|
2808
|
+
}
|
|
2809
|
+
const match = matches[0]
|
|
2810
|
+
if (!match) {
|
|
2811
|
+
console.error(`No item found matching: ${showId}`)
|
|
2812
|
+
process.exit(1)
|
|
2813
|
+
}
|
|
2814
|
+
|
|
2815
|
+
if (jsonFlag) {
|
|
2816
|
+
console.log(JSON.stringify(match, null, 2))
|
|
2817
|
+
} else {
|
|
2818
|
+
for (const [key, val] of Object.entries(match)) {
|
|
2819
|
+
if (val == null) continue
|
|
2820
|
+
if (typeof val === 'object') {
|
|
2821
|
+
console.log(`${key}: ${JSON.stringify(val)}`)
|
|
2822
|
+
} else {
|
|
2823
|
+
console.log(`${key}: ${val}`)
|
|
2824
|
+
}
|
|
2825
|
+
}
|
|
2826
|
+
}
|
|
2827
|
+
process.exit(0)
|
|
2828
|
+
|
|
2829
|
+
} else {
|
|
2830
|
+
console.error(`Unknown items subcommand: ${itemsSub}`)
|
|
2831
|
+
console.error('Usage:')
|
|
2832
|
+
console.error(' compose items List all items')
|
|
2833
|
+
console.error(' compose items list List all items')
|
|
2834
|
+
console.error(' compose items show <id> Show detail for a specific item')
|
|
2835
|
+
console.error(' --json Output as JSON')
|
|
2836
|
+
process.exit(1)
|
|
2837
|
+
}
|
|
2838
|
+
|
|
2695
2839
|
} else {
|
|
2696
2840
|
console.error(`Unknown command: ${cmd}`)
|
|
2697
2841
|
process.exit(1)
|