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.
@@ -15,6 +15,24 @@ const { DOMAIN_CHAIN_RELATIVE_PATH } = require('../spec/domain-modeling');
15
15
  const { SceStateStore } = require('../state/sce-state-store');
16
16
  const TemplateManager = require('../templates/template-manager');
17
17
  const { TemplateError } = require('../templates/template-error');
18
+ const { buildMagicballStatusLanguage } = require('../magicball/status-language');
19
+ const {
20
+ buildCapabilityInventorySceneAdvice,
21
+ buildCapabilityInventorySummaryStats,
22
+ buildCapabilityInventorySummaryRecommendations,
23
+ buildCapabilityInventoryQuickFilters,
24
+ resolveCapabilityTriadPriority,
25
+ sortCapabilityInventoryEntries,
26
+ filterCapabilityInventoryEntries
27
+ } = require('../magicball/capability-inventory-view-model');
28
+ const { runCapabilityInventoryService } = require('../capability/inventory-service');
29
+ const {
30
+ listCapabilityCatalogService,
31
+ searchCapabilityCatalogService,
32
+ showCapabilityTemplateService,
33
+ matchCapabilityTemplatesService,
34
+ useCapabilityTemplateService
35
+ } = require('../capability/catalog-service');
18
36
  const packageJson = require('../../package.json');
19
37
 
20
38
  const DEFAULT_ITERATION_DIR = '.sce/reports/capability-iteration';
@@ -820,210 +838,8 @@ async function listCapabilityInventorySceneIds(options = {}, dependencies = {})
820
838
  return [];
821
839
  }
822
840
 
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
841
  function buildCapabilityMagicballStatus(input = {}) {
840
- const attention = normalizeText(input.attention_level) || 'medium';
841
- return {
842
- attention_level: attention,
843
- status_tone: attention === 'critical' ? 'danger' : (attention === 'high' ? 'warning' : (attention === 'low' ? 'success' : 'info')),
844
- status_label: normalizeText(input.status_label) || null,
845
- blocking_summary: normalizeText(input.blocking_summary) || null,
846
- recommended_action: normalizeText(input.recommended_action) || null
847
- };
848
- }
849
-
850
- function buildCapabilityInventorySceneAdvice(entry) {
851
- const sceneId = String(entry && entry.scene_id || 'scene.unknown');
852
- const releaseUi = entry && entry.release_readiness_ui ? entry.release_readiness_ui : { publish_ready: true, blocking_missing: [] };
853
- const missing = Array.isArray(releaseUi.blocking_missing) ? releaseUi.blocking_missing : [];
854
- const valueScore = Number(entry && entry.score_preview && entry.score_preview.value_score || 0);
855
-
856
- let attentionLevel = 'low';
857
- let recommendedAction = '可直接发布';
858
- let blockingSummary = '已满足发布前置条件';
859
- let nextAction = 'publish';
860
-
861
- if (!releaseUi.publish_ready) {
862
- if (missing.includes('decision_strategy')) {
863
- attentionLevel = 'critical';
864
- recommendedAction = '补齐决策策略';
865
- blockingSummary = '缺决策策略,暂不可发布';
866
- nextAction = 'fill_decision_strategy';
867
- } else if (missing.includes('business_rules')) {
868
- attentionLevel = 'high';
869
- recommendedAction = '补齐业务规则';
870
- blockingSummary = '缺业务规则,暂不可发布';
871
- nextAction = 'fill_business_rules';
872
- } else if (missing.includes('entity_relation')) {
873
- attentionLevel = 'medium';
874
- recommendedAction = '补齐实体关系';
875
- blockingSummary = '缺实体关系,暂不可发布';
876
- nextAction = 'fill_entity_relation';
877
- } else {
878
- attentionLevel = 'medium';
879
- recommendedAction = '补齐本体能力';
880
- blockingSummary = '本体能力不完整,暂不可发布';
881
- nextAction = 'repair_ontology_core';
882
- }
883
- } else if (valueScore >= 70) {
884
- attentionLevel = 'low';
885
- recommendedAction = '进入模板构建';
886
- blockingSummary = '能力成熟度较高,可进入模板构建/发布';
887
- nextAction = 'build_template';
888
- } else {
889
- attentionLevel = 'medium';
890
- recommendedAction = '继续补充任务证据';
891
- blockingSummary = '已可发布,但建议先补强任务与验证证据';
892
- nextAction = 'strengthen_evidence';
893
- }
894
-
895
- return {
896
- attention_level: attentionLevel,
897
- recommended_action: recommendedAction,
898
- blocking_summary: blockingSummary,
899
- next_action: nextAction,
900
- next_command: 'sce capability extract --scene ' + sceneId + ' --json',
901
- mb_status: buildCapabilityMagicballStatus({
902
- attention_level: attentionLevel,
903
- status_label: releaseUi.publish_ready ? 'publish_ready' : 'blocked',
904
- blocking_summary: blockingSummary,
905
- recommended_action: recommendedAction
906
- })
907
- };
908
- }
909
-
910
- function buildCapabilityInventorySummaryStats(entries) {
911
- const items = Array.isArray(entries) ? entries : [];
912
- const summary = {
913
- publish_ready_count: 0,
914
- blocked_count: 0,
915
- missing_triads: {
916
- decision_strategy: 0,
917
- business_rules: 0,
918
- entity_relation: 0
919
- }
920
- };
921
-
922
- for (const entry of items) {
923
- const ready = Boolean(entry && entry.release_readiness_ui && entry.release_readiness_ui.publish_ready);
924
- if (ready) {
925
- summary.publish_ready_count += 1;
926
- } else {
927
- summary.blocked_count += 1;
928
- }
929
-
930
- const missing = Array.isArray(entry && entry.release_readiness_ui && entry.release_readiness_ui.blocking_missing)
931
- ? entry.release_readiness_ui.blocking_missing
932
- : [];
933
- for (const triad of Object.keys(summary.missing_triads)) {
934
- if (missing.includes(triad)) {
935
- summary.missing_triads[triad] += 1;
936
- }
937
- }
938
- }
939
-
940
- return summary;
941
- }
942
-
943
- function buildCapabilityInventorySummaryRecommendations(entries) {
944
- const items = Array.isArray(entries) ? entries : [];
945
- const recommendations = [];
946
- const blocked = items.filter((item) => !(item && item.release_readiness_ui && item.release_readiness_ui.publish_ready));
947
- const missingDecision = blocked.filter((item) => Array.isArray(item.release_readiness_ui && item.release_readiness_ui.blocking_missing) && item.release_readiness_ui.blocking_missing.includes('decision_strategy'));
948
- const missingRules = blocked.filter((item) => Array.isArray(item.release_readiness_ui && item.release_readiness_ui.blocking_missing) && item.release_readiness_ui.blocking_missing.includes('business_rules'));
949
- const readyScenes = items.filter((item) => item && item.release_readiness_ui && item.release_readiness_ui.publish_ready);
950
-
951
- if (missingDecision.length > 0) {
952
- recommendations.push('优先处理缺决策策略的 scene(' + missingDecision.length + ')');
953
- }
954
- if (missingRules.length > 0) {
955
- recommendations.push('其次处理缺业务规则的 scene(' + missingRules.length + ')');
956
- }
957
- if (readyScenes.length > 0) {
958
- recommendations.push('可优先推进可发布 scene 进入模板构建(' + readyScenes.length + ')');
959
- }
960
- if (blocked.length === 0 && readyScenes.length === 0 && items.length > 0) {
961
- recommendations.push('当前 scene 已基本稳定,可继续补强验证证据');
962
- }
963
-
964
- return recommendations;
965
- }
966
-
967
- function buildCapabilityInventoryQuickFilters(summaryStats) {
968
- const stats = summaryStats || { blocked_count: 0, missing_triads: {} };
969
- const filters = [];
970
- if (Number(stats.blocked_count || 0) > 0) {
971
- filters.push({ id: 'blocked', label: '不可发布', query: { release_ready: false, missing_triad: null } });
972
- }
973
- for (const triad of ['decision_strategy', 'business_rules', 'entity_relation']) {
974
- if (Number(stats.missing_triads && stats.missing_triads[triad] || 0) > 0) {
975
- filters.push({ id: 'missing_' + triad, label: '缺' + triad, query: { release_ready: false, missing_triad: triad } });
976
- }
977
- }
978
- if (Number(stats.publish_ready_count || 0) > 0) {
979
- filters.push({ id: 'ready', label: '可发布', query: { release_ready: true, missing_triad: null } });
980
- }
981
- return filters;
982
- }
983
-
984
- function sortCapabilityInventoryEntries(entries) {
985
- return [...(Array.isArray(entries) ? entries : [])].sort((left, right) => {
986
- const leftReady = Boolean(left && left.release_readiness_ui && left.release_readiness_ui.publish_ready);
987
- const rightReady = Boolean(right && right.release_readiness_ui && right.release_readiness_ui.publish_ready);
988
- if (leftReady !== rightReady) {
989
- return leftReady ? 1 : -1;
990
- }
991
-
992
- const triadDelta = resolveCapabilityTriadPriority(left) - resolveCapabilityTriadPriority(right);
993
- if (triadDelta !== 0) {
994
- return triadDelta;
995
- }
996
-
997
- const leftValue = Number(left && left.score_preview && left.score_preview.value_score || 0);
998
- const rightValue = Number(right && right.score_preview && right.score_preview.value_score || 0);
999
- if (leftValue !== rightValue) {
1000
- return rightValue - leftValue;
1001
- }
1002
-
1003
- return String(left && left.scene_id || '').localeCompare(String(right && right.scene_id || ''));
1004
- });
1005
- }
1006
-
1007
- function filterCapabilityInventoryEntries(entries, options = {}) {
1008
- const normalizedMissingTriad = normalizeText(options.missingTriad || options.missing_triad).toLowerCase();
1009
- const releaseReadyFilter = normalizeText(options.releaseReady || options.release_ready).toLowerCase();
1010
- return (Array.isArray(entries) ? entries : []).filter((entry) => {
1011
- if (releaseReadyFilter) {
1012
- const expected = ['1', 'true', 'yes', 'ready'].includes(releaseReadyFilter);
1013
- if (Boolean(entry.release_readiness_ui && entry.release_readiness_ui.publish_ready) !== expected) {
1014
- return false;
1015
- }
1016
- }
1017
- if (normalizedMissingTriad) {
1018
- const missing = Array.isArray(entry.release_readiness_ui && entry.release_readiness_ui.blocking_missing)
1019
- ? entry.release_readiness_ui.blocking_missing
1020
- : [];
1021
- if (!missing.includes(normalizedMissingTriad)) {
1022
- return false;
1023
- }
1024
- }
1025
- return true;
1026
- });
842
+ return buildMagicballStatusLanguage(input);
1027
843
  }
1028
844
 
1029
845
  async function runCapabilityInventoryCommand(options = {}, dependencies = {}) {
@@ -1031,70 +847,27 @@ async function runCapabilityInventoryCommand(options = {}, dependencies = {}) {
1031
847
  const fileSystem = dependencies.fileSystem || fs;
1032
848
  const env = dependencies.env || process.env;
1033
849
  const sceneIds = await listCapabilityInventorySceneIds(options, { projectPath, fileSystem, env });
1034
- const limit = toPositiveInteger(options.limit, sceneIds.length || 20);
1035
- const scenes = [];
1036
-
1037
- for (const sceneId of sceneIds.slice(0, limit)) {
1038
- const candidate = await buildCapabilityCandidatePayload(sceneId, {
1039
- specs: options.specs,
1040
- sample_limit: options.sample_limit
1041
- }, dependencies);
1042
- const score = buildScoreFromCandidate(candidate);
1043
- const releaseReadiness = buildCapabilityReleaseReadiness({
1044
- scene_id: sceneId,
1045
- ontology_scope: candidate.ontology_scope,
1046
- ontology_core: candidate.ontology_core
1047
- });
1048
- const sceneEntry = {
1049
- scene_id: sceneId,
1050
- summary: candidate.summary,
1051
- source: candidate.source,
1052
- ontology_scope: candidate.ontology_scope,
1053
- ontology_core: candidate.ontology_core,
1054
- ontology_core_ui: buildOntologyCoreUiState(candidate.ontology_core),
1055
- release_readiness: releaseReadiness,
1056
- release_readiness_ui: buildCapabilityReleaseReadinessUi(releaseReadiness),
1057
- score_preview: score
1058
- };
1059
- scenes.push({
1060
- ...sceneEntry,
1061
- ...buildCapabilityInventorySceneAdvice(sceneEntry)
1062
- });
1063
- }
1064
-
1065
- const filteredScenes = sortCapabilityInventoryEntries(filterCapabilityInventoryEntries(scenes, options));
1066
- const summaryStats = buildCapabilityInventorySummaryStats(filteredScenes);
1067
- const releaseReadyFilterRaw = normalizeText(options.releaseReady || options.release_ready).toLowerCase();
1068
- const payload = {
1069
- mode: 'capability-inventory',
1070
- generated_at: new Date().toISOString(),
1071
- query: {
1072
- protocol_version: '1.0',
1073
- scene_id: normalizeText(options.scene || options.sceneId || options.scene_id) || null,
1074
- limit: limit,
1075
- sample_limit: toPositiveInteger(options.sample_limit, 5),
1076
- filters: {
1077
- release_ready: releaseReadyFilterRaw ? ['1', 'true', 'yes', 'ready'].includes(releaseReadyFilterRaw) : null,
1078
- missing_triad: normalizeText(options.missingTriad || options.missing_triad) || null
1079
- }
1080
- },
1081
- scene_total: scenes.length,
1082
- scene_count: filteredScenes.length,
1083
- summary_stats: summaryStats,
1084
- summary_recommendations: buildCapabilityInventorySummaryRecommendations(filteredScenes),
1085
- quick_filters: buildCapabilityInventoryQuickFilters(summaryStats),
1086
- sort: {
1087
- strategy: 'publish_ready -> missing_triad_priority -> value_score_desc -> scene_id',
1088
- triad_priority: ['decision_strategy', 'business_rules', 'entity_relation']
1089
- },
1090
- scenes: filteredScenes
1091
- };
850
+ const payload = await runCapabilityInventoryService(options, {
851
+ sceneIds,
852
+ buildCandidatePayload: buildCapabilityCandidatePayload,
853
+ buildScoreFromCandidate,
854
+ buildCapabilityReleaseReadiness,
855
+ buildOntologyCoreUiState,
856
+ buildCapabilityReleaseReadinessUi,
857
+ buildCapabilityInventorySceneAdvice,
858
+ buildCapabilityInventorySummaryStats,
859
+ buildCapabilityInventorySummaryRecommendations,
860
+ buildCapabilityInventoryQuickFilters,
861
+ sortCapabilityInventoryEntries,
862
+ filterCapabilityInventoryEntries,
863
+ runtime: dependencies
864
+ });
1092
865
 
1093
866
  if (normalizeBoolean(options.json, false)) {
1094
867
  return payload;
1095
868
  }
1096
869
  console.log(chalk.green('✅ Capability inventory generated'));
1097
- console.log(chalk.gray(' Scenes: ' + filteredScenes.length));
870
+ console.log(chalk.gray(' Scenes: ' + payload.scene_count));
1098
871
  return payload;
1099
872
  }
1100
873
 
@@ -1355,242 +1128,84 @@ function displayCapabilityCatalog(templates, options = {}) {
1355
1128
  }
1356
1129
 
1357
1130
  async function listCapabilityCatalog(options = {}) {
1358
- const manager = new TemplateManager();
1359
- const templates = filterCapabilityCatalogEntries((await manager.listTemplates({
1360
- category: options.category,
1361
- source: options.source,
1362
- templateType: 'capability-template',
1363
- compatibleWith: options.compatibleWith,
1364
- riskLevel: options.risk
1365
- })).map((template) => enrichCapabilityTemplateForUi(template)), options);
1131
+ const payload = await listCapabilityCatalogService(options, {
1132
+ filterCapabilityCatalogEntries,
1133
+ enrichCapabilityTemplateForUi
1134
+ });
1366
1135
  if (normalizeBoolean(options.json, false)) {
1367
- return {
1368
- mode: 'capability-catalog-list',
1369
- templates
1370
- };
1136
+ return payload;
1371
1137
  }
1372
- displayCapabilityCatalog(templates, options);
1373
- return { templates };
1138
+ displayCapabilityCatalog(payload.templates, options);
1139
+ return { templates: payload.templates };
1374
1140
  }
1375
1141
 
1376
1142
  async function searchCapabilityCatalog(keyword, options = {}) {
1377
- const manager = new TemplateManager();
1378
- const templates = filterCapabilityCatalogEntries((await manager.searchTemplates(keyword, {
1379
- category: options.category,
1380
- source: options.source,
1381
- templateType: 'capability-template',
1382
- compatibleWith: options.compatibleWith,
1383
- riskLevel: options.risk
1384
- })).map((template) => enrichCapabilityTemplateForUi(template)), options);
1143
+ const payload = await searchCapabilityCatalogService(keyword, {
1144
+ filterCapabilityCatalogEntries,
1145
+ enrichCapabilityTemplateForUi
1146
+ });
1385
1147
  if (normalizeBoolean(options.json, false)) {
1386
- return {
1387
- mode: 'capability-catalog-search',
1388
- keyword,
1389
- templates
1390
- };
1148
+ return payload;
1391
1149
  }
1392
- displayCapabilityCatalog(templates, options);
1393
- return { templates };
1150
+ displayCapabilityCatalog(payload.templates, options);
1151
+ return { templates: payload.templates };
1394
1152
  }
1395
1153
 
1396
1154
  async function showCapabilityTemplate(templatePath, options = {}) {
1397
- const manager = new TemplateManager();
1398
- const template = enrichCapabilityTemplateForUi(await manager.showTemplate(templatePath));
1399
- const { sourceName, templateId } = parseTemplatePath(templatePath);
1400
- await manager.ensureCached(sourceName);
1401
- const sourcePath = manager.cacheManager.getSourceCachePath(sourceName);
1402
- const templateDir = path.join(sourcePath, templateId);
1403
- const capabilityFile = path.join(templateDir, 'capability-template.json');
1404
- let templatePayload = null;
1405
- if (await fs.pathExists(capabilityFile)) {
1406
- try {
1407
- templatePayload = await fs.readJson(capabilityFile);
1408
- } catch (_error) {
1409
- templatePayload = null;
1410
- }
1411
- }
1412
- const result = {
1413
- mode: 'capability-catalog-show',
1414
- template,
1415
- template_file: await fs.pathExists(capabilityFile) ? capabilityFile : null,
1416
- payload: templatePayload
1417
- };
1155
+ const payload = await showCapabilityTemplateService(templatePath, {
1156
+ parseTemplatePath,
1157
+ enrichCapabilityTemplateForUi
1158
+ });
1418
1159
  if (normalizeBoolean(options.json, false)) {
1419
- return result;
1160
+ return payload;
1420
1161
  }
1421
1162
  console.log(chalk.green('✅ Capability template loaded'));
1422
- console.log(chalk.gray(` ID: ${template.id}`));
1423
- console.log(chalk.gray(` Name: ${template.name}`));
1424
- if (templatePayload) {
1163
+ console.log(chalk.gray(' ID: ' + payload.template.id));
1164
+ console.log(chalk.gray(' Name: ' + payload.template.name));
1165
+ if (payload.payload) {
1425
1166
  console.log(chalk.gray(' Payload: capability-template.json loaded'));
1426
1167
  }
1427
- return result;
1168
+ return payload;
1428
1169
  }
1429
1170
 
1430
1171
  async function matchCapabilityTemplates(options = {}) {
1431
- const projectPath = options.projectPath || process.cwd();
1432
- const fileSystem = options.fileSystem || fs;
1433
- const specId = normalizeText(options.spec || options.specId);
1434
- if (!specId) {
1435
- throw new Error('spec is required for capability match');
1436
- }
1437
- const chain = await loadSpecDomainChain(projectPath, specId, fileSystem);
1438
- if (!chain.exists && normalizeBoolean(options.strict, false)) {
1439
- throw new Error(`problem-domain-chain missing for spec ${specId}`);
1440
- }
1441
- if (chain.error && normalizeBoolean(options.strict, false)) {
1442
- throw new Error(`problem-domain-chain invalid: ${chain.error}`);
1443
- }
1444
- const domainChain = chain.payload || {};
1445
- const specScope = buildOntologyScopeFromChain(domainChain);
1446
- const queryTokens = normalizeTokenList(options.query)
1447
- .concat(normalizeTokenList(domainChain.problem && domainChain.problem.statement))
1448
- .concat(normalizeTokenList(domainChain.scene_id));
1449
- const manager = new TemplateManager();
1450
- const templates = await manager.listTemplates({
1451
- source: options.source,
1452
- templateType: 'capability-template',
1453
- compatibleWith: options.compatibleWith,
1454
- riskLevel: options.risk
1172
+ const payload = await matchCapabilityTemplatesService(options, {
1173
+ loadSpecDomainChain,
1174
+ buildOntologyScopeFromChain,
1175
+ normalizeTokenList,
1176
+ buildOntologyOverlap,
1177
+ buildKeywordScore,
1178
+ buildCoreOntologySummary,
1179
+ buildOntologyCoreUiState
1455
1180
  });
1456
- const matches = templates.map((template) => {
1457
- const overlap = buildOntologyOverlap(specScope, template.ontology_scope || {});
1458
- const scenarioScore = template.applicable_scenarios && domainChain.scene_id
1459
- ? (template.applicable_scenarios.includes(domainChain.scene_id) ? 1 : 0)
1460
- : 0;
1461
- const keywordScore = buildKeywordScore(template, queryTokens);
1462
- const totalScore = (overlap.score * 0.6) + (scenarioScore * 0.2) + (keywordScore * 0.2);
1463
- return {
1464
- template_id: template.id,
1465
- source: template.source,
1466
- name: template.name,
1467
- description: template.description,
1468
- category: template.category,
1469
- risk_level: template.risk_level,
1470
- ontology_core: template.ontology_core || buildCoreOntologySummary(template.ontology_scope || {}),
1471
- ontology_core_ui: buildOntologyCoreUiState(template.ontology_core || buildCoreOntologySummary(template.ontology_scope || {})),
1472
- score: Math.round(totalScore * 100),
1473
- score_components: {
1474
- ontology: Number(overlap.score.toFixed(3)),
1475
- scenario: scenarioScore,
1476
- keyword: Number(keywordScore.toFixed(3))
1477
- },
1478
- overlap
1479
- };
1480
- }).sort((a, b) => b.score - a.score);
1481
-
1482
- const limit = toPositiveInteger(options.limit, 10);
1483
- const payload = {
1484
- mode: 'capability-match',
1485
- spec_id: specId,
1486
- scene_id: domainChain.scene_id || null,
1487
- query: normalizeText(options.query) || null,
1488
- ontology_source: chain.exists ? chain.path : null,
1489
- match_count: matches.length,
1490
- matches: matches.slice(0, limit),
1491
- warnings: chain.exists ? [] : ['problem-domain-chain missing; ontology-based match unavailable']
1492
- };
1493
1181
  if (normalizeBoolean(options.json, false)) {
1494
1182
  return payload;
1495
1183
  }
1496
1184
  console.log(chalk.green('✅ Capability match completed'));
1497
- console.log(chalk.gray(` Spec: ${specId}`));
1498
- console.log(chalk.gray(` Matches: ${payload.matches.length}`));
1185
+ console.log(chalk.gray(' Spec: ' + payload.spec_id));
1186
+ console.log(chalk.gray(' Matches: ' + payload.matches.length));
1499
1187
  return payload;
1500
1188
  }
1501
1189
 
1502
1190
  async function useCapabilityTemplate(options = {}) {
1503
- const projectPath = options.projectPath || process.cwd();
1504
- const fileSystem = options.fileSystem || fs;
1505
- const templateId = normalizeText(options.template || options.id);
1506
- if (!templateId) {
1507
- throw new Error('template is required for capability use');
1508
- }
1509
- if (normalizeBoolean(options.apply, false) && normalizeBoolean(options.write, true) === false) {
1510
- throw new Error('cannot use --apply with --no-write');
1511
- }
1512
- const specId = normalizeText(options.spec || options.specId) || null;
1513
- const manager = new TemplateManager();
1514
- const template = await manager.showTemplate(templateId);
1515
- const { sourceName, templateId: parsedTemplateId } = parseTemplatePath(templateId);
1516
- await manager.ensureCached(sourceName);
1517
- const sourcePath = manager.cacheManager.getSourceCachePath(sourceName);
1518
- const templateDir = path.join(sourcePath, parsedTemplateId);
1519
- const capabilityFile = path.join(templateDir, 'capability-template.json');
1520
- let templatePayload = null;
1521
- if (await fileSystem.pathExists(capabilityFile)) {
1522
- try {
1523
- templatePayload = await fileSystem.readJson(capabilityFile);
1524
- } catch (_error) {
1525
- templatePayload = null;
1526
- }
1527
- }
1528
-
1529
- const recommendedTasks = [];
1530
- if (templatePayload && templatePayload.source_candidate && Array.isArray(templatePayload.source_candidate.specs)) {
1531
- templatePayload.source_candidate.specs.forEach((spec) => {
1532
- const sample = Array.isArray(spec.task_sample) ? spec.task_sample : [];
1533
- sample.forEach((task) => {
1534
- if (task && task.title) {
1535
- recommendedTasks.push({
1536
- title: task.title,
1537
- source_spec_id: spec.spec_id || null,
1538
- source_task_id: task.id || null
1539
- });
1540
- }
1541
- });
1542
- });
1543
- }
1544
- if (recommendedTasks.length === 0) {
1545
- recommendedTasks.push({ title: `Implement capability scope: ${template.name || parsedTemplateId}` });
1546
- }
1547
-
1548
- const plan = {
1549
- mode: 'capability-use-plan',
1550
- generated_at: new Date().toISOString(),
1551
- template: {
1552
- id: template.id,
1553
- name: template.name,
1554
- source: template.source,
1555
- description: template.description,
1556
- ontology_scope: template.ontology_scope || {},
1557
- ontology_core: template.ontology_core || buildCoreOntologySummary(template.ontology_scope || {}),
1558
- ontology_core_ui: buildOntologyCoreUiState(template.ontology_core || buildCoreOntologySummary(template.ontology_scope || {}))
1559
- },
1560
- spec_id: specId,
1561
- recommended_tasks: recommendedTasks
1562
- };
1563
-
1564
- const outputPath = normalizeText(options.out) || buildDefaultUsePlanPath(specId || 'spec', template.id);
1565
- if (normalizeBoolean(options.write, true)) {
1566
- await fileSystem.ensureDir(path.dirname(path.join(projectPath, outputPath)));
1567
- await fileSystem.writeJson(path.join(projectPath, outputPath), plan, { spaces: 2 });
1568
- plan.output_file = outputPath;
1569
- }
1570
-
1571
- if (normalizeBoolean(options.apply, false)) {
1572
- if (!specId) {
1573
- throw new Error('spec is required for --apply');
1574
- }
1575
- plan.apply = await appendCapabilityPlanToSpecTasks({
1576
- projectPath,
1577
- spec: specId,
1578
- sectionTitle: options.sectionTitle
1579
- }, plan, fileSystem);
1580
- }
1581
-
1191
+ const payload = await useCapabilityTemplateService(options, {
1192
+ parseTemplatePath,
1193
+ buildCoreOntologySummary,
1194
+ buildOntologyCoreUiState,
1195
+ buildDefaultUsePlanPath,
1196
+ appendCapabilityPlanToSpecTasks
1197
+ });
1582
1198
  if (!normalizeBoolean(options.json, false)) {
1583
1199
  console.log(chalk.green('✅ Capability use plan generated'));
1584
- console.log(chalk.gray(` Template: ${template.id}`));
1585
- if (specId) {
1586
- console.log(chalk.gray(` Spec: ${specId}`));
1200
+ console.log(chalk.gray(' Template: ' + payload.template.id));
1201
+ if (payload.spec_id) {
1202
+ console.log(chalk.gray(' Spec: ' + payload.spec_id));
1587
1203
  }
1588
- if (plan.output_file) {
1589
- console.log(chalk.gray(` Output: ${plan.output_file}`));
1204
+ if (payload.output_file) {
1205
+ console.log(chalk.gray(' Output: ' + payload.output_file));
1590
1206
  }
1591
1207
  }
1592
-
1593
- return plan;
1208
+ return payload;
1594
1209
  }
1595
1210
 
1596
1211
  function registerCapabilityCommands(program) {
@@ -1,4 +1,4 @@
1
- const path = require('path');
1
+ const path = require('path');
2
2
  const zlib = require('zlib');
3
3
  const crypto = require('crypto');
4
4
  const { spawnSync } = require('child_process');