scene-capability-engine 3.6.20 → 3.6.22

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/CHANGELOG.md CHANGED
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [3.6.22] - 2026-03-06
11
+
12
+ ### Added
13
+ - New `sce capability inventory` command provides scene-level triad/readiness aggregation for homepage views.
14
+ - Magicball capability iteration docs now define homepage inventory API and triage usage.
15
+
16
+ ## [3.6.21] - 2026-03-06
17
+
18
+ ### Added
19
+ - New `sce capability inventory` command provides scene-level triad/readiness aggregation for homepage views.
20
+ - Capability catalog list/search now supports `--release-ready` and `--missing-triad` filters for pre-publish triage.
21
+ - Magicball capability library docs now show list-level triage filter examples.
22
+
10
23
  ## [3.6.20] - 2026-03-06
11
24
 
12
25
  ### Added
package/README.md CHANGED
@@ -218,5 +218,5 @@ MIT. See [LICENSE](LICENSE).
218
218
 
219
219
  ---
220
220
 
221
- **Version**: 3.6.20
221
+ **Version**: 3.6.22
222
222
  **Last Updated**: 2026-03-05
package/README.zh.md CHANGED
@@ -218,5 +218,5 @@ MIT,见 [LICENSE](LICENSE)。
218
218
 
219
219
  ---
220
220
 
221
- **版本**:3.6.20
221
+ **版本**:3.6.22
222
222
  **最后更新**:2026-03-05
@@ -1869,6 +1869,13 @@ sce scene template-render --package scene-erp --values '{"entity_name":"Order"}'
1869
1869
 
1870
1870
  ### Capability Iteration (scene -> template -> ontology)
1871
1871
 
1872
+ ```bash
1873
+ # 0) Build scene-level homepage inventory
1874
+ sce capability inventory --json
1875
+ sce capability inventory --release-ready false --missing-triad decision_strategy --json
1876
+ ```
1877
+
1878
+
1872
1879
  Capability candidates are now evaluated against the ontology core triad by default:
1873
1880
  - entity + relation
1874
1881
  - business rule
@@ -1903,6 +1910,8 @@ Catalog payloads also expose `release_readiness_ui` for pre-publish sorting and
1903
1910
  ```bash
1904
1911
  # List capability templates
1905
1912
  sce capability catalog list --json
1913
+ sce capability catalog list --release-ready true --json
1914
+ sce capability catalog list --missing-triad decision_strategy --json
1906
1915
 
1907
1916
  # Search capability templates
1908
1917
  sce capability catalog search "customer order" --json
@@ -15,7 +15,31 @@
15
15
 
16
16
  ## 2. 建议 API 列表
17
17
 
18
- ### 2.1 Extract
18
+ ### 2.1 Inventory
19
+
20
+ ```
21
+ POST /api/capability/inventory
22
+ ```
23
+
24
+ 请求:
25
+ ```json
26
+ {
27
+ "release_ready": false,
28
+ "missing_triad": "decision_strategy"
29
+ }
30
+ ```
31
+
32
+ 执行 CLI:
33
+ ```bash
34
+ sce capability inventory --release-ready false --missing-triad decision_strategy --json
35
+ ```
36
+
37
+ 响应:
38
+ - 返回 `capability-inventory` payload,重点消费 `scenes[].ontology_core_ui` 与 `scenes[].release_readiness_ui`
39
+
40
+ ---
41
+
42
+ ### 2.2 Extract
19
43
 
20
44
  ```
21
45
  POST /api/capability/extract
@@ -40,7 +64,7 @@ sce capability extract --scene <scene_id> --specs <specs> --sample-limit <n> --j
40
64
 
41
65
  ---
42
66
 
43
- ### 2.2 Score
67
+ ### 2.3 Score
44
68
 
45
69
  ```
46
70
  POST /api/capability/score
@@ -63,7 +87,7 @@ sce capability score --input <candidate_file> --json
63
87
 
64
88
  ---
65
89
 
66
- ### 2.3 Map
90
+ ### 2.4 Map
67
91
 
68
92
  ```
69
93
  POST /api/capability/map
@@ -91,7 +115,7 @@ sce capability map --input <candidate_file> --mapping <ontology_file> \
91
115
 
92
116
  ---
93
117
 
94
- ### 2.4 Register
118
+ ### 2.5 Register
95
119
 
96
120
  ```
97
121
  POST /api/capability/register
@@ -147,8 +171,9 @@ sce capability register --input <template_file> --risk-level <level> --difficult
147
171
 
148
172
  ## 5. 推荐顺序
149
173
 
150
- 1. `/api/capability/extract`
151
- 2. `/api/capability/score`
152
- 3. `/api/capability/map`
153
- 4. `/api/capability/register`
174
+ 1. `/api/capability/inventory`
175
+ 2. `/api/capability/extract`
176
+ 3. `/api/capability/score`
177
+ 4. `/api/capability/map`
178
+ 5. `/api/capability/register`
154
179
 
@@ -98,7 +98,13 @@
98
98
 
99
99
  ## 4. SCE 接口参数(CLI 可封装)
100
100
 
101
- ### 4.1 提取候选能力
101
+ ### 4.1 Scene 盘点首页聚合
102
+ ```bash
103
+ sce capability inventory --json
104
+ sce capability inventory --release-ready false --missing-triad decision_strategy --json
105
+ ```
106
+
107
+ ### 4.2 提取候选能力
102
108
  ```bash
103
109
  sce capability extract --scene <sceneId> --json
104
110
  ```
@@ -20,7 +20,9 @@
20
20
 
21
21
  ```bash
22
22
  sce capability catalog list --json
23
- sce capability catalog search "customer order" --json
23
+ sce capability catalog list --release-ready true --json
24
+ sce capability catalog list --missing-triad decision_strategy --json
25
+ sce capability catalog search "customer order" --release-ready false --json
24
26
  sce capability catalog show <template-id> --json
25
27
  ```
26
28
 
@@ -723,46 +723,10 @@ function buildTemplateCandidate(candidate, mapping, options) {
723
723
  };
724
724
  }
725
725
 
726
- function buildRegistryEntry(templateCandidate, options) {
727
- const riskLevel = normalizeText(options && options.risk_level) || 'medium';
728
- const difficulty = normalizeText(options && options.difficulty) || 'intermediate';
729
- const applicable = normalizeStringArray(options && options.applicable_scenarios);
730
- const tags = normalizeStringArray(options && options.tags);
731
- const sceneId = buildSceneIdFromCandidate(templateCandidate);
732
- const safeTags = tags.length > 0 ? tags : ['capability', sceneId];
733
- const safeApplicable = applicable.length > 0 ? applicable : [sceneId];
734
-
735
- return {
736
- id: templateCandidate.template_id,
737
- name: templateCandidate.name,
738
- category: templateCandidate.category,
739
- description: templateCandidate.description,
740
- difficulty,
741
- tags: safeTags,
742
- applicable_scenarios: safeApplicable,
743
- files: ['capability-template.json'],
744
- template_type: 'capability-template',
745
- min_sce_version: packageJson.version,
746
- max_sce_version: null,
747
- risk_level: riskLevel,
748
- rollback_contract: {
749
- supported: false,
750
- strategy: 'n/a'
751
- },
752
- ontology_scope: templateCandidate.ontology_scope
753
- };
754
- }
755
-
756
- async function runCapabilityExtractCommand(options = {}, dependencies = {}) {
726
+ async function buildCapabilityCandidatePayload(sceneId, options = {}, dependencies = {}) {
757
727
  const projectPath = dependencies.projectPath || process.cwd();
758
728
  const fileSystem = dependencies.fileSystem || fs;
759
729
  const env = dependencies.env || process.env;
760
- const sceneId = normalizeText(options.scene || options.sceneId || options.scene_id);
761
- const writeOutput = normalizeBoolean(options.write, true);
762
-
763
- if (!sceneId) {
764
- throw new Error('scene is required for capability extract');
765
- }
766
730
 
767
731
  const specResolution = await resolveSceneSpecs(sceneId, {
768
732
  specs: options.specs
@@ -816,7 +780,7 @@ async function runCapabilityExtractCommand(options = {}, dependencies = {}) {
816
780
 
817
781
  const ontologyScope = mergeOntologyScopes(ontologyScopes);
818
782
  const ontologyCore = buildCoreOntologySummary(ontologyScope);
819
- const payload = {
783
+ return {
820
784
  mode: 'capability-extract',
821
785
  scene_id: sceneId,
822
786
  generated_at: new Date().toISOString(),
@@ -835,7 +799,138 @@ async function runCapabilityExtractCommand(options = {}, dependencies = {}) {
835
799
  ontology_missing_triads: ontologyCore.missing
836
800
  }
837
801
  };
802
+ }
803
+
804
+ async function listCapabilityInventorySceneIds(options = {}, dependencies = {}) {
805
+ const projectPath = dependencies.projectPath || process.cwd();
806
+ const fileSystem = dependencies.fileSystem || fs;
807
+ const env = dependencies.env || process.env;
808
+ const explicitScene = normalizeText(options.scene || options.sceneId || options.scene_id);
809
+ if (explicitScene) {
810
+ return [explicitScene];
811
+ }
812
+ const fromFile = await loadSceneIndexFromFile(projectPath, fileSystem);
813
+ if (fromFile && fromFile.data && fromFile.data.scenes) {
814
+ return Object.keys(fromFile.data.scenes).sort();
815
+ }
816
+ const fromState = await loadSceneIndexFromState(projectPath, fileSystem, env);
817
+ if (fromState && fromState.data && fromState.data.scenes) {
818
+ return Object.keys(fromState.data.scenes).sort();
819
+ }
820
+ return [];
821
+ }
822
+
823
+ function filterCapabilityInventoryEntries(entries, options = {}) {
824
+ const normalizedMissingTriad = normalizeText(options.missingTriad || options.missing_triad).toLowerCase();
825
+ const releaseReadyFilter = normalizeText(options.releaseReady || options.release_ready).toLowerCase();
826
+ return (Array.isArray(entries) ? entries : []).filter((entry) => {
827
+ if (releaseReadyFilter) {
828
+ const expected = ['1', 'true', 'yes', 'ready'].includes(releaseReadyFilter);
829
+ if (Boolean(entry.release_readiness_ui && entry.release_readiness_ui.publish_ready) !== expected) {
830
+ return false;
831
+ }
832
+ }
833
+ if (normalizedMissingTriad) {
834
+ const missing = Array.isArray(entry.release_readiness_ui && entry.release_readiness_ui.blocking_missing)
835
+ ? entry.release_readiness_ui.blocking_missing
836
+ : [];
837
+ if (!missing.includes(normalizedMissingTriad)) {
838
+ return false;
839
+ }
840
+ }
841
+ return true;
842
+ });
843
+ }
844
+
845
+ async function runCapabilityInventoryCommand(options = {}, dependencies = {}) {
846
+ const projectPath = dependencies.projectPath || process.cwd();
847
+ const fileSystem = dependencies.fileSystem || fs;
848
+ const env = dependencies.env || process.env;
849
+ const sceneIds = await listCapabilityInventorySceneIds(options, { projectPath, fileSystem, env });
850
+ const limit = toPositiveInteger(options.limit, sceneIds.length || 20);
851
+ const scenes = [];
852
+
853
+ for (const sceneId of sceneIds.slice(0, limit)) {
854
+ const candidate = await buildCapabilityCandidatePayload(sceneId, {
855
+ specs: options.specs,
856
+ sample_limit: options.sample_limit
857
+ }, dependencies);
858
+ const score = buildScoreFromCandidate(candidate);
859
+ const releaseReadiness = buildCapabilityReleaseReadiness({
860
+ scene_id: sceneId,
861
+ ontology_scope: candidate.ontology_scope,
862
+ ontology_core: candidate.ontology_core
863
+ });
864
+ scenes.push({
865
+ scene_id: sceneId,
866
+ summary: candidate.summary,
867
+ source: candidate.source,
868
+ ontology_scope: candidate.ontology_scope,
869
+ ontology_core: candidate.ontology_core,
870
+ ontology_core_ui: buildOntologyCoreUiState(candidate.ontology_core),
871
+ release_readiness: releaseReadiness,
872
+ release_readiness_ui: buildCapabilityReleaseReadinessUi(releaseReadiness),
873
+ score_preview: score
874
+ });
875
+ }
876
+
877
+ const filteredScenes = filterCapabilityInventoryEntries(scenes, options);
878
+ const payload = {
879
+ mode: 'capability-inventory',
880
+ generated_at: new Date().toISOString(),
881
+ scene_count: filteredScenes.length,
882
+ scenes: filteredScenes
883
+ };
884
+
885
+ if (normalizeBoolean(options.json, false)) {
886
+ return payload;
887
+ }
888
+ console.log(chalk.green('✅ Capability inventory generated'));
889
+ console.log(chalk.gray(' Scenes: ' + filteredScenes.length));
890
+ return payload;
891
+ }
892
+
893
+ function buildRegistryEntry(templateCandidate, options) {
894
+ const riskLevel = normalizeText(options && options.risk_level) || 'medium';
895
+ const difficulty = normalizeText(options && options.difficulty) || 'intermediate';
896
+ const applicable = normalizeStringArray(options && options.applicable_scenarios);
897
+ const tags = normalizeStringArray(options && options.tags);
898
+ const sceneId = buildSceneIdFromCandidate(templateCandidate);
899
+ const safeTags = tags.length > 0 ? tags : ['capability', sceneId];
900
+ const safeApplicable = applicable.length > 0 ? applicable : [sceneId];
901
+
902
+ return {
903
+ id: templateCandidate.template_id,
904
+ name: templateCandidate.name,
905
+ category: templateCandidate.category,
906
+ description: templateCandidate.description,
907
+ difficulty,
908
+ tags: safeTags,
909
+ applicable_scenarios: safeApplicable,
910
+ files: ['capability-template.json'],
911
+ template_type: 'capability-template',
912
+ min_sce_version: packageJson.version,
913
+ max_sce_version: null,
914
+ risk_level: riskLevel,
915
+ rollback_contract: {
916
+ supported: false,
917
+ strategy: 'n/a'
918
+ },
919
+ ontology_scope: templateCandidate.ontology_scope
920
+ };
921
+ }
922
+
923
+ async function runCapabilityExtractCommand(options = {}, dependencies = {}) {
924
+ const projectPath = dependencies.projectPath || process.cwd();
925
+ const fileSystem = dependencies.fileSystem || fs;
926
+ const sceneId = normalizeText(options.scene || options.sceneId || options.scene_id);
927
+ const writeOutput = normalizeBoolean(options.write, true);
928
+
929
+ if (!sceneId) {
930
+ throw new Error('scene is required for capability extract');
931
+ }
838
932
 
933
+ const payload = await buildCapabilityCandidatePayload(sceneId, options, dependencies);
839
934
  const outputPath = normalizeText(options.out) || buildDefaultCandidatePath(sceneId);
840
935
  if (writeOutput) {
841
936
  await fileSystem.ensureDir(path.dirname(path.join(projectPath, outputPath)));
@@ -845,17 +940,16 @@ async function runCapabilityExtractCommand(options = {}, dependencies = {}) {
845
940
 
846
941
  if (!normalizeBoolean(options.json, false)) {
847
942
  console.log(chalk.green('✅ Capability candidate extracted'));
848
- console.log(chalk.gray(` Scene: ${sceneId}`));
849
- console.log(chalk.gray(` Specs: ${payload.summary.spec_count}`));
850
- console.log(chalk.gray(` Tasks: ${payload.summary.task_total}`));
943
+ console.log(chalk.gray(' Scene: ' + sceneId));
944
+ console.log(chalk.gray(' Specs: ' + payload.summary.spec_count));
945
+ console.log(chalk.gray(' Tasks: ' + payload.summary.task_total));
851
946
  if (payload.output_file) {
852
- console.log(chalk.gray(` Output: ${payload.output_file}`));
947
+ console.log(chalk.gray(' Output: ' + payload.output_file));
853
948
  }
854
949
  }
855
950
 
856
951
  return payload;
857
952
  }
858
-
859
953
  async function runCapabilityScoreCommand(options = {}, dependencies = {}) {
860
954
  const projectPath = dependencies.projectPath || process.cwd();
861
955
  const fileSystem = dependencies.fileSystem || fs;
@@ -1001,6 +1095,34 @@ async function runCapabilityRegisterCommand(options = {}, dependencies = {}) {
1001
1095
  return result;
1002
1096
  }
1003
1097
 
1098
+ function filterCapabilityCatalogEntries(templates, options = {}) {
1099
+ const entries = Array.isArray(templates) ? templates : [];
1100
+ const normalizedMissingTriad = normalizeText(options.missingTriad || options.missing_triad).toLowerCase();
1101
+ const releaseReadyFilter = normalizeText(options.releaseReady || options.release_ready).toLowerCase();
1102
+
1103
+ return entries.filter((entry) => {
1104
+ const template = enrichCapabilityTemplateForUi(entry);
1105
+
1106
+ if (releaseReadyFilter) {
1107
+ const expected = ['1', 'true', 'yes', 'ready'].includes(releaseReadyFilter);
1108
+ if (template.release_readiness_ui.publish_ready !== expected) {
1109
+ return false;
1110
+ }
1111
+ }
1112
+
1113
+ if (normalizedMissingTriad) {
1114
+ const missing = Array.isArray(template.release_readiness_ui.blocking_missing)
1115
+ ? template.release_readiness_ui.blocking_missing
1116
+ : [];
1117
+ if (!missing.includes(normalizedMissingTriad)) {
1118
+ return false;
1119
+ }
1120
+ }
1121
+
1122
+ return true;
1123
+ });
1124
+ }
1125
+
1004
1126
  function displayCapabilityCatalog(templates, options = {}) {
1005
1127
  const total = Array.isArray(templates) ? templates.length : 0;
1006
1128
  console.log(chalk.red('🔥') + ' Capability Library');
@@ -1026,13 +1148,13 @@ function displayCapabilityCatalog(templates, options = {}) {
1026
1148
 
1027
1149
  async function listCapabilityCatalog(options = {}) {
1028
1150
  const manager = new TemplateManager();
1029
- const templates = (await manager.listTemplates({
1151
+ const templates = filterCapabilityCatalogEntries((await manager.listTemplates({
1030
1152
  category: options.category,
1031
1153
  source: options.source,
1032
1154
  templateType: 'capability-template',
1033
1155
  compatibleWith: options.compatibleWith,
1034
1156
  riskLevel: options.risk
1035
- })).map((template) => enrichCapabilityTemplateForUi(template));
1157
+ })).map((template) => enrichCapabilityTemplateForUi(template)), options);
1036
1158
  if (normalizeBoolean(options.json, false)) {
1037
1159
  return {
1038
1160
  mode: 'capability-catalog-list',
@@ -1045,13 +1167,13 @@ async function listCapabilityCatalog(options = {}) {
1045
1167
 
1046
1168
  async function searchCapabilityCatalog(keyword, options = {}) {
1047
1169
  const manager = new TemplateManager();
1048
- const templates = (await manager.searchTemplates(keyword, {
1170
+ const templates = filterCapabilityCatalogEntries((await manager.searchTemplates(keyword, {
1049
1171
  category: options.category,
1050
1172
  source: options.source,
1051
1173
  templateType: 'capability-template',
1052
1174
  compatibleWith: options.compatibleWith,
1053
1175
  riskLevel: options.risk
1054
- })).map((template) => enrichCapabilityTemplateForUi(template));
1176
+ })).map((template) => enrichCapabilityTemplateForUi(template)), options);
1055
1177
  if (normalizeBoolean(options.json, false)) {
1056
1178
  return {
1057
1179
  mode: 'capability-catalog-search',
@@ -1291,6 +1413,34 @@ function registerCapabilityCommands(program) {
1291
1413
  });
1292
1414
  });
1293
1415
 
1416
+ capabilityCmd
1417
+ .command('inventory')
1418
+ .description('Build scene-level capability inventory for homepage views')
1419
+ .option('--scene <scene-id>', 'Single scene identifier')
1420
+ .option('--sample-limit <n>', 'Max tasks per spec in sample', '5')
1421
+ .option('--limit <n>', 'Max scenes to include')
1422
+ .option('--release-ready <bool>', 'Filter by publish readiness')
1423
+ .option('--missing-triad <name>', 'Filter by missing triad (entity_relation|business_rules|decision_strategy)')
1424
+ .option('--json', 'Output JSON to stdout')
1425
+ .action(async (options) => {
1426
+ try {
1427
+ const payload = await runCapabilityInventoryCommand({
1428
+ scene: options.scene,
1429
+ sample_limit: options.sampleLimit,
1430
+ limit: options.limit,
1431
+ releaseReady: options.releaseReady,
1432
+ missingTriad: options.missingTriad,
1433
+ json: options.json
1434
+ });
1435
+ if (options.json) {
1436
+ console.log(JSON.stringify(payload, null, 2));
1437
+ }
1438
+ } catch (error) {
1439
+ console.log();
1440
+ console.log(chalk.red('❌ Error:'), error.message);
1441
+ process.exit(1);
1442
+ }
1443
+ });
1294
1444
  capabilityCmd
1295
1445
  .command('score')
1296
1446
  .description('Score a capability candidate')
@@ -1361,6 +1511,8 @@ function registerCapabilityCommands(program) {
1361
1511
  .option('--category <name>', 'Template category filter')
1362
1512
  .option('--compatible-with <semver>', 'SCE version compatibility')
1363
1513
  .option('--risk <level>', 'Risk level filter')
1514
+ .option('--release-ready <bool>', 'Filter by publish readiness')
1515
+ .option('--missing-triad <name>', 'Filter by missing triad (entity_relation|business_rules|decision_strategy)')
1364
1516
  .option('--json', 'Output JSON to stdout')
1365
1517
  .action(async (options) => {
1366
1518
  try {
@@ -1369,6 +1521,8 @@ function registerCapabilityCommands(program) {
1369
1521
  category: options.category,
1370
1522
  compatibleWith: options.compatibleWith,
1371
1523
  risk: options.risk,
1524
+ releaseReady: options.releaseReady,
1525
+ missingTriad: options.missingTriad,
1372
1526
  json: options.json
1373
1527
  });
1374
1528
  if (options.json) {
@@ -1393,6 +1547,8 @@ function registerCapabilityCommands(program) {
1393
1547
  .option('--category <name>', 'Template category filter')
1394
1548
  .option('--compatible-with <semver>', 'SCE version compatibility')
1395
1549
  .option('--risk <level>', 'Risk level filter')
1550
+ .option('--release-ready <bool>', 'Filter by publish readiness')
1551
+ .option('--missing-triad <name>', 'Filter by missing triad (entity_relation|business_rules|decision_strategy)')
1396
1552
  .option('--json', 'Output JSON to stdout')
1397
1553
  .action(async (keyword, options) => {
1398
1554
  try {
@@ -1401,6 +1557,8 @@ function registerCapabilityCommands(program) {
1401
1557
  category: options.category,
1402
1558
  compatibleWith: options.compatibleWith,
1403
1559
  risk: options.risk,
1560
+ releaseReady: options.releaseReady,
1561
+ missingTriad: options.missingTriad,
1404
1562
  json: options.json
1405
1563
  });
1406
1564
  if (options.json) {
@@ -1494,11 +1652,14 @@ module.exports = {
1494
1652
  runCapabilityScoreCommand,
1495
1653
  runCapabilityMapCommand,
1496
1654
  runCapabilityRegisterCommand,
1655
+ runCapabilityInventoryCommand,
1497
1656
  listCapabilityCatalog,
1498
1657
  searchCapabilityCatalog,
1499
1658
  showCapabilityTemplate,
1500
1659
  matchCapabilityTemplates,
1501
1660
  useCapabilityTemplate,
1502
1661
  enrichCapabilityTemplateForUi,
1503
- buildCapabilityReleaseReadinessUi
1662
+ buildCapabilityReleaseReadinessUi,
1663
+ filterCapabilityCatalogEntries,
1664
+ filterCapabilityInventoryEntries
1504
1665
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scene-capability-engine",
3
- "version": "3.6.20",
3
+ "version": "3.6.22",
4
4
  "description": "SCE (Scene Capability Engine) - A CLI tool and npm package for spec-driven development with AI coding assistants.",
5
5
  "main": "index.js",
6
6
  "bin": {