scene-capability-engine 3.6.21 → 3.6.23

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,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [3.6.23] - 2026-03-06
11
+
12
+ ### Added
13
+ - Capability inventory now applies a default homepage sort: blocked scenes first, then triad-gap priority, then value score.
14
+ - Magicball capability iteration docs now define the default scene inventory ordering contract.
15
+
16
+ ## [3.6.22] - 2026-03-06
17
+
18
+ ### Added
19
+ - New `sce capability inventory` command provides scene-level triad/readiness aggregation for homepage views.
20
+ - Magicball capability iteration docs now define homepage inventory API and triage usage.
21
+
10
22
  ## [3.6.21] - 2026-03-06
11
23
 
12
24
  ### Added
25
+ - New `sce capability inventory` command provides scene-level triad/readiness aggregation for homepage views.
26
+ - Capability inventory now applies default sorting: publish-ready last, then triad-gap priority, then value score.
13
27
  - Capability catalog list/search now supports `--release-ready` and `--missing-triad` filters for pre-publish triage.
14
28
  - Magicball capability library docs now show list-level triage filter examples.
15
29
 
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.23
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.23
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
  ```
@@ -186,3 +192,10 @@ sce capability register --input <template.json> --json
186
192
  - 发布页直接消费 `release_readiness.ready`
187
193
  - 若为 `false`,展示 `blockers[].reason`、`blockers[].missing`、`blockers[].remediation`
188
194
  - 默认阻断文案:`能力模板未达到发布条件`
195
+
196
+ ## 10. 默认排序
197
+
198
+ - 先显示 `release_readiness_ui.publish_ready = false` 的 scene
199
+ - 再按 triad 缺口优先级排序:`decision_strategy` -> `business_rules` -> `entity_relation`
200
+ - 再按 `score_preview.value_score` 降序
201
+ - 最后按 `scene_id` 升序
@@ -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,181 @@ 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
+ }
838
822
 
823
+ function resolveCapabilityTriadPriority(entry) {
824
+ const missing = Array.isArray(entry && entry.release_readiness_ui && entry.release_readiness_ui.blocking_missing)
825
+ ? entry.release_readiness_ui.blocking_missing
826
+ : [];
827
+ if (missing.includes('decision_strategy')) {
828
+ return 0;
829
+ }
830
+ if (missing.includes('business_rules')) {
831
+ return 1;
832
+ }
833
+ if (missing.includes('entity_relation')) {
834
+ return 2;
835
+ }
836
+ return 3;
837
+ }
838
+
839
+ function sortCapabilityInventoryEntries(entries) {
840
+ return [...(Array.isArray(entries) ? entries : [])].sort((left, right) => {
841
+ const leftReady = Boolean(left && left.release_readiness_ui && left.release_readiness_ui.publish_ready);
842
+ const rightReady = Boolean(right && right.release_readiness_ui && right.release_readiness_ui.publish_ready);
843
+ if (leftReady !== rightReady) {
844
+ return leftReady ? 1 : -1;
845
+ }
846
+
847
+ const triadDelta = resolveCapabilityTriadPriority(left) - resolveCapabilityTriadPriority(right);
848
+ if (triadDelta !== 0) {
849
+ return triadDelta;
850
+ }
851
+
852
+ const leftValue = Number(left && left.score_preview && left.score_preview.value_score || 0);
853
+ const rightValue = Number(right && right.score_preview && right.score_preview.value_score || 0);
854
+ if (leftValue !== rightValue) {
855
+ return rightValue - leftValue;
856
+ }
857
+
858
+ return String(left && left.scene_id || '').localeCompare(String(right && right.scene_id || ''));
859
+ });
860
+ }
861
+
862
+ function filterCapabilityInventoryEntries(entries, options = {}) {
863
+ const normalizedMissingTriad = normalizeText(options.missingTriad || options.missing_triad).toLowerCase();
864
+ const releaseReadyFilter = normalizeText(options.releaseReady || options.release_ready).toLowerCase();
865
+ return (Array.isArray(entries) ? entries : []).filter((entry) => {
866
+ if (releaseReadyFilter) {
867
+ const expected = ['1', 'true', 'yes', 'ready'].includes(releaseReadyFilter);
868
+ if (Boolean(entry.release_readiness_ui && entry.release_readiness_ui.publish_ready) !== expected) {
869
+ return false;
870
+ }
871
+ }
872
+ if (normalizedMissingTriad) {
873
+ const missing = Array.isArray(entry.release_readiness_ui && entry.release_readiness_ui.blocking_missing)
874
+ ? entry.release_readiness_ui.blocking_missing
875
+ : [];
876
+ if (!missing.includes(normalizedMissingTriad)) {
877
+ return false;
878
+ }
879
+ }
880
+ return true;
881
+ });
882
+ }
883
+
884
+ async function runCapabilityInventoryCommand(options = {}, dependencies = {}) {
885
+ const projectPath = dependencies.projectPath || process.cwd();
886
+ const fileSystem = dependencies.fileSystem || fs;
887
+ const env = dependencies.env || process.env;
888
+ const sceneIds = await listCapabilityInventorySceneIds(options, { projectPath, fileSystem, env });
889
+ const limit = toPositiveInteger(options.limit, sceneIds.length || 20);
890
+ const scenes = [];
891
+
892
+ for (const sceneId of sceneIds.slice(0, limit)) {
893
+ const candidate = await buildCapabilityCandidatePayload(sceneId, {
894
+ specs: options.specs,
895
+ sample_limit: options.sample_limit
896
+ }, dependencies);
897
+ const score = buildScoreFromCandidate(candidate);
898
+ const releaseReadiness = buildCapabilityReleaseReadiness({
899
+ scene_id: sceneId,
900
+ ontology_scope: candidate.ontology_scope,
901
+ ontology_core: candidate.ontology_core
902
+ });
903
+ scenes.push({
904
+ scene_id: sceneId,
905
+ summary: candidate.summary,
906
+ source: candidate.source,
907
+ ontology_scope: candidate.ontology_scope,
908
+ ontology_core: candidate.ontology_core,
909
+ ontology_core_ui: buildOntologyCoreUiState(candidate.ontology_core),
910
+ release_readiness: releaseReadiness,
911
+ release_readiness_ui: buildCapabilityReleaseReadinessUi(releaseReadiness),
912
+ score_preview: score
913
+ });
914
+ }
915
+
916
+ const filteredScenes = sortCapabilityInventoryEntries(filterCapabilityInventoryEntries(scenes, options));
917
+ const payload = {
918
+ mode: 'capability-inventory',
919
+ generated_at: new Date().toISOString(),
920
+ scene_count: filteredScenes.length,
921
+ sort: {
922
+ strategy: 'publish_ready -> missing_triad_priority -> value_score_desc -> scene_id',
923
+ triad_priority: ['decision_strategy', 'business_rules', 'entity_relation']
924
+ },
925
+ scenes: filteredScenes
926
+ };
927
+
928
+ if (normalizeBoolean(options.json, false)) {
929
+ return payload;
930
+ }
931
+ console.log(chalk.green('✅ Capability inventory generated'));
932
+ console.log(chalk.gray(' Scenes: ' + filteredScenes.length));
933
+ return payload;
934
+ }
935
+
936
+ function buildRegistryEntry(templateCandidate, options) {
937
+ const riskLevel = normalizeText(options && options.risk_level) || 'medium';
938
+ const difficulty = normalizeText(options && options.difficulty) || 'intermediate';
939
+ const applicable = normalizeStringArray(options && options.applicable_scenarios);
940
+ const tags = normalizeStringArray(options && options.tags);
941
+ const sceneId = buildSceneIdFromCandidate(templateCandidate);
942
+ const safeTags = tags.length > 0 ? tags : ['capability', sceneId];
943
+ const safeApplicable = applicable.length > 0 ? applicable : [sceneId];
944
+
945
+ return {
946
+ id: templateCandidate.template_id,
947
+ name: templateCandidate.name,
948
+ category: templateCandidate.category,
949
+ description: templateCandidate.description,
950
+ difficulty,
951
+ tags: safeTags,
952
+ applicable_scenarios: safeApplicable,
953
+ files: ['capability-template.json'],
954
+ template_type: 'capability-template',
955
+ min_sce_version: packageJson.version,
956
+ max_sce_version: null,
957
+ risk_level: riskLevel,
958
+ rollback_contract: {
959
+ supported: false,
960
+ strategy: 'n/a'
961
+ },
962
+ ontology_scope: templateCandidate.ontology_scope
963
+ };
964
+ }
965
+
966
+ async function runCapabilityExtractCommand(options = {}, dependencies = {}) {
967
+ const projectPath = dependencies.projectPath || process.cwd();
968
+ const fileSystem = dependencies.fileSystem || fs;
969
+ const sceneId = normalizeText(options.scene || options.sceneId || options.scene_id);
970
+ const writeOutput = normalizeBoolean(options.write, true);
971
+
972
+ if (!sceneId) {
973
+ throw new Error('scene is required for capability extract');
974
+ }
975
+
976
+ const payload = await buildCapabilityCandidatePayload(sceneId, options, dependencies);
839
977
  const outputPath = normalizeText(options.out) || buildDefaultCandidatePath(sceneId);
840
978
  if (writeOutput) {
841
979
  await fileSystem.ensureDir(path.dirname(path.join(projectPath, outputPath)));
@@ -845,17 +983,16 @@ async function runCapabilityExtractCommand(options = {}, dependencies = {}) {
845
983
 
846
984
  if (!normalizeBoolean(options.json, false)) {
847
985
  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}`));
986
+ console.log(chalk.gray(' Scene: ' + sceneId));
987
+ console.log(chalk.gray(' Specs: ' + payload.summary.spec_count));
988
+ console.log(chalk.gray(' Tasks: ' + payload.summary.task_total));
851
989
  if (payload.output_file) {
852
- console.log(chalk.gray(` Output: ${payload.output_file}`));
990
+ console.log(chalk.gray(' Output: ' + payload.output_file));
853
991
  }
854
992
  }
855
993
 
856
994
  return payload;
857
995
  }
858
-
859
996
  async function runCapabilityScoreCommand(options = {}, dependencies = {}) {
860
997
  const projectPath = dependencies.projectPath || process.cwd();
861
998
  const fileSystem = dependencies.fileSystem || fs;
@@ -1319,6 +1456,34 @@ function registerCapabilityCommands(program) {
1319
1456
  });
1320
1457
  });
1321
1458
 
1459
+ capabilityCmd
1460
+ .command('inventory')
1461
+ .description('Build scene-level capability inventory for homepage views')
1462
+ .option('--scene <scene-id>', 'Single scene identifier')
1463
+ .option('--sample-limit <n>', 'Max tasks per spec in sample', '5')
1464
+ .option('--limit <n>', 'Max scenes to include')
1465
+ .option('--release-ready <bool>', 'Filter by publish readiness')
1466
+ .option('--missing-triad <name>', 'Filter by missing triad (entity_relation|business_rules|decision_strategy)')
1467
+ .option('--json', 'Output JSON to stdout')
1468
+ .action(async (options) => {
1469
+ try {
1470
+ const payload = await runCapabilityInventoryCommand({
1471
+ scene: options.scene,
1472
+ sample_limit: options.sampleLimit,
1473
+ limit: options.limit,
1474
+ releaseReady: options.releaseReady,
1475
+ missingTriad: options.missingTriad,
1476
+ json: options.json
1477
+ });
1478
+ if (options.json) {
1479
+ console.log(JSON.stringify(payload, null, 2));
1480
+ }
1481
+ } catch (error) {
1482
+ console.log();
1483
+ console.log(chalk.red('❌ Error:'), error.message);
1484
+ process.exit(1);
1485
+ }
1486
+ });
1322
1487
  capabilityCmd
1323
1488
  .command('score')
1324
1489
  .description('Score a capability candidate')
@@ -1530,6 +1695,7 @@ module.exports = {
1530
1695
  runCapabilityScoreCommand,
1531
1696
  runCapabilityMapCommand,
1532
1697
  runCapabilityRegisterCommand,
1698
+ runCapabilityInventoryCommand,
1533
1699
  listCapabilityCatalog,
1534
1700
  searchCapabilityCatalog,
1535
1701
  showCapabilityTemplate,
@@ -1537,5 +1703,7 @@ module.exports = {
1537
1703
  useCapabilityTemplate,
1538
1704
  enrichCapabilityTemplateForUi,
1539
1705
  buildCapabilityReleaseReadinessUi,
1540
- filterCapabilityCatalogEntries
1706
+ filterCapabilityCatalogEntries,
1707
+ filterCapabilityInventoryEntries,
1708
+ sortCapabilityInventoryEntries
1541
1709
  };
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.23",
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": {