@hongmaple0820/scale-engine 0.19.0 → 0.21.0

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.
Files changed (52) hide show
  1. package/README.en.md +17 -3
  2. package/README.md +143 -9
  3. package/dist/api/cli.js +1187 -30
  4. package/dist/api/cli.js.map +1 -1
  5. package/dist/codegraph/CodeIntelligence.d.ts +135 -0
  6. package/dist/codegraph/CodeIntelligence.js +460 -0
  7. package/dist/codegraph/CodeIntelligence.js.map +1 -0
  8. package/dist/context/ContextBudget.d.ts +90 -0
  9. package/dist/context/ContextBudget.js +322 -0
  10. package/dist/context/ContextBudget.js.map +1 -0
  11. package/dist/eval/WorkflowEval.d.ts +161 -0
  12. package/dist/eval/WorkflowEval.js +379 -0
  13. package/dist/eval/WorkflowEval.js.map +1 -0
  14. package/dist/governance/GovernanceRoi.d.ts +25 -0
  15. package/dist/governance/GovernanceRoi.js +70 -0
  16. package/dist/governance/GovernanceRoi.js.map +1 -0
  17. package/dist/governance/ProgressiveGovernance.d.ts +22 -0
  18. package/dist/governance/ProgressiveGovernance.js +159 -0
  19. package/dist/governance/ProgressiveGovernance.js.map +1 -0
  20. package/dist/memory/MemoryBrain.d.ts +135 -0
  21. package/dist/memory/MemoryBrain.js +635 -0
  22. package/dist/memory/MemoryBrain.js.map +1 -0
  23. package/dist/memory/index.d.ts +1 -0
  24. package/dist/memory/index.js +1 -0
  25. package/dist/memory/index.js.map +1 -1
  26. package/dist/output/GovernanceDashboard.d.ts +57 -0
  27. package/dist/output/GovernanceDashboard.js +250 -0
  28. package/dist/output/GovernanceDashboard.js.map +1 -0
  29. package/dist/output/index.d.ts +2 -0
  30. package/dist/output/index.js +1 -0
  31. package/dist/output/index.js.map +1 -1
  32. package/dist/skills/SkillRadar.d.ts +83 -0
  33. package/dist/skills/SkillRadar.js +384 -0
  34. package/dist/skills/SkillRadar.js.map +1 -0
  35. package/dist/workflow/GovernanceTemplates.js +220 -194
  36. package/dist/workflow/GovernanceTemplates.js.map +1 -1
  37. package/dist/workflow/UpgradeManager.d.ts +140 -0
  38. package/dist/workflow/UpgradeManager.js +434 -0
  39. package/dist/workflow/UpgradeManager.js.map +1 -0
  40. package/docs/CODE_INTELLIGENCE.md +138 -0
  41. package/docs/CONTEXT_BUDGET.md +87 -0
  42. package/docs/GOVERNANCE_DASHBOARD.md +69 -0
  43. package/docs/MEMORY_BRAIN.md +104 -0
  44. package/docs/README.md +17 -8
  45. package/docs/SKILL_RADAR.md +115 -0
  46. package/docs/WORKFLOW_EVAL.md +151 -0
  47. package/docs/start/README.md +5 -1
  48. package/examples/demo-projects/agent-governance-demo/CONTEXT.md +14 -0
  49. package/examples/demo-projects/agent-governance-demo/README.md +32 -21
  50. package/examples/demo-projects/agent-governance-demo/docs/CONTEXT-MAP.md +14 -0
  51. package/examples/demo-projects/agent-governance-demo/package.json +6 -1
  52. package/package.json +7 -1
package/dist/api/cli.js CHANGED
@@ -11,6 +11,9 @@ import { BruteRetryDetector, PrematureDoneDetector, BlameShiftDetector } from '.
11
11
  import { DangerousCommandDetector, SecretLeakDetector, RoleGateDetector, ScopeCreepDetector, BUILT_IN_ROLES } from '../guardrails/advancedDetectors.js';
12
12
  import { SQLiteKnowledgeBase } from '../knowledge/SQLiteKnowledgeBase.js';
13
13
  import { ContextBuilder } from '../context/ContextBuilder.js';
14
+ import { buildContextPack, doctorContextBudget, scanContextBudget, writeContextBudgetReport, } from '../context/ContextBudget.js';
15
+ import { buildCodeGraphContext, createCodeGraphRoiReport, impactCodeGraph, inspectCodeIntelligence, queryCodeGraph, writeCodeIntelligenceConfig, } from '../codegraph/CodeIntelligence.js';
16
+ import { WorkflowEvalStore, compareWorkflowEvalRuns, renderWorkflowEvalReport, runWorkflowEvalSuite, } from '../eval/WorkflowEval.js';
14
17
  import { FSMAgentBridge } from '../fsm/FSMAgentBridge.js';
15
18
  import { CapabilityRegistry } from '../capabilities/CapabilityRegistry.js';
16
19
  import { SkillRegistry } from '../skills/SkillRegistry.js';
@@ -24,6 +27,7 @@ import { quickStart, detectPlatform, governanceNextSteps } from './quickstart.js
24
27
  import { SkillDiscovery } from '../skills/SkillDiscovery.js';
25
28
  import { inspectRequiredWorkflowSkills, inspectWorkflowSkills } from '../skills/SkillDoctor.js';
26
29
  import { evaluateSkillInstallSafety, listSkillRepositoryEntries, recommendSkillWorkflow, renderSkillRepositoryMarkdown, } from '../skills/SkillRepository.js';
30
+ import { evaluateSkillRadar, inspectSkillSupplyChain, renderSkillRadarMarkdown, } from '../skills/SkillRadar.js';
27
31
  import { listLeadershipPresets, renderLeadershipPresetsMarkdown } from '../agents/LeadershipPresets.js';
28
32
  import { listWorkflowPresets, getPresetsByScenario } from '../workflows/presets.js';
29
33
  import { EvidenceStore } from '../workflow/EvidenceStore.js';
@@ -33,6 +37,9 @@ import { WorkflowEngine } from '../workflow/WorkflowEngine.js';
33
37
  import { resolveVerificationTargets, } from '../workflow/VerificationProfile.js';
34
38
  import { writeGovernanceTemplates } from '../workflow/GovernanceTemplates.js';
35
39
  import { computeGovernanceDrift } from '../workflow/GovernanceLock.js';
40
+ import { applyUpgradePlan, createThirdPartyUpdateReport, createUpgradeCheckReport, createUpgradePlanReport, rollbackLatestUpgrade, writeUpgradePlanHtml, } from '../workflow/UpgradeManager.js';
41
+ import { createGovernanceRoiReport } from '../governance/GovernanceRoi.js';
42
+ import { evaluateProgressiveGovernance, normalizeGovernanceMode } from '../governance/ProgressiveGovernance.js';
36
43
  import { baselineEngineeringStandards, doctorEngineeringStandards, scanEngineeringStandards, settleEngineeringStandards, } from '../workflow/EngineeringStandards.js';
37
44
  import { doctorResourceAssets, scanResourceAssets, settleResourceAssets } from '../workflow/ResourceGovernance.js';
38
45
  import { analyzeContextGovernance, renderContextGrillPrompt, writeContextGovernanceTemplates, } from '../workflow/ContextGovernance.js';
@@ -48,13 +55,14 @@ import { ToolEvidenceStore } from '../tools/ToolEvidenceStore.js';
48
55
  import { ToolOrchestrator } from '../tools/ToolOrchestrator.js';
49
56
  import { loadToolPolicy, toolPolicyTemplate } from '../tools/ToolPolicy.js';
50
57
  import { doctorHtmlArtifacts, renderHtmlArtifact, resolveHtmlArtifactForOpen, settleHtmlArtifacts, } from '../output/HTMLArtifactLayer.js';
58
+ import { renderGovernanceDashboard } from '../output/GovernanceDashboard.js';
51
59
  import { cleanupWorkspaceLifecycle, inspectWorkspaceLifecycle, } from '../workflow/WorkspaceLifecycle.js';
52
60
  import { inspectWorkspaceSafety } from '../workflow/WorkspaceSafety.js';
53
61
  import { RuntimeEvidenceLedger, SessionLedger, doctorRuntimeEvidence, evaluateFinalReportReadiness, } from '../runtime/index.js';
54
- import { MemoryFabric, doctorMemoryFabric, renderContextPackMarkdown, renderMemoryLearningCandidateMarkdown, settleMemoryLearning, } from '../memory/index.js';
62
+ import { MemoryFabric, MemoryBrain, doctorMemoryFabric, renderContextPackMarkdown, renderMemoryLearningCandidateMarkdown, settleMemoryLearning, } from '../memory/index.js';
55
63
  import { resolveWorkspaceTopology, workspaceTopologyPath, workspaceTopologyTemplate, } from '../workflow/WorkspaceTopology.js';
56
64
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
57
- import { isAbsolute, join, resolve } from 'node:path';
65
+ import { dirname, isAbsolute, join, resolve } from 'node:path';
58
66
  import { execFileSync } from 'node:child_process';
59
67
  import { pathToFileURL } from 'node:url';
60
68
  import { SCALE_ENGINE_VERSION } from '../version.js';
@@ -919,9 +927,546 @@ const contextGrill = defineCommand({
919
927
  console.log(`\nArtifact: ${artifactPath}`);
920
928
  },
921
929
  });
930
+ function parsePositiveIntArg(value, name) {
931
+ if (value === undefined || value === null || value === '')
932
+ return undefined;
933
+ const parsed = Number.parseInt(String(value), 10);
934
+ if (Number.isNaN(parsed) || parsed <= 0) {
935
+ throw new Error(`${name} must be a positive integer.`);
936
+ }
937
+ return parsed;
938
+ }
939
+ const contextBudget = defineCommand({
940
+ meta: { name: 'budget', description: 'Report Always/on-demand/evidence/archive/generated context token cost' },
941
+ args: {
942
+ dir: { type: 'string', default: PROJECT_DIR, description: 'Project directory' },
943
+ 'max-always': { type: 'string', description: 'Maximum Always-loaded estimated tokens' },
944
+ 'max-task': { type: 'string', description: 'Maximum task context estimated tokens' },
945
+ write: { type: 'boolean', default: false, description: 'Write .scale/context-budget.json' },
946
+ json: { type: 'boolean', default: false },
947
+ },
948
+ run({ args }) {
949
+ const projectDir = resolve(String(args.dir ?? PROJECT_DIR));
950
+ const scaleDir = resolveScaleDirForProject(projectDir);
951
+ const report = scanContextBudget({
952
+ projectDir,
953
+ scaleDir,
954
+ maxAlwaysTokens: parsePositiveIntArg(args['max-always'], '--max-always'),
955
+ maxTaskTokens: parsePositiveIntArg(args['max-task'], '--max-task'),
956
+ });
957
+ const path = isTruthyFlag(args.write) ? writeContextBudgetReport(report) : undefined;
958
+ if (args.json) {
959
+ console.log(JSON.stringify({ ...report, path }, null, 2));
960
+ return;
961
+ }
962
+ console.log('SCALE Context Budget');
963
+ console.log(` Project: ${report.projectDir}`);
964
+ console.log(` Total: ${report.summary.totalTokens} estimated tokens across ${report.summary.totalFiles} files`);
965
+ console.log(` Always: ${report.summary.alwaysTokens}/${report.thresholds.maxAlwaysTokens}`);
966
+ for (const [category, summary] of Object.entries(report.summary.byCategory)) {
967
+ console.log(` ${category}: ${summary.tokens} tokens in ${summary.files} files`);
968
+ }
969
+ for (const recommendation of report.recommendations)
970
+ console.log(` recommendation: ${recommendation}`);
971
+ if (path)
972
+ console.log(` wrote: ${path}`);
973
+ },
974
+ });
975
+ const contextPack = defineCommand({
976
+ meta: { name: 'pack', description: 'Build a lazy-loaded context pack for a task' },
977
+ args: {
978
+ dir: { type: 'string', default: PROJECT_DIR, description: 'Project directory' },
979
+ task: { type: 'string', required: true, description: 'Current task or question' },
980
+ 'task-id': { type: 'string', description: 'Task id' },
981
+ level: { type: 'string', default: 'M', description: 'Task level: S, M, L, or CRITICAL' },
982
+ files: { type: 'string', description: 'Comma-separated files or modules in scope' },
983
+ budget: { type: 'string', description: 'Maximum estimated tokens for the context pack' },
984
+ json: { type: 'boolean', default: false },
985
+ },
986
+ run({ args }) {
987
+ const projectDir = resolve(String(args.dir ?? PROJECT_DIR));
988
+ const scaleDir = resolveScaleDirForProject(projectDir);
989
+ const pack = buildContextPack({
990
+ projectDir,
991
+ scaleDir,
992
+ task: String(args.task),
993
+ taskId: args['task-id'] ? String(args['task-id']) : undefined,
994
+ level: String(args.level ?? 'M'),
995
+ files: parseCommaList(args.files),
996
+ budget: parsePositiveIntArg(args.budget, '--budget'),
997
+ });
998
+ if (args.json) {
999
+ console.log(JSON.stringify(pack, null, 2));
1000
+ return;
1001
+ }
1002
+ console.log('SCALE Context Pack');
1003
+ console.log(` Task: ${pack.task.task}`);
1004
+ console.log(` Budget: ${pack.totalEstimatedTokens}/${pack.task.budget}`);
1005
+ for (const section of pack.sections) {
1006
+ console.log(` [${section.included ? 'IN' : 'OUT'}] ${section.id}: ${section.estimatedTokens} tokens`);
1007
+ }
1008
+ },
1009
+ });
1010
+ const contextDoctor = defineCommand({
1011
+ meta: { name: 'doctor', description: 'Check context budget thresholds and generated-artifact loading risk' },
1012
+ args: {
1013
+ dir: { type: 'string', default: PROJECT_DIR, description: 'Project directory' },
1014
+ 'max-always': { type: 'string', description: 'Maximum Always-loaded estimated tokens' },
1015
+ 'max-task': { type: 'string', description: 'Maximum task context estimated tokens' },
1016
+ json: { type: 'boolean', default: false },
1017
+ },
1018
+ run({ args }) {
1019
+ const projectDir = resolve(String(args.dir ?? PROJECT_DIR));
1020
+ const scaleDir = resolveScaleDirForProject(projectDir);
1021
+ const report = doctorContextBudget({
1022
+ projectDir,
1023
+ scaleDir,
1024
+ maxAlwaysTokens: parsePositiveIntArg(args['max-always'], '--max-always'),
1025
+ maxTaskTokens: parsePositiveIntArg(args['max-task'], '--max-task'),
1026
+ });
1027
+ if (args.json) {
1028
+ console.log(JSON.stringify(report, null, 2));
1029
+ if (!report.ok)
1030
+ process.exitCode = 1;
1031
+ return;
1032
+ }
1033
+ console.log(`SCALE Context Doctor: ${report.ok ? 'OK' : 'FAILED'}`);
1034
+ for (const check of report.checks) {
1035
+ console.log(` [${check.status.toUpperCase()}] ${check.name}: ${check.message}`);
1036
+ }
1037
+ if (!report.ok)
1038
+ process.exitCode = 1;
1039
+ },
1040
+ });
922
1041
  const context = defineCommand({
923
1042
  meta: { name: 'context', description: 'Context assembly' },
924
- subCommands: { build: contextBuild, status: contextStatus, inject: contextInject, glossary: contextGlossary, init: contextInit, grill: contextGrill },
1043
+ subCommands: { build: contextBuild, status: contextStatus, inject: contextInject, glossary: contextGlossary, init: contextInit, grill: contextGrill, budget: contextBudget, pack: contextPack, doctor: contextDoctor },
1044
+ });
1045
+ // ============================================================================
1046
+ // codegraph command - Adapter-first code intelligence
1047
+ // ============================================================================
1048
+ const codegraphStatus = defineCommand({
1049
+ meta: { name: 'status', description: 'Inspect CodeGraph, Graphify, and fallback code intelligence providers' },
1050
+ args: {
1051
+ dir: { type: 'string', default: PROJECT_DIR, description: 'Project directory' },
1052
+ json: { type: 'boolean', default: false },
1053
+ },
1054
+ run({ args }) {
1055
+ const projectDir = resolve(String(args.dir ?? PROJECT_DIR));
1056
+ const scaleDir = resolveScaleDirForProject(projectDir);
1057
+ const report = inspectCodeIntelligence({
1058
+ projectDir,
1059
+ scaleDir,
1060
+ });
1061
+ if (args.json) {
1062
+ console.log(JSON.stringify(report, null, 2));
1063
+ return;
1064
+ }
1065
+ console.log('SCALE Code Intelligence Status');
1066
+ console.log(` Config: ${report.configPath} (${report.configExists ? 'found' : 'default'})`);
1067
+ for (const provider of report.providers) {
1068
+ console.log(` [${provider.available ? 'AVAILABLE' : 'UNAVAILABLE'}] ${provider.id} (${provider.type}): ${provider.reason}`);
1069
+ }
1070
+ console.log(` Fallback: ${report.fallback.available ? 'available' : 'disabled'} (${report.fallback.tools.join(', ')})`);
1071
+ for (const recommendation of report.recommendations)
1072
+ console.log(` recommendation: ${recommendation}`);
1073
+ },
1074
+ });
1075
+ const codegraphInit = defineCommand({
1076
+ meta: { name: 'init', description: 'Create .scale/code-intelligence.json provider configuration' },
1077
+ args: {
1078
+ dir: { type: 'string', default: PROJECT_DIR, description: 'Project directory' },
1079
+ force: { type: 'boolean', default: false, description: 'Overwrite existing configuration' },
1080
+ json: { type: 'boolean', default: false },
1081
+ },
1082
+ run({ args }) {
1083
+ const projectDir = resolve(String(args.dir ?? PROJECT_DIR));
1084
+ const scaleDir = resolveScaleDirForProject(projectDir);
1085
+ const result = writeCodeIntelligenceConfig({
1086
+ projectDir,
1087
+ scaleDir,
1088
+ force: isTruthyFlag(args.force),
1089
+ });
1090
+ if (args.json) {
1091
+ console.log(JSON.stringify(result, null, 2));
1092
+ return;
1093
+ }
1094
+ console.log(`SCALE Code Intelligence Config: ${result.written ? 'written' : 'exists'}`);
1095
+ console.log(` ${result.path}`);
1096
+ },
1097
+ });
1098
+ const codegraphQuery = defineCommand({
1099
+ meta: { name: 'query', description: 'Query code intelligence providers, with explicit fallback when graph data is unavailable' },
1100
+ args: {
1101
+ query: { type: 'positional', required: true, description: 'Symbol, function, class, route, or text query' },
1102
+ dir: { type: 'string', default: PROJECT_DIR, description: 'Project directory' },
1103
+ json: { type: 'boolean', default: false },
1104
+ },
1105
+ run({ args }) {
1106
+ const projectDir = resolve(String(args.dir ?? PROJECT_DIR));
1107
+ const scaleDir = resolveScaleDirForProject(projectDir);
1108
+ const report = queryCodeGraph({
1109
+ projectDir,
1110
+ scaleDir,
1111
+ query: String(args.query),
1112
+ });
1113
+ if (args.json) {
1114
+ console.log(JSON.stringify(report, null, 2));
1115
+ return;
1116
+ }
1117
+ printCodeGraphReport(report);
1118
+ },
1119
+ });
1120
+ const codegraphImpact = defineCommand({
1121
+ meta: { name: 'impact', description: 'Find likely impacted files for a symbol' },
1122
+ args: {
1123
+ symbol: { type: 'string', required: true, description: 'Symbol to analyze' },
1124
+ dir: { type: 'string', default: PROJECT_DIR, description: 'Project directory' },
1125
+ json: { type: 'boolean', default: false },
1126
+ },
1127
+ run({ args }) {
1128
+ const projectDir = resolve(String(args.dir ?? PROJECT_DIR));
1129
+ const scaleDir = resolveScaleDirForProject(projectDir);
1130
+ const report = impactCodeGraph({
1131
+ projectDir,
1132
+ scaleDir,
1133
+ symbol: String(args.symbol),
1134
+ });
1135
+ if (args.json) {
1136
+ console.log(JSON.stringify(report, null, 2));
1137
+ return;
1138
+ }
1139
+ printCodeGraphReport(report);
1140
+ },
1141
+ });
1142
+ const codegraphContext = defineCommand({
1143
+ meta: { name: 'context', description: 'Build a budgeted file context recommendation from code intelligence' },
1144
+ args: {
1145
+ symbol: { type: 'string', required: true, description: 'Symbol to analyze' },
1146
+ budget: { type: 'string', description: 'Maximum estimated tokens for recommended files' },
1147
+ dir: { type: 'string', default: PROJECT_DIR, description: 'Project directory' },
1148
+ json: { type: 'boolean', default: false },
1149
+ },
1150
+ run({ args }) {
1151
+ const projectDir = resolve(String(args.dir ?? PROJECT_DIR));
1152
+ const scaleDir = resolveScaleDirForProject(projectDir);
1153
+ const report = buildCodeGraphContext({
1154
+ projectDir,
1155
+ scaleDir,
1156
+ symbol: String(args.symbol),
1157
+ budget: parsePositiveIntArg(args.budget, '--budget'),
1158
+ });
1159
+ if (args.json) {
1160
+ console.log(JSON.stringify(report, null, 2));
1161
+ return;
1162
+ }
1163
+ printCodeGraphReport(report);
1164
+ console.log(` Context budget: ${report.totalEstimatedTokens}/${report.budget}`);
1165
+ for (const file of report.contextFiles) {
1166
+ console.log(` [${file.included ? 'IN' : 'OUT'}] ${file.path}: ${file.estimatedTokens} tokens`);
1167
+ }
1168
+ },
1169
+ });
1170
+ const codegraphRoi = defineCommand({
1171
+ meta: { name: 'roi', description: 'Estimate exploration ROI from code intelligence or fallback query results' },
1172
+ args: {
1173
+ query: { type: 'string', description: 'Text query' },
1174
+ symbol: { type: 'string', description: 'Symbol to analyze' },
1175
+ dir: { type: 'string', default: PROJECT_DIR, description: 'Project directory' },
1176
+ json: { type: 'boolean', default: false },
1177
+ },
1178
+ run({ args }) {
1179
+ if (!args.query && !args.symbol)
1180
+ throw new Error('Provide --query or --symbol.');
1181
+ const projectDir = resolve(String(args.dir ?? PROJECT_DIR));
1182
+ const scaleDir = resolveScaleDirForProject(projectDir);
1183
+ const report = createCodeGraphRoiReport({
1184
+ projectDir,
1185
+ scaleDir,
1186
+ query: args.query ? String(args.query) : undefined,
1187
+ symbol: args.symbol ? String(args.symbol) : undefined,
1188
+ });
1189
+ if (args.json) {
1190
+ console.log(JSON.stringify(report, null, 2));
1191
+ return;
1192
+ }
1193
+ console.log('SCALE Code Intelligence ROI');
1194
+ console.log(` Query: ${report.query}`);
1195
+ console.log(` Provider: ${report.provider ?? 'fallback'}`);
1196
+ console.log(` Fallback: ${report.fallbackUsed}`);
1197
+ console.log(` Graph hits: ${report.metrics.graphHits}`);
1198
+ console.log(` Reads saved: ${report.metrics.fileReadsSaved}`);
1199
+ console.log(` Recommendation: ${report.recommendation}`);
1200
+ },
1201
+ });
1202
+ function printCodeGraphReport(report) {
1203
+ console.log('SCALE Code Intelligence');
1204
+ console.log(` Mode: ${report.mode}`);
1205
+ console.log(` Query: ${report.query}`);
1206
+ console.log(` Provider: ${report.provider ?? 'fallback'}`);
1207
+ console.log(` Fallback used: ${report.fallbackUsed}`);
1208
+ console.log(` Confidence: ${report.confidence}`);
1209
+ console.log(` Files: ${report.files.length}`);
1210
+ console.log(` Estimated reads saved: ${report.roi.fileReadsSaved}`);
1211
+ for (const hit of report.hits.slice(0, 12)) {
1212
+ const line = hit.line ? `:${hit.line}` : '';
1213
+ const symbol = hit.symbol ? ` ${hit.symbol}` : '';
1214
+ console.log(` - ${hit.file}${line}${symbol} (${hit.reason})`);
1215
+ }
1216
+ for (const warning of report.warnings)
1217
+ console.log(` warning: ${warning}`);
1218
+ }
1219
+ const codegraph = defineCommand({
1220
+ meta: { name: 'codegraph', description: 'Adapter-first code intelligence and exploration ROI' },
1221
+ subCommands: { status: codegraphStatus, init: codegraphInit, query: codegraphQuery, impact: codegraphImpact, context: codegraphContext, roi: codegraphRoi },
1222
+ });
1223
+ // ============================================================================
1224
+ // eval command - Workflow eval baseline and failure replay
1225
+ // ============================================================================
1226
+ const evalInit = defineCommand({
1227
+ meta: { name: 'init', description: 'Create a lightweight workflow eval suite under .scale/evals' },
1228
+ args: {
1229
+ dir: { type: 'string', default: PROJECT_DIR, description: 'Project directory' },
1230
+ suite: { type: 'string', default: 'workflow-baseline', description: 'Suite id' },
1231
+ force: { type: 'boolean', default: false, description: 'Overwrite the existing suite file' },
1232
+ json: { type: 'boolean', default: false },
1233
+ },
1234
+ run({ args }) {
1235
+ const projectDir = resolve(String(args.dir ?? PROJECT_DIR));
1236
+ const scaleDir = resolveScaleDirForProject(projectDir);
1237
+ const store = new WorkflowEvalStore({
1238
+ projectDir,
1239
+ scaleDir,
1240
+ });
1241
+ const result = store.initSuite(String(args.suite ?? 'workflow-baseline'), isTruthyFlag(args.force));
1242
+ if (args.json) {
1243
+ console.log(JSON.stringify(result, null, 2));
1244
+ return;
1245
+ }
1246
+ console.log(`SCALE Workflow Eval Suite: ${result.written ? 'written' : 'exists'}`);
1247
+ console.log(` Suite: ${result.suite.id}`);
1248
+ console.log(` Path: ${result.path}`);
1249
+ console.log(` Cases: ${result.suite.cases.length}`);
1250
+ },
1251
+ });
1252
+ const evalRun = defineCommand({
1253
+ meta: { name: 'run', description: 'Run a workflow eval suite and preserve failure replay artifacts' },
1254
+ args: {
1255
+ dir: { type: 'string', default: PROJECT_DIR, description: 'Project directory' },
1256
+ suite: { type: 'string', default: 'workflow-baseline', description: 'Suite id or JSON path' },
1257
+ json: { type: 'boolean', default: false },
1258
+ },
1259
+ async run({ args }) {
1260
+ const projectDir = resolve(String(args.dir ?? PROJECT_DIR));
1261
+ const scaleDir = resolveScaleDirForProject(projectDir);
1262
+ const result = await runWorkflowEvalSuite({
1263
+ projectDir,
1264
+ scaleDir,
1265
+ suite: String(args.suite ?? 'workflow-baseline'),
1266
+ });
1267
+ if (args.json) {
1268
+ console.log(JSON.stringify(result, null, 2));
1269
+ if (!result.run.ok)
1270
+ process.exitCode = 1;
1271
+ return;
1272
+ }
1273
+ console.log(`SCALE Workflow Eval: ${result.run.ok ? 'PASS' : 'FAIL'}`);
1274
+ console.log(` Run: ${result.run.id}`);
1275
+ console.log(` Suite: ${result.run.suiteId}`);
1276
+ console.log(` Pass@1: ${(result.run.metrics.passAt1Rate * 100).toFixed(1)}%`);
1277
+ console.log(` Pass@3: ${(result.run.metrics.passAt3Rate * 100).toFixed(1)}%`);
1278
+ console.log(` Tool calls: ${result.run.metrics.totalToolCalls}`);
1279
+ console.log(` Estimated tokens: ${result.run.metrics.estimatedTokens}`);
1280
+ console.log(` Failures: ${result.run.metrics.failureReplayCount}`);
1281
+ console.log(` Run path: ${result.runPath}`);
1282
+ for (const failurePath of result.failurePaths)
1283
+ console.log(` Failure replay: ${failurePath}`);
1284
+ if (!result.run.ok)
1285
+ process.exitCode = 1;
1286
+ },
1287
+ });
1288
+ const evalCompare = defineCommand({
1289
+ meta: { name: 'compare', description: 'Compare two workflow eval runs by pass rate, iterations, tool calls, and token estimate' },
1290
+ args: {
1291
+ dir: { type: 'string', default: PROJECT_DIR, description: 'Project directory' },
1292
+ baseline: { type: 'string', required: true, description: 'Baseline run id or JSON path' },
1293
+ candidate: { type: 'string', required: true, description: 'Candidate run id or JSON path' },
1294
+ json: { type: 'boolean', default: false },
1295
+ },
1296
+ run({ args }) {
1297
+ const projectDir = resolve(String(args.dir ?? PROJECT_DIR));
1298
+ const scaleDir = resolveScaleDirForProject(projectDir);
1299
+ const comparison = compareWorkflowEvalRuns({
1300
+ projectDir,
1301
+ scaleDir,
1302
+ baseline: String(args.baseline),
1303
+ candidate: String(args.candidate),
1304
+ });
1305
+ if (args.json) {
1306
+ console.log(JSON.stringify(comparison, null, 2));
1307
+ return;
1308
+ }
1309
+ console.log(`SCALE Workflow Eval Compare: ${comparison.recommendation}`);
1310
+ console.log(` Baseline: ${comparison.baseline.id}`);
1311
+ console.log(` Candidate: ${comparison.candidate.id}`);
1312
+ console.log(` Delta Pass@1: ${(comparison.delta.passAt1Rate * 100).toFixed(1)}%`);
1313
+ console.log(` Delta Pass@3: ${(comparison.delta.passAt3Rate * 100).toFixed(1)}%`);
1314
+ console.log(` Delta fix iterations: ${comparison.delta.averageFixIterations.toFixed(2)}`);
1315
+ console.log(` Delta tool calls: ${comparison.delta.totalToolCalls}`);
1316
+ console.log(` Delta estimated tokens: ${comparison.delta.estimatedTokens}`);
1317
+ console.log(` Delta human corrections: ${comparison.delta.humanCorrections}`);
1318
+ },
1319
+ });
1320
+ const evalReport = defineCommand({
1321
+ meta: { name: 'report', description: 'Render a Markdown workflow eval report from a saved run' },
1322
+ args: {
1323
+ dir: { type: 'string', default: PROJECT_DIR, description: 'Project directory' },
1324
+ run: { type: 'string', required: true, description: 'Run id or JSON path' },
1325
+ output: { type: 'string', alias: 'o', description: 'Write report to a Markdown file' },
1326
+ json: { type: 'boolean', default: false },
1327
+ },
1328
+ run({ args }) {
1329
+ const projectDir = resolve(String(args.dir ?? PROJECT_DIR));
1330
+ const scaleDir = resolveScaleDirForProject(projectDir);
1331
+ const store = new WorkflowEvalStore({ projectDir, scaleDir });
1332
+ const run = store.loadRun(String(args.run));
1333
+ const markdown = renderWorkflowEvalReport(run);
1334
+ const outputPath = args.output ? resolve(projectDir, String(args.output)) : undefined;
1335
+ if (outputPath) {
1336
+ mkdirSync(dirname(outputPath), { recursive: true });
1337
+ writeFileSync(outputPath, markdown, 'utf-8');
1338
+ }
1339
+ if (args.json) {
1340
+ console.log(JSON.stringify({ runId: run.id, outputPath, markdown }, null, 2));
1341
+ return;
1342
+ }
1343
+ if (outputPath)
1344
+ console.log(`Workflow eval report written: ${outputPath}`);
1345
+ else
1346
+ console.log(markdown);
1347
+ },
1348
+ });
1349
+ const evalFailures = defineCommand({
1350
+ meta: { name: 'failures', description: 'List failure replay records for workflow improvement' },
1351
+ args: {
1352
+ dir: { type: 'string', default: PROJECT_DIR, description: 'Project directory' },
1353
+ 'task-id': { type: 'string', description: 'Filter by task/case id' },
1354
+ since: { type: 'string', default: '30d', description: 'Window such as 30d; use all for no date filter' },
1355
+ json: { type: 'boolean', default: false },
1356
+ },
1357
+ run({ args }) {
1358
+ const projectDir = resolve(String(args.dir ?? PROJECT_DIR));
1359
+ const scaleDir = resolveScaleDirForProject(projectDir);
1360
+ const store = new WorkflowEvalStore({
1361
+ projectDir,
1362
+ scaleDir,
1363
+ });
1364
+ const failures = store.listFailures({
1365
+ taskId: args['task-id'] ? String(args['task-id']) : undefined,
1366
+ sinceDays: parseSinceDays(args.since),
1367
+ });
1368
+ if (args.json) {
1369
+ console.log(JSON.stringify({ count: failures.length, failures }, null, 2));
1370
+ return;
1371
+ }
1372
+ console.log(`SCALE Failure Replays: ${failures.length}`);
1373
+ for (const failure of failures) {
1374
+ console.log(` [${failure.status}] ${failure.id} ${failure.category} task=${failure.taskId}`);
1375
+ console.log(` prevention: ${failure.prevention}`);
1376
+ }
1377
+ },
1378
+ });
1379
+ const evalReplay = defineCommand({
1380
+ meta: { name: 'replay', description: 'Show failure replay records by failure id or task id' },
1381
+ args: {
1382
+ id: { type: 'positional', description: 'Failure replay id' },
1383
+ dir: { type: 'string', default: PROJECT_DIR, description: 'Project directory' },
1384
+ 'task-id': { type: 'string', description: 'Task/case id to replay' },
1385
+ json: { type: 'boolean', default: false },
1386
+ },
1387
+ run({ args }) {
1388
+ const projectDir = resolve(String(args.dir ?? PROJECT_DIR));
1389
+ const scaleDir = resolveScaleDirForProject(projectDir);
1390
+ const store = new WorkflowEvalStore({
1391
+ projectDir,
1392
+ scaleDir,
1393
+ });
1394
+ const failures = args.id
1395
+ ? [store.getFailure(String(args.id))].filter(Boolean)
1396
+ : store.listFailures({ taskId: args['task-id'] ? String(args['task-id']) : undefined });
1397
+ if (args.json) {
1398
+ console.log(JSON.stringify({ count: failures.length, failures }, null, 2));
1399
+ if (failures.length === 0)
1400
+ process.exitCode = 1;
1401
+ return;
1402
+ }
1403
+ if (failures.length === 0) {
1404
+ console.log('No failure replay records found.');
1405
+ process.exitCode = 1;
1406
+ return;
1407
+ }
1408
+ for (const failure of failures) {
1409
+ if (!failure)
1410
+ continue;
1411
+ console.log(`Failure Replay: ${failure.id}`);
1412
+ console.log(` Task: ${failure.task}`);
1413
+ console.log(` Category: ${failure.category}`);
1414
+ console.log(` Phase: ${failure.phase}`);
1415
+ console.log(` Wrong turn: ${failure.wrongTurn}`);
1416
+ console.log(` Evidence: ${failure.evidence}`);
1417
+ console.log(` Correction: ${failure.correction}`);
1418
+ console.log(` Prevention: ${failure.prevention}`);
1419
+ if (failure.replayCommand)
1420
+ console.log(` Replay command: ${failure.replayCommand}`);
1421
+ }
1422
+ },
1423
+ });
1424
+ const evalPromoteFailure = defineCommand({
1425
+ meta: { name: 'promote-failure', description: 'Promote a failure replay into a workflow improvement candidate' },
1426
+ args: {
1427
+ id: { type: 'positional', required: true, description: 'Failure replay id' },
1428
+ dir: { type: 'string', default: PROJECT_DIR, description: 'Project directory' },
1429
+ json: { type: 'boolean', default: false },
1430
+ },
1431
+ run({ args }) {
1432
+ const projectDir = resolve(String(args.dir ?? PROJECT_DIR));
1433
+ const scaleDir = resolveScaleDirForProject(projectDir);
1434
+ const store = new WorkflowEvalStore({
1435
+ projectDir,
1436
+ scaleDir,
1437
+ });
1438
+ const candidate = store.promoteFailure(String(args.id));
1439
+ if (args.json) {
1440
+ console.log(JSON.stringify(candidate, null, 2));
1441
+ return;
1442
+ }
1443
+ console.log(`Workflow improvement candidate: ${candidate.id}`);
1444
+ console.log(` Failure: ${candidate.failureId}`);
1445
+ console.log(` Category: ${candidate.category}`);
1446
+ console.log(` Recommendation: ${candidate.recommendation}`);
1447
+ },
1448
+ });
1449
+ function parseSinceDays(value) {
1450
+ const text = String(value ?? '').trim().toLowerCase();
1451
+ if (!text || text === 'all')
1452
+ return undefined;
1453
+ const match = text.match(/^(\d+)(d|day|days)?$/);
1454
+ if (!match)
1455
+ return undefined;
1456
+ const days = Number.parseInt(match[1], 10);
1457
+ return Number.isFinite(days) && days > 0 ? days : undefined;
1458
+ }
1459
+ const evalCommand = defineCommand({
1460
+ meta: { name: 'eval', description: 'Workflow eval harness, pass@k metrics, and failure replay' },
1461
+ subCommands: {
1462
+ init: evalInit,
1463
+ run: evalRun,
1464
+ compare: evalCompare,
1465
+ report: evalReport,
1466
+ failures: evalFailures,
1467
+ replay: evalReplay,
1468
+ 'promote-failure': evalPromoteFailure,
1469
+ },
925
1470
  });
926
1471
  // ============================================================================
927
1472
  // diagnose command - evidence-first debugging loop
@@ -1850,38 +2395,242 @@ const init = defineCommand({
1850
2395
  console.log(` → ${step}`);
1851
2396
  },
1852
2397
  });
1853
- // ============================================================================
1854
- // governance command — Generated governance asset tooling
1855
- // ============================================================================
1856
- const governanceDiff = defineCommand({
1857
- meta: { name: 'diff', description: 'Check generated governance files for drift' },
2398
+ // ============================================================================
2399
+ // governance command — Generated governance asset tooling
2400
+ // ============================================================================
2401
+ const governanceDiff = defineCommand({
2402
+ meta: { name: 'diff', description: 'Check generated governance files for drift' },
2403
+ args: {
2404
+ dir: { type: 'string', default: '.', description: 'Project directory' },
2405
+ json: { type: 'boolean', default: false, description: 'Print JSON output' },
2406
+ },
2407
+ run({ args }) {
2408
+ const report = computeGovernanceDrift(args.dir);
2409
+ if (args.json) {
2410
+ console.log(JSON.stringify(report, null, 2));
2411
+ return;
2412
+ }
2413
+ if (!report.lockExists) {
2414
+ console.log('No governance lock found. Run: scale init --governance-pack <pack>');
2415
+ return;
2416
+ }
2417
+ if (report.missing.length === 0 && report.changed.length === 0) {
2418
+ console.log('Governance generated files are clean.');
2419
+ return;
2420
+ }
2421
+ for (const item of report.missing)
2422
+ console.log(`missing: ${item.path}`);
2423
+ for (const item of report.changed)
2424
+ console.log(`changed: ${item.path}`);
2425
+ },
2426
+ });
2427
+ const governanceModeCommand = defineCommand({
2428
+ meta: { name: 'mode', description: 'Evaluate progressive governance mode from task text and changed files' },
2429
+ args: {
2430
+ task: { type: 'string', description: 'Task or requirement description' },
2431
+ files: { type: 'string', description: 'Comma-separated changed or target files' },
2432
+ 'requested-mode': { type: 'string', description: 'Requested governance mode: minimal, standard, expanded, or critical' },
2433
+ json: { type: 'boolean', default: false },
2434
+ },
2435
+ run({ args }) {
2436
+ const report = evaluateProgressiveGovernance({
2437
+ task: args.task ? String(args.task) : undefined,
2438
+ changedFiles: parseCommaList(args.files),
2439
+ requestedMode: normalizeGovernanceMode(args['requested-mode']),
2440
+ });
2441
+ if (args.json) {
2442
+ console.log(JSON.stringify(report, null, 2));
2443
+ return;
2444
+ }
2445
+ console.log('SCALE Progressive Governance');
2446
+ console.log(` Recommended: ${report.recommendedMode}`);
2447
+ console.log(` Effective: ${report.effectiveMode}`);
2448
+ if (report.escalated)
2449
+ console.log(` Escalated from requested mode: ${report.requestedMode}`);
2450
+ for (const signal of report.signals) {
2451
+ console.log(` [${signal.mode}] ${signal.id}: ${signal.reason}`);
2452
+ }
2453
+ for (const behavior of report.requiredBehaviors) {
2454
+ console.log(` behavior: ${behavior}`);
2455
+ }
2456
+ },
2457
+ });
2458
+ const governanceRoiCommand = defineCommand({
2459
+ meta: { name: 'roi', description: 'Report benefit and overhead signals for active governance modules' },
2460
+ args: {
2461
+ dir: { type: 'string', default: PROJECT_DIR, description: 'Project directory' },
2462
+ 'task-id': { type: 'string', description: 'Task id' },
2463
+ task: { type: 'string', description: 'Task or requirement description' },
2464
+ files: { type: 'string', description: 'Comma-separated changed or target files' },
2465
+ 'requested-mode': { type: 'string', description: 'Requested governance mode' },
2466
+ 'code-query': { type: 'string', description: 'Optional code intelligence query to include in ROI' },
2467
+ symbol: { type: 'string', description: 'Optional symbol impact query to include in ROI' },
2468
+ json: { type: 'boolean', default: false },
2469
+ },
2470
+ run({ args }) {
2471
+ const projectDir = resolve(String(args.dir ?? PROJECT_DIR));
2472
+ const scaleDir = resolveScaleDirForProject(projectDir);
2473
+ const governanceReport = evaluateProgressiveGovernance({
2474
+ task: args.task ? String(args.task) : undefined,
2475
+ changedFiles: parseCommaList(args.files),
2476
+ requestedMode: normalizeGovernanceMode(args['requested-mode']),
2477
+ });
2478
+ const contextBudget = scanContextBudget({ projectDir, scaleDir });
2479
+ const codeIntelligence = args.symbol
2480
+ ? impactCodeGraph({ projectDir, scaleDir, symbol: String(args.symbol) })
2481
+ : args['code-query']
2482
+ ? queryCodeGraph({ projectDir, scaleDir, query: String(args['code-query']) })
2483
+ : undefined;
2484
+ const report = createGovernanceRoiReport({
2485
+ taskId: args['task-id'] ? String(args['task-id']) : undefined,
2486
+ contextBudget,
2487
+ governance: governanceReport,
2488
+ codeIntelligence,
2489
+ });
2490
+ if (args.json) {
2491
+ console.log(JSON.stringify(report, null, 2));
2492
+ return;
2493
+ }
2494
+ console.log('SCALE Governance ROI');
2495
+ console.log(` Recommendation: ${report.summary.recommendation}`);
2496
+ for (const module of report.modules) {
2497
+ console.log(` [${module.evidenceLevel}] ${module.module}`);
2498
+ console.log(` benefit: ${module.benefit}`);
2499
+ console.log(` overhead: ${module.overhead}`);
2500
+ console.log(` recommendation: ${module.recommendation}`);
2501
+ }
2502
+ },
2503
+ });
2504
+ const governance = defineCommand({
2505
+ meta: { name: 'governance', description: 'Governance template pack tools' },
2506
+ subCommands: { diff: governanceDiff, mode: governanceModeCommand, roi: governanceRoiCommand },
2507
+ });
2508
+ // ============================================================================
2509
+ // upgrade command - Safe workflow/template/capability update planning
2510
+ // ============================================================================
2511
+ const upgradeCheck = defineCommand({
2512
+ meta: { name: 'check', description: 'Check SCALE workflow, governance pack, and third-party capability update status' },
2513
+ args: {
2514
+ dir: { type: 'string', default: PROJECT_DIR, description: 'Project directory' },
2515
+ 'target-version': { type: 'string', description: 'Target SCALE Engine version; defaults to the running CLI version' },
2516
+ json: { type: 'boolean', default: false, description: 'Print JSON output' },
2517
+ },
2518
+ run({ args }) {
2519
+ const report = createUpgradeCheckReport({
2520
+ projectDir: args.dir,
2521
+ targetScaleVersion: args['target-version'] ? String(args['target-version']) : undefined,
2522
+ });
2523
+ if (args.json) {
2524
+ console.log(JSON.stringify(report, null, 2));
2525
+ return;
2526
+ }
2527
+ console.log('SCALE Upgrade Check');
2528
+ console.log(` Project: ${report.projectDir}`);
2529
+ console.log(` Status: ${report.status}`);
2530
+ console.log(` SCALE Engine: ${report.scaleEngine.currentVersion ?? 'none'} -> ${report.scaleEngine.latestVersion}`);
2531
+ console.log(` Governance pack: ${report.governancePack.id ?? 'none'} v${report.governancePack.currentVersion ?? 'none'} -> v${report.governancePack.latestVersion ?? 'none'}`);
2532
+ console.log(` Generated files: ${report.generatedFiles.clean} clean, ${report.generatedFiles.changed} changed, ${report.generatedFiles.missing} missing`);
2533
+ console.log(` Third-party policy: ${report.thirdParty.policy}; review required: ${report.thirdParty.reviewRequired}`);
2534
+ console.log(' Next:');
2535
+ for (const command of report.recommendedCommands)
2536
+ console.log(` ${command}`);
2537
+ },
2538
+ });
2539
+ const upgradePlan = defineCommand({
2540
+ meta: { name: 'plan', description: 'Create a non-destructive SCALE upgrade plan' },
1858
2541
  args: {
1859
- dir: { type: 'string', default: '.', description: 'Project directory' },
2542
+ dir: { type: 'string', default: PROJECT_DIR, description: 'Project directory' },
2543
+ 'target-version': { type: 'string', description: 'Target SCALE Engine version; defaults to the running CLI version' },
2544
+ html: { type: 'boolean', default: false, description: 'Write .scale/reports/upgrade-plan.html' },
1860
2545
  json: { type: 'boolean', default: false, description: 'Print JSON output' },
1861
2546
  },
1862
2547
  run({ args }) {
1863
- const report = computeGovernanceDrift(args.dir);
2548
+ const report = createUpgradePlanReport({
2549
+ projectDir: args.dir,
2550
+ targetScaleVersion: args['target-version'] ? String(args['target-version']) : undefined,
2551
+ });
2552
+ const htmlPath = args.html ? writeUpgradePlanHtml(report) : undefined;
1864
2553
  if (args.json) {
1865
- console.log(JSON.stringify(report, null, 2));
2554
+ console.log(JSON.stringify({ ...report, htmlPath }, null, 2));
1866
2555
  return;
1867
2556
  }
1868
- if (!report.lockExists) {
1869
- console.log('No governance lock found. Run: scale init --governance-pack <pack>');
2557
+ console.log('SCALE Upgrade Plan');
2558
+ console.log(` Project: ${report.projectDir}`);
2559
+ console.log(` Status: ${report.status}`);
2560
+ console.log(` Apply mode: ${report.applyMode}`);
2561
+ if (report.blockers.length > 0) {
2562
+ console.log(' Blockers:');
2563
+ for (const blocker of report.blockers)
2564
+ console.log(` [${blocker.code}] ${blocker.path ?? ''} ${blocker.message}`);
2565
+ }
2566
+ console.log(' Steps:');
2567
+ for (const step of report.steps) {
2568
+ const path = step.path ? ` ${step.path}` : '';
2569
+ const command = step.command ? ` -> ${step.command}` : '';
2570
+ console.log(` [${step.risk}] ${step.action}${path}: ${step.reason}${command}`);
2571
+ }
2572
+ if (htmlPath)
2573
+ console.log(` HTML: ${htmlPath}`);
2574
+ },
2575
+ });
2576
+ const upgradeApply = defineCommand({
2577
+ meta: { name: 'apply', description: 'Guarded entrypoint for applying an upgrade plan' },
2578
+ args: {
2579
+ dir: { type: 'string', default: PROJECT_DIR, description: 'Project directory' },
2580
+ confirm: { type: 'boolean', default: false, description: 'Confirm that the current plan was reviewed' },
2581
+ json: { type: 'boolean', default: false, description: 'Print JSON output' },
2582
+ },
2583
+ run({ args }) {
2584
+ const result = applyUpgradePlan({
2585
+ projectDir: args.dir,
2586
+ confirm: isTruthyFlag(args.confirm),
2587
+ });
2588
+ if (args.json) {
2589
+ console.log(JSON.stringify(result, null, 2));
2590
+ if (!result.ok)
2591
+ process.exitCode = 1;
1870
2592
  return;
1871
2593
  }
1872
- if (report.missing.length === 0 && report.changed.length === 0) {
1873
- console.log('Governance generated files are clean.');
2594
+ console.log('SCALE Upgrade Apply');
2595
+ console.log(` Applied: ${result.applied}`);
2596
+ console.log(` Reason: ${result.reason}`);
2597
+ console.log(` Apply mode: ${result.plan.applyMode}`);
2598
+ if (result.backup)
2599
+ console.log(` Backup: ${result.backup.manifestPath}`);
2600
+ for (const path of result.changedFiles)
2601
+ console.log(` changed: ${path}`);
2602
+ if (!result.ok)
2603
+ process.exitCode = 1;
2604
+ },
2605
+ });
2606
+ const upgradeRollback = defineCommand({
2607
+ meta: { name: 'rollback', description: 'Explain rollback state for SCALE upgrades' },
2608
+ args: {
2609
+ dir: { type: 'string', default: PROJECT_DIR, description: 'Project directory' },
2610
+ json: { type: 'boolean', default: false, description: 'Print JSON output' },
2611
+ },
2612
+ run({ args }) {
2613
+ const result = rollbackLatestUpgrade({ projectDir: args.dir });
2614
+ if (args.json) {
2615
+ console.log(JSON.stringify(result, null, 2));
2616
+ if (!result.ok)
2617
+ process.exitCode = 1;
1874
2618
  return;
1875
2619
  }
1876
- for (const item of report.missing)
1877
- console.log(`missing: ${item.path}`);
1878
- for (const item of report.changed)
1879
- console.log(`changed: ${item.path}`);
2620
+ console.log('SCALE Upgrade Rollback');
2621
+ console.log(` Applied: ${result.applied}`);
2622
+ console.log(` Reason: ${result.reason}`);
2623
+ if (result.backup)
2624
+ console.log(` Backup: ${result.backup.manifestPath}`);
2625
+ for (const path of result.restoredFiles)
2626
+ console.log(` restored: ${path}`);
2627
+ if (!result.ok)
2628
+ process.exitCode = 1;
1880
2629
  },
1881
2630
  });
1882
- const governance = defineCommand({
1883
- meta: { name: 'governance', description: 'Governance template pack tools' },
1884
- subCommands: { diff: governanceDiff },
2631
+ const upgrade = defineCommand({
2632
+ meta: { name: 'upgrade', description: 'Safe update planning for SCALE workflow, generated templates, skills, MCP, and CLI tools' },
2633
+ subCommands: { check: upgradeCheck, plan: upgradePlan, apply: upgradeApply, rollback: upgradeRollback },
1885
2634
  });
1886
2635
  // ============================================================================
1887
2636
  // assets command - Resource lifecycle governance
@@ -2283,9 +3032,46 @@ const artifactOpen = defineCommand({
2283
3032
  console.log(url);
2284
3033
  },
2285
3034
  });
3035
+ const artifactDashboard = defineCommand({
3036
+ meta: { name: 'dashboard', description: 'Render a governance HTML dashboard from runtime, eval, memory, resource, and artifact evidence' },
3037
+ args: {
3038
+ dir: { type: 'string', default: '.', description: 'Project directory' },
3039
+ 'task-id': { type: 'string', description: 'Optional task id to scope runtime/eval evidence and task HTML artifacts' },
3040
+ output: { type: 'string', alias: 'o', description: 'Output HTML path; defaults to .scale/reports/governance-dashboard.html' },
3041
+ theme: { type: 'string', default: 'auto', description: 'Theme mode: dark/light/auto' },
3042
+ lang: { type: 'string', default: 'zh', description: 'HTML language: zh/en' },
3043
+ json: { type: 'boolean', default: false, description: 'Print JSON output' },
3044
+ },
3045
+ run({ args }) {
3046
+ const projectDir = resolve(String(args.dir ?? PROJECT_DIR));
3047
+ const scaleDir = resolveScaleDirForProject(projectDir);
3048
+ const report = renderGovernanceDashboard({
3049
+ projectDir,
3050
+ scaleDir,
3051
+ taskId: typeof args['task-id'] === 'string' ? args['task-id'] : undefined,
3052
+ output: typeof args.output === 'string' ? args.output : undefined,
3053
+ theme: normalizeThemeArg(args.theme),
3054
+ lang: normalizeLangArg(args.lang),
3055
+ });
3056
+ if (args.json) {
3057
+ console.log(JSON.stringify(report, null, 2));
3058
+ if (!report.ok)
3059
+ process.exitCode = 1;
3060
+ return;
3061
+ }
3062
+ console.log(`SCALE Governance Dashboard: ${report.ok ? 'OK' : 'ATTENTION'}`);
3063
+ console.log(` HTML: ${report.outputPath}`);
3064
+ console.log(` Manifest: ${report.manifestPath}`);
3065
+ for (const finding of report.findings) {
3066
+ console.log(` [${finding.severity.toUpperCase()}] ${finding.code}: ${finding.message}`);
3067
+ }
3068
+ if (!report.ok)
3069
+ process.exitCode = 1;
3070
+ },
3071
+ });
2286
3072
  const artifact = defineCommand({
2287
3073
  meta: { name: 'artifact', description: 'Derived HTML artifact rendering and safety checks' },
2288
- subCommands: { render: artifactRender, doctor: artifactDoctor, settle: artifactSettle, open: artifactOpen },
3074
+ subCommands: { render: artifactRender, doctor: artifactDoctor, settle: artifactSettle, open: artifactOpen, dashboard: artifactDashboard },
2289
3075
  });
2290
3076
  function normalizeThemeArg(value) {
2291
3077
  const normalized = String(value ?? 'auto').trim().toLowerCase();
@@ -2706,6 +3492,31 @@ function parseMemoryBudget(value) {
2706
3492
  }
2707
3493
  return parsed;
2708
3494
  }
3495
+ function normalizeMemorySource(value) {
3496
+ const normalized = String(value ?? 'evidence').trim().toLowerCase();
3497
+ if (normalized === 'evidence' || normalized === 'candidate' || normalized === 'failure')
3498
+ return normalized;
3499
+ throw new Error('--from must be evidence, candidate, or failure.');
3500
+ }
3501
+ function normalizeMemoryNodeType(value) {
3502
+ if (value === undefined || value === null || value === '')
3503
+ return undefined;
3504
+ const normalized = String(value).trim().toLowerCase();
3505
+ if (normalized === 'fact' || normalized === 'decision' || normalized === 'incident' || normalized === 'relation' || normalized === 'contradiction')
3506
+ return normalized;
3507
+ throw new Error('--type must be fact, decision, incident, relation, or contradiction.');
3508
+ }
3509
+ function normalizeMemoryScope(value) {
3510
+ if (value === undefined || value === null || value === '')
3511
+ return undefined;
3512
+ const normalized = String(value).trim().toLowerCase();
3513
+ if (normalized === 'project' || normalized === 'workspace' || normalized === 'global-candidate')
3514
+ return normalized;
3515
+ throw new Error('--scope must be project, workspace, or global-candidate.');
3516
+ }
3517
+ function memoryBrain() {
3518
+ return new MemoryBrain({ projectDir: PROJECT_DIR, scaleDir: SCALE_DIR });
3519
+ }
2709
3520
  const memoryPack = defineCommand({
2710
3521
  meta: { name: 'pack', description: 'Build a compact context pack from runtime evidence, session events, knowledge, and graph status' },
2711
3522
  args: {
@@ -2840,9 +3651,233 @@ const memorySettle = defineCommand({
2840
3651
  console.log(`\nWrote: ${settlement.files.markdown}`);
2841
3652
  },
2842
3653
  });
3654
+ const memoryIngest = defineCommand({
3655
+ meta: { name: 'ingest', description: 'Ingest runtime evidence, learning candidates, or failure replays into the project memory brain' },
3656
+ args: {
3657
+ from: { type: 'string', default: 'evidence', description: 'Source: evidence, candidate, or failure' },
3658
+ 'task-id': { type: 'string', description: 'Task id to scope runtime evidence' },
3659
+ 'session-id': { type: 'string', description: 'Session id to scope runtime evidence' },
3660
+ 'candidate-id': { type: 'string', description: 'Memory learning candidate id' },
3661
+ 'failure-id': { type: 'string', description: 'Workflow eval failure replay id' },
3662
+ type: { type: 'string', description: 'Memory type override: fact/decision/incident/relation/contradiction' },
3663
+ scope: { type: 'string', description: 'Memory scope: project/workspace/global-candidate' },
3664
+ json: { type: 'boolean', default: false },
3665
+ },
3666
+ run({ args }) {
3667
+ let from;
3668
+ let type;
3669
+ let scope;
3670
+ try {
3671
+ from = normalizeMemorySource(args.from);
3672
+ type = normalizeMemoryNodeType(args.type);
3673
+ scope = normalizeMemoryScope(args.scope);
3674
+ }
3675
+ catch (error) {
3676
+ console.error(error.message);
3677
+ process.exit(1);
3678
+ return;
3679
+ }
3680
+ const report = memoryBrain().ingest({
3681
+ from,
3682
+ taskId: args['task-id'],
3683
+ sessionId: args['session-id'],
3684
+ candidateId: args['candidate-id'],
3685
+ failureId: args['failure-id'],
3686
+ type,
3687
+ scope,
3688
+ });
3689
+ if (args.json) {
3690
+ console.log(JSON.stringify(report, null, 2));
3691
+ if (!report.ok)
3692
+ process.exitCode = 1;
3693
+ return;
3694
+ }
3695
+ console.log('\nSCALE Memory Ingest');
3696
+ console.log(` Source: ${report.source}`);
3697
+ console.log(` Created: ${report.created}`);
3698
+ console.log(` Skipped: ${report.skipped}`);
3699
+ for (const node of report.nodes)
3700
+ console.log(` [${node.status}] ${node.id}: ${node.title}`);
3701
+ for (const warning of report.warnings)
3702
+ console.log(` [WARN] ${warning}`);
3703
+ if (!report.ok)
3704
+ process.exitCode = 1;
3705
+ },
3706
+ });
3707
+ const memoryQuery = defineCommand({
3708
+ meta: { name: 'query', description: 'Query concise project-scoped long-term memory with evidence references' },
3709
+ args: {
3710
+ query: { type: 'positional', required: true, description: 'Search query' },
3711
+ limit: { type: 'string', default: '8', description: 'Maximum number of memory nodes' },
3712
+ status: { type: 'string', description: 'Filter by status: candidate/active/stale/rejected' },
3713
+ json: { type: 'boolean', default: false },
3714
+ },
3715
+ run({ args }) {
3716
+ const limit = Number.parseInt(String(args.limit ?? '8'), 10);
3717
+ const status = args.status ? String(args.status) : undefined;
3718
+ const report = memoryBrain().query(String(args.query), {
3719
+ limit: Number.isFinite(limit) && limit > 0 ? limit : 8,
3720
+ status,
3721
+ });
3722
+ if (args.json) {
3723
+ console.log(JSON.stringify(report, null, 2));
3724
+ return;
3725
+ }
3726
+ console.log('\nSCALE Memory Query');
3727
+ console.log(` Query: ${report.query}`);
3728
+ console.log(` Results: ${report.count}`);
3729
+ for (const node of report.nodes) {
3730
+ console.log(` [${node.status}/${node.type}] ${node.id}: ${node.title}`);
3731
+ console.log(` confidence: ${node.confidence}; evidence: ${node.evidencePaths.join(', ') || 'none'}`);
3732
+ console.log(` ${node.summary}`);
3733
+ }
3734
+ },
3735
+ });
3736
+ const memoryContradictions = defineCommand({
3737
+ meta: { name: 'contradictions', description: 'Report conflicting project memory instead of silently resolving it' },
3738
+ args: {
3739
+ json: { type: 'boolean', default: false },
3740
+ },
3741
+ run({ args }) {
3742
+ const report = memoryBrain().contradictions();
3743
+ if (args.json) {
3744
+ console.log(JSON.stringify(report, null, 2));
3745
+ if (!report.ok)
3746
+ process.exitCode = 1;
3747
+ return;
3748
+ }
3749
+ console.log('\nSCALE Memory Contradictions');
3750
+ console.log(` Count: ${report.count}`);
3751
+ for (const item of report.contradictions) {
3752
+ console.log(` [CONFLICT] ${item.title}`);
3753
+ console.log(` nodes: ${item.nodeIds.join(', ')}`);
3754
+ console.log(` evidence: ${item.evidencePaths.join(', ') || 'none'}`);
3755
+ }
3756
+ if (!report.ok)
3757
+ process.exitCode = 1;
3758
+ },
3759
+ });
3760
+ const memoryDream = defineCommand({
3761
+ meta: { name: 'dream', description: 'Run memory maintenance: duplicates, stale memories, contradictions, and promotion candidates' },
3762
+ args: {
3763
+ json: { type: 'boolean', default: false },
3764
+ },
3765
+ run({ args }) {
3766
+ const report = memoryBrain().dream();
3767
+ if (args.json) {
3768
+ console.log(JSON.stringify(report, null, 2));
3769
+ if (!report.ok)
3770
+ process.exitCode = 1;
3771
+ return;
3772
+ }
3773
+ console.log('\nSCALE Memory Dream');
3774
+ console.log(` Total: ${report.summary.total}`);
3775
+ console.log(` Active: ${report.summary.active}`);
3776
+ console.log(` Candidates: ${report.summary.candidate}`);
3777
+ console.log(` Contradictions: ${report.summary.contradictions}`);
3778
+ console.log(` Duplicate groups: ${report.summary.duplicateGroups}`);
3779
+ for (const item of report.promotionCandidates)
3780
+ console.log(` [PROMOTE?] ${item.id}: ${item.title}`);
3781
+ for (const item of report.staleCandidates)
3782
+ console.log(` [STALE] ${item.id}: ${item.reason}`);
3783
+ if (!report.ok)
3784
+ process.exitCode = 1;
3785
+ },
3786
+ });
3787
+ const memoryPromote = defineCommand({
3788
+ meta: { name: 'promote', description: 'Promote a memory candidate to active project memory after evidence review' },
3789
+ args: {
3790
+ id: { type: 'positional', required: true, description: 'Memory node id or learning candidate id' },
3791
+ scope: { type: 'string', description: 'Scope override: project/workspace/global-candidate' },
3792
+ json: { type: 'boolean', default: false },
3793
+ },
3794
+ run({ args }) {
3795
+ let scope;
3796
+ try {
3797
+ scope = normalizeMemoryScope(args.scope);
3798
+ }
3799
+ catch (error) {
3800
+ console.error(error.message);
3801
+ process.exit(1);
3802
+ return;
3803
+ }
3804
+ const report = memoryBrain().promote(String(args.id), { scope });
3805
+ if (args.json) {
3806
+ console.log(JSON.stringify(report, null, 2));
3807
+ if (!report.ok)
3808
+ process.exitCode = 1;
3809
+ return;
3810
+ }
3811
+ console.log('\nSCALE Memory Promote');
3812
+ console.log(` Status: ${report.ok ? 'promoted' : 'blocked'}`);
3813
+ if (report.node)
3814
+ console.log(` Node: ${report.node.id} (${report.node.status})`);
3815
+ for (const warning of report.warnings)
3816
+ console.log(` [WARN] ${warning}`);
3817
+ if (!report.ok)
3818
+ process.exitCode = 1;
3819
+ },
3820
+ });
3821
+ const memoryExport = defineCommand({
3822
+ meta: { name: 'export', description: 'Export project memory as JSONL' },
3823
+ args: {
3824
+ output: { type: 'string', alias: 'o', description: 'Output JSONL file; stdout when omitted' },
3825
+ json: { type: 'boolean', default: false },
3826
+ },
3827
+ run({ args }) {
3828
+ const jsonl = memoryBrain().exportJsonl();
3829
+ if (args.output) {
3830
+ const outputPath = resolve(PROJECT_DIR, String(args.output));
3831
+ ensureDir(dirname(outputPath));
3832
+ writeFileSync(outputPath, jsonl, 'utf-8');
3833
+ if (args.json) {
3834
+ console.log(JSON.stringify({ ok: true, outputPath, bytes: jsonl.length }, null, 2));
3835
+ return;
3836
+ }
3837
+ console.log(`[OK] Memory JSONL exported: ${outputPath}`);
3838
+ return;
3839
+ }
3840
+ console.log(jsonl);
3841
+ },
3842
+ });
3843
+ const memoryImport = defineCommand({
3844
+ meta: { name: 'import', description: 'Import project memory from JSONL' },
3845
+ args: {
3846
+ file: { type: 'positional', required: true, description: 'Input JSONL file' },
3847
+ json: { type: 'boolean', default: false },
3848
+ },
3849
+ run({ args }) {
3850
+ const filePath = resolve(PROJECT_DIR, String(args.file));
3851
+ const report = memoryBrain().importJsonl(filePath);
3852
+ if (args.json) {
3853
+ console.log(JSON.stringify(report, null, 2));
3854
+ if (!report.ok)
3855
+ process.exitCode = 1;
3856
+ return;
3857
+ }
3858
+ console.log('\nSCALE Memory Import');
3859
+ console.log(` Imported: ${report.imported}`);
3860
+ console.log(` Skipped: ${report.skipped}`);
3861
+ for (const warning of report.warnings)
3862
+ console.log(` [WARN] ${warning}`);
3863
+ if (!report.ok)
3864
+ process.exitCode = 1;
3865
+ },
3866
+ });
2843
3867
  const memory = defineCommand({
2844
- meta: { name: 'memory', description: 'Memory Fabric context packs and budget diagnostics' },
2845
- subCommands: { pack: memoryPack, doctor: memoryDoctor, settle: memorySettle },
3868
+ meta: { name: 'memory', description: 'Memory Fabric context packs and project-scoped long-term memory' },
3869
+ subCommands: {
3870
+ pack: memoryPack,
3871
+ doctor: memoryDoctor,
3872
+ settle: memorySettle,
3873
+ ingest: memoryIngest,
3874
+ query: memoryQuery,
3875
+ contradictions: memoryContradictions,
3876
+ dream: memoryDream,
3877
+ promote: memoryPromote,
3878
+ export: memoryExport,
3879
+ import: memoryImport,
3880
+ },
2846
3881
  });
2847
3882
  // ============================================================================
2848
3883
  // out-of-scope command — 借鉴 mattpocock/skills 的 .out-of-scope/ 设计
@@ -3038,13 +4073,16 @@ const skillPlanCommand = defineCommand({
3038
4073
  const skillDoctorCommand = defineCommand({
3039
4074
  meta: { name: 'doctor', description: 'Check workflow skill installation status' },
3040
4075
  args: {
3041
- dir: { type: 'string', default: '.', description: 'Project directory' },
4076
+ dir: { type: 'string', default: PROJECT_DIR, description: 'Project directory' },
4077
+ 'supply-chain': { type: 'boolean', default: false, description: 'Include supply-chain safety review for known skill sources' },
3042
4078
  json: { type: 'boolean', default: false, description: 'Output skill doctor report as JSON' },
3043
4079
  },
3044
4080
  run({ args }) {
3045
- const report = inspectWorkflowSkills({ projectDir: args.dir });
4081
+ const projectDir = resolve(String(args.dir ?? PROJECT_DIR));
4082
+ const report = inspectWorkflowSkills({ projectDir });
4083
+ const supplyChain = isTruthyFlag(args['supply-chain']) ? inspectSkillSupplyChain({ projectDir }) : undefined;
3046
4084
  if (args.json) {
3047
- console.log(JSON.stringify(report, null, 2));
4085
+ console.log(JSON.stringify(supplyChain ? { installation: report, supplyChain } : report, null, 2));
3048
4086
  return;
3049
4087
  }
3050
4088
  console.log('\nSCALE Skill Doctor');
@@ -3056,7 +4094,18 @@ const skillDoctorCommand = defineCommand({
3056
4094
  if (!skill.installed)
3057
4095
  console.log(` install: ${skill.installCommand}`);
3058
4096
  }
3059
- if (!report.ok)
4097
+ if (supplyChain) {
4098
+ console.log('\nSkill Supply Chain');
4099
+ console.log(` Evaluated: ${supplyChain.evaluated}`);
4100
+ console.log(` Blocked: ${supplyChain.blocked}`);
4101
+ console.log(` Warnings: ${supplyChain.warnings}`);
4102
+ for (const entry of supplyChain.entries.filter(entry => entry.blocked || entry.findings.length > 0)) {
4103
+ console.log(` [${entry.blocked ? 'BLOCKED' : 'WARN'}] ${entry.id}: ${entry.risk}`);
4104
+ for (const finding of entry.findings)
4105
+ console.log(` - ${finding.rule}: ${finding.message}`);
4106
+ }
4107
+ }
4108
+ if (!report.ok || supplyChain?.ok === false)
3060
4109
  process.exitCode = 1;
3061
4110
  },
3062
4111
  });
@@ -3168,6 +4217,59 @@ const skillSafetyCommand = defineCommand({
3168
4217
  process.exitCode = 1;
3169
4218
  },
3170
4219
  });
4220
+ const skillRadarCommand = defineCommand({
4221
+ meta: { name: 'radar', description: 'Recommend skills, MCP, and CLI capabilities with confidence, safety, and evidence requirements' },
4222
+ args: {
4223
+ task: { type: 'string', required: true, description: 'Task description' },
4224
+ phase: { type: 'string', description: 'Workflow phase' },
4225
+ level: { type: 'string', default: 'M', description: 'Task level: S, M, L, or CRITICAL' },
4226
+ files: { type: 'string', description: 'Comma-separated changed or relevant files' },
4227
+ services: { type: 'string', description: 'Comma-separated services or modules' },
4228
+ dir: { type: 'string', default: PROJECT_DIR, description: 'Project directory' },
4229
+ output: { type: 'string', alias: 'o', description: 'Write markdown report to file' },
4230
+ json: { type: 'boolean', default: false, description: 'Output radar report as JSON' },
4231
+ },
4232
+ run({ args }) {
4233
+ const projectDir = resolve(String(args.dir ?? PROJECT_DIR));
4234
+ const scaleDir = resolveScaleDirForProject(projectDir);
4235
+ const report = evaluateSkillRadar({
4236
+ projectDir,
4237
+ scaleDir,
4238
+ task: String(args.task),
4239
+ phase: args.phase ? String(args.phase) : undefined,
4240
+ level: String(args.level ?? 'M'),
4241
+ files: parseCommaList(args.files),
4242
+ services: parseCommaList(args.services),
4243
+ });
4244
+ if (args.output) {
4245
+ const outputPath = resolve(projectDir, String(args.output));
4246
+ ensureDir(dirname(outputPath));
4247
+ writeFileSync(outputPath, renderSkillRadarMarkdown(report), 'utf-8');
4248
+ }
4249
+ if (args.json) {
4250
+ console.log(JSON.stringify(report, null, 2));
4251
+ if (!report.ok)
4252
+ process.exitCode = 1;
4253
+ return;
4254
+ }
4255
+ console.log('\nSCALE Skill Radar');
4256
+ console.log(` Task: ${report.task}`);
4257
+ console.log(` Level: ${report.level}`);
4258
+ console.log(` Domains: ${report.detectedDomains.map(domain => `${domain.domain}:${domain.score}`).join(', ') || 'none'}`);
4259
+ console.log(` Policy: ${report.policyMode}`);
4260
+ console.log(` Tools: ${report.toolSummary.installed}/${report.toolSummary.total} installed`);
4261
+ for (const item of report.recommendations.slice(0, 8)) {
4262
+ console.log(` [${item.action}] ${item.id} confidence=${item.confidence.toFixed(2)} safety=${item.safetyLevel}`);
4263
+ console.log(` evidence: ${item.requiredEvidence.join(', ') || 'none'}`);
4264
+ if (item.safetyLevel === 'blocked' || item.action === 'suggest-fallback')
4265
+ console.log(` fallback: ${item.fallback}`);
4266
+ }
4267
+ if (args.output)
4268
+ console.log(` Report: ${resolve(projectDir, String(args.output))}`);
4269
+ if (!report.ok)
4270
+ process.exitCode = 1;
4271
+ },
4272
+ });
3171
4273
  const skillRecommendCommand = defineCommand({
3172
4274
  meta: { name: 'recommend', description: 'Recommend a composable skill workflow for a task' },
3173
4275
  args: {
@@ -3193,6 +4295,30 @@ const skillRecommendCommand = defineCommand({
3193
4295
  console.log(` - ${reason}`);
3194
4296
  },
3195
4297
  });
4298
+ const skillOutdatedCommand = defineCommand({
4299
+ meta: { name: 'outdated', description: 'List skill update surfaces without installing or upgrading anything' },
4300
+ args: {
4301
+ dir: { type: 'string', default: PROJECT_DIR, description: 'Project directory' },
4302
+ json: { type: 'boolean', default: false, description: 'Print JSON output' },
4303
+ },
4304
+ run({ args }) {
4305
+ const report = createThirdPartyUpdateReport('skill');
4306
+ if (args.json) {
4307
+ console.log(JSON.stringify({ ...report, projectDir: resolve(String(args.dir ?? PROJECT_DIR)) }, null, 2));
4308
+ return;
4309
+ }
4310
+ console.log('\nSCALE Skill Outdated');
4311
+ console.log(` Policy: ${report.policy}`);
4312
+ console.log(` Skills: ${report.summary.total}`);
4313
+ console.log(` Review required: ${report.reviewRequired}`);
4314
+ for (const entry of report.entries) {
4315
+ console.log(` [${entry.updatePolicy}] ${entry.id} trust=${entry.trust} latest=${entry.latestVersion}`);
4316
+ if (entry.source)
4317
+ console.log(` source: ${entry.source}`);
4318
+ console.log(` reason: ${entry.reason}`);
4319
+ }
4320
+ },
4321
+ });
3196
4322
  const skill = defineCommand({
3197
4323
  meta: { name: 'skill', description: 'Skill discovery and management' },
3198
4324
  subCommands: {
@@ -3202,7 +4328,9 @@ const skill = defineCommand({
3202
4328
  check: skillCheckCommand,
3203
4329
  repo: skillRepoCommand,
3204
4330
  safety: skillSafetyCommand,
4331
+ radar: skillRadarCommand,
3205
4332
  recommend: skillRecommendCommand,
4333
+ outdated: skillOutdatedCommand,
3206
4334
  },
3207
4335
  });
3208
4336
  // ============================================================================
@@ -3448,9 +4576,34 @@ const toolEvidenceCommand = defineCommand({
3448
4576
  process.exitCode = 1;
3449
4577
  },
3450
4578
  });
4579
+ const toolOutdatedCommand = defineCommand({
4580
+ meta: { name: 'outdated', description: 'List MCP, browser, desktop, and external CLI update surfaces without installing anything' },
4581
+ args: {
4582
+ dir: { type: 'string', default: PROJECT_DIR, description: 'Project directory' },
4583
+ json: { type: 'boolean', default: false, description: 'Print JSON output' },
4584
+ },
4585
+ run({ args }) {
4586
+ const report = createThirdPartyUpdateReport(['cli', 'mcp', 'browser', 'desktop']);
4587
+ if (args.json) {
4588
+ console.log(JSON.stringify({ ...report, projectDir: resolve(String(args.dir ?? PROJECT_DIR)) }, null, 2));
4589
+ return;
4590
+ }
4591
+ console.log('\nSCALE Tool Outdated');
4592
+ console.log(` Policy: ${report.policy}`);
4593
+ console.log(` Tools: ${report.summary.total}`);
4594
+ console.log(` Review required: ${report.reviewRequired}`);
4595
+ console.log(` Blocked: ${report.summary.blocked}`);
4596
+ for (const entry of report.entries) {
4597
+ console.log(` [${entry.updatePolicy}] ${entry.id} category=${entry.category} trust=${entry.trust} latest=${entry.latestVersion}`);
4598
+ if (entry.source)
4599
+ console.log(` source: ${entry.source}`);
4600
+ console.log(` reason: ${entry.reason}`);
4601
+ }
4602
+ },
4603
+ });
3451
4604
  const tool = defineCommand({
3452
4605
  meta: { name: 'tool', description: 'Skills, MCP, browser, desktop, and external CLI governance' },
3453
- subCommands: { policy: toolPolicyCommand, doctor: toolDoctorCommand, plan: toolPlanCommand, run: toolRunCommand, evidence: toolEvidenceCommand },
4606
+ subCommands: { policy: toolPolicyCommand, doctor: toolDoctorCommand, plan: toolPlanCommand, run: toolRunCommand, evidence: toolEvidenceCommand, outdated: toolOutdatedCommand },
3454
4607
  });
3455
4608
  // ============================================================================
3456
4609
  // agent commands — Multi-Agent 协作系统 (Phase 9)
@@ -3629,7 +4782,10 @@ const main = defineCommand({
3629
4782
  evolve,
3630
4783
  stats,
3631
4784
  preflight,
4785
+ upgrade,
3632
4786
  governance,
4787
+ codegraph,
4788
+ eval: evalCommand,
3633
4789
  artifact,
3634
4790
  assets,
3635
4791
  standards,
@@ -3644,6 +4800,7 @@ const main = defineCommand({
3644
4800
  diagnose,
3645
4801
  tdd,
3646
4802
  tool,
4803
+ tools: tool,
3647
4804
  skill,
3648
4805
  skills: skill,
3649
4806
  agent,