scene-capability-engine 3.6.29 → 3.6.32

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,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.32] - 2026-03-07
11
+
12
+ ### Fixed
13
+ - Restored auto session/governance retention and stats CLI paths after refactor, including full integration coverage for version/legacy/takeover/auto flows.
14
+
15
+ ## [3.6.31] - 2026-03-07
16
+
17
+ ### Added
18
+ - Continued refactor pass: extracted command-adjacent Magicball, capability, studio, scene, and auto helper/service modules to reduce top-heavy command files.
19
+ - Added Magicball contract index to make schema discovery and frontend adaptation deterministic.
20
+
21
+ ## [3.6.30] - 2026-03-07
22
+
23
+ ### Added
24
+ - Continued internal refactor: extracted shared Magicball modules, capability services, studio task envelope/intents, scene doctor helpers, and auto session metrics helpers.
25
+ - Added Magicball contract index for schema/discovery entrypoint.
26
+
10
27
  ## [3.6.29] - 2026-03-06
11
28
 
12
29
  ### Added
package/README.md CHANGED
@@ -218,5 +218,5 @@ MIT. See [LICENSE](LICENSE).
218
218
 
219
219
  ---
220
220
 
221
- **Version**: 3.6.29
221
+ **Version**: 3.6.32
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.29
221
+ **版本**:3.6.32
222
222
  **最后更新**:2026-03-05
@@ -0,0 +1,25 @@
1
+ # Magicball Contract Index
2
+
3
+ Schema references:
4
+ - `docs/agent-runtime/magicball-status.schema.json`
5
+ - `docs/agent-runtime/magicball-task-feedback.schema.json`
6
+ - `docs/agent-runtime/magicball-timeline-view.schema.json`
7
+ - `docs/agent-runtime/capability-iteration-ui.schema.json`
8
+
9
+ Recommended consumption order:
10
+ 1. `magicball-status.schema.json`
11
+ 2. `magicball-task-feedback.schema.json`
12
+ 3. `magicball-timeline-view.schema.json`
13
+ 4. `capability-iteration-ui.schema.json`
14
+
15
+ Usage mapping:
16
+ - Task cards: `task.feedback_model`
17
+ - Timeline panels: `timeline list/show -> view_model`
18
+ - Capability inventory homepage: `capability inventory`
19
+
20
+ Implementation modules:
21
+ - `lib/magicball/status-language.js`
22
+ - `lib/magicball/task-feedback-model.js`
23
+ - `lib/magicball/capability-inventory-view-model.js`
24
+ - `lib/magicball/timeline-view-model.js`
25
+ - `lib/capability/inventory-service.js`
@@ -1905,6 +1905,8 @@ Schema references:
1905
1905
  - Magicball shared status: `docs/agent-runtime/magicball-status.schema.json`
1906
1906
  - Magicball task feedback: `docs/agent-runtime/magicball-task-feedback.schema.json`
1907
1907
  - Magicball timeline view: `docs/agent-runtime/magicball-timeline-view.schema.json`
1908
+ - Magicball contract index: `docs/agent-runtime/magicball-contract-index.md`
1909
+ Implementation modules are listed in the contract index for internal maintainers.
1908
1910
  - Ontology mapping: `docs/ontology/capability-mapping.schema.json`
1909
1911
 
1910
1912
  ### Capability Library Reuse (query -> match -> use)
@@ -146,6 +146,7 @@ sce capability register --input <template.json> --json
146
146
 
147
147
  - UI 契约:`docs/agent-runtime/capability-iteration-ui.schema.json`
148
148
  - 统一状态语言:`docs/agent-runtime/magicball-status.schema.json`
149
+ - 契约索引:`docs/agent-runtime/magicball-contract-index.md`
149
150
  - 本体映射 schema:`docs/ontology/capability-mapping.schema.json`
150
151
 
151
152
  ---
@@ -8,6 +8,7 @@ Schema references:
8
8
  - `docs/agent-runtime/magicball-status.schema.json`
9
9
  - `docs/agent-runtime/magicball-task-feedback.schema.json`
10
10
  - `docs/agent-runtime/magicball-timeline-view.schema.json`
11
+ - `docs/agent-runtime/magicball-contract-index.md`
11
12
 
12
13
 
13
14
  SCE 现在在 `task` 下增加:
@@ -138,3 +139,10 @@ SCE 现在会在任务反馈模型中提供 `mb_status`:
138
139
  - `recommended_action`
139
140
 
140
141
  Magicball 可直接用这组字段控制颜色、图标、提示文案。
142
+
143
+ ## 7. 维护说明
144
+
145
+ - 共享实现位于 `lib/magicball/*`
146
+ - `studio` 命令只负责任务流编排与事件产出
147
+ - `timeline` 命令只负责快照读写与 view model 挂载
148
+ - 共享契约入口:`docs/agent-runtime/magicball-contract-index.md`
@@ -0,0 +1,53 @@
1
+ function buildStatusCounts(entries = [], normalizeStatusToken = (value) => value) {
2
+ const counts = {};
3
+ const safeEntries = Array.isArray(entries) ? entries : [];
4
+ for (const entry of safeEntries) {
5
+ const status = normalizeStatusToken(entry && entry.status) || 'unknown';
6
+ counts[status] = (counts[status] || 0) + 1;
7
+ }
8
+ return counts;
9
+ }
10
+
11
+ function buildQueueFormatCounts(entries = []) {
12
+ const counts = {};
13
+ const safeEntries = Array.isArray(entries) ? entries : [];
14
+ for (const entry of safeEntries) {
15
+ const format = String(entry && entry.queue_format ? entry.queue_format : '').trim().toLowerCase() || 'unknown';
16
+ counts[format] = (counts[format] || 0) + 1;
17
+ }
18
+ return counts;
19
+ }
20
+
21
+ function buildMasterSpecCounts(entries = []) {
22
+ const counts = {};
23
+ const safeEntries = Array.isArray(entries) ? entries : [];
24
+ for (const entry of safeEntries) {
25
+ const masterSpec = String(entry && entry.master_spec ? entry.master_spec : '').trim();
26
+ if (!masterSpec) {
27
+ continue;
28
+ }
29
+ counts[masterSpec] = (counts[masterSpec] || 0) + 1;
30
+ }
31
+ return counts;
32
+ }
33
+
34
+ function buildTopCountEntries(counterMap, limit = 10) {
35
+ const source = counterMap && typeof counterMap === 'object' ? counterMap : {};
36
+ const maxItems = Number.isInteger(limit) && limit > 0 ? limit : 10;
37
+ return Object.entries(source)
38
+ .map(([key, count]) => ({ key, count: Number(count) || 0 }))
39
+ .sort((left, right) => {
40
+ if (right.count !== left.count) {
41
+ return right.count - left.count;
42
+ }
43
+ return left.key.localeCompare(right.key);
44
+ })
45
+ .slice(0, maxItems);
46
+ }
47
+
48
+ module.exports = {
49
+ buildStatusCounts,
50
+ buildQueueFormatCounts,
51
+ buildMasterSpecCounts,
52
+ buildTopCountEntries
53
+ };
@@ -0,0 +1,248 @@
1
+ const path = require('path');
2
+ const fsExtra = require('fs-extra');
3
+ const TemplateManager = require('../templates/template-manager');
4
+
5
+ function normalizeText(value) {
6
+ if (typeof value !== 'string') {
7
+ return '';
8
+ }
9
+ return value.trim();
10
+ }
11
+
12
+ function normalizeBoolean(value, fallback = false) {
13
+ if (typeof value === 'boolean') {
14
+ return value;
15
+ }
16
+ const normalized = normalizeText(String(value || '')).toLowerCase();
17
+ if (!normalized) {
18
+ return fallback;
19
+ }
20
+ if (['1', 'true', 'yes', 'y', 'on'].includes(normalized)) {
21
+ return true;
22
+ }
23
+ if (['0', 'false', 'no', 'n', 'off'].includes(normalized)) {
24
+ return false;
25
+ }
26
+ return fallback;
27
+ }
28
+
29
+ function toPositiveInteger(value, fallback) {
30
+ const parsed = Number.parseInt(String(value), 10);
31
+ if (!Number.isFinite(parsed) || parsed <= 0) {
32
+ return fallback;
33
+ }
34
+ return parsed;
35
+ }
36
+
37
+ async function listCapabilityCatalogService(options = {}, dependencies = {}) {
38
+ const manager = dependencies.manager || new TemplateManager();
39
+ const templates = dependencies.filterCapabilityCatalogEntries((await manager.listTemplates({
40
+ category: options.category,
41
+ source: options.source,
42
+ templateType: 'capability-template',
43
+ compatibleWith: options.compatibleWith,
44
+ riskLevel: options.risk
45
+ })).map((template) => dependencies.enrichCapabilityTemplateForUi(template)), options);
46
+ return {
47
+ mode: 'capability-catalog-list',
48
+ templates
49
+ };
50
+ }
51
+
52
+ async function searchCapabilityCatalogService(keyword, options = {}, dependencies = {}) {
53
+ const manager = dependencies.manager || new TemplateManager();
54
+ const templates = dependencies.filterCapabilityCatalogEntries((await manager.searchTemplates(keyword, {
55
+ category: options.category,
56
+ source: options.source,
57
+ templateType: 'capability-template',
58
+ compatibleWith: options.compatibleWith,
59
+ riskLevel: options.risk
60
+ })).map((template) => dependencies.enrichCapabilityTemplateForUi(template)), options);
61
+ return {
62
+ mode: 'capability-catalog-search',
63
+ keyword,
64
+ templates
65
+ };
66
+ }
67
+
68
+ async function showCapabilityTemplateService(templatePath, options = {}, dependencies = {}) {
69
+ const manager = dependencies.manager || new TemplateManager();
70
+ const template = dependencies.enrichCapabilityTemplateForUi(await manager.showTemplate(templatePath));
71
+ const parsed = dependencies.parseTemplatePath(templatePath);
72
+ await manager.ensureCached(parsed.sourceName);
73
+ const sourcePath = manager.cacheManager.getSourceCachePath(parsed.sourceName);
74
+ const templateDir = path.join(sourcePath, parsed.templateId);
75
+ const capabilityFile = path.join(templateDir, 'capability-template.json');
76
+ let templatePayload = null;
77
+ if (await fsExtra.pathExists(capabilityFile)) {
78
+ try {
79
+ templatePayload = await fsExtra.readJson(capabilityFile);
80
+ } catch (_error) {
81
+ templatePayload = null;
82
+ }
83
+ }
84
+ return {
85
+ mode: 'capability-catalog-show',
86
+ template,
87
+ template_file: await fsExtra.pathExists(capabilityFile) ? capabilityFile : null,
88
+ payload: templatePayload
89
+ };
90
+ }
91
+
92
+ async function matchCapabilityTemplatesService(options = {}, dependencies = {}) {
93
+ const projectPath = options.projectPath || process.cwd();
94
+ const fileSystem = options.fileSystem || fsExtra;
95
+ const specId = normalizeText(options.spec || options.specId);
96
+ if (!specId) {
97
+ throw new Error('spec is required for capability match');
98
+ }
99
+ const chain = await dependencies.loadSpecDomainChain(projectPath, specId, fileSystem);
100
+ if (!chain.exists && normalizeBoolean(options.strict, false)) {
101
+ throw new Error('problem-domain-chain missing for spec ' + specId);
102
+ }
103
+ if (chain.error && normalizeBoolean(options.strict, false)) {
104
+ throw new Error('problem-domain-chain invalid: ' + chain.error);
105
+ }
106
+ const domainChain = chain.payload || {};
107
+ const specScope = dependencies.buildOntologyScopeFromChain(domainChain);
108
+ const queryTokens = dependencies.normalizeTokenList(options.query)
109
+ .concat(dependencies.normalizeTokenList(domainChain.problem && domainChain.problem.statement))
110
+ .concat(dependencies.normalizeTokenList(domainChain.scene_id));
111
+ const manager = dependencies.manager || new TemplateManager();
112
+ const templates = await manager.listTemplates({
113
+ source: options.source,
114
+ templateType: 'capability-template',
115
+ compatibleWith: options.compatibleWith,
116
+ riskLevel: options.risk
117
+ });
118
+ const matches = templates.map((template) => {
119
+ const overlap = dependencies.buildOntologyOverlap(specScope, template.ontology_scope || {});
120
+ const scenarioScore = template.applicable_scenarios && domainChain.scene_id
121
+ ? (template.applicable_scenarios.includes(domainChain.scene_id) ? 1 : 0)
122
+ : 0;
123
+ const keywordScore = dependencies.buildKeywordScore(template, queryTokens);
124
+ const totalScore = (overlap.score * 0.6) + (scenarioScore * 0.2) + (keywordScore * 0.2);
125
+ const ontologyCore = template.ontology_core || dependencies.buildCoreOntologySummary(template.ontology_scope || {});
126
+ return {
127
+ template_id: template.id,
128
+ source: template.source,
129
+ name: template.name,
130
+ description: template.description,
131
+ category: template.category,
132
+ risk_level: template.risk_level,
133
+ ontology_core: ontologyCore,
134
+ ontology_core_ui: dependencies.buildOntologyCoreUiState(ontologyCore),
135
+ score: Math.round(totalScore * 100),
136
+ score_components: {
137
+ ontology: Number(overlap.score.toFixed(3)),
138
+ scenario: scenarioScore,
139
+ keyword: Number(keywordScore.toFixed(3))
140
+ },
141
+ overlap
142
+ };
143
+ }).sort((a, b) => b.score - a.score);
144
+
145
+ const limit = toPositiveInteger(options.limit, 10);
146
+ return {
147
+ mode: 'capability-match',
148
+ spec_id: specId,
149
+ scene_id: domainChain.scene_id || null,
150
+ query: normalizeText(options.query) || null,
151
+ ontology_source: chain.exists ? chain.path : null,
152
+ match_count: matches.length,
153
+ matches: matches.slice(0, limit),
154
+ warnings: chain.exists ? [] : ['problem-domain-chain missing; ontology-based match unavailable']
155
+ };
156
+ }
157
+
158
+ async function useCapabilityTemplateService(options = {}, dependencies = {}) {
159
+ const projectPath = options.projectPath || process.cwd();
160
+ const fileSystem = options.fileSystem || fsExtra;
161
+ const templateId = normalizeText(options.template || options.id);
162
+ if (!templateId) {
163
+ throw new Error('template is required for capability use');
164
+ }
165
+ if (normalizeBoolean(options.apply, false) && normalizeBoolean(options.write, true) === false) {
166
+ throw new Error('cannot use --apply with --no-write');
167
+ }
168
+ const specId = normalizeText(options.spec || options.specId) || null;
169
+ const manager = dependencies.manager || new TemplateManager();
170
+ const template = await manager.showTemplate(templateId);
171
+ const parsed = dependencies.parseTemplatePath(templateId);
172
+ await manager.ensureCached(parsed.sourceName);
173
+ const sourcePath = manager.cacheManager.getSourceCachePath(parsed.sourceName);
174
+ const templateDir = path.join(sourcePath, parsed.templateId);
175
+ const capabilityFile = path.join(templateDir, 'capability-template.json');
176
+ let templatePayload = null;
177
+ if (await fileSystem.pathExists(capabilityFile)) {
178
+ try {
179
+ templatePayload = await fileSystem.readJson(capabilityFile);
180
+ } catch (_error) {
181
+ templatePayload = null;
182
+ }
183
+ }
184
+
185
+ const recommendedTasks = [];
186
+ if (templatePayload && templatePayload.source_candidate && Array.isArray(templatePayload.source_candidate.specs)) {
187
+ templatePayload.source_candidate.specs.forEach((spec) => {
188
+ const sample = Array.isArray(spec.task_sample) ? spec.task_sample : [];
189
+ sample.forEach((task) => {
190
+ if (task && task.title) {
191
+ recommendedTasks.push({
192
+ title: task.title,
193
+ source_spec_id: spec.spec_id || null,
194
+ source_task_id: task.id || null
195
+ });
196
+ }
197
+ });
198
+ });
199
+ }
200
+ if (recommendedTasks.length === 0) {
201
+ recommendedTasks.push({ title: 'Implement capability scope: ' + (template.name || parsed.templateId) });
202
+ }
203
+
204
+ const ontologyCore = template.ontology_core || dependencies.buildCoreOntologySummary(template.ontology_scope || {});
205
+ const plan = {
206
+ mode: 'capability-use-plan',
207
+ generated_at: new Date().toISOString(),
208
+ template: {
209
+ id: template.id,
210
+ name: template.name,
211
+ source: template.source,
212
+ description: template.description,
213
+ ontology_scope: template.ontology_scope || {},
214
+ ontology_core: ontologyCore,
215
+ ontology_core_ui: dependencies.buildOntologyCoreUiState(ontologyCore)
216
+ },
217
+ spec_id: specId,
218
+ recommended_tasks: recommendedTasks
219
+ };
220
+
221
+ const outputPath = normalizeText(options.out) || dependencies.buildDefaultUsePlanPath(specId || 'spec', template.id);
222
+ if (normalizeBoolean(options.write, true)) {
223
+ await fileSystem.ensureDir(path.dirname(path.join(projectPath, outputPath)));
224
+ await fileSystem.writeJson(path.join(projectPath, outputPath), plan, { spaces: 2 });
225
+ plan.output_file = outputPath;
226
+ }
227
+
228
+ if (normalizeBoolean(options.apply, false)) {
229
+ if (!specId) {
230
+ throw new Error('spec is required for --apply');
231
+ }
232
+ plan.apply = await dependencies.appendCapabilityPlanToSpecTasks({
233
+ projectPath,
234
+ spec: specId,
235
+ sectionTitle: options.sectionTitle
236
+ }, plan, fileSystem);
237
+ }
238
+
239
+ return plan;
240
+ }
241
+
242
+ module.exports = {
243
+ listCapabilityCatalogService,
244
+ searchCapabilityCatalogService,
245
+ showCapabilityTemplateService,
246
+ matchCapabilityTemplatesService,
247
+ useCapabilityTemplateService
248
+ };
@@ -0,0 +1,100 @@
1
+ function normalizeText(value) {
2
+ if (typeof value !== 'string') {
3
+ return '';
4
+ }
5
+ return value.trim();
6
+ }
7
+
8
+ function normalizeBoolean(value, fallback = false) {
9
+ if (typeof value === 'boolean') {
10
+ return value;
11
+ }
12
+ const normalized = normalizeText(String(value || '')).toLowerCase();
13
+ if (!normalized) {
14
+ return fallback;
15
+ }
16
+ if (['1', 'true', 'yes', 'y', 'on'].includes(normalized)) {
17
+ return true;
18
+ }
19
+ if (['0', 'false', 'no', 'n', 'off'].includes(normalized)) {
20
+ return false;
21
+ }
22
+ return fallback;
23
+ }
24
+
25
+ function toPositiveInteger(value, fallback) {
26
+ const parsed = Number.parseInt(String(value), 10);
27
+ if (!Number.isFinite(parsed) || parsed <= 0) {
28
+ return fallback;
29
+ }
30
+ return parsed;
31
+ }
32
+
33
+ async function runCapabilityInventoryService(options = {}, dependencies = {}) {
34
+ const sceneIds = Array.isArray(dependencies.sceneIds) ? dependencies.sceneIds : [];
35
+ const limit = toPositiveInteger(options.limit, sceneIds.length || 20);
36
+ const scenes = [];
37
+
38
+ for (const sceneId of sceneIds.slice(0, limit)) {
39
+ const candidate = await dependencies.buildCandidatePayload(sceneId, {
40
+ specs: options.specs,
41
+ sample_limit: options.sample_limit
42
+ }, dependencies.runtime || {});
43
+ const score = dependencies.buildScoreFromCandidate(candidate);
44
+ const releaseReadiness = dependencies.buildCapabilityReleaseReadiness({
45
+ scene_id: sceneId,
46
+ ontology_scope: candidate.ontology_scope,
47
+ ontology_core: candidate.ontology_core
48
+ });
49
+ const sceneEntry = {
50
+ scene_id: sceneId,
51
+ summary: candidate.summary,
52
+ source: candidate.source,
53
+ ontology_scope: candidate.ontology_scope,
54
+ ontology_core: candidate.ontology_core,
55
+ ontology_core_ui: dependencies.buildOntologyCoreUiState(candidate.ontology_core),
56
+ release_readiness: releaseReadiness,
57
+ release_readiness_ui: dependencies.buildCapabilityReleaseReadinessUi(releaseReadiness),
58
+ score_preview: score
59
+ };
60
+ scenes.push({
61
+ ...sceneEntry,
62
+ ...dependencies.buildCapabilityInventorySceneAdvice(sceneEntry)
63
+ });
64
+ }
65
+
66
+ const filteredScenes = dependencies.sortCapabilityInventoryEntries(
67
+ dependencies.filterCapabilityInventoryEntries(scenes, options)
68
+ );
69
+ const summaryStats = dependencies.buildCapabilityInventorySummaryStats(filteredScenes);
70
+ const releaseReadyFilterRaw = normalizeText(options.releaseReady || options.release_ready).toLowerCase();
71
+
72
+ return {
73
+ mode: 'capability-inventory',
74
+ generated_at: new Date().toISOString(),
75
+ query: {
76
+ protocol_version: '1.0',
77
+ scene_id: normalizeText(options.scene || options.sceneId || options.scene_id) || null,
78
+ limit,
79
+ sample_limit: toPositiveInteger(options.sample_limit, 5),
80
+ filters: {
81
+ release_ready: releaseReadyFilterRaw ? ['1', 'true', 'yes', 'ready'].includes(releaseReadyFilterRaw) : null,
82
+ missing_triad: normalizeText(options.missingTriad || options.missing_triad) || null
83
+ }
84
+ },
85
+ scene_total: scenes.length,
86
+ scene_count: filteredScenes.length,
87
+ summary_stats: summaryStats,
88
+ summary_recommendations: dependencies.buildCapabilityInventorySummaryRecommendations(filteredScenes),
89
+ quick_filters: dependencies.buildCapabilityInventoryQuickFilters(summaryStats),
90
+ sort: {
91
+ strategy: 'publish_ready -> missing_triad_priority -> value_score_desc -> scene_id',
92
+ triad_priority: ['decision_strategy', 'business_rules', 'entity_relation']
93
+ },
94
+ scenes: filteredScenes
95
+ };
96
+ }
97
+
98
+ module.exports = {
99
+ runCapabilityInventoryService
100
+ };
@@ -1,4 +1,4 @@
1
- /**
1
+ /**
2
2
  * Autonomous Control CLI Commands
3
3
  */
4
4