scene-capability-engine 3.6.21 → 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,9 +7,16 @@ 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
+
10
16
  ## [3.6.21] - 2026-03-06
11
17
 
12
18
  ### Added
19
+ - New `sce capability inventory` command provides scene-level triad/readiness aggregation for homepage views.
13
20
  - Capability catalog list/search now supports `--release-ready` and `--missing-triad` filters for pre-publish triage.
14
21
  - Magicball capability library docs now show list-level triage filter examples.
15
22
 
package/README.md CHANGED
@@ -218,5 +218,5 @@ MIT. See [LICENSE](LICENSE).
218
218
 
219
219
  ---
220
220
 
221
- **Version**: 3.6.21
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.21
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
@@ -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
  ```
@@ -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
+ }
838
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
+ }
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;
@@ -1319,6 +1413,34 @@ function registerCapabilityCommands(program) {
1319
1413
  });
1320
1414
  });
1321
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
+ });
1322
1444
  capabilityCmd
1323
1445
  .command('score')
1324
1446
  .description('Score a capability candidate')
@@ -1530,6 +1652,7 @@ module.exports = {
1530
1652
  runCapabilityScoreCommand,
1531
1653
  runCapabilityMapCommand,
1532
1654
  runCapabilityRegisterCommand,
1655
+ runCapabilityInventoryCommand,
1533
1656
  listCapabilityCatalog,
1534
1657
  searchCapabilityCatalog,
1535
1658
  showCapabilityTemplate,
@@ -1537,5 +1660,6 @@ module.exports = {
1537
1660
  useCapabilityTemplate,
1538
1661
  enrichCapabilityTemplateForUi,
1539
1662
  buildCapabilityReleaseReadinessUi,
1540
- filterCapabilityCatalogEntries
1663
+ filterCapabilityCatalogEntries,
1664
+ filterCapabilityInventoryEntries
1541
1665
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scene-capability-engine",
3
- "version": "3.6.21",
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": {