@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.
- package/README.en.md +17 -3
- package/README.md +143 -9
- package/dist/api/cli.js +1187 -30
- package/dist/api/cli.js.map +1 -1
- package/dist/codegraph/CodeIntelligence.d.ts +135 -0
- package/dist/codegraph/CodeIntelligence.js +460 -0
- package/dist/codegraph/CodeIntelligence.js.map +1 -0
- package/dist/context/ContextBudget.d.ts +90 -0
- package/dist/context/ContextBudget.js +322 -0
- package/dist/context/ContextBudget.js.map +1 -0
- package/dist/eval/WorkflowEval.d.ts +161 -0
- package/dist/eval/WorkflowEval.js +379 -0
- package/dist/eval/WorkflowEval.js.map +1 -0
- package/dist/governance/GovernanceRoi.d.ts +25 -0
- package/dist/governance/GovernanceRoi.js +70 -0
- package/dist/governance/GovernanceRoi.js.map +1 -0
- package/dist/governance/ProgressiveGovernance.d.ts +22 -0
- package/dist/governance/ProgressiveGovernance.js +159 -0
- package/dist/governance/ProgressiveGovernance.js.map +1 -0
- package/dist/memory/MemoryBrain.d.ts +135 -0
- package/dist/memory/MemoryBrain.js +635 -0
- package/dist/memory/MemoryBrain.js.map +1 -0
- package/dist/memory/index.d.ts +1 -0
- package/dist/memory/index.js +1 -0
- package/dist/memory/index.js.map +1 -1
- package/dist/output/GovernanceDashboard.d.ts +57 -0
- package/dist/output/GovernanceDashboard.js +250 -0
- package/dist/output/GovernanceDashboard.js.map +1 -0
- package/dist/output/index.d.ts +2 -0
- package/dist/output/index.js +1 -0
- package/dist/output/index.js.map +1 -1
- package/dist/skills/SkillRadar.d.ts +83 -0
- package/dist/skills/SkillRadar.js +384 -0
- package/dist/skills/SkillRadar.js.map +1 -0
- package/dist/workflow/GovernanceTemplates.js +220 -194
- package/dist/workflow/GovernanceTemplates.js.map +1 -1
- package/dist/workflow/UpgradeManager.d.ts +140 -0
- package/dist/workflow/UpgradeManager.js +434 -0
- package/dist/workflow/UpgradeManager.js.map +1 -0
- package/docs/CODE_INTELLIGENCE.md +138 -0
- package/docs/CONTEXT_BUDGET.md +87 -0
- package/docs/GOVERNANCE_DASHBOARD.md +69 -0
- package/docs/MEMORY_BRAIN.md +104 -0
- package/docs/README.md +17 -8
- package/docs/SKILL_RADAR.md +115 -0
- package/docs/WORKFLOW_EVAL.md +151 -0
- package/docs/start/README.md +5 -1
- package/examples/demo-projects/agent-governance-demo/CONTEXT.md +14 -0
- package/examples/demo-projects/agent-governance-demo/README.md +32 -21
- package/examples/demo-projects/agent-governance-demo/docs/CONTEXT-MAP.md +14 -0
- package/examples/demo-projects/agent-governance-demo/package.json +6 -1
- 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:
|
|
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 =
|
|
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
|
-
|
|
1869
|
-
|
|
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
|
-
|
|
1873
|
-
|
|
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
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
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
|
|
1883
|
-
meta: { name: '
|
|
1884
|
-
subCommands: {
|
|
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
|
|
2845
|
-
subCommands: {
|
|
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:
|
|
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
|
|
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 (
|
|
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,
|