scene-capability-engine 3.6.10 → 3.6.13
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 +29 -0
- package/README.md +1 -1
- package/README.zh.md +1 -1
- package/docs/command-reference.md +40 -0
- package/docs/magicball-capability-library.md +117 -0
- package/docs/magicball-task-quality-governance.md +301 -0
- package/lib/commands/capability.js +490 -0
- package/lib/commands/task.js +271 -0
- package/lib/task/task-quality-policy.js +109 -0
- package/lib/task/task-quality.js +378 -0
- package/package.json +1 -1
- package/template/.sce/config/task-quality-policy.json +8 -0
|
@@ -11,7 +11,10 @@ const path = require('path');
|
|
|
11
11
|
const chalk = require('chalk');
|
|
12
12
|
const TaskClaimer = require('../task/task-claimer');
|
|
13
13
|
const { runStudioSpecGovernance } = require('../studio/spec-intake-governor');
|
|
14
|
+
const { DOMAIN_CHAIN_RELATIVE_PATH } = require('../spec/domain-modeling');
|
|
14
15
|
const { SceStateStore } = require('../state/sce-state-store');
|
|
16
|
+
const TemplateManager = require('../templates/template-manager');
|
|
17
|
+
const { TemplateError } = require('../templates/template-error');
|
|
15
18
|
const packageJson = require('../../package.json');
|
|
16
19
|
|
|
17
20
|
const DEFAULT_ITERATION_DIR = '.sce/reports/capability-iteration';
|
|
@@ -31,6 +34,20 @@ function normalizeStringArray(value) {
|
|
|
31
34
|
return value.map((item) => normalizeText(item)).filter(Boolean);
|
|
32
35
|
}
|
|
33
36
|
|
|
37
|
+
function normalizeTokenList(value) {
|
|
38
|
+
if (Array.isArray(value)) {
|
|
39
|
+
return normalizeStringArray(value).map((item) => item.toLowerCase());
|
|
40
|
+
}
|
|
41
|
+
const text = normalizeText(value);
|
|
42
|
+
if (!text) {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
return text
|
|
46
|
+
.split(/[^a-zA-Z0-9._-]+/g)
|
|
47
|
+
.map((item) => item.trim().toLowerCase())
|
|
48
|
+
.filter(Boolean);
|
|
49
|
+
}
|
|
50
|
+
|
|
34
51
|
function normalizeBoolean(value, fallback = false) {
|
|
35
52
|
if (typeof value === 'boolean') {
|
|
36
53
|
return value;
|
|
@@ -71,6 +88,12 @@ function buildDefaultTemplatePath(sceneId) {
|
|
|
71
88
|
return path.join(DEFAULT_ITERATION_DIR, `${safeScene}.template.json`);
|
|
72
89
|
}
|
|
73
90
|
|
|
91
|
+
function buildDefaultUsePlanPath(specId, templateId) {
|
|
92
|
+
const safeSpec = normalizeText(specId).replace(/[^\w.-]+/g, '_') || 'spec';
|
|
93
|
+
const safeTemplate = normalizeText(templateId).replace(/[^\w.-]+/g, '_') || 'template';
|
|
94
|
+
return path.join(DEFAULT_ITERATION_DIR, 'usage', `${safeSpec}.${safeTemplate}.plan.json`);
|
|
95
|
+
}
|
|
96
|
+
|
|
74
97
|
function buildDefaultExportDir(templateId) {
|
|
75
98
|
const safeId = normalizeText(templateId).replace(/[^\w.-]+/g, '_') || 'capability';
|
|
76
99
|
return path.join(DEFAULT_EXPORT_ROOT, `capability-${safeId}`);
|
|
@@ -80,6 +103,95 @@ function buildSceneIdFromCandidate(candidate) {
|
|
|
80
103
|
return normalizeText(candidate && candidate.scene_id) || 'scene.unknown';
|
|
81
104
|
}
|
|
82
105
|
|
|
106
|
+
function parseTemplatePath(templatePath) {
|
|
107
|
+
const normalized = normalizeText(templatePath);
|
|
108
|
+
if (!normalized) {
|
|
109
|
+
return { sourceName: 'official', templateId: '' };
|
|
110
|
+
}
|
|
111
|
+
if (normalized.includes(':')) {
|
|
112
|
+
const [sourceName, templateId] = normalized.split(':', 2);
|
|
113
|
+
return { sourceName: normalizeText(sourceName) || 'official', templateId: normalizeText(templateId) };
|
|
114
|
+
}
|
|
115
|
+
return { sourceName: 'official', templateId: normalized };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function buildOntologyScopeFromChain(domainChain) {
|
|
119
|
+
const ontology = domainChain && domainChain.ontology ? domainChain.ontology : {};
|
|
120
|
+
return {
|
|
121
|
+
domains: normalizeStringArray(domainChain && domainChain.scene_id ? [domainChain.scene_id] : []),
|
|
122
|
+
entities: normalizeStringArray(ontology.entity),
|
|
123
|
+
relations: normalizeStringArray(ontology.relation),
|
|
124
|
+
business_rules: normalizeStringArray(ontology.business_rule),
|
|
125
|
+
decisions: normalizeStringArray(ontology.decision_policy)
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function buildOntologyOverlap(specScope, templateScope) {
|
|
130
|
+
const fields = ['domains', 'entities', 'relations', 'business_rules', 'decisions'];
|
|
131
|
+
const details = {};
|
|
132
|
+
let weightedTotal = 0;
|
|
133
|
+
let weightedMatched = 0;
|
|
134
|
+
let bucketCount = 0;
|
|
135
|
+
|
|
136
|
+
fields.forEach((field) => {
|
|
137
|
+
const expected = normalizeTokenList(specScope && specScope[field]);
|
|
138
|
+
const provided = normalizeTokenList(templateScope && templateScope[field]);
|
|
139
|
+
const providedSet = new Set(provided);
|
|
140
|
+
const matched = expected.filter((item) => providedSet.has(item));
|
|
141
|
+
const expectedCount = expected.length;
|
|
142
|
+
const matchedCount = matched.length;
|
|
143
|
+
const coverage = expectedCount > 0 ? matchedCount / expectedCount : 0;
|
|
144
|
+
if (expectedCount > 0) {
|
|
145
|
+
weightedTotal += 1;
|
|
146
|
+
weightedMatched += coverage;
|
|
147
|
+
bucketCount += 1;
|
|
148
|
+
}
|
|
149
|
+
details[field] = {
|
|
150
|
+
expected,
|
|
151
|
+
provided,
|
|
152
|
+
matched,
|
|
153
|
+
expected_count: expectedCount,
|
|
154
|
+
matched_count: matchedCount,
|
|
155
|
+
coverage_ratio: Number(coverage.toFixed(3))
|
|
156
|
+
};
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
const score = bucketCount > 0 ? weightedMatched / weightedTotal : 0;
|
|
160
|
+
return {
|
|
161
|
+
score,
|
|
162
|
+
details
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function buildKeywordScore(template, queryTokens) {
|
|
167
|
+
if (!queryTokens || queryTokens.length === 0) {
|
|
168
|
+
return 0;
|
|
169
|
+
}
|
|
170
|
+
const haystack = [
|
|
171
|
+
template.id,
|
|
172
|
+
template.name,
|
|
173
|
+
template.description,
|
|
174
|
+
...(template.tags || []),
|
|
175
|
+
...(template.applicable_scenarios || [])
|
|
176
|
+
].map((item) => `${item || ''}`.toLowerCase());
|
|
177
|
+
const hits = queryTokens.filter((token) => haystack.some((value) => value.includes(token))).length;
|
|
178
|
+
return hits / queryTokens.length;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async function loadSpecDomainChain(projectPath, specId, fileSystem) {
|
|
182
|
+
const specPath = path.join(projectPath, '.sce', 'specs', specId);
|
|
183
|
+
const domainChainPath = path.join(specPath, DOMAIN_CHAIN_RELATIVE_PATH);
|
|
184
|
+
if (!await fileSystem.pathExists(domainChainPath)) {
|
|
185
|
+
return { exists: false, path: domainChainPath, payload: null };
|
|
186
|
+
}
|
|
187
|
+
try {
|
|
188
|
+
const payload = await fileSystem.readJson(domainChainPath);
|
|
189
|
+
return { exists: true, path: domainChainPath, payload };
|
|
190
|
+
} catch (error) {
|
|
191
|
+
return { exists: true, path: domainChainPath, payload: null, error: error.message };
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
83
195
|
async function loadSceneIndexFromFile(projectPath, fileSystem) {
|
|
84
196
|
const indexPath = path.join(projectPath, '.sce', 'spec-governance', 'scene-index.json');
|
|
85
197
|
if (!await fileSystem.pathExists(indexPath)) {
|
|
@@ -537,6 +649,249 @@ async function runCapabilityRegisterCommand(options = {}, dependencies = {}) {
|
|
|
537
649
|
return result;
|
|
538
650
|
}
|
|
539
651
|
|
|
652
|
+
function displayCapabilityCatalog(templates, options = {}) {
|
|
653
|
+
const total = Array.isArray(templates) ? templates.length : 0;
|
|
654
|
+
console.log(chalk.red('🔥') + ' Capability Library');
|
|
655
|
+
if (total === 0) {
|
|
656
|
+
console.log(chalk.yellow('No capability templates found.'));
|
|
657
|
+
if (options.source) {
|
|
658
|
+
console.log(chalk.gray(`Try removing filters or run ${chalk.cyan('sce templates update')}.`));
|
|
659
|
+
}
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
templates.forEach((template) => {
|
|
663
|
+
const sourcePrefix = template.source && template.source !== 'official'
|
|
664
|
+
? chalk.gray(`[${template.source}] `)
|
|
665
|
+
: '';
|
|
666
|
+
console.log(`${sourcePrefix}${chalk.cyan(template.id)} ${chalk.gray(`(${template.category})`)}`);
|
|
667
|
+
console.log(` ${template.name}`);
|
|
668
|
+
console.log(` ${chalk.gray(template.description)}`);
|
|
669
|
+
console.log();
|
|
670
|
+
});
|
|
671
|
+
console.log(chalk.gray(`Total: ${total} capability template(s)`));
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
async function listCapabilityCatalog(options = {}) {
|
|
675
|
+
const manager = new TemplateManager();
|
|
676
|
+
const templates = await manager.listTemplates({
|
|
677
|
+
category: options.category,
|
|
678
|
+
source: options.source,
|
|
679
|
+
templateType: 'capability-template',
|
|
680
|
+
compatibleWith: options.compatibleWith,
|
|
681
|
+
riskLevel: options.risk
|
|
682
|
+
});
|
|
683
|
+
if (normalizeBoolean(options.json, false)) {
|
|
684
|
+
return {
|
|
685
|
+
mode: 'capability-catalog-list',
|
|
686
|
+
templates
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
displayCapabilityCatalog(templates, options);
|
|
690
|
+
return { templates };
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
async function searchCapabilityCatalog(keyword, options = {}) {
|
|
694
|
+
const manager = new TemplateManager();
|
|
695
|
+
const templates = await manager.searchTemplates(keyword, {
|
|
696
|
+
category: options.category,
|
|
697
|
+
source: options.source,
|
|
698
|
+
templateType: 'capability-template',
|
|
699
|
+
compatibleWith: options.compatibleWith,
|
|
700
|
+
riskLevel: options.risk
|
|
701
|
+
});
|
|
702
|
+
if (normalizeBoolean(options.json, false)) {
|
|
703
|
+
return {
|
|
704
|
+
mode: 'capability-catalog-search',
|
|
705
|
+
keyword,
|
|
706
|
+
templates
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
displayCapabilityCatalog(templates, options);
|
|
710
|
+
return { templates };
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
async function showCapabilityTemplate(templatePath, options = {}) {
|
|
714
|
+
const manager = new TemplateManager();
|
|
715
|
+
const template = await manager.showTemplate(templatePath);
|
|
716
|
+
const { sourceName, templateId } = parseTemplatePath(templatePath);
|
|
717
|
+
await manager.ensureCached(sourceName);
|
|
718
|
+
const sourcePath = manager.cacheManager.getSourceCachePath(sourceName);
|
|
719
|
+
const templateDir = path.join(sourcePath, templateId);
|
|
720
|
+
const capabilityFile = path.join(templateDir, 'capability-template.json');
|
|
721
|
+
let templatePayload = null;
|
|
722
|
+
if (await fs.pathExists(capabilityFile)) {
|
|
723
|
+
try {
|
|
724
|
+
templatePayload = await fs.readJson(capabilityFile);
|
|
725
|
+
} catch (_error) {
|
|
726
|
+
templatePayload = null;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
const result = {
|
|
730
|
+
mode: 'capability-catalog-show',
|
|
731
|
+
template,
|
|
732
|
+
template_file: await fs.pathExists(capabilityFile) ? capabilityFile : null,
|
|
733
|
+
payload: templatePayload
|
|
734
|
+
};
|
|
735
|
+
if (normalizeBoolean(options.json, false)) {
|
|
736
|
+
return result;
|
|
737
|
+
}
|
|
738
|
+
console.log(chalk.green('✅ Capability template loaded'));
|
|
739
|
+
console.log(chalk.gray(` ID: ${template.id}`));
|
|
740
|
+
console.log(chalk.gray(` Name: ${template.name}`));
|
|
741
|
+
if (templatePayload) {
|
|
742
|
+
console.log(chalk.gray(' Payload: capability-template.json loaded'));
|
|
743
|
+
}
|
|
744
|
+
return result;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
async function matchCapabilityTemplates(options = {}) {
|
|
748
|
+
const projectPath = options.projectPath || process.cwd();
|
|
749
|
+
const fileSystem = options.fileSystem || fs;
|
|
750
|
+
const specId = normalizeText(options.spec || options.specId);
|
|
751
|
+
if (!specId) {
|
|
752
|
+
throw new Error('spec is required for capability match');
|
|
753
|
+
}
|
|
754
|
+
const chain = await loadSpecDomainChain(projectPath, specId, fileSystem);
|
|
755
|
+
if (!chain.exists && normalizeBoolean(options.strict, false)) {
|
|
756
|
+
throw new Error(`problem-domain-chain missing for spec ${specId}`);
|
|
757
|
+
}
|
|
758
|
+
if (chain.error && normalizeBoolean(options.strict, false)) {
|
|
759
|
+
throw new Error(`problem-domain-chain invalid: ${chain.error}`);
|
|
760
|
+
}
|
|
761
|
+
const domainChain = chain.payload || {};
|
|
762
|
+
const specScope = buildOntologyScopeFromChain(domainChain);
|
|
763
|
+
const queryTokens = normalizeTokenList(options.query)
|
|
764
|
+
.concat(normalizeTokenList(domainChain.problem && domainChain.problem.statement))
|
|
765
|
+
.concat(normalizeTokenList(domainChain.scene_id));
|
|
766
|
+
const manager = new TemplateManager();
|
|
767
|
+
const templates = await manager.listTemplates({
|
|
768
|
+
source: options.source,
|
|
769
|
+
templateType: 'capability-template',
|
|
770
|
+
compatibleWith: options.compatibleWith,
|
|
771
|
+
riskLevel: options.risk
|
|
772
|
+
});
|
|
773
|
+
const matches = templates.map((template) => {
|
|
774
|
+
const overlap = buildOntologyOverlap(specScope, template.ontology_scope || {});
|
|
775
|
+
const scenarioScore = template.applicable_scenarios && domainChain.scene_id
|
|
776
|
+
? (template.applicable_scenarios.includes(domainChain.scene_id) ? 1 : 0)
|
|
777
|
+
: 0;
|
|
778
|
+
const keywordScore = buildKeywordScore(template, queryTokens);
|
|
779
|
+
const totalScore = (overlap.score * 0.6) + (scenarioScore * 0.2) + (keywordScore * 0.2);
|
|
780
|
+
return {
|
|
781
|
+
template_id: template.id,
|
|
782
|
+
source: template.source,
|
|
783
|
+
name: template.name,
|
|
784
|
+
description: template.description,
|
|
785
|
+
category: template.category,
|
|
786
|
+
risk_level: template.risk_level,
|
|
787
|
+
score: Math.round(totalScore * 100),
|
|
788
|
+
score_components: {
|
|
789
|
+
ontology: Number(overlap.score.toFixed(3)),
|
|
790
|
+
scenario: scenarioScore,
|
|
791
|
+
keyword: Number(keywordScore.toFixed(3))
|
|
792
|
+
},
|
|
793
|
+
overlap
|
|
794
|
+
};
|
|
795
|
+
}).sort((a, b) => b.score - a.score);
|
|
796
|
+
|
|
797
|
+
const limit = toPositiveInteger(options.limit, 10);
|
|
798
|
+
const payload = {
|
|
799
|
+
mode: 'capability-match',
|
|
800
|
+
spec_id: specId,
|
|
801
|
+
scene_id: domainChain.scene_id || null,
|
|
802
|
+
query: normalizeText(options.query) || null,
|
|
803
|
+
ontology_source: chain.exists ? chain.path : null,
|
|
804
|
+
match_count: matches.length,
|
|
805
|
+
matches: matches.slice(0, limit),
|
|
806
|
+
warnings: chain.exists ? [] : ['problem-domain-chain missing; ontology-based match unavailable']
|
|
807
|
+
};
|
|
808
|
+
if (normalizeBoolean(options.json, false)) {
|
|
809
|
+
return payload;
|
|
810
|
+
}
|
|
811
|
+
console.log(chalk.green('✅ Capability match completed'));
|
|
812
|
+
console.log(chalk.gray(` Spec: ${specId}`));
|
|
813
|
+
console.log(chalk.gray(` Matches: ${payload.matches.length}`));
|
|
814
|
+
return payload;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
async function useCapabilityTemplate(options = {}) {
|
|
818
|
+
const projectPath = options.projectPath || process.cwd();
|
|
819
|
+
const fileSystem = options.fileSystem || fs;
|
|
820
|
+
const templateId = normalizeText(options.template || options.id);
|
|
821
|
+
if (!templateId) {
|
|
822
|
+
throw new Error('template is required for capability use');
|
|
823
|
+
}
|
|
824
|
+
const specId = normalizeText(options.spec || options.specId) || null;
|
|
825
|
+
const manager = new TemplateManager();
|
|
826
|
+
const template = await manager.showTemplate(templateId);
|
|
827
|
+
const { sourceName, templateId: parsedTemplateId } = parseTemplatePath(templateId);
|
|
828
|
+
await manager.ensureCached(sourceName);
|
|
829
|
+
const sourcePath = manager.cacheManager.getSourceCachePath(sourceName);
|
|
830
|
+
const templateDir = path.join(sourcePath, parsedTemplateId);
|
|
831
|
+
const capabilityFile = path.join(templateDir, 'capability-template.json');
|
|
832
|
+
let templatePayload = null;
|
|
833
|
+
if (await fileSystem.pathExists(capabilityFile)) {
|
|
834
|
+
try {
|
|
835
|
+
templatePayload = await fileSystem.readJson(capabilityFile);
|
|
836
|
+
} catch (_error) {
|
|
837
|
+
templatePayload = null;
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
const recommendedTasks = [];
|
|
842
|
+
if (templatePayload && templatePayload.source_candidate && Array.isArray(templatePayload.source_candidate.specs)) {
|
|
843
|
+
templatePayload.source_candidate.specs.forEach((spec) => {
|
|
844
|
+
const sample = Array.isArray(spec.task_sample) ? spec.task_sample : [];
|
|
845
|
+
sample.forEach((task) => {
|
|
846
|
+
if (task && task.title) {
|
|
847
|
+
recommendedTasks.push({
|
|
848
|
+
title: task.title,
|
|
849
|
+
source_spec_id: spec.spec_id || null,
|
|
850
|
+
source_task_id: task.id || null
|
|
851
|
+
});
|
|
852
|
+
}
|
|
853
|
+
});
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
if (recommendedTasks.length === 0) {
|
|
857
|
+
recommendedTasks.push({ title: `Implement capability scope: ${template.name || parsedTemplateId}` });
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
const plan = {
|
|
861
|
+
mode: 'capability-use-plan',
|
|
862
|
+
generated_at: new Date().toISOString(),
|
|
863
|
+
template: {
|
|
864
|
+
id: template.id,
|
|
865
|
+
name: template.name,
|
|
866
|
+
source: template.source,
|
|
867
|
+
description: template.description,
|
|
868
|
+
ontology_scope: template.ontology_scope || {}
|
|
869
|
+
},
|
|
870
|
+
spec_id: specId,
|
|
871
|
+
recommended_tasks: recommendedTasks
|
|
872
|
+
};
|
|
873
|
+
|
|
874
|
+
const outputPath = normalizeText(options.out) || buildDefaultUsePlanPath(specId || 'spec', template.id);
|
|
875
|
+
if (normalizeBoolean(options.write, true)) {
|
|
876
|
+
await fileSystem.ensureDir(path.dirname(path.join(projectPath, outputPath)));
|
|
877
|
+
await fileSystem.writeJson(path.join(projectPath, outputPath), plan, { spaces: 2 });
|
|
878
|
+
plan.output_file = outputPath;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
if (!normalizeBoolean(options.json, false)) {
|
|
882
|
+
console.log(chalk.green('✅ Capability use plan generated'));
|
|
883
|
+
console.log(chalk.gray(` Template: ${template.id}`));
|
|
884
|
+
if (specId) {
|
|
885
|
+
console.log(chalk.gray(` Spec: ${specId}`));
|
|
886
|
+
}
|
|
887
|
+
if (plan.output_file) {
|
|
888
|
+
console.log(chalk.gray(` Output: ${plan.output_file}`));
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
return plan;
|
|
893
|
+
}
|
|
894
|
+
|
|
540
895
|
function registerCapabilityCommands(program) {
|
|
541
896
|
const capabilityCmd = program
|
|
542
897
|
.command('capability')
|
|
@@ -623,6 +978,141 @@ function registerCapabilityCommands(program) {
|
|
|
623
978
|
tags
|
|
624
979
|
});
|
|
625
980
|
});
|
|
981
|
+
|
|
982
|
+
const catalogCmd = capabilityCmd
|
|
983
|
+
.command('catalog')
|
|
984
|
+
.description('Browse and reuse capability templates');
|
|
985
|
+
|
|
986
|
+
catalogCmd
|
|
987
|
+
.command('list')
|
|
988
|
+
.description('List capability templates')
|
|
989
|
+
.option('--source <name>', 'Template source name')
|
|
990
|
+
.option('--category <name>', 'Template category filter')
|
|
991
|
+
.option('--compatible-with <semver>', 'SCE version compatibility')
|
|
992
|
+
.option('--risk <level>', 'Risk level filter')
|
|
993
|
+
.option('--json', 'Output JSON to stdout')
|
|
994
|
+
.action(async (options) => {
|
|
995
|
+
try {
|
|
996
|
+
const payload = await listCapabilityCatalog({
|
|
997
|
+
source: options.source,
|
|
998
|
+
category: options.category,
|
|
999
|
+
compatibleWith: options.compatibleWith,
|
|
1000
|
+
risk: options.risk,
|
|
1001
|
+
json: options.json
|
|
1002
|
+
});
|
|
1003
|
+
if (options.json) {
|
|
1004
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
1005
|
+
}
|
|
1006
|
+
} catch (error) {
|
|
1007
|
+
console.log();
|
|
1008
|
+
console.log(chalk.red('❌ Error:'), error.message);
|
|
1009
|
+
if (error instanceof TemplateError && error.suggestions) {
|
|
1010
|
+
console.log();
|
|
1011
|
+
console.log(chalk.yellow('💡 Suggestions:'));
|
|
1012
|
+
error.suggestions.forEach((suggestion) => console.log(` • ${suggestion}`));
|
|
1013
|
+
}
|
|
1014
|
+
process.exit(1);
|
|
1015
|
+
}
|
|
1016
|
+
});
|
|
1017
|
+
|
|
1018
|
+
catalogCmd
|
|
1019
|
+
.command('search <keyword>')
|
|
1020
|
+
.description('Search capability templates')
|
|
1021
|
+
.option('--source <name>', 'Template source name')
|
|
1022
|
+
.option('--category <name>', 'Template category filter')
|
|
1023
|
+
.option('--compatible-with <semver>', 'SCE version compatibility')
|
|
1024
|
+
.option('--risk <level>', 'Risk level filter')
|
|
1025
|
+
.option('--json', 'Output JSON to stdout')
|
|
1026
|
+
.action(async (keyword, options) => {
|
|
1027
|
+
try {
|
|
1028
|
+
const payload = await searchCapabilityCatalog(keyword, {
|
|
1029
|
+
source: options.source,
|
|
1030
|
+
category: options.category,
|
|
1031
|
+
compatibleWith: options.compatibleWith,
|
|
1032
|
+
risk: options.risk,
|
|
1033
|
+
json: options.json
|
|
1034
|
+
});
|
|
1035
|
+
if (options.json) {
|
|
1036
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
1037
|
+
}
|
|
1038
|
+
} catch (error) {
|
|
1039
|
+
console.log();
|
|
1040
|
+
console.log(chalk.red('❌ Error:'), error.message);
|
|
1041
|
+
if (error instanceof TemplateError && error.suggestions) {
|
|
1042
|
+
console.log();
|
|
1043
|
+
console.log(chalk.yellow('💡 Suggestions:'));
|
|
1044
|
+
error.suggestions.forEach((suggestion) => console.log(` • ${suggestion}`));
|
|
1045
|
+
}
|
|
1046
|
+
process.exit(1);
|
|
1047
|
+
}
|
|
1048
|
+
});
|
|
1049
|
+
|
|
1050
|
+
catalogCmd
|
|
1051
|
+
.command('show <template-id>')
|
|
1052
|
+
.description('Show capability template details')
|
|
1053
|
+
.option('--json', 'Output JSON to stdout')
|
|
1054
|
+
.action(async (templateId, options) => {
|
|
1055
|
+
try {
|
|
1056
|
+
const payload = await showCapabilityTemplate(templateId, { json: options.json });
|
|
1057
|
+
if (options.json) {
|
|
1058
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
1059
|
+
}
|
|
1060
|
+
} catch (error) {
|
|
1061
|
+
console.log();
|
|
1062
|
+
console.log(chalk.red('❌ Error:'), error.message);
|
|
1063
|
+
if (error instanceof TemplateError && error.suggestions) {
|
|
1064
|
+
console.log();
|
|
1065
|
+
console.log(chalk.yellow('💡 Suggestions:'));
|
|
1066
|
+
error.suggestions.forEach((suggestion) => console.log(` • ${suggestion}`));
|
|
1067
|
+
}
|
|
1068
|
+
process.exit(1);
|
|
1069
|
+
}
|
|
1070
|
+
});
|
|
1071
|
+
|
|
1072
|
+
capabilityCmd
|
|
1073
|
+
.command('match')
|
|
1074
|
+
.description('Match capability templates to a spec using ontology scope')
|
|
1075
|
+
.requiredOption('--spec <spec-id>', 'Spec identifier')
|
|
1076
|
+
.option('--query <text>', 'Additional keyword query')
|
|
1077
|
+
.option('--source <name>', 'Template source name')
|
|
1078
|
+
.option('--compatible-with <semver>', 'SCE version compatibility')
|
|
1079
|
+
.option('--risk <level>', 'Risk level filter')
|
|
1080
|
+
.option('--limit <n>', 'Max match results', '10')
|
|
1081
|
+
.option('--strict', 'Fail if domain-chain missing or invalid')
|
|
1082
|
+
.option('--json', 'Output JSON to stdout')
|
|
1083
|
+
.action(async (options) => {
|
|
1084
|
+
try {
|
|
1085
|
+
const payload = await matchCapabilityTemplates(options);
|
|
1086
|
+
if (options.json) {
|
|
1087
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
1088
|
+
}
|
|
1089
|
+
} catch (error) {
|
|
1090
|
+
console.log();
|
|
1091
|
+
console.log(chalk.red('❌ Error:'), error.message);
|
|
1092
|
+
process.exit(1);
|
|
1093
|
+
}
|
|
1094
|
+
});
|
|
1095
|
+
|
|
1096
|
+
capabilityCmd
|
|
1097
|
+
.command('use')
|
|
1098
|
+
.description('Generate a capability usage plan for a spec')
|
|
1099
|
+
.requiredOption('--template <template-id>', 'Capability template identifier')
|
|
1100
|
+
.option('--spec <spec-id>', 'Spec identifier')
|
|
1101
|
+
.option('--out <path>', 'Output JSON path')
|
|
1102
|
+
.option('--no-write', 'Skip writing output file')
|
|
1103
|
+
.option('--json', 'Output JSON to stdout')
|
|
1104
|
+
.action(async (options) => {
|
|
1105
|
+
try {
|
|
1106
|
+
const payload = await useCapabilityTemplate(options);
|
|
1107
|
+
if (options.json) {
|
|
1108
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
1109
|
+
}
|
|
1110
|
+
} catch (error) {
|
|
1111
|
+
console.log();
|
|
1112
|
+
console.log(chalk.red('❌ Error:'), error.message);
|
|
1113
|
+
process.exit(1);
|
|
1114
|
+
}
|
|
1115
|
+
});
|
|
626
1116
|
}
|
|
627
1117
|
|
|
628
1118
|
module.exports = {
|