scene-capability-engine 3.6.28 → 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 +23 -0
- package/README.md +1 -1
- package/README.zh.md +1 -1
- package/docs/agent-runtime/magicball-contract-index.md +25 -0
- package/docs/agent-runtime/magicball-status.schema.json +33 -0
- package/docs/agent-runtime/magicball-task-feedback.schema.json +79 -0
- package/docs/agent-runtime/magicball-timeline-view.schema.json +51 -0
- package/docs/command-reference.md +5 -0
- package/docs/magicball-capability-iteration-ui.md +2 -0
- package/docs/magicball-capability-library.md +2 -0
- package/docs/magicball-task-feedback-timeline-guide.md +14 -0
- package/lib/auto/session-metrics.js +53 -0
- package/lib/capability/catalog-service.js +248 -0
- package/lib/capability/inventory-service.js +100 -0
- package/lib/commands/auto.js +1 -1
- package/lib/commands/capability.js +81 -466
- package/lib/commands/scene.js +1 -1
- package/lib/commands/studio.js +147 -582
- package/lib/commands/timeline.js +6 -82
- package/lib/magicball/capability-inventory-view-model.js +213 -0
- package/lib/magicball/status-language.js +29 -0
- package/lib/magicball/task-feedback-model.js +113 -0
- package/lib/magicball/timeline-view-model.js +95 -0
- package/lib/scene/doctor-feedback.js +3541 -0
- package/lib/studio/task-envelope.js +269 -0
- package/lib/studio/task-intent.js +149 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,29 @@ 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
|
+
|
|
27
|
+
## [3.6.29] - 2026-03-06
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
- Added shared Magicball schemas for status, task feedback, and timeline view contracts.
|
|
31
|
+
- Command reference and Magicball integration docs now reference the shared schemas explicitly.
|
|
32
|
+
|
|
10
33
|
## [3.6.28] - 2026-03-06
|
|
11
34
|
|
|
12
35
|
### Added
|
package/README.md
CHANGED
package/README.zh.md
CHANGED
|
@@ -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`
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://scene-capability-engine.dev/contracts/magicball-status.schema.json",
|
|
4
|
+
"title": "Magicball Shared Status Contract",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": [
|
|
7
|
+
"attention_level",
|
|
8
|
+
"status_tone",
|
|
9
|
+
"status_label",
|
|
10
|
+
"blocking_summary",
|
|
11
|
+
"recommended_action"
|
|
12
|
+
],
|
|
13
|
+
"properties": {
|
|
14
|
+
"attention_level": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"enum": ["critical", "high", "medium", "low"]
|
|
17
|
+
},
|
|
18
|
+
"status_tone": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"enum": ["danger", "warning", "info", "success"]
|
|
21
|
+
},
|
|
22
|
+
"status_label": {
|
|
23
|
+
"type": ["string", "null"]
|
|
24
|
+
},
|
|
25
|
+
"blocking_summary": {
|
|
26
|
+
"type": ["string", "null"]
|
|
27
|
+
},
|
|
28
|
+
"recommended_action": {
|
|
29
|
+
"type": ["string", "null"]
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"additionalProperties": false
|
|
33
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://scene-capability-engine.dev/contracts/magicball-task-feedback.schema.json",
|
|
4
|
+
"title": "Magicball Task Feedback View Contract",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": [
|
|
7
|
+
"version",
|
|
8
|
+
"problem",
|
|
9
|
+
"execution",
|
|
10
|
+
"diagnosis",
|
|
11
|
+
"evidence",
|
|
12
|
+
"next_step",
|
|
13
|
+
"mb_status"
|
|
14
|
+
],
|
|
15
|
+
"properties": {
|
|
16
|
+
"version": {
|
|
17
|
+
"type": "string"
|
|
18
|
+
},
|
|
19
|
+
"problem": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"required": ["component", "action", "expected", "actual"],
|
|
22
|
+
"properties": {
|
|
23
|
+
"component": { "type": ["string", "null"] },
|
|
24
|
+
"action": { "type": ["string", "null"] },
|
|
25
|
+
"expected": { "type": ["string", "null"] },
|
|
26
|
+
"actual": { "type": ["string", "null"] }
|
|
27
|
+
},
|
|
28
|
+
"additionalProperties": false
|
|
29
|
+
},
|
|
30
|
+
"execution": {
|
|
31
|
+
"type": "object",
|
|
32
|
+
"required": ["stage", "status", "summary", "blocking_summary"],
|
|
33
|
+
"properties": {
|
|
34
|
+
"stage": { "type": "string" },
|
|
35
|
+
"status": { "type": "string" },
|
|
36
|
+
"summary": { "type": "array", "items": { "type": "string" } },
|
|
37
|
+
"blocking_summary": { "type": ["string", "null"] }
|
|
38
|
+
},
|
|
39
|
+
"additionalProperties": false
|
|
40
|
+
},
|
|
41
|
+
"diagnosis": {
|
|
42
|
+
"type": "object",
|
|
43
|
+
"required": ["hypothesis", "chain_checkpoint", "root_cause_confidence"],
|
|
44
|
+
"properties": {
|
|
45
|
+
"hypothesis": { "type": ["string", "null"] },
|
|
46
|
+
"chain_checkpoint": { "type": "string" },
|
|
47
|
+
"root_cause_confidence": { "type": "string", "enum": ["low", "medium", "high"] }
|
|
48
|
+
},
|
|
49
|
+
"additionalProperties": false
|
|
50
|
+
},
|
|
51
|
+
"evidence": {
|
|
52
|
+
"type": "object",
|
|
53
|
+
"required": ["file_count", "file_paths", "command_count", "error_count", "verification_result", "regression_scope"],
|
|
54
|
+
"properties": {
|
|
55
|
+
"file_count": { "type": "integer", "minimum": 0 },
|
|
56
|
+
"file_paths": { "type": "array", "items": { "type": "string" } },
|
|
57
|
+
"command_count": { "type": "integer", "minimum": 0 },
|
|
58
|
+
"error_count": { "type": "integer", "minimum": 0 },
|
|
59
|
+
"verification_result": { "type": "string" },
|
|
60
|
+
"regression_scope": { "type": "array", "items": { "type": "string" } }
|
|
61
|
+
},
|
|
62
|
+
"additionalProperties": false
|
|
63
|
+
},
|
|
64
|
+
"next_step": {
|
|
65
|
+
"type": "object",
|
|
66
|
+
"required": ["recommended_action", "next_action", "next_command"],
|
|
67
|
+
"properties": {
|
|
68
|
+
"recommended_action": { "type": ["string", "null"] },
|
|
69
|
+
"next_action": { "type": ["string", "null"] },
|
|
70
|
+
"next_command": { "type": ["string", "null"] }
|
|
71
|
+
},
|
|
72
|
+
"additionalProperties": false
|
|
73
|
+
},
|
|
74
|
+
"mb_status": {
|
|
75
|
+
"$ref": "./magicball-status.schema.json"
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
"additionalProperties": false
|
|
79
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://scene-capability-engine.dev/contracts/magicball-timeline-view.schema.json",
|
|
4
|
+
"title": "Magicball Timeline View Contract",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["summary", "entries"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"summary": {
|
|
9
|
+
"type": "object",
|
|
10
|
+
"required": ["total", "latest_snapshot_id", "latest_created_at", "dirty_snapshot_count", "scene_count", "trigger_counts"],
|
|
11
|
+
"properties": {
|
|
12
|
+
"total": { "type": "integer", "minimum": 0 },
|
|
13
|
+
"latest_snapshot_id": { "type": ["string", "null"] },
|
|
14
|
+
"latest_created_at": { "type": ["string", "null"] },
|
|
15
|
+
"dirty_snapshot_count": { "type": "integer", "minimum": 0 },
|
|
16
|
+
"scene_count": { "type": "integer", "minimum": 0 },
|
|
17
|
+
"trigger_counts": {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"additionalProperties": { "type": "integer", "minimum": 0 }
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"additionalProperties": false
|
|
23
|
+
},
|
|
24
|
+
"entries": {
|
|
25
|
+
"type": "array",
|
|
26
|
+
"items": {
|
|
27
|
+
"type": "object",
|
|
28
|
+
"required": ["snapshot_id", "title", "subtitle", "trigger", "event", "created_at", "scene_id", "session_id", "file_count", "branch", "head", "dirty_count", "attention_level", "show_command", "restore_command"],
|
|
29
|
+
"properties": {
|
|
30
|
+
"snapshot_id": { "type": ["string", "null"] },
|
|
31
|
+
"title": { "type": ["string", "null"] },
|
|
32
|
+
"subtitle": { "type": ["string", "null"] },
|
|
33
|
+
"trigger": { "type": ["string", "null"] },
|
|
34
|
+
"event": { "type": ["string", "null"] },
|
|
35
|
+
"created_at": { "type": ["string", "null"] },
|
|
36
|
+
"scene_id": { "type": ["string", "null"] },
|
|
37
|
+
"session_id": { "type": ["string", "null"] },
|
|
38
|
+
"file_count": { "type": "integer", "minimum": 0 },
|
|
39
|
+
"branch": { "type": ["string", "null"] },
|
|
40
|
+
"head": { "type": ["string", "null"] },
|
|
41
|
+
"dirty_count": { "type": "integer", "minimum": 0 },
|
|
42
|
+
"attention_level": { "type": "string", "enum": ["high", "medium", "low"] },
|
|
43
|
+
"show_command": { "type": ["string", "null"] },
|
|
44
|
+
"restore_command": { "type": ["string", "null"] }
|
|
45
|
+
},
|
|
46
|
+
"additionalProperties": false
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"additionalProperties": false
|
|
51
|
+
}
|
|
@@ -1902,6 +1902,11 @@ sce capability register --input .sce/reports/capability-iteration/scene.customer
|
|
|
1902
1902
|
|
|
1903
1903
|
Schema references:
|
|
1904
1904
|
- UI contract: `docs/agent-runtime/capability-iteration-ui.schema.json`
|
|
1905
|
+
- Magicball shared status: `docs/agent-runtime/magicball-status.schema.json`
|
|
1906
|
+
- Magicball task feedback: `docs/agent-runtime/magicball-task-feedback.schema.json`
|
|
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.
|
|
1905
1910
|
- Ontology mapping: `docs/ontology/capability-mapping.schema.json`
|
|
1906
1911
|
|
|
1907
1912
|
### Capability Library Reuse (query -> match -> use)
|
|
@@ -145,6 +145,8 @@ sce capability register --input <template.json> --json
|
|
|
145
145
|
## 5. 数据契约(前端对接)
|
|
146
146
|
|
|
147
147
|
- UI 契约:`docs/agent-runtime/capability-iteration-ui.schema.json`
|
|
148
|
+
- 统一状态语言:`docs/agent-runtime/magicball-status.schema.json`
|
|
149
|
+
- 契约索引:`docs/agent-runtime/magicball-contract-index.md`
|
|
148
150
|
- 本体映射 schema:`docs/ontology/capability-mapping.schema.json`
|
|
149
151
|
|
|
150
152
|
---
|
|
@@ -4,6 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
## 1. 任务反馈模型(来源:`sce studio events --json`)
|
|
6
6
|
|
|
7
|
+
Schema references:
|
|
8
|
+
- `docs/agent-runtime/magicball-status.schema.json`
|
|
9
|
+
- `docs/agent-runtime/magicball-task-feedback.schema.json`
|
|
10
|
+
- `docs/agent-runtime/magicball-timeline-view.schema.json`
|
|
11
|
+
- `docs/agent-runtime/magicball-contract-index.md`
|
|
12
|
+
|
|
13
|
+
|
|
7
14
|
SCE 现在在 `task` 下增加:
|
|
8
15
|
- `feedback_model.version`
|
|
9
16
|
- `feedback_model.problem`
|
|
@@ -132,3 +139,10 @@ SCE 现在会在任务反馈模型中提供 `mb_status`:
|
|
|
132
139
|
- `recommended_action`
|
|
133
140
|
|
|
134
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
|
+
};
|