@hongmaple0820/scale-engine 0.15.0 → 0.16.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 +13 -5
- package/README.md +13 -5
- package/dist/agents/LeadershipPresets.d.ts +16 -0
- package/dist/agents/LeadershipPresets.js +152 -0
- package/dist/agents/LeadershipPresets.js.map +1 -0
- package/dist/api/cli.js +729 -5
- package/dist/api/cli.js.map +1 -1
- package/dist/api/doctor.d.ts +2 -0
- package/dist/api/doctor.js +83 -0
- package/dist/api/doctor.js.map +1 -1
- package/dist/api/mcp.js +2 -1
- package/dist/api/mcp.js.map +1 -1
- package/dist/artifact/types.d.ts +4 -0
- package/dist/artifact/types.js.map +1 -1
- package/dist/capabilities/InstalledSkillsIntegration.d.ts +3 -0
- package/dist/capabilities/InstalledSkillsIntegration.js +41 -17
- package/dist/capabilities/InstalledSkillsIntegration.js.map +1 -1
- package/dist/cli/phaseCommands.d.ts +14 -0
- package/dist/cli/phaseCommands.js +214 -5
- package/dist/cli/phaseCommands.js.map +1 -1
- package/dist/cli/vibeCommands.d.ts +20 -0
- package/dist/cli/vibeCommands.js +150 -173
- package/dist/cli/vibeCommands.js.map +1 -1
- package/dist/core/logger.d.ts +2 -0
- package/dist/core/logger.js +33 -1
- package/dist/core/logger.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/output/HTMLDocumentRenderer.js +3 -2
- package/dist/output/HTMLDocumentRenderer.js.map +1 -1
- package/dist/prompts/VibeTemplateGallery.d.ts +25 -0
- package/dist/prompts/VibeTemplateGallery.js +295 -0
- package/dist/prompts/VibeTemplateGallery.js.map +1 -0
- package/dist/skills/ExternalSkills.js +9 -4
- package/dist/skills/ExternalSkills.js.map +1 -1
- package/dist/skills/SkillDiscovery.js +5 -3
- package/dist/skills/SkillDiscovery.js.map +1 -1
- package/dist/skills/SkillDoctor.js +178 -1
- package/dist/skills/SkillDoctor.js.map +1 -1
- package/dist/skills/SkillInstaller.js +5 -0
- package/dist/skills/SkillInstaller.js.map +1 -1
- package/dist/skills/SkillRepository.d.ts +63 -0
- package/dist/skills/SkillRepository.js +365 -0
- package/dist/skills/SkillRepository.js.map +1 -0
- package/dist/skills/routing/SkillPolicy.js +168 -5
- package/dist/skills/routing/SkillPolicy.js.map +1 -1
- package/dist/tools/ToolCapabilityRegistry.d.ts +46 -0
- package/dist/tools/ToolCapabilityRegistry.js +223 -0
- package/dist/tools/ToolCapabilityRegistry.js.map +1 -0
- package/dist/tools/ToolEvidenceGate.d.ts +39 -0
- package/dist/tools/ToolEvidenceGate.js +117 -0
- package/dist/tools/ToolEvidenceGate.js.map +1 -0
- package/dist/tools/ToolEvidenceStore.d.ts +58 -0
- package/dist/tools/ToolEvidenceStore.js +129 -0
- package/dist/tools/ToolEvidenceStore.js.map +1 -0
- package/dist/tools/ToolOrchestrator.d.ts +67 -0
- package/dist/tools/ToolOrchestrator.js +193 -0
- package/dist/tools/ToolOrchestrator.js.map +1 -0
- package/dist/tools/ToolPolicy.d.ts +33 -0
- package/dist/tools/ToolPolicy.js +157 -0
- package/dist/tools/ToolPolicy.js.map +1 -0
- package/dist/tools/index.d.ts +5 -0
- package/dist/tools/index.js +6 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.js +15 -0
- package/dist/version.js.map +1 -0
- package/dist/workflow/EngineeringStandards.d.ts +212 -0
- package/dist/workflow/EngineeringStandards.js +1021 -0
- package/dist/workflow/EngineeringStandards.js.map +1 -0
- package/dist/workflow/GovernanceTemplatePacks.d.ts +1 -1
- package/dist/workflow/GovernanceTemplatePacks.js +101 -18
- package/dist/workflow/GovernanceTemplatePacks.js.map +1 -1
- package/dist/workflow/GovernanceTemplates.d.ts +1 -1
- package/dist/workflow/GovernanceTemplates.js +225 -37
- package/dist/workflow/GovernanceTemplates.js.map +1 -1
- package/dist/workflow/ResourceGovernance.d.ts +120 -0
- package/dist/workflow/ResourceGovernance.js +512 -0
- package/dist/workflow/ResourceGovernance.js.map +1 -0
- package/dist/workflow/TaskArtifactScaffolder.js +3 -0
- package/dist/workflow/TaskArtifactScaffolder.js.map +1 -1
- package/dist/workflow/VerificationProfile.d.ts +2 -0
- package/dist/workflow/VerificationProfile.js +7 -0
- package/dist/workflow/VerificationProfile.js.map +1 -1
- package/dist/workflow/index.d.ts +2 -0
- package/dist/workflow/index.js +2 -0
- package/dist/workflow/index.js.map +1 -1
- package/package.json +2 -2
package/dist/api/cli.js
CHANGED
|
@@ -23,6 +23,8 @@ import { Doctor } from './doctor.js';
|
|
|
23
23
|
import { quickStart, detectPlatform } from './quickstart.js';
|
|
24
24
|
import { SkillDiscovery } from '../skills/SkillDiscovery.js';
|
|
25
25
|
import { inspectRequiredWorkflowSkills, inspectWorkflowSkills } from '../skills/SkillDoctor.js';
|
|
26
|
+
import { evaluateSkillInstallSafety, listSkillRepositoryEntries, recommendSkillWorkflow, renderSkillRepositoryMarkdown, } from '../skills/SkillRepository.js';
|
|
27
|
+
import { listLeadershipPresets, renderLeadershipPresetsMarkdown } from '../agents/LeadershipPresets.js';
|
|
26
28
|
import { listWorkflowPresets, getPresetsByScenario } from '../workflows/presets.js';
|
|
27
29
|
import { EvidenceStore } from '../workflow/EvidenceStore.js';
|
|
28
30
|
import { OutOfScopeStore } from '../workflow/OutOfScopeStore.js';
|
|
@@ -31,13 +33,22 @@ import { WorkflowEngine } from '../workflow/WorkflowEngine.js';
|
|
|
31
33
|
import { resolveVerificationTargets } from '../workflow/VerificationProfile.js';
|
|
32
34
|
import { writeGovernanceTemplates } from '../workflow/GovernanceTemplates.js';
|
|
33
35
|
import { computeGovernanceDrift } from '../workflow/GovernanceLock.js';
|
|
36
|
+
import { baselineEngineeringStandards, doctorEngineeringStandards, scanEngineeringStandards, settleEngineeringStandards, } from '../workflow/EngineeringStandards.js';
|
|
37
|
+
import { doctorResourceAssets, scanResourceAssets, settleResourceAssets } from '../workflow/ResourceGovernance.js';
|
|
34
38
|
import { TaskMetricsStore } from '../workflow/TaskMetricsStore.js';
|
|
35
39
|
import { checkTaskArtifactCompleteness } from '../workflow/TaskArtifactScaffolder.js';
|
|
36
40
|
import { WorkflowArtifactWriter } from '../workflow/WorkflowArtifactWriter.js';
|
|
41
|
+
import { inspectToolCapabilities } from '../tools/ToolCapabilityRegistry.js';
|
|
42
|
+
import { evaluateToolEvidenceGate } from '../tools/ToolEvidenceGate.js';
|
|
43
|
+
import { ToolEvidenceStore } from '../tools/ToolEvidenceStore.js';
|
|
44
|
+
import { ToolOrchestrator } from '../tools/ToolOrchestrator.js';
|
|
45
|
+
import { loadToolPolicy, toolPolicyTemplate } from '../tools/ToolPolicy.js';
|
|
37
46
|
import { cleanupWorkspaceLifecycle, inspectWorkspaceLifecycle, } from '../workflow/WorkspaceLifecycle.js';
|
|
38
47
|
import { resolveWorkspaceTopology, workspaceTopologyPath, workspaceTopologyTemplate, } from '../workflow/WorkspaceTopology.js';
|
|
39
48
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
40
49
|
import { join, resolve } from 'node:path';
|
|
50
|
+
import { execFileSync } from 'node:child_process';
|
|
51
|
+
import { SCALE_ENGINE_VERSION } from '../version.js';
|
|
41
52
|
// ============================================================================
|
|
42
53
|
// Engine bootstrap (单例 + lazy init)
|
|
43
54
|
// ============================================================================
|
|
@@ -69,6 +80,42 @@ function gatesForPreflightProfile(profile) {
|
|
|
69
80
|
return ['G3', 'G0', 'G4', 'G5'];
|
|
70
81
|
return ['G3', 'G0', 'G4', 'G5', 'G6', 'G7'];
|
|
71
82
|
}
|
|
83
|
+
function evaluateEngineeringStandardsGate(options) {
|
|
84
|
+
const mode = normalizeEngineeringStandardsGateMode(options.policy.engineeringStandardsGate);
|
|
85
|
+
if (mode === 'off') {
|
|
86
|
+
return {
|
|
87
|
+
mode,
|
|
88
|
+
checked: false,
|
|
89
|
+
blocked: false,
|
|
90
|
+
ok: true,
|
|
91
|
+
findings: [],
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
const settlement = options.settle && options.artifactsDir
|
|
95
|
+
? settleEngineeringStandards({
|
|
96
|
+
projectDir: PROJECT_DIR,
|
|
97
|
+
scaleDir: SCALE_DIR,
|
|
98
|
+
taskId: options.taskId,
|
|
99
|
+
artifactsDir: options.artifactsDir,
|
|
100
|
+
})
|
|
101
|
+
: undefined;
|
|
102
|
+
const doctor = settlement?.doctor ?? doctorEngineeringStandards({
|
|
103
|
+
projectDir: PROJECT_DIR,
|
|
104
|
+
scaleDir: SCALE_DIR,
|
|
105
|
+
});
|
|
106
|
+
return {
|
|
107
|
+
mode,
|
|
108
|
+
checked: true,
|
|
109
|
+
blocked: mode === 'block' && !doctor.ok,
|
|
110
|
+
ok: doctor.ok,
|
|
111
|
+
findings: doctor.findings,
|
|
112
|
+
summary: doctor.scan.summary,
|
|
113
|
+
standardsImpactPath: settlement?.standardsImpactPath,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function normalizeEngineeringStandardsGateMode(value) {
|
|
117
|
+
return value === 'off' || value === 'block' ? value : 'warn';
|
|
118
|
+
}
|
|
72
119
|
let _engine = null;
|
|
73
120
|
function getEngine() {
|
|
74
121
|
if (!_engine)
|
|
@@ -854,6 +901,52 @@ function printWorkspaceLifecycle(report) {
|
|
|
854
901
|
for (const action of report.finish.nextActions)
|
|
855
902
|
console.log(` [NEXT] ${action}`);
|
|
856
903
|
}
|
|
904
|
+
function compactList(values, limit = 5) {
|
|
905
|
+
if (values.length <= limit)
|
|
906
|
+
return values.join(', ');
|
|
907
|
+
return `${values.slice(0, limit).join(', ')} (+${values.length - limit} more)`;
|
|
908
|
+
}
|
|
909
|
+
function printWorkspaceSummary(report) {
|
|
910
|
+
const dirtyChildren = report.childRepositories
|
|
911
|
+
.filter(child => !child.clean)
|
|
912
|
+
.map(child => child.relativePath);
|
|
913
|
+
const unpushedChildren = report.childRepositories
|
|
914
|
+
.filter(child => child.ahead > 0 || (report.topology.finishPolicy.requirePushedBranches && report.topology.topology === 'moe' && !child.upstream && Boolean(child.branch)))
|
|
915
|
+
.map(child => child.relativePath);
|
|
916
|
+
const noUpstreamChildren = report.childRepositories
|
|
917
|
+
.filter(child => !child.upstream && Boolean(child.branch))
|
|
918
|
+
.map(child => child.relativePath);
|
|
919
|
+
const rootStatus = report.root.clean
|
|
920
|
+
? 'clean'
|
|
921
|
+
: `dirty (staged=${report.root.staged}, unstaged=${report.root.unstaged}, untracked=${report.root.untracked})`;
|
|
922
|
+
const status = report.finish.blockers.length > 0 ? 'BLOCKED' : 'READY';
|
|
923
|
+
console.log('\nSCALE Workspace Summary');
|
|
924
|
+
console.log(` Status: ${status}`);
|
|
925
|
+
console.log(` Topology: ${report.topology.topology}${report.topology.configured ? '' : ' (default)'}`);
|
|
926
|
+
console.log(` Root: ${rootStatus}`);
|
|
927
|
+
console.log(` Children: ${report.childRepositories.length} total, ${dirtyChildren.length} dirty, ${unpushedChildren.length} unpushed, ${noUpstreamChildren.length} no upstream`);
|
|
928
|
+
if (dirtyChildren.length > 0)
|
|
929
|
+
console.log(` Dirty child repositories: ${compactList(dirtyChildren)}`);
|
|
930
|
+
if (unpushedChildren.length > 0)
|
|
931
|
+
console.log(` Unpushed child repositories: ${compactList(unpushedChildren)}`);
|
|
932
|
+
if (report.finish.blockers.length > 0) {
|
|
933
|
+
console.log('\n Blockers:');
|
|
934
|
+
for (const blocker of report.finish.blockers.slice(0, 8))
|
|
935
|
+
console.log(` - ${blocker}`);
|
|
936
|
+
if (report.finish.blockers.length > 8)
|
|
937
|
+
console.log(` - ... ${report.finish.blockers.length - 8} more blocker(s)`);
|
|
938
|
+
}
|
|
939
|
+
if (report.finish.warnings.length > 0) {
|
|
940
|
+
console.log(`\n Warnings: ${report.finish.warnings.length} warning(s); run scale workspace finish --json for details`);
|
|
941
|
+
}
|
|
942
|
+
console.log('\n Next:');
|
|
943
|
+
const nextActions = report.finish.blockers.length > 0
|
|
944
|
+
? report.finish.nextActions
|
|
945
|
+
: ['Proceed with scale ship <task-id> or cleanup when the branch policy is satisfied'];
|
|
946
|
+
for (const action of nextActions.slice(0, 3))
|
|
947
|
+
console.log(` - ${action}`);
|
|
948
|
+
console.log(' - Run scale workspace finish --json for full details');
|
|
949
|
+
}
|
|
857
950
|
function printWorkspaceTopology(topology, written) {
|
|
858
951
|
console.log('\nSCALE Workspace Topology');
|
|
859
952
|
console.log(` Topology: ${topology.topology}${topology.configured ? '' : ' (default)'}`);
|
|
@@ -886,6 +979,7 @@ const workspaceStatus = defineCommand({
|
|
|
886
979
|
meta: { name: 'status', description: 'Inspect root worktree and child repository lifecycle state' },
|
|
887
980
|
args: {
|
|
888
981
|
dir: { type: 'string', description: 'Repository or worktree directory; defaults to current project directory' },
|
|
982
|
+
summary: { type: 'boolean', default: false, description: 'Print concise human summary instead of the full repository listing' },
|
|
889
983
|
json: { type: 'boolean', default: false },
|
|
890
984
|
},
|
|
891
985
|
async run({ args }) {
|
|
@@ -893,6 +987,9 @@ const workspaceStatus = defineCommand({
|
|
|
893
987
|
if (args.json) {
|
|
894
988
|
console.log(JSON.stringify(report, null, 2));
|
|
895
989
|
}
|
|
990
|
+
else if (isTruthyFlag(args.summary)) {
|
|
991
|
+
printWorkspaceSummary(report);
|
|
992
|
+
}
|
|
896
993
|
else {
|
|
897
994
|
printWorkspaceLifecycle(report);
|
|
898
995
|
}
|
|
@@ -933,6 +1030,7 @@ const workspaceFinish = defineCommand({
|
|
|
933
1030
|
meta: { name: 'finish', description: 'Check whether a temporary worktree can be safely finished or cleaned up' },
|
|
934
1031
|
args: {
|
|
935
1032
|
dir: { type: 'string', description: 'Repository or worktree directory; defaults to current project directory' },
|
|
1033
|
+
summary: { type: 'boolean', default: false, description: 'Print concise human summary instead of the full repository listing' },
|
|
936
1034
|
json: { type: 'boolean', default: false },
|
|
937
1035
|
},
|
|
938
1036
|
async run({ args }) {
|
|
@@ -946,6 +1044,9 @@ const workspaceFinish = defineCommand({
|
|
|
946
1044
|
if (args.json) {
|
|
947
1045
|
console.log(JSON.stringify(result, null, 2));
|
|
948
1046
|
}
|
|
1047
|
+
else if (isTruthyFlag(args.summary)) {
|
|
1048
|
+
printWorkspaceSummary(report);
|
|
1049
|
+
}
|
|
949
1050
|
else {
|
|
950
1051
|
printWorkspaceLifecycle(report);
|
|
951
1052
|
}
|
|
@@ -960,6 +1061,7 @@ const workspaceCleanup = defineCommand({
|
|
|
960
1061
|
'dry-run': { type: 'boolean', default: false, description: 'Preview cleanup; this is the default unless --apply is set' },
|
|
961
1062
|
apply: { type: 'boolean', default: false, description: 'Actually run git worktree remove after safety checks' },
|
|
962
1063
|
confirm: { type: 'string', description: 'Required confirmation token for --apply, usually the worktree branch name' },
|
|
1064
|
+
summary: { type: 'boolean', default: false, description: 'Print concise human summary before the cleanup plan' },
|
|
963
1065
|
json: { type: 'boolean', default: false },
|
|
964
1066
|
},
|
|
965
1067
|
async run({ args }) {
|
|
@@ -971,6 +1073,14 @@ const workspaceCleanup = defineCommand({
|
|
|
971
1073
|
if (args.json) {
|
|
972
1074
|
console.log(JSON.stringify(result, null, 2));
|
|
973
1075
|
}
|
|
1076
|
+
else if (isTruthyFlag(args.summary)) {
|
|
1077
|
+
printWorkspaceSummary(result.report);
|
|
1078
|
+
console.log('\n Cleanup:');
|
|
1079
|
+
console.log(` Mode: ${result.mode}`);
|
|
1080
|
+
console.log(` Can apply: ${result.canApply ? 'yes' : 'no'}`);
|
|
1081
|
+
console.log(` Applied: ${result.applied ? 'yes' : 'no'}`);
|
|
1082
|
+
console.log(` Confirmation token: ${result.confirmationToken ?? '(unavailable)'}`);
|
|
1083
|
+
}
|
|
974
1084
|
else {
|
|
975
1085
|
printWorkspaceCleanup(result);
|
|
976
1086
|
}
|
|
@@ -1022,6 +1132,9 @@ const preflight = defineCommand({
|
|
|
1022
1132
|
profile: args.profile,
|
|
1023
1133
|
service: args.service,
|
|
1024
1134
|
});
|
|
1135
|
+
const engineeringStandards = evaluateEngineeringStandardsGate({
|
|
1136
|
+
policy: resolved.policy,
|
|
1137
|
+
});
|
|
1025
1138
|
const targetResults = [];
|
|
1026
1139
|
if (!args.json) {
|
|
1027
1140
|
console.log('\nSCALE Preflight');
|
|
@@ -1030,6 +1143,13 @@ const preflight = defineCommand({
|
|
|
1030
1143
|
console.log(` Profile: ${resolved.profileName}`);
|
|
1031
1144
|
console.log(` Preflight profile: ${preflightProfile}`);
|
|
1032
1145
|
console.log(` Gates: ${gateStages.join(', ')}`);
|
|
1146
|
+
if (engineeringStandards.checked) {
|
|
1147
|
+
const status = engineeringStandards.blocked ? 'BLOCKED' : engineeringStandards.ok ? 'OK' : 'WARN';
|
|
1148
|
+
console.log(` Engineering standards: ${status} (${engineeringStandards.mode})`);
|
|
1149
|
+
}
|
|
1150
|
+
else {
|
|
1151
|
+
console.log(' Engineering standards: skipped');
|
|
1152
|
+
}
|
|
1033
1153
|
}
|
|
1034
1154
|
for (const target of resolved.targets) {
|
|
1035
1155
|
if (!args.json) {
|
|
@@ -1061,7 +1181,9 @@ const preflight = defineCommand({
|
|
|
1061
1181
|
}
|
|
1062
1182
|
}
|
|
1063
1183
|
}
|
|
1064
|
-
const passed = targetResults.length > 0 &&
|
|
1184
|
+
const passed = targetResults.length > 0 &&
|
|
1185
|
+
targetResults.every(target => target.passed) &&
|
|
1186
|
+
!engineeringStandards.blocked;
|
|
1065
1187
|
const result = {
|
|
1066
1188
|
phase: 'PREFLIGHT',
|
|
1067
1189
|
profile: resolved.profileName,
|
|
@@ -1069,6 +1191,7 @@ const preflight = defineCommand({
|
|
|
1069
1191
|
gates: gateStages,
|
|
1070
1192
|
services: targetResults.map(target => target.service).filter(Boolean),
|
|
1071
1193
|
policy: resolved.policy,
|
|
1194
|
+
engineeringStandards,
|
|
1072
1195
|
targets: targetResults,
|
|
1073
1196
|
passed,
|
|
1074
1197
|
};
|
|
@@ -1207,7 +1330,7 @@ const init = defineCommand({
|
|
|
1207
1330
|
'governance-pack': {
|
|
1208
1331
|
type: 'string',
|
|
1209
1332
|
default: 'standard',
|
|
1210
|
-
description: 'Governance template pack (standard/project-scaffold/moe-workspace/go-service-matrix/node-library/frontend-app)',
|
|
1333
|
+
description: 'Governance template pack (standard/project-scaffold/moe-workspace/resource-governance/go-service-matrix/node-library/frontend-app)',
|
|
1211
1334
|
},
|
|
1212
1335
|
quick: { type: 'boolean', default: false, description: 'Quick start with auto-detection' },
|
|
1213
1336
|
interactive: { type: 'boolean', default: false, description: 'Interactive configuration mode with prompts' },
|
|
@@ -1414,6 +1537,259 @@ const governance = defineCommand({
|
|
|
1414
1537
|
subCommands: { diff: governanceDiff },
|
|
1415
1538
|
});
|
|
1416
1539
|
// ============================================================================
|
|
1540
|
+
// assets command - Resource lifecycle governance
|
|
1541
|
+
// ============================================================================
|
|
1542
|
+
const assetsScan = defineCommand({
|
|
1543
|
+
meta: { name: 'scan', description: 'Classify project docs, reports, media, scripts, and temporary outputs' },
|
|
1544
|
+
args: {
|
|
1545
|
+
dir: { type: 'string', default: '.', description: 'Project directory' },
|
|
1546
|
+
json: { type: 'boolean', default: false, description: 'Print JSON output' },
|
|
1547
|
+
},
|
|
1548
|
+
run({ args }) {
|
|
1549
|
+
const report = scanResourceAssets({ projectDir: args.dir });
|
|
1550
|
+
if (args.json) {
|
|
1551
|
+
console.log(JSON.stringify(report, null, 2));
|
|
1552
|
+
return;
|
|
1553
|
+
}
|
|
1554
|
+
console.log('SCALE Asset Scan');
|
|
1555
|
+
console.log(` Project: ${report.projectDir}`);
|
|
1556
|
+
console.log(` Total resources: ${report.summary.total}`);
|
|
1557
|
+
console.log(` Tracked forbidden: ${report.summary.trackedForbidden}`);
|
|
1558
|
+
console.log(` Large tracked: ${report.summary.largeTracked}`);
|
|
1559
|
+
console.log(` Expired: ${report.summary.expired}`);
|
|
1560
|
+
console.log('\nBy type:');
|
|
1561
|
+
for (const [type, count] of Object.entries(report.summary.byType)) {
|
|
1562
|
+
if (count > 0)
|
|
1563
|
+
console.log(` ${type}: ${count}`);
|
|
1564
|
+
}
|
|
1565
|
+
},
|
|
1566
|
+
});
|
|
1567
|
+
const assetsDoctor = defineCommand({
|
|
1568
|
+
meta: { name: 'doctor', description: 'Find resource lifecycle and Git policy problems' },
|
|
1569
|
+
args: {
|
|
1570
|
+
dir: { type: 'string', default: '.', description: 'Project directory' },
|
|
1571
|
+
json: { type: 'boolean', default: false, description: 'Print JSON output' },
|
|
1572
|
+
},
|
|
1573
|
+
run({ args }) {
|
|
1574
|
+
const report = doctorResourceAssets({ projectDir: args.dir });
|
|
1575
|
+
if (args.json) {
|
|
1576
|
+
console.log(JSON.stringify(report, null, 2));
|
|
1577
|
+
return;
|
|
1578
|
+
}
|
|
1579
|
+
console.log(`SCALE Asset Doctor: ${report.ok ? 'OK' : 'FAILED'}`);
|
|
1580
|
+
if (report.findings.length === 0) {
|
|
1581
|
+
console.log(' No resource lifecycle findings.');
|
|
1582
|
+
return;
|
|
1583
|
+
}
|
|
1584
|
+
for (const finding of report.findings) {
|
|
1585
|
+
const path = finding.path ? ` ${finding.path}` : '';
|
|
1586
|
+
console.log(` [${finding.severity.toUpperCase()}] ${finding.code}${path}: ${finding.message}`);
|
|
1587
|
+
if (finding.fix)
|
|
1588
|
+
console.log(` fix: ${finding.fix}`);
|
|
1589
|
+
}
|
|
1590
|
+
if (!report.ok)
|
|
1591
|
+
process.exitCode = 1;
|
|
1592
|
+
},
|
|
1593
|
+
});
|
|
1594
|
+
const assetsSettle = defineCommand({
|
|
1595
|
+
meta: { name: 'settle', description: 'Record resource lifecycle settlement evidence for a task' },
|
|
1596
|
+
args: {
|
|
1597
|
+
dir: { type: 'string', default: '.', description: 'Project directory' },
|
|
1598
|
+
'task-id': { type: 'string', description: 'Task id for the settlement record' },
|
|
1599
|
+
'artifact-dir': { type: 'string', description: 'Task artifact directory where resource-impact.md should be updated' },
|
|
1600
|
+
json: { type: 'boolean', default: false, description: 'Print JSON output' },
|
|
1601
|
+
},
|
|
1602
|
+
run({ args }) {
|
|
1603
|
+
const report = settleResourceAssets({
|
|
1604
|
+
projectDir: args.dir,
|
|
1605
|
+
taskId: args['task-id'],
|
|
1606
|
+
artifactsDir: args['artifact-dir'],
|
|
1607
|
+
});
|
|
1608
|
+
if (args.json) {
|
|
1609
|
+
console.log(JSON.stringify(report, null, 2));
|
|
1610
|
+
}
|
|
1611
|
+
else {
|
|
1612
|
+
console.log(`SCALE Asset Settlement: ${report.ok ? 'OK' : 'FAILED'}`);
|
|
1613
|
+
if (report.resourceImpactPath)
|
|
1614
|
+
console.log(` Resource impact: ${report.resourceImpactPath}`);
|
|
1615
|
+
if (report.doctor.findings.length > 0) {
|
|
1616
|
+
for (const finding of report.doctor.findings) {
|
|
1617
|
+
const path = finding.path ? ` ${finding.path}` : '';
|
|
1618
|
+
console.log(` [${finding.severity.toUpperCase()}] ${finding.code}${path}: ${finding.message}`);
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
if (!report.ok)
|
|
1623
|
+
process.exitCode = 1;
|
|
1624
|
+
},
|
|
1625
|
+
});
|
|
1626
|
+
const assets = defineCommand({
|
|
1627
|
+
meta: { name: 'assets', description: 'Resource lifecycle governance for generated and maintained project assets' },
|
|
1628
|
+
subCommands: { scan: assetsScan, doctor: assetsDoctor, settle: assetsSettle },
|
|
1629
|
+
});
|
|
1630
|
+
// ============================================================================
|
|
1631
|
+
// standards command - Engineering standards governance
|
|
1632
|
+
// ============================================================================
|
|
1633
|
+
function resolveChangedFilesArg(args) {
|
|
1634
|
+
const explicit = splitChangedFiles(args['changed-files']);
|
|
1635
|
+
if (explicit.length > 0)
|
|
1636
|
+
return explicit;
|
|
1637
|
+
if (!args.changed)
|
|
1638
|
+
return undefined;
|
|
1639
|
+
return readGitChangedFiles(args.dir ?? '.');
|
|
1640
|
+
}
|
|
1641
|
+
function splitChangedFiles(value) {
|
|
1642
|
+
if (!value)
|
|
1643
|
+
return [];
|
|
1644
|
+
return value
|
|
1645
|
+
.split(/[\n,]/)
|
|
1646
|
+
.map(item => item.trim())
|
|
1647
|
+
.filter(Boolean);
|
|
1648
|
+
}
|
|
1649
|
+
function readGitChangedFiles(projectDir) {
|
|
1650
|
+
const tracked = readGitPathList(projectDir, ['diff', '--name-only', '--diff-filter=ACMRTUXB', 'HEAD', '--']);
|
|
1651
|
+
const untracked = readGitPathList(projectDir, ['ls-files', '--others', '--exclude-standard']);
|
|
1652
|
+
return Array.from(new Set([...tracked, ...untracked]));
|
|
1653
|
+
}
|
|
1654
|
+
function readGitPathList(projectDir, args) {
|
|
1655
|
+
try {
|
|
1656
|
+
return execFileSync('git', ['-C', projectDir, ...args], { encoding: 'utf-8' })
|
|
1657
|
+
.split(/\r?\n/)
|
|
1658
|
+
.map(item => item.trim())
|
|
1659
|
+
.filter(Boolean);
|
|
1660
|
+
}
|
|
1661
|
+
catch {
|
|
1662
|
+
return [];
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
const standardsScan = defineCommand({
|
|
1666
|
+
meta: { name: 'scan', description: 'Scan source files for engineering standard violations' },
|
|
1667
|
+
args: {
|
|
1668
|
+
dir: { type: 'string', default: '.', description: 'Project directory' },
|
|
1669
|
+
changed: { type: 'boolean', default: false, description: 'Scan changed Git files only' },
|
|
1670
|
+
'changed-files': { type: 'string', description: 'Comma or newline separated file list to scan' },
|
|
1671
|
+
json: { type: 'boolean', default: false, description: 'Print JSON output' },
|
|
1672
|
+
},
|
|
1673
|
+
run({ args }) {
|
|
1674
|
+
const report = scanEngineeringStandards({ projectDir: args.dir, changedFiles: resolveChangedFilesArg(args) });
|
|
1675
|
+
if (args.json) {
|
|
1676
|
+
console.log(JSON.stringify(report, null, 2));
|
|
1677
|
+
return;
|
|
1678
|
+
}
|
|
1679
|
+
console.log('SCALE Standards Scan');
|
|
1680
|
+
console.log(` Project: ${report.projectDir}`);
|
|
1681
|
+
console.log(` Files scanned: ${report.summary.filesScanned}`);
|
|
1682
|
+
console.log(` Findings: ${report.summary.totalFindings}`);
|
|
1683
|
+
console.log(` Blocking findings: ${report.summary.blockingFindings}`);
|
|
1684
|
+
for (const finding of report.findings.slice(0, 20)) {
|
|
1685
|
+
const line = finding.line ? `:${finding.line}` : '';
|
|
1686
|
+
console.log(` [${finding.severity.toUpperCase()}] ${finding.ruleId} ${finding.path}${line}: ${finding.message}`);
|
|
1687
|
+
}
|
|
1688
|
+
if (report.findings.length > 20)
|
|
1689
|
+
console.log(` ... ${report.findings.length - 20} more finding(s)`);
|
|
1690
|
+
},
|
|
1691
|
+
});
|
|
1692
|
+
const standardsDoctor = defineCommand({
|
|
1693
|
+
meta: { name: 'doctor', description: 'Find blocking engineering standards problems' },
|
|
1694
|
+
args: {
|
|
1695
|
+
dir: { type: 'string', default: '.', description: 'Project directory' },
|
|
1696
|
+
changed: { type: 'boolean', default: false, description: 'Scan changed Git files only' },
|
|
1697
|
+
'changed-files': { type: 'string', description: 'Comma or newline separated file list to scan' },
|
|
1698
|
+
json: { type: 'boolean', default: false, description: 'Print JSON output' },
|
|
1699
|
+
},
|
|
1700
|
+
run({ args }) {
|
|
1701
|
+
const report = doctorEngineeringStandards({ projectDir: args.dir, changedFiles: resolveChangedFilesArg(args) });
|
|
1702
|
+
if (args.json) {
|
|
1703
|
+
console.log(JSON.stringify(report, null, 2));
|
|
1704
|
+
if (!report.ok)
|
|
1705
|
+
process.exitCode = 1;
|
|
1706
|
+
return;
|
|
1707
|
+
}
|
|
1708
|
+
console.log(`SCALE Standards Doctor: ${report.ok ? 'OK' : 'FAILED'}`);
|
|
1709
|
+
if (report.findings.length === 0) {
|
|
1710
|
+
console.log(' No engineering standards findings.');
|
|
1711
|
+
return;
|
|
1712
|
+
}
|
|
1713
|
+
for (const finding of report.findings) {
|
|
1714
|
+
const line = finding.line ? `:${finding.line}` : '';
|
|
1715
|
+
console.log(` [${finding.severity.toUpperCase()}] ${finding.ruleId} ${finding.path}${line}: ${finding.message}`);
|
|
1716
|
+
if (finding.fix)
|
|
1717
|
+
console.log(` fix: ${finding.fix}`);
|
|
1718
|
+
}
|
|
1719
|
+
if (!report.ok)
|
|
1720
|
+
process.exitCode = 1;
|
|
1721
|
+
},
|
|
1722
|
+
});
|
|
1723
|
+
const standardsSettle = defineCommand({
|
|
1724
|
+
meta: { name: 'settle', description: 'Record engineering standards settlement evidence for a task' },
|
|
1725
|
+
args: {
|
|
1726
|
+
dir: { type: 'string', default: '.', description: 'Project directory' },
|
|
1727
|
+
'task-id': { type: 'string', description: 'Task id for the settlement record' },
|
|
1728
|
+
'artifact-dir': { type: 'string', description: 'Task artifact directory where standards-impact.md should be updated' },
|
|
1729
|
+
changed: { type: 'boolean', default: false, description: 'Scan changed Git files only' },
|
|
1730
|
+
'changed-files': { type: 'string', description: 'Comma or newline separated file list to scan' },
|
|
1731
|
+
json: { type: 'boolean', default: false, description: 'Print JSON output' },
|
|
1732
|
+
},
|
|
1733
|
+
run({ args }) {
|
|
1734
|
+
const report = settleEngineeringStandards({
|
|
1735
|
+
projectDir: args.dir,
|
|
1736
|
+
taskId: args['task-id'],
|
|
1737
|
+
artifactsDir: args['artifact-dir'],
|
|
1738
|
+
changedFiles: resolveChangedFilesArg(args),
|
|
1739
|
+
});
|
|
1740
|
+
if (args.json) {
|
|
1741
|
+
console.log(JSON.stringify(report, null, 2));
|
|
1742
|
+
}
|
|
1743
|
+
else {
|
|
1744
|
+
console.log(`SCALE Standards Settlement: ${report.ok ? 'OK' : 'FAILED'}`);
|
|
1745
|
+
if (report.standardsImpactPath)
|
|
1746
|
+
console.log(` Standards impact: ${report.standardsImpactPath}`);
|
|
1747
|
+
for (const finding of report.doctor.findings) {
|
|
1748
|
+
const line = finding.line ? `:${finding.line}` : '';
|
|
1749
|
+
console.log(` [${finding.severity.toUpperCase()}] ${finding.ruleId} ${finding.path}${line}: ${finding.message}`);
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
if (!report.ok)
|
|
1753
|
+
process.exitCode = 1;
|
|
1754
|
+
},
|
|
1755
|
+
});
|
|
1756
|
+
const standardsBaseline = defineCommand({
|
|
1757
|
+
meta: { name: 'baseline', description: 'Generate a legacy standards baseline and classification report' },
|
|
1758
|
+
args: {
|
|
1759
|
+
dir: { type: 'string', default: '.', description: 'Project directory' },
|
|
1760
|
+
write: { type: 'boolean', default: false, description: 'Write .scale/engineering-standards-baseline.json' },
|
|
1761
|
+
'task-id': { type: 'string', description: 'Task id for the legacy debt report' },
|
|
1762
|
+
'artifact-dir': { type: 'string', description: 'Directory where standards-legacy-debt.md should be written' },
|
|
1763
|
+
reason: { type: 'string', default: 'legacy standards debt accepted for staged remediation', description: 'Reason recorded on generated baseline entries' },
|
|
1764
|
+
json: { type: 'boolean', default: false, description: 'Print JSON output' },
|
|
1765
|
+
},
|
|
1766
|
+
run({ args }) {
|
|
1767
|
+
const report = baselineEngineeringStandards({
|
|
1768
|
+
projectDir: args.dir,
|
|
1769
|
+
writeBaseline: args.write,
|
|
1770
|
+
taskId: args['task-id'],
|
|
1771
|
+
artifactsDir: args['artifact-dir'],
|
|
1772
|
+
reason: args.reason,
|
|
1773
|
+
});
|
|
1774
|
+
if (args.json) {
|
|
1775
|
+
console.log(JSON.stringify(report, null, 2));
|
|
1776
|
+
return;
|
|
1777
|
+
}
|
|
1778
|
+
console.log(`Standards baseline: ${report.wroteBaseline ? 'written' : 'dry-run'}`);
|
|
1779
|
+
console.log(` Baseline entries: ${report.baselineEntries.length}`);
|
|
1780
|
+
console.log(` Blocking findings: ${report.debt.blockingFindings}`);
|
|
1781
|
+
console.log(` Baseline path: ${report.baselinePath}`);
|
|
1782
|
+
if (report.legacyDebtPath)
|
|
1783
|
+
console.log(` Legacy debt report: ${report.legacyDebtPath}`);
|
|
1784
|
+
if (!report.wroteBaseline)
|
|
1785
|
+
console.log(' Re-run with --write to update .scale/engineering-standards-baseline.json.');
|
|
1786
|
+
},
|
|
1787
|
+
});
|
|
1788
|
+
const standards = defineCommand({
|
|
1789
|
+
meta: { name: 'standards', description: 'Engineering standards governance for logs, security, architecture, database, and code quality' },
|
|
1790
|
+
subCommands: { scan: standardsScan, doctor: standardsDoctor, settle: standardsSettle, baseline: standardsBaseline },
|
|
1791
|
+
});
|
|
1792
|
+
// ============================================================================
|
|
1417
1793
|
// evolve command
|
|
1418
1794
|
// ============================================================================
|
|
1419
1795
|
const evolve = defineCommand({
|
|
@@ -1849,9 +2225,331 @@ const skillCheckCommand = defineCommand({
|
|
|
1849
2225
|
process.exitCode = 1;
|
|
1850
2226
|
},
|
|
1851
2227
|
});
|
|
2228
|
+
const skillRepoCommand = defineCommand({
|
|
2229
|
+
meta: { name: 'repo', description: 'Show SCALE progressive skill repository guide' },
|
|
2230
|
+
args: {
|
|
2231
|
+
category: { type: 'string', description: 'Filter by category: ui/browser/desktop/testing/review/docs/agent-cli/role-library/discovery' },
|
|
2232
|
+
output: { type: 'string', alias: 'o', description: 'Write markdown guide to file' },
|
|
2233
|
+
json: { type: 'boolean', default: false },
|
|
2234
|
+
},
|
|
2235
|
+
run({ args }) {
|
|
2236
|
+
if (args.json) {
|
|
2237
|
+
console.log(JSON.stringify(listSkillRepositoryEntries(args.category ? { category: args.category } : undefined), null, 2));
|
|
2238
|
+
return;
|
|
2239
|
+
}
|
|
2240
|
+
const markdown = renderSkillRepositoryMarkdown();
|
|
2241
|
+
if (args.output) {
|
|
2242
|
+
const outputPath = resolve(PROJECT_DIR, args.output);
|
|
2243
|
+
ensureDir(resolve(outputPath, '..'));
|
|
2244
|
+
writeFileSync(outputPath, markdown, 'utf-8');
|
|
2245
|
+
console.log(`[OK] Skill 仓库指南已生成: ${outputPath}`);
|
|
2246
|
+
return;
|
|
2247
|
+
}
|
|
2248
|
+
console.log(markdown);
|
|
2249
|
+
},
|
|
2250
|
+
});
|
|
2251
|
+
const skillSafetyCommand = defineCommand({
|
|
2252
|
+
meta: { name: 'safety', description: 'Evaluate skill install command and source safety' },
|
|
2253
|
+
args: {
|
|
2254
|
+
source: { type: 'string', description: 'Skill source URL' },
|
|
2255
|
+
command: { type: 'string', description: 'Install command to review' },
|
|
2256
|
+
json: { type: 'boolean', default: false },
|
|
2257
|
+
},
|
|
2258
|
+
run({ args }) {
|
|
2259
|
+
const report = evaluateSkillInstallSafety({
|
|
2260
|
+
sourceUrl: args.source,
|
|
2261
|
+
installCommand: args.command,
|
|
2262
|
+
});
|
|
2263
|
+
if (args.json) {
|
|
2264
|
+
console.log(JSON.stringify(report, null, 2));
|
|
2265
|
+
return;
|
|
2266
|
+
}
|
|
2267
|
+
console.log('\nSCALE Skill Safety');
|
|
2268
|
+
console.log(` Risk: ${report.risk}`);
|
|
2269
|
+
console.log(` Blocked: ${report.blocked}`);
|
|
2270
|
+
for (const finding of report.findings) {
|
|
2271
|
+
console.log(` [${finding.severity.toUpperCase()}] ${finding.rule}: ${finding.message}`);
|
|
2272
|
+
}
|
|
2273
|
+
console.log(' Required checks:');
|
|
2274
|
+
for (const check of report.requiredChecks)
|
|
2275
|
+
console.log(` - ${check}`);
|
|
2276
|
+
if (report.blocked)
|
|
2277
|
+
process.exitCode = 1;
|
|
2278
|
+
},
|
|
2279
|
+
});
|
|
2280
|
+
const skillRecommendCommand = defineCommand({
|
|
2281
|
+
meta: { name: 'recommend', description: 'Recommend a composable skill workflow for a task' },
|
|
2282
|
+
args: {
|
|
2283
|
+
task: { type: 'string', required: true, description: 'Task description' },
|
|
2284
|
+
phase: { type: 'string', description: 'Workflow phase' },
|
|
2285
|
+
json: { type: 'boolean', default: false },
|
|
2286
|
+
},
|
|
2287
|
+
run({ args }) {
|
|
2288
|
+
const plan = recommendSkillWorkflow({
|
|
2289
|
+
description: args.task,
|
|
2290
|
+
phase: args.phase,
|
|
2291
|
+
});
|
|
2292
|
+
if (args.json) {
|
|
2293
|
+
console.log(JSON.stringify(plan, null, 2));
|
|
2294
|
+
return;
|
|
2295
|
+
}
|
|
2296
|
+
console.log('\nSCALE Skill Recommendation');
|
|
2297
|
+
console.log(` Primary: ${plan.primarySkills.join(', ') || 'none'}`);
|
|
2298
|
+
console.log(` Supporting: ${plan.supportingSkills.join(', ') || 'none'}`);
|
|
2299
|
+
console.log(` Safety required: ${plan.safetyRequired}`);
|
|
2300
|
+
console.log(` Evidence: ${plan.requiredEvidence.join(', ') || 'none'}`);
|
|
2301
|
+
for (const reason of plan.rationale)
|
|
2302
|
+
console.log(` - ${reason}`);
|
|
2303
|
+
},
|
|
2304
|
+
});
|
|
1852
2305
|
const skill = defineCommand({
|
|
1853
2306
|
meta: { name: 'skill', description: 'Skill discovery and management' },
|
|
1854
|
-
subCommands: {
|
|
2307
|
+
subCommands: {
|
|
2308
|
+
scan: skillScan,
|
|
2309
|
+
doctor: skillDoctorCommand,
|
|
2310
|
+
plan: skillPlanCommand,
|
|
2311
|
+
check: skillCheckCommand,
|
|
2312
|
+
repo: skillRepoCommand,
|
|
2313
|
+
safety: skillSafetyCommand,
|
|
2314
|
+
recommend: skillRecommendCommand,
|
|
2315
|
+
},
|
|
2316
|
+
});
|
|
2317
|
+
// ============================================================================
|
|
2318
|
+
// tool command - Skills/MCP/CLI orchestration governance
|
|
2319
|
+
// ============================================================================
|
|
2320
|
+
function normalizeToolMode(value) {
|
|
2321
|
+
const normalized = String(value ?? 'evidence-required');
|
|
2322
|
+
if (normalized === 'off' || normalized === 'advisory' || normalized === 'evidence-required' || normalized === 'block')
|
|
2323
|
+
return normalized;
|
|
2324
|
+
return 'evidence-required';
|
|
2325
|
+
}
|
|
2326
|
+
function parseToolIds(value) {
|
|
2327
|
+
const raw = String(value ?? '').trim();
|
|
2328
|
+
if (!raw)
|
|
2329
|
+
return undefined;
|
|
2330
|
+
return raw.split(',').map(item => item.trim()).filter(Boolean);
|
|
2331
|
+
}
|
|
2332
|
+
function parseCommaList(value) {
|
|
2333
|
+
return parseToolIds(value) ?? [];
|
|
2334
|
+
}
|
|
2335
|
+
function createToolExecutionPlanFromArgs(args) {
|
|
2336
|
+
const projectDir = resolve(String(args.dir ?? PROJECT_DIR));
|
|
2337
|
+
const level = normalizeTaskArtifactLevel(args.level ?? 'M');
|
|
2338
|
+
const skillPolicy = loadSkillRoutingPolicy(projectDir, SCALE_DIR);
|
|
2339
|
+
const skillPlan = createSkillPlan({
|
|
2340
|
+
taskId: String(args['task-id'] ?? `TOOL-${Date.now()}`),
|
|
2341
|
+
taskName: String(args.task ?? 'Tool orchestration task'),
|
|
2342
|
+
description: String(args.task ?? ''),
|
|
2343
|
+
level,
|
|
2344
|
+
files: parseCommaList(args.files),
|
|
2345
|
+
services: parseCommaList(args.services),
|
|
2346
|
+
policy: skillPolicy,
|
|
2347
|
+
});
|
|
2348
|
+
const toolPolicy = loadToolPolicy(projectDir, SCALE_DIR);
|
|
2349
|
+
const toolIds = uniqueStrings([
|
|
2350
|
+
...skillPlan.requiredSkills,
|
|
2351
|
+
...skillPlan.recommendedSkills,
|
|
2352
|
+
...Object.keys(toolPolicy.tools).filter(toolId => {
|
|
2353
|
+
const config = toolPolicy.tools[toolId];
|
|
2354
|
+
const domains = new Set(skillPlan.intents.map(intent => intent.domain));
|
|
2355
|
+
return config.enabled && (config.requiredFor.some(domain => domains.has(domain)) ||
|
|
2356
|
+
(config.recommendedFor ?? []).some(domain => domains.has(domain)));
|
|
2357
|
+
}),
|
|
2358
|
+
]);
|
|
2359
|
+
const capabilityReport = inspectToolCapabilities({
|
|
2360
|
+
projectDir,
|
|
2361
|
+
toolIds,
|
|
2362
|
+
});
|
|
2363
|
+
const orchestrator = new ToolOrchestrator({
|
|
2364
|
+
projectDir,
|
|
2365
|
+
policy: toolPolicy,
|
|
2366
|
+
capabilityReport,
|
|
2367
|
+
evidenceStore: new ToolEvidenceStore({ projectDir, scaleDir: SCALE_DIR }),
|
|
2368
|
+
});
|
|
2369
|
+
return {
|
|
2370
|
+
projectDir,
|
|
2371
|
+
skillPlan,
|
|
2372
|
+
orchestrator,
|
|
2373
|
+
plan: orchestrator.plan({ skillPlan }),
|
|
2374
|
+
capabilityReport,
|
|
2375
|
+
};
|
|
2376
|
+
}
|
|
2377
|
+
function uniqueStrings(items) {
|
|
2378
|
+
return [...new Set(items)];
|
|
2379
|
+
}
|
|
2380
|
+
const toolPolicyCommand = defineCommand({
|
|
2381
|
+
meta: { name: 'policy', description: 'Show resolved tool orchestration policy' },
|
|
2382
|
+
args: {
|
|
2383
|
+
dir: { type: 'string', default: '.', description: 'Project directory' },
|
|
2384
|
+
mode: { type: 'string', description: 'Render a starter policy mode instead of reading .scale/tools.json' },
|
|
2385
|
+
json: { type: 'boolean', default: false },
|
|
2386
|
+
},
|
|
2387
|
+
run({ args }) {
|
|
2388
|
+
const policy = args.mode
|
|
2389
|
+
? JSON.parse(toolPolicyTemplate(normalizeToolMode(args.mode)))
|
|
2390
|
+
: loadToolPolicy(args.dir, SCALE_DIR);
|
|
2391
|
+
if (args.json) {
|
|
2392
|
+
console.log(JSON.stringify(policy, null, 2));
|
|
2393
|
+
return;
|
|
2394
|
+
}
|
|
2395
|
+
console.log('\nSCALE Tool Policy');
|
|
2396
|
+
console.log(` Mode: ${policy.mode}`);
|
|
2397
|
+
console.log(` Tools: ${Object.keys(policy.tools).length}`);
|
|
2398
|
+
for (const [id, config] of Object.entries(policy.tools)) {
|
|
2399
|
+
const state = config.enabled ? '[ON]' : '[OFF]';
|
|
2400
|
+
console.log(` ${state} ${id}: requiredFor=${config.requiredFor.join(',') || 'none'}`);
|
|
2401
|
+
}
|
|
2402
|
+
},
|
|
2403
|
+
});
|
|
2404
|
+
const toolDoctorCommand = defineCommand({
|
|
2405
|
+
meta: { name: 'doctor', description: 'Check skill, MCP, and CLI tool availability' },
|
|
2406
|
+
args: {
|
|
2407
|
+
dir: { type: 'string', default: '.', description: 'Project directory' },
|
|
2408
|
+
tools: { type: 'string', description: 'Comma-separated tool ids to check' },
|
|
2409
|
+
json: { type: 'boolean', default: false },
|
|
2410
|
+
},
|
|
2411
|
+
run({ args }) {
|
|
2412
|
+
const report = inspectToolCapabilities({
|
|
2413
|
+
projectDir: args.dir,
|
|
2414
|
+
toolIds: parseToolIds(args.tools),
|
|
2415
|
+
});
|
|
2416
|
+
if (args.json) {
|
|
2417
|
+
console.log(JSON.stringify(report, null, 2));
|
|
2418
|
+
}
|
|
2419
|
+
else {
|
|
2420
|
+
console.log('\nSCALE Tool Doctor');
|
|
2421
|
+
console.log(` Installed: ${report.summary.installed}/${report.summary.total}`);
|
|
2422
|
+
for (const entry of report.tools) {
|
|
2423
|
+
console.log(` ${entry.installed ? '[OK]' : '[MISSING]'} ${entry.id}`);
|
|
2424
|
+
if (entry.detectedPath)
|
|
2425
|
+
console.log(` path: ${entry.detectedPath}`);
|
|
2426
|
+
if (entry.version)
|
|
2427
|
+
console.log(` version: ${entry.version}`);
|
|
2428
|
+
if (entry.missingReason)
|
|
2429
|
+
console.log(` reason: ${entry.missingReason}`);
|
|
2430
|
+
}
|
|
2431
|
+
}
|
|
2432
|
+
if (!report.ok)
|
|
2433
|
+
process.exitCode = 1;
|
|
2434
|
+
},
|
|
2435
|
+
});
|
|
2436
|
+
const toolPlanCommand = defineCommand({
|
|
2437
|
+
meta: { name: 'plan', description: 'Create a tool execution plan from task intent' },
|
|
2438
|
+
args: {
|
|
2439
|
+
dir: { type: 'string', default: '.', description: 'Project directory' },
|
|
2440
|
+
'task-id': { type: 'string', required: true, description: 'Task id for evidence linkage' },
|
|
2441
|
+
task: { type: 'string', required: true, description: 'Task description' },
|
|
2442
|
+
level: { type: 'string', default: 'M', description: 'Task level: S, M, L, or CRITICAL' },
|
|
2443
|
+
files: { type: 'string', description: 'Comma-separated changed or target files' },
|
|
2444
|
+
services: { type: 'string', description: 'Comma-separated affected services' },
|
|
2445
|
+
json: { type: 'boolean', default: false },
|
|
2446
|
+
},
|
|
2447
|
+
run({ args }) {
|
|
2448
|
+
const result = createToolExecutionPlanFromArgs(args);
|
|
2449
|
+
if (args.json) {
|
|
2450
|
+
console.log(JSON.stringify(result.plan, null, 2));
|
|
2451
|
+
return;
|
|
2452
|
+
}
|
|
2453
|
+
console.log('\nSCALE Tool Plan');
|
|
2454
|
+
console.log(` Task: ${result.plan.taskId}`);
|
|
2455
|
+
console.log(` Mode: ${result.plan.mode}`);
|
|
2456
|
+
console.log(` Steps: ${result.plan.steps.length}`);
|
|
2457
|
+
for (const step of result.plan.steps) {
|
|
2458
|
+
console.log(` ${step.status === 'ready' ? '[READY]' : '[MISSING]'} ${step.toolId} (${step.adapter}) required=${step.required}`);
|
|
2459
|
+
}
|
|
2460
|
+
for (const blocker of result.plan.blockers)
|
|
2461
|
+
console.log(` [BLOCKER] ${blocker}`);
|
|
2462
|
+
for (const warning of result.plan.warnings)
|
|
2463
|
+
console.log(` [WARN] ${warning}`);
|
|
2464
|
+
},
|
|
2465
|
+
});
|
|
2466
|
+
const toolRunCommand = defineCommand({
|
|
2467
|
+
meta: { name: 'run', description: 'Run or dry-run a tool execution plan and write tool evidence' },
|
|
2468
|
+
args: {
|
|
2469
|
+
dir: { type: 'string', default: '.', description: 'Project directory' },
|
|
2470
|
+
'task-id': { type: 'string', required: true, description: 'Task id for evidence linkage' },
|
|
2471
|
+
task: { type: 'string', required: true, description: 'Task description' },
|
|
2472
|
+
level: { type: 'string', default: 'M', description: 'Task level: S, M, L, or CRITICAL' },
|
|
2473
|
+
files: { type: 'string', description: 'Comma-separated changed or target files' },
|
|
2474
|
+
services: { type: 'string', description: 'Comma-separated affected services' },
|
|
2475
|
+
'dry-run': { type: 'boolean', default: false, description: 'Plan and record skipped evidence without executing tools' },
|
|
2476
|
+
json: { type: 'boolean', default: false },
|
|
2477
|
+
},
|
|
2478
|
+
async run({ args }) {
|
|
2479
|
+
const result = createToolExecutionPlanFromArgs(args);
|
|
2480
|
+
const report = await result.orchestrator.run(result.plan, {
|
|
2481
|
+
dryRun: isTruthyFlag(args['dry-run']),
|
|
2482
|
+
});
|
|
2483
|
+
if (args.json) {
|
|
2484
|
+
console.log(JSON.stringify(report, null, 2));
|
|
2485
|
+
}
|
|
2486
|
+
else {
|
|
2487
|
+
console.log('\nSCALE Tool Run');
|
|
2488
|
+
console.log(` Task: ${report.taskId}`);
|
|
2489
|
+
console.log(` Dry-run: ${report.dryRun}`);
|
|
2490
|
+
console.log(` Evidence: ${report.evidence.length}`);
|
|
2491
|
+
for (const record of report.evidence) {
|
|
2492
|
+
console.log(` [${record.status.toUpperCase()}] ${record.tool} -> ${record.id}`);
|
|
2493
|
+
}
|
|
2494
|
+
for (const blocker of report.blockers)
|
|
2495
|
+
console.log(` [BLOCKER] ${blocker}`);
|
|
2496
|
+
for (const warning of report.warnings)
|
|
2497
|
+
console.log(` [WARN] ${warning}`);
|
|
2498
|
+
}
|
|
2499
|
+
if (!report.ok)
|
|
2500
|
+
process.exitCode = 1;
|
|
2501
|
+
},
|
|
2502
|
+
});
|
|
2503
|
+
const toolEvidenceCommand = defineCommand({
|
|
2504
|
+
meta: { name: 'evidence', description: 'Check required tool execution evidence for a task' },
|
|
2505
|
+
args: {
|
|
2506
|
+
dir: { type: 'string', default: '.', description: 'Project directory' },
|
|
2507
|
+
'task-id': { type: 'string', required: true, description: 'Task id for evidence linkage' },
|
|
2508
|
+
task: { type: 'string', required: true, description: 'Task description' },
|
|
2509
|
+
level: { type: 'string', default: 'M', description: 'Task level: S, M, L, or CRITICAL' },
|
|
2510
|
+
files: { type: 'string', description: 'Comma-separated changed or target files' },
|
|
2511
|
+
services: { type: 'string', description: 'Comma-separated affected services' },
|
|
2512
|
+
mode: { type: 'string', description: 'Override tool gate mode: off, advisory, evidence-required, or block' },
|
|
2513
|
+
'allow-skipped': { type: 'boolean', default: false, description: 'Allow skipped/manual fallback evidence to satisfy required tools' },
|
|
2514
|
+
json: { type: 'boolean', default: false },
|
|
2515
|
+
},
|
|
2516
|
+
run({ args }) {
|
|
2517
|
+
const result = createToolExecutionPlanFromArgs(args);
|
|
2518
|
+
const gate = evaluateToolEvidenceGate({
|
|
2519
|
+
projectDir: result.projectDir,
|
|
2520
|
+
level: normalizeTaskArtifactLevel(args.level ?? 'M'),
|
|
2521
|
+
plan: result.plan,
|
|
2522
|
+
evidenceStore: new ToolEvidenceStore({ projectDir: result.projectDir, scaleDir: SCALE_DIR }),
|
|
2523
|
+
mode: args.mode ? normalizeToolMode(args.mode) : result.plan.mode,
|
|
2524
|
+
allowSkipped: isTruthyFlag(args['allow-skipped']),
|
|
2525
|
+
});
|
|
2526
|
+
if (args.json) {
|
|
2527
|
+
console.log(JSON.stringify(gate, null, 2));
|
|
2528
|
+
}
|
|
2529
|
+
else {
|
|
2530
|
+
console.log('\nSCALE Tool Evidence Gate');
|
|
2531
|
+
console.log(` Task: ${gate.taskId ?? args['task-id']}`);
|
|
2532
|
+
console.log(` Mode: ${gate.mode}`);
|
|
2533
|
+
console.log(` Complete: ${gate.complete}`);
|
|
2534
|
+
console.log(` Required tools: ${gate.requiredTools.join(', ') || 'none'}`);
|
|
2535
|
+
for (const item of gate.missing)
|
|
2536
|
+
console.log(` [MISSING] ${item.toolId}: ${item.reason}`);
|
|
2537
|
+
for (const item of gate.failed)
|
|
2538
|
+
console.log(` [FAILED] ${item.toolId}: ${item.reason}`);
|
|
2539
|
+
for (const item of gate.skipped)
|
|
2540
|
+
console.log(` [SKIPPED] ${item.toolId}: ${item.reason}`);
|
|
2541
|
+
for (const item of gate.passed)
|
|
2542
|
+
console.log(` [PASS] ${item.toolId}: ${item.evidenceId ?? 'evidence'}`);
|
|
2543
|
+
for (const warning of gate.warnings)
|
|
2544
|
+
console.log(` [WARN] ${warning}`);
|
|
2545
|
+
}
|
|
2546
|
+
if (gate.blocked)
|
|
2547
|
+
process.exitCode = 1;
|
|
2548
|
+
},
|
|
2549
|
+
});
|
|
2550
|
+
const tool = defineCommand({
|
|
2551
|
+
meta: { name: 'tool', description: 'Skills, MCP, browser, desktop, and external CLI governance' },
|
|
2552
|
+
subCommands: { policy: toolPolicyCommand, doctor: toolDoctorCommand, plan: toolPlanCommand, run: toolRunCommand, evidence: toolEvidenceCommand },
|
|
1855
2553
|
});
|
|
1856
2554
|
// ============================================================================
|
|
1857
2555
|
// agent commands — Multi-Agent 协作系统 (Phase 9)
|
|
@@ -1908,9 +2606,32 @@ const agentProfiles = defineCommand({
|
|
|
1908
2606
|
}
|
|
1909
2607
|
},
|
|
1910
2608
|
});
|
|
2609
|
+
const agentLeaders = defineCommand({
|
|
2610
|
+
meta: { name: 'leaders', description: 'List SCALE leader presets such as CEO and CTO' },
|
|
2611
|
+
args: {
|
|
2612
|
+
output: { type: 'string', alias: 'o', description: 'Write markdown guide to file' },
|
|
2613
|
+
json: { type: 'boolean', default: false },
|
|
2614
|
+
},
|
|
2615
|
+
async run({ args }) {
|
|
2616
|
+
const presets = listLeadershipPresets();
|
|
2617
|
+
if (args.json) {
|
|
2618
|
+
console.log(JSON.stringify(presets, null, 2));
|
|
2619
|
+
return;
|
|
2620
|
+
}
|
|
2621
|
+
const markdown = renderLeadershipPresetsMarkdown();
|
|
2622
|
+
if (args.output) {
|
|
2623
|
+
const outputPath = resolve(PROJECT_DIR, args.output);
|
|
2624
|
+
ensureDir(resolve(outputPath, '..'));
|
|
2625
|
+
writeFileSync(outputPath, markdown, 'utf-8');
|
|
2626
|
+
console.log(`[OK] 领导者角色指南已生成: ${outputPath}`);
|
|
2627
|
+
return;
|
|
2628
|
+
}
|
|
2629
|
+
console.log(markdown);
|
|
2630
|
+
},
|
|
2631
|
+
});
|
|
1911
2632
|
const agent = defineCommand({
|
|
1912
2633
|
meta: { name: 'agent', description: 'Multi-Agent system management' },
|
|
1913
|
-
subCommands: { spawn: agentSpawn, list: agentList, profiles: agentProfiles },
|
|
2634
|
+
subCommands: { spawn: agentSpawn, list: agentList, profiles: agentProfiles, leaders: agentLeaders },
|
|
1914
2635
|
});
|
|
1915
2636
|
// ============================================================================
|
|
1916
2637
|
// team commands — 团队协作 (Phase 9)
|
|
@@ -1976,7 +2697,7 @@ import * as phaseCommands from '../cli/phaseCommands.js';
|
|
|
1976
2697
|
import * as liteCommands from '../cli/liteCommands.js';
|
|
1977
2698
|
import * as vibeCommands from '../cli/vibeCommands.js';
|
|
1978
2699
|
const main = defineCommand({
|
|
1979
|
-
meta: { name: 'scale', version:
|
|
2700
|
+
meta: { name: 'scale', version: SCALE_ENGINE_VERSION, description: `SCALE Engine v${SCALE_ENGINE_VERSION} CLI - hardened phase workflow gates, governance templates, platform adapters, skill routing, and verification automation` },
|
|
1980
2701
|
subCommands: {
|
|
1981
2702
|
// Lite Mode (agent-skills style interactive entry)
|
|
1982
2703
|
lite: liteCommands.liteCommand,
|
|
@@ -2008,12 +2729,15 @@ const main = defineCommand({
|
|
|
2008
2729
|
stats,
|
|
2009
2730
|
preflight,
|
|
2010
2731
|
governance,
|
|
2732
|
+
assets,
|
|
2733
|
+
standards,
|
|
2011
2734
|
metrics,
|
|
2012
2735
|
'task-artifacts': taskArtifacts,
|
|
2013
2736
|
workspace,
|
|
2014
2737
|
status,
|
|
2015
2738
|
workflow,
|
|
2016
2739
|
evidence,
|
|
2740
|
+
tool,
|
|
2017
2741
|
skill,
|
|
2018
2742
|
skills: skill,
|
|
2019
2743
|
agent,
|