@hongmaple0820/scale-engine 0.47.0 → 0.49.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 +8 -2
- package/README.md +9 -3
- package/dist/agents/evidenceDiscipline.d.ts +7 -0
- package/dist/agents/evidenceDiscipline.js +21 -0
- package/dist/agents/evidenceDiscipline.js.map +1 -0
- package/dist/agents/profiles.js +8 -1
- package/dist/agents/profiles.js.map +1 -1
- package/dist/agents/types.d.ts +1 -0
- package/dist/api/cli.js +975 -6222
- package/dist/api/cli.js.map +1 -1
- package/dist/artifact/types.d.ts +59 -0
- package/dist/artifact/types.js.map +1 -1
- package/dist/cli/artifactCrudCommands.d.ts +67 -0
- package/dist/cli/artifactCrudCommands.js +182 -0
- package/dist/cli/artifactCrudCommands.js.map +1 -0
- package/dist/cli/codegraphCommands.d.ts +1 -0
- package/dist/cli/codegraphCommands.js +241 -0
- package/dist/cli/codegraphCommands.js.map +1 -0
- package/dist/cli/contextCommands.d.ts +1 -0
- package/dist/cli/contextCommands.js +415 -0
- package/dist/cli/contextCommands.js.map +1 -0
- package/dist/cli/cortexCommands.d.ts +36 -0
- package/dist/cli/cortexCommands.js +76 -4
- package/dist/cli/cortexCommands.js.map +1 -1
- package/dist/cli/dependencyTddCommands.d.ts +92 -0
- package/dist/cli/dependencyTddCommands.js +174 -0
- package/dist/cli/dependencyTddCommands.js.map +1 -0
- package/dist/cli/diagnoseHuntCommands.d.ts +135 -0
- package/dist/cli/diagnoseHuntCommands.js +224 -0
- package/dist/cli/diagnoseHuntCommands.js.map +1 -0
- package/dist/cli/engineBootstrap.d.ts +39 -0
- package/dist/cli/engineBootstrap.js +129 -0
- package/dist/cli/engineBootstrap.js.map +1 -0
- package/dist/cli/evalCommands.d.ts +1 -0
- package/dist/cli/evalCommands.js +273 -0
- package/dist/cli/evalCommands.js.map +1 -0
- package/dist/cli/evolveDoctorCommands.d.ts +18 -0
- package/dist/cli/evolveDoctorCommands.js +59 -0
- package/dist/cli/evolveDoctorCommands.js.map +1 -0
- package/dist/cli/gateInlineCommands.d.ts +43 -0
- package/dist/cli/gateInlineCommands.js +74 -0
- package/dist/cli/gateInlineCommands.js.map +1 -0
- package/dist/cli/initConfigCommands.d.ts +138 -0
- package/dist/cli/initConfigCommands.js +602 -0
- package/dist/cli/initConfigCommands.js.map +1 -0
- package/dist/cli/metaGovernanceCommands.d.ts +11 -0
- package/dist/cli/metaGovernanceCommands.js +55 -0
- package/dist/cli/metaGovernanceCommands.js.map +1 -0
- package/dist/cli/phaseCommands.d.ts +53 -1
- package/dist/cli/phaseCommands.js +317 -22
- package/dist/cli/phaseCommands.js.map +1 -1
- package/dist/cli/runtimeSkillCommands.d.ts +6 -0
- package/dist/cli/runtimeSkillCommands.js +1515 -0
- package/dist/cli/runtimeSkillCommands.js.map +1 -0
- package/dist/cli/sessionCommands.d.ts +17 -0
- package/dist/cli/sessionCommands.js +38 -0
- package/dist/cli/sessionCommands.js.map +1 -0
- package/dist/cli/toolAgentCommands.d.ts +3 -0
- package/dist/cli/toolAgentCommands.js +441 -0
- package/dist/cli/toolAgentCommands.js.map +1 -0
- package/dist/cli/transitionCommands.d.ts +62 -0
- package/dist/cli/transitionCommands.js +174 -0
- package/dist/cli/transitionCommands.js.map +1 -0
- package/dist/cli/upgradeAssetsCommands.d.ts +44 -0
- package/dist/cli/upgradeAssetsCommands.js +933 -0
- package/dist/cli/upgradeAssetsCommands.js.map +1 -0
- package/dist/cli/workflowEvidenceCommands.d.ts +34 -0
- package/dist/cli/workflowEvidenceCommands.js +130 -0
- package/dist/cli/workflowEvidenceCommands.js.map +1 -0
- package/dist/cortex/InstinctStore.d.ts +32 -1
- package/dist/cortex/InstinctStore.js +235 -42
- package/dist/cortex/InstinctStore.js.map +1 -1
- package/dist/cortex/InstinctValidation.d.ts +9 -0
- package/dist/cortex/InstinctValidation.js +55 -0
- package/dist/cortex/InstinctValidation.js.map +1 -0
- package/dist/cortex/SessionInjector.js +13 -6
- package/dist/cortex/SessionInjector.js.map +1 -1
- package/dist/eval/BenchmarkPublisher.d.ts +2 -0
- package/dist/eval/BenchmarkPublisher.js +43 -0
- package/dist/eval/BenchmarkPublisher.js.map +1 -1
- package/dist/guardrails/ast/confirmers.d.ts +18 -0
- package/dist/guardrails/ast/confirmers.js +69 -0
- package/dist/guardrails/ast/confirmers.js.map +1 -0
- package/dist/guardrails/ast/parse.d.ts +20 -0
- package/dist/guardrails/ast/parse.js +51 -0
- package/dist/guardrails/ast/parse.js.map +1 -0
- package/dist/output/HTMLDocumentRenderer.d.ts +9 -0
- package/dist/output/HTMLDocumentRenderer.js +19 -0
- package/dist/output/HTMLDocumentRenderer.js.map +1 -1
- package/dist/review/FreshContextVerifier.d.ts +35 -0
- package/dist/review/FreshContextVerifier.js +120 -0
- package/dist/review/FreshContextVerifier.js.map +1 -0
- package/dist/review/JsonLlmClient.d.ts +37 -0
- package/dist/review/JsonLlmClient.js +94 -0
- package/dist/review/JsonLlmClient.js.map +1 -0
- package/dist/review/LlmJudge.d.ts +61 -0
- package/dist/review/LlmJudge.js +167 -0
- package/dist/review/LlmJudge.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/workflow/BoundaryEnforcement.d.ts +60 -0
- package/dist/workflow/BoundaryEnforcement.js +182 -0
- package/dist/workflow/BoundaryEnforcement.js.map +1 -0
- package/dist/workflow/EngineeringStandards.js +19 -9
- package/dist/workflow/EngineeringStandards.js.map +1 -1
- package/dist/workflow/GateCatalog.js +12 -2
- package/dist/workflow/GateCatalog.js.map +1 -1
- package/dist/workflow/ProfileEnforcement.d.ts +7 -0
- package/dist/workflow/ProfileEnforcement.js +12 -0
- package/dist/workflow/ProfileEnforcement.js.map +1 -0
- package/dist/workflow/ReviewStore.d.ts +10 -0
- package/dist/workflow/ReviewStore.js.map +1 -1
- package/dist/workflow/SurfaceCoverage.d.ts +19 -0
- package/dist/workflow/SurfaceCoverage.js +57 -0
- package/dist/workflow/SurfaceCoverage.js.map +1 -0
- package/dist/workflow/gates/EnhancedGates.js +2 -0
- package/dist/workflow/gates/EnhancedGates.js.map +1 -1
- package/dist/workflow/gates/TestIntegrityGate.d.ts +51 -0
- package/dist/workflow/gates/TestIntegrityGate.js +175 -0
- package/dist/workflow/gates/TestIntegrityGate.js.map +1 -0
- package/dist/workflow/types.d.ts +1 -1
- package/docs/guides/DEVELOPMENT_WORKFLOW.md +28 -0
- package/docs/workflow/E2E_EXAMPLE.md +133 -0
- package/docs/workflow/README.md +6 -0
- package/docs/workflow/TEMPLATE_GUIDE.md +162 -0
- package/docs/workflow/templates/plan.md +26 -0
- package/docs/workflow/templates/spec.md +28 -0
- package/package.json +3 -1
|
@@ -0,0 +1,933 @@
|
|
|
1
|
+
// SCALE Engine — Upgrade, Assets, Standards, and Artifact CLI Commands
|
|
2
|
+
// Extracted from api/cli.ts (lines 2042-2944)
|
|
3
|
+
import { defineCommand } from 'citty';
|
|
4
|
+
import { createInterface } from 'node:readline';
|
|
5
|
+
import { existsSync } from 'node:fs';
|
|
6
|
+
import { resolve } from 'node:path';
|
|
7
|
+
import { execFileSync } from 'node:child_process';
|
|
8
|
+
import { pathToFileURL } from 'node:url';
|
|
9
|
+
import { PROJECT_DIR, isTruthyFlag, resolveScaleDirForProject, } from './engineBootstrap.js';
|
|
10
|
+
import { normalizeLanguage } from '../i18n/Language.js';
|
|
11
|
+
import { applyUpgradePlan, createUpgradeCheckReport, createUpgradePlanReport, createUpgradeRecommendReport, rollbackLatestUpgrade, writeUpgradePlanHtml, } from '../workflow/UpgradeManager.js';
|
|
12
|
+
import { baselineEngineeringStandards, doctorEngineeringStandards, scanEngineeringStandards, settleEngineeringStandards, } from '../workflow/EngineeringStandards.js';
|
|
13
|
+
import { doctorResourceAssets, scanResourceAssets, settleResourceAssets } from '../workflow/ResourceGovernance.js';
|
|
14
|
+
import { doctorHtmlArtifacts, renderHtmlArtifact, resolveHtmlArtifactForOpen, settleHtmlArtifacts, } from '../output/HTMLArtifactLayer.js';
|
|
15
|
+
import { renderGovernanceDashboard } from '../output/GovernanceDashboard.js';
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// Shared helpers
|
|
18
|
+
// ============================================================================
|
|
19
|
+
function normalizeThemeArg(value) {
|
|
20
|
+
const normalized = String(value ?? 'auto').trim().toLowerCase();
|
|
21
|
+
if (normalized === 'dark' || normalized === 'light' || normalized === 'auto')
|
|
22
|
+
return normalized;
|
|
23
|
+
return 'auto';
|
|
24
|
+
}
|
|
25
|
+
function normalizeLangArg(value) {
|
|
26
|
+
return normalizeLanguage(value ?? process.env.SCALE_LANG);
|
|
27
|
+
}
|
|
28
|
+
function splitChangedFiles(value) {
|
|
29
|
+
if (!value)
|
|
30
|
+
return [];
|
|
31
|
+
return value
|
|
32
|
+
.split(/[\n,]/)
|
|
33
|
+
.map(item => item.trim())
|
|
34
|
+
.filter(Boolean);
|
|
35
|
+
}
|
|
36
|
+
function readGitPathList(projectDir, args) {
|
|
37
|
+
try {
|
|
38
|
+
return execFileSync('git', ['-C', projectDir, ...args], {
|
|
39
|
+
encoding: 'utf-8',
|
|
40
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
41
|
+
})
|
|
42
|
+
.split(/\r?\n/)
|
|
43
|
+
.map(item => item.trim())
|
|
44
|
+
.filter(Boolean);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function readGitChangedFiles(projectDir) {
|
|
51
|
+
const tracked = readGitPathList(projectDir, ['diff', '--name-only', '--diff-filter=ACMRTUXB', 'HEAD', '--']);
|
|
52
|
+
const untracked = readGitPathList(projectDir, ['ls-files', '--others', '--exclude-standard']);
|
|
53
|
+
return Array.from(new Set([...tracked, ...untracked]));
|
|
54
|
+
}
|
|
55
|
+
function resolveChangedFilesArg(args) {
|
|
56
|
+
const explicit = splitChangedFiles(args['changed-files']);
|
|
57
|
+
if (explicit.length > 0)
|
|
58
|
+
return explicit;
|
|
59
|
+
if (!args.changed)
|
|
60
|
+
return undefined;
|
|
61
|
+
return readGitChangedFiles(args.dir ?? '.');
|
|
62
|
+
}
|
|
63
|
+
function readGitChangedFilesForStandards(projectDir) {
|
|
64
|
+
try {
|
|
65
|
+
execFileSync('git', ['-C', projectDir, 'rev-parse', '--is-inside-work-tree'], {
|
|
66
|
+
encoding: 'utf-8',
|
|
67
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
return readGitChangedFiles(projectDir);
|
|
74
|
+
}
|
|
75
|
+
function launchLocalFile(path) {
|
|
76
|
+
try {
|
|
77
|
+
if (process.platform === 'win32') {
|
|
78
|
+
execFileSync('cmd', ['/c', 'start', '', path], { stdio: 'ignore' });
|
|
79
|
+
}
|
|
80
|
+
else if (process.platform === 'darwin') {
|
|
81
|
+
execFileSync('open', [path], { stdio: 'ignore' });
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
execFileSync('xdg-open', [path], { stdio: 'ignore' });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// Opening is convenience-only; artifact doctor/render remains the source of truth.
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function formatUpgradeBlockerMessage(code, fallback, lang) {
|
|
92
|
+
if (lang !== 'zh')
|
|
93
|
+
return fallback;
|
|
94
|
+
if (code === 'missing-governance-lock')
|
|
95
|
+
return '缺少治理锁文件,无法判断哪些生成文件可以安全升级。';
|
|
96
|
+
if (code === 'local-generated-file-changed')
|
|
97
|
+
return '受管生成文件已有本地改动,需要三方对比或人工审阅后再升级。';
|
|
98
|
+
return fallback;
|
|
99
|
+
}
|
|
100
|
+
function formatUpgradeStepReason(action, fallback, lang) {
|
|
101
|
+
if (lang !== 'zh')
|
|
102
|
+
return fallback;
|
|
103
|
+
switch (action) {
|
|
104
|
+
case 'initialize-governance-lock':
|
|
105
|
+
return '先创建治理锁文件,后续才能安全升级生成的治理资产。';
|
|
106
|
+
case 'upgrade-scale-engine':
|
|
107
|
+
return fallback.replace('SCALE Engine changed from', 'SCALE Engine 版本变化:').replace(' to ', ' -> ');
|
|
108
|
+
case 'upgrade-governance-pack':
|
|
109
|
+
return fallback.replace('Governance pack', '治理包').replace('changed from', '版本变化:').replace(' to ', ' -> ');
|
|
110
|
+
case 'refresh-managed-generated-files':
|
|
111
|
+
return fallback.replace('clean managed governance files can be refreshed automatically; local edits still block automatic apply.', '个干净受管治理文件可自动刷新;已有本地改动的文件仍会阻止自动应用。');
|
|
112
|
+
case 'restore-missing-generated-file':
|
|
113
|
+
return '该文件由治理锁管理,但当前本地缺失,可从当前治理包恢复。';
|
|
114
|
+
case 'review-local-change':
|
|
115
|
+
return '需要保留、合并或明确替换本地改动,不能自动覆盖。';
|
|
116
|
+
case 'review-third-party-capability':
|
|
117
|
+
return fallback
|
|
118
|
+
.replace('updates require manual-review; SCALE never auto-installs third-party capabilities.', '更新需要人工审阅;SCALE 不会自动安装第三方能力。')
|
|
119
|
+
.replace('updates require blocked; SCALE never auto-installs third-party capabilities.', '更新默认阻断;SCALE 不会自动安装第三方能力。');
|
|
120
|
+
case 'adopt-ai-os-runtime':
|
|
121
|
+
return '运行 AI OS 一键接入路径,生成运行态目录、首份 dry-run、benchmark 和 doctor 报告。';
|
|
122
|
+
case 'migrate-ai-os-runtime':
|
|
123
|
+
return 'AI OS 运行态目录缺失;接入 beta runtime 前先创建目录结构。';
|
|
124
|
+
case 'check-ai-os-runtime':
|
|
125
|
+
return '依赖 AI OS beta 编排前,先复核运行态就绪状态。';
|
|
126
|
+
case 'run-preflight':
|
|
127
|
+
return '完成已接受的升级后,运行项目级预检。';
|
|
128
|
+
default:
|
|
129
|
+
return fallback;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function formatUpgradeCommand(command, lang) {
|
|
133
|
+
if (command === 'scale ai-os adopt --dir . --task "Adopt AI OS runtime" --json') {
|
|
134
|
+
return lang === 'zh'
|
|
135
|
+
? 'scale ai-os adopt --dir . --task "接入 AI OS runtime" --lang zh'
|
|
136
|
+
: 'scale ai-os adopt --dir . --task "Adopt AI OS runtime" --lang en';
|
|
137
|
+
}
|
|
138
|
+
if (command === 'scale ai-os doctor --dir . --json') {
|
|
139
|
+
return lang === 'zh' ? 'scale ai-os doctor --dir . --lang zh' : 'scale ai-os doctor --dir . --lang en';
|
|
140
|
+
}
|
|
141
|
+
if (command === 'scale ai-os migrate --dir . --json') {
|
|
142
|
+
return 'scale ai-os migrate --dir .';
|
|
143
|
+
}
|
|
144
|
+
return command;
|
|
145
|
+
}
|
|
146
|
+
function formatUpgradeApplyReason(reason, lang) {
|
|
147
|
+
if (lang !== 'zh')
|
|
148
|
+
return reason;
|
|
149
|
+
switch (reason) {
|
|
150
|
+
case 'Review scale upgrade plan first, then rerun with --confirm.':
|
|
151
|
+
return '请先审阅 SCALE 升级计划,再使用 --confirm 重新运行。';
|
|
152
|
+
case 'Upgrade requires manual review because generated files have local changes or the lock is missing.':
|
|
153
|
+
return '生成文件存在本地改动或缺少锁文件,本次升级需要人工审阅。';
|
|
154
|
+
case 'Cannot apply without a governance lock and pack id.':
|
|
155
|
+
return '缺少治理锁文件或治理包 ID,无法应用升级。';
|
|
156
|
+
case 'No safe upgrade changes were needed.':
|
|
157
|
+
return '没有需要应用的安全升级变更。';
|
|
158
|
+
case 'Safe upgrade changes were applied.':
|
|
159
|
+
return '已应用安全升级变更。';
|
|
160
|
+
case 'No SCALE-managed upgrade backup was found.':
|
|
161
|
+
return '未找到 SCALE 管理的升级备份。';
|
|
162
|
+
case 'Latest SCALE-managed upgrade backup was rolled back.':
|
|
163
|
+
return '已回滚最近一次 SCALE 管理的升级备份。';
|
|
164
|
+
default:
|
|
165
|
+
return reason;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
async function askUpgradeWizardQuestion(question) {
|
|
169
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
170
|
+
try {
|
|
171
|
+
return await new Promise(resolve => rl.question(question, resolve));
|
|
172
|
+
}
|
|
173
|
+
finally {
|
|
174
|
+
rl.close();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
function isUpgradeSubcommandInvocation(argv) {
|
|
178
|
+
const upgradeIndex = argv.findIndex((value, index) => index > 1 && value === 'upgrade');
|
|
179
|
+
if (upgradeIndex < 0)
|
|
180
|
+
return false;
|
|
181
|
+
const positional = argv.slice(upgradeIndex + 1).find(value => !value.startsWith('-') && !value.includes('='));
|
|
182
|
+
return positional === 'check' || positional === 'plan' || positional === 'apply' || positional === 'rollback';
|
|
183
|
+
}
|
|
184
|
+
function renderUpgradeWizardReport(report, lang) {
|
|
185
|
+
const plan = report.plan;
|
|
186
|
+
if (lang === 'zh') {
|
|
187
|
+
console.log('\nSCALE 升级向导');
|
|
188
|
+
console.log(` 项目: ${plan.projectDir}`);
|
|
189
|
+
console.log(` 状态: ${plan.status}`);
|
|
190
|
+
console.log(` 应用模式: ${plan.applyMode}`);
|
|
191
|
+
console.log(` 阻塞项: ${plan.blockers.length}`);
|
|
192
|
+
console.log(` 步骤数: ${plan.steps.length}`);
|
|
193
|
+
if (report.htmlPath)
|
|
194
|
+
console.log(` HTML 计划: ${report.htmlPath}`);
|
|
195
|
+
if (report.cancelled)
|
|
196
|
+
console.log(' 结果: 已取消');
|
|
197
|
+
else if (report.applyResult) {
|
|
198
|
+
console.log(` 结果: ${report.applyResult.applied ? '已应用' : '未应用'}`);
|
|
199
|
+
console.log(` 原因: ${formatUpgradeApplyReason(report.applyResult.reason, lang)}`);
|
|
200
|
+
for (const path of report.applyResult.changedFiles)
|
|
201
|
+
console.log(` 已变更: ${path}`);
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
console.log(' 结果: 已生成计划,未应用变更');
|
|
205
|
+
}
|
|
206
|
+
console.log(' 下一步:');
|
|
207
|
+
for (const command of plan.check.recommendedCommands.slice(0, 5))
|
|
208
|
+
console.log(` ${formatUpgradeCommand(command, lang)}`);
|
|
209
|
+
if (plan.blockers.length > 0)
|
|
210
|
+
console.log(' 先解决阻塞项后再执行 scale upgrade apply --confirm');
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
console.log('\nSCALE Upgrade Wizard');
|
|
214
|
+
console.log(` Project: ${plan.projectDir}`);
|
|
215
|
+
console.log(` Status: ${plan.status}`);
|
|
216
|
+
console.log(` Apply mode: ${plan.applyMode}`);
|
|
217
|
+
console.log(` Blockers: ${plan.blockers.length}`);
|
|
218
|
+
console.log(` Steps: ${plan.steps.length}`);
|
|
219
|
+
if (report.htmlPath)
|
|
220
|
+
console.log(` HTML plan: ${report.htmlPath}`);
|
|
221
|
+
if (report.cancelled)
|
|
222
|
+
console.log(' Result: cancelled');
|
|
223
|
+
else if (report.applyResult) {
|
|
224
|
+
console.log(` Result: ${report.applyResult.applied ? 'applied' : 'not applied'}`);
|
|
225
|
+
console.log(` Reason: ${report.applyResult.reason}`);
|
|
226
|
+
for (const path of report.applyResult.changedFiles)
|
|
227
|
+
console.log(` changed: ${path}`);
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
console.log(' Result: plan generated; no changes applied');
|
|
231
|
+
}
|
|
232
|
+
console.log(' Next:');
|
|
233
|
+
for (const command of plan.check.recommendedCommands.slice(0, 5))
|
|
234
|
+
console.log(` ${formatUpgradeCommand(command, lang)}`);
|
|
235
|
+
if (plan.blockers.length > 0)
|
|
236
|
+
console.log(' Resolve blockers before running scale upgrade apply --confirm');
|
|
237
|
+
}
|
|
238
|
+
// ============================================================================
|
|
239
|
+
// upgrade command - Safe workflow/template/capability update planning
|
|
240
|
+
// ============================================================================
|
|
241
|
+
const upgradeCheck = defineCommand({
|
|
242
|
+
meta: { name: 'check', description: '检查 SCALE 工作流、治理包和第三方能力更新状态 / Check SCALE workflow, governance pack, and third-party capability update status' },
|
|
243
|
+
args: {
|
|
244
|
+
dir: { type: 'string', default: PROJECT_DIR, description: '项目目录 / Project directory' },
|
|
245
|
+
'target-version': { type: 'string', description: '目标 SCALE Engine 版本,默认使用当前 CLI 版本 / Target SCALE Engine version' },
|
|
246
|
+
lang: { type: 'string', default: 'zh', description: '输出语言 zh/en / Output language' },
|
|
247
|
+
json: { type: 'boolean', default: false, description: '输出 JSON / Print JSON output' },
|
|
248
|
+
},
|
|
249
|
+
run({ args }) {
|
|
250
|
+
const lang = normalizeLangArg(args.lang);
|
|
251
|
+
const projectDir = resolve(String(args.dir ?? PROJECT_DIR));
|
|
252
|
+
const report = createUpgradeCheckReport({
|
|
253
|
+
projectDir,
|
|
254
|
+
scaleDir: resolveScaleDirForProject(projectDir),
|
|
255
|
+
targetScaleVersion: args['target-version'] ? String(args['target-version']) : undefined,
|
|
256
|
+
});
|
|
257
|
+
if (args.json) {
|
|
258
|
+
console.log(JSON.stringify(report, null, 2));
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
if (lang === 'zh') {
|
|
262
|
+
console.log('SCALE 升级检查');
|
|
263
|
+
console.log(` 项目: ${report.projectDir}`);
|
|
264
|
+
console.log(` 状态: ${report.status}`);
|
|
265
|
+
console.log(` SCALE Engine: ${report.scaleEngine.currentVersion ?? '无'} -> ${report.scaleEngine.latestVersion}`);
|
|
266
|
+
console.log(` 治理包: ${report.governancePack.id ?? '无'} v${report.governancePack.currentVersion ?? '无'} -> v${report.governancePack.latestVersion ?? '无'}`);
|
|
267
|
+
console.log(` 受管生成文件: ${report.generatedFiles.clean} 个干净, ${report.generatedFiles.changed} 个本地改动, ${report.generatedFiles.missing} 个缺失`);
|
|
268
|
+
console.log(` 第三方能力策略: ${report.thirdParty.policy}; 需要人工审查: ${report.thirdParty.reviewRequired}`);
|
|
269
|
+
console.log(` AI OS Runtime: ${report.aiOsRuntime.status}`);
|
|
270
|
+
console.log(' 下一步:');
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
console.log('SCALE Upgrade Check');
|
|
274
|
+
console.log(` Project: ${report.projectDir}`);
|
|
275
|
+
console.log(` Status: ${report.status}`);
|
|
276
|
+
console.log(` SCALE Engine: ${report.scaleEngine.currentVersion ?? 'none'} -> ${report.scaleEngine.latestVersion}`);
|
|
277
|
+
console.log(` Governance pack: ${report.governancePack.id ?? 'none'} v${report.governancePack.currentVersion ?? 'none'} -> v${report.governancePack.latestVersion ?? 'none'}`);
|
|
278
|
+
console.log(` Generated files: ${report.generatedFiles.clean} clean, ${report.generatedFiles.changed} changed, ${report.generatedFiles.missing} missing`);
|
|
279
|
+
console.log(` Third-party policy: ${report.thirdParty.policy}; review required: ${report.thirdParty.reviewRequired}`);
|
|
280
|
+
console.log(` AI OS Runtime: ${report.aiOsRuntime.status}`);
|
|
281
|
+
console.log(' Next:');
|
|
282
|
+
}
|
|
283
|
+
for (const command of report.recommendedCommands)
|
|
284
|
+
console.log(` ${formatUpgradeCommand(command, lang)}`);
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
const upgradePlan = defineCommand({
|
|
288
|
+
meta: { name: 'plan', description: '生成非破坏性的 SCALE 升级计划 / Create a non-destructive SCALE upgrade plan' },
|
|
289
|
+
args: {
|
|
290
|
+
dir: { type: 'string', default: PROJECT_DIR, description: '项目目录 / Project directory' },
|
|
291
|
+
'target-version': { type: 'string', description: '目标 SCALE Engine 版本,默认使用当前 CLI 版本 / Target SCALE Engine version' },
|
|
292
|
+
html: { type: 'boolean', default: false, description: '写入 .scale/reports/upgrade-plan.html / Write HTML plan' },
|
|
293
|
+
lang: { type: 'string', default: 'zh', description: '输出语言 zh/en / Output language' },
|
|
294
|
+
json: { type: 'boolean', default: false, description: '输出 JSON / Print JSON output' },
|
|
295
|
+
},
|
|
296
|
+
run({ args }) {
|
|
297
|
+
const lang = normalizeLangArg(args.lang);
|
|
298
|
+
const projectDir = resolve(String(args.dir ?? PROJECT_DIR));
|
|
299
|
+
const report = createUpgradePlanReport({
|
|
300
|
+
projectDir,
|
|
301
|
+
scaleDir: resolveScaleDirForProject(projectDir),
|
|
302
|
+
targetScaleVersion: args['target-version'] ? String(args['target-version']) : undefined,
|
|
303
|
+
});
|
|
304
|
+
const htmlPath = args.html ? writeUpgradePlanHtml(report, undefined, lang) : undefined;
|
|
305
|
+
if (args.json) {
|
|
306
|
+
console.log(JSON.stringify({ ...report, htmlPath }, null, 2));
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
if (lang === 'zh') {
|
|
310
|
+
console.log('SCALE 升级计划');
|
|
311
|
+
console.log(` 项目: ${report.projectDir}`);
|
|
312
|
+
console.log(` 状态: ${report.status}`);
|
|
313
|
+
console.log(` 应用模式: ${report.applyMode}`);
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
console.log('SCALE Upgrade Plan');
|
|
317
|
+
console.log(` Project: ${report.projectDir}`);
|
|
318
|
+
console.log(` Status: ${report.status}`);
|
|
319
|
+
console.log(` Apply mode: ${report.applyMode}`);
|
|
320
|
+
}
|
|
321
|
+
if (report.blockers.length > 0) {
|
|
322
|
+
console.log(lang === 'zh' ? ' 阻塞项:' : ' Blockers:');
|
|
323
|
+
for (const blocker of report.blockers)
|
|
324
|
+
console.log(` [${blocker.code}] ${blocker.path ?? ''} ${formatUpgradeBlockerMessage(blocker.code, blocker.message, lang)}`);
|
|
325
|
+
}
|
|
326
|
+
console.log(lang === 'zh' ? ' 步骤:' : ' Steps:');
|
|
327
|
+
for (const step of report.steps) {
|
|
328
|
+
const path = step.path ? ` ${step.path}` : '';
|
|
329
|
+
const command = step.command ? ` -> ${formatUpgradeCommand(step.command, lang)}` : '';
|
|
330
|
+
console.log(` [${step.risk}] ${step.action}${path}: ${formatUpgradeStepReason(step.action, step.reason, lang)}${command}`);
|
|
331
|
+
}
|
|
332
|
+
if (htmlPath)
|
|
333
|
+
console.log(` HTML: ${htmlPath}`);
|
|
334
|
+
},
|
|
335
|
+
});
|
|
336
|
+
const upgradeRecommend = defineCommand({
|
|
337
|
+
meta: { name: 'recommend', description: '自动分析升级风险并推荐操作 / Auto-analyze upgrade risk and recommend actions' },
|
|
338
|
+
args: {
|
|
339
|
+
dir: { type: 'string', default: PROJECT_DIR, description: '项目目录 / Project directory' },
|
|
340
|
+
'target-version': { type: 'string', description: '目标 SCALE Engine 版本 / Target SCALE Engine version' },
|
|
341
|
+
'auto-apply': { type: 'boolean', default: false, description: '如果安全则自动应用 / Auto-apply if safe' },
|
|
342
|
+
lang: { type: 'string', default: 'zh', description: '输出语言 zh/en / Output language' },
|
|
343
|
+
json: { type: 'boolean', default: false, description: '输出 JSON / Print JSON output' },
|
|
344
|
+
},
|
|
345
|
+
run({ args }) {
|
|
346
|
+
const lang = normalizeLangArg(args.lang);
|
|
347
|
+
const projectDir = resolve(String(args.dir ?? PROJECT_DIR));
|
|
348
|
+
const report = createUpgradeRecommendReport({
|
|
349
|
+
projectDir,
|
|
350
|
+
scaleDir: resolveScaleDirForProject(projectDir),
|
|
351
|
+
targetScaleVersion: args['target-version'] ? String(args['target-version']) : undefined,
|
|
352
|
+
});
|
|
353
|
+
if (args.json) {
|
|
354
|
+
console.log(JSON.stringify(report, null, 2));
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
const riskEmoji = report.riskLevel === 'high' ? '🔴' : report.riskLevel === 'medium' ? '🟡' : '🟢';
|
|
358
|
+
const recEmoji = report.recommendation === 'safe-to-apply' ? '✅' : report.recommendation === 'blocked' ? '🚫' : '⚠️';
|
|
359
|
+
if (lang === 'zh') {
|
|
360
|
+
console.log('SCALE 升级推荐');
|
|
361
|
+
console.log(` 项目: ${report.projectDir}`);
|
|
362
|
+
console.log(` ${riskEmoji} 风险分数: ${report.riskScore} (${report.riskLevel})`);
|
|
363
|
+
console.log(` ${recEmoji} 推荐: ${report.recommendation}`);
|
|
364
|
+
console.log(` 摘要: ${report.summary}`);
|
|
365
|
+
console.log(` 应用模式: ${report.applyMode}`);
|
|
366
|
+
}
|
|
367
|
+
else {
|
|
368
|
+
console.log('SCALE Upgrade Recommend');
|
|
369
|
+
console.log(` Project: ${report.projectDir}`);
|
|
370
|
+
console.log(` ${riskEmoji} Risk score: ${report.riskScore} (${report.riskLevel})`);
|
|
371
|
+
console.log(` ${recEmoji} Recommendation: ${report.recommendation}`);
|
|
372
|
+
console.log(` Summary: ${report.summary}`);
|
|
373
|
+
console.log(` Apply mode: ${report.applyMode}`);
|
|
374
|
+
}
|
|
375
|
+
if (report.blockers.length > 0) {
|
|
376
|
+
console.log(lang === 'zh' ? ' 阻塞项:' : ' Blockers:');
|
|
377
|
+
for (const blocker of report.blockers)
|
|
378
|
+
console.log(` [${blocker.code}] ${blocker.message}`);
|
|
379
|
+
}
|
|
380
|
+
if (report.steps.length > 0) {
|
|
381
|
+
console.log(lang === 'zh' ? ' 步骤:' : ' Steps:');
|
|
382
|
+
for (const step of report.steps)
|
|
383
|
+
console.log(` [${step.risk}] ${step.action}: ${step.reason}`);
|
|
384
|
+
}
|
|
385
|
+
console.log(lang === 'zh' ? ' 建议命令:' : ' Suggested commands:');
|
|
386
|
+
for (const cmd of report.autoCommands)
|
|
387
|
+
console.log(` ${cmd}`);
|
|
388
|
+
// Auto-apply if requested and safe
|
|
389
|
+
if (args['auto-apply'] && report.recommendation === 'safe-to-apply') {
|
|
390
|
+
console.log(lang === 'zh' ? '\n 自动应用中...' : '\n Auto-applying...');
|
|
391
|
+
const result = applyUpgradePlan({
|
|
392
|
+
projectDir,
|
|
393
|
+
scaleDir: resolveScaleDirForProject(projectDir),
|
|
394
|
+
confirm: true,
|
|
395
|
+
autoBackup: true,
|
|
396
|
+
});
|
|
397
|
+
console.log(lang === 'zh' ? ` 结果: ${result.reason}` : ` Result: ${result.reason}`);
|
|
398
|
+
if (result.gitBackup?.ok)
|
|
399
|
+
console.log(lang === 'zh' ? ` Git 备份: ${result.gitBackup.branch}` : ` Git backup: ${result.gitBackup.branch}`);
|
|
400
|
+
}
|
|
401
|
+
},
|
|
402
|
+
});
|
|
403
|
+
const upgradeApply = defineCommand({
|
|
404
|
+
meta: { name: 'apply', description: '按已审阅计划安全应用升级 / Guarded entrypoint for applying an upgrade plan' },
|
|
405
|
+
args: {
|
|
406
|
+
dir: { type: 'string', default: PROJECT_DIR, description: '项目目录 / Project directory' },
|
|
407
|
+
confirm: { type: 'boolean', default: false, description: '确认当前升级计划已经审阅 / Confirm the current plan was reviewed' },
|
|
408
|
+
'auto-backup': { type: 'boolean', default: false, description: '应用前自动创建 git 分支备份 / Create git branch backup before applying' },
|
|
409
|
+
lang: { type: 'string', default: 'zh', description: '输出语言 zh/en / Output language' },
|
|
410
|
+
json: { type: 'boolean', default: false, description: '输出 JSON / Print JSON output' },
|
|
411
|
+
},
|
|
412
|
+
run({ args }) {
|
|
413
|
+
const lang = normalizeLangArg(args.lang);
|
|
414
|
+
const projectDir = resolve(String(args.dir ?? PROJECT_DIR));
|
|
415
|
+
const result = applyUpgradePlan({
|
|
416
|
+
projectDir,
|
|
417
|
+
scaleDir: resolveScaleDirForProject(projectDir),
|
|
418
|
+
confirm: isTruthyFlag(args.confirm),
|
|
419
|
+
autoBackup: isTruthyFlag(args['auto-backup']),
|
|
420
|
+
});
|
|
421
|
+
if (args.json) {
|
|
422
|
+
console.log(JSON.stringify(result, null, 2));
|
|
423
|
+
if (!result.ok)
|
|
424
|
+
process.exitCode = 1;
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
console.log(lang === 'zh' ? 'SCALE 应用升级' : 'SCALE Upgrade Apply');
|
|
428
|
+
console.log(lang === 'zh' ? ` 已应用: ${result.applied}` : ` Applied: ${result.applied}`);
|
|
429
|
+
console.log(lang === 'zh' ? ` 原因: ${formatUpgradeApplyReason(result.reason, lang)}` : ` Reason: ${result.reason}`);
|
|
430
|
+
console.log(lang === 'zh' ? ` 应用模式: ${result.plan.applyMode}` : ` Apply mode: ${result.plan.applyMode}`);
|
|
431
|
+
if (result.gitBackup) {
|
|
432
|
+
if (result.gitBackup.ok) {
|
|
433
|
+
console.log(lang === 'zh' ? ` Git 备份分支: ${result.gitBackup.branch}` : ` Git backup branch: ${result.gitBackup.branch}`);
|
|
434
|
+
}
|
|
435
|
+
else {
|
|
436
|
+
console.log(lang === 'zh' ? ` Git 备份失败: ${result.gitBackup.error}` : ` Git backup failed: ${result.gitBackup.error}`);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
if (result.backup)
|
|
440
|
+
console.log(lang === 'zh' ? ` 文件备份: ${result.backup.manifestPath}` : ` File backup: ${result.backup.manifestPath}`);
|
|
441
|
+
for (const path of result.changedFiles)
|
|
442
|
+
console.log(lang === 'zh' ? ` 已变更: ${path}` : ` changed: ${path}`);
|
|
443
|
+
if (!result.ok)
|
|
444
|
+
process.exitCode = 1;
|
|
445
|
+
},
|
|
446
|
+
});
|
|
447
|
+
const upgradeRollback = defineCommand({
|
|
448
|
+
meta: { name: 'rollback', description: '回滚最近一次 SCALE 托管升级 / Roll back the latest SCALE-managed upgrade' },
|
|
449
|
+
args: {
|
|
450
|
+
dir: { type: 'string', default: PROJECT_DIR, description: '项目目录 / Project directory' },
|
|
451
|
+
lang: { type: 'string', default: 'zh', description: '输出语言 zh/en / Output language' },
|
|
452
|
+
json: { type: 'boolean', default: false, description: '输出 JSON / Print JSON output' },
|
|
453
|
+
},
|
|
454
|
+
run({ args }) {
|
|
455
|
+
const lang = normalizeLangArg(args.lang);
|
|
456
|
+
const result = rollbackLatestUpgrade({ projectDir: args.dir });
|
|
457
|
+
if (args.json) {
|
|
458
|
+
console.log(JSON.stringify(result, null, 2));
|
|
459
|
+
if (!result.ok)
|
|
460
|
+
process.exitCode = 1;
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
console.log(lang === 'zh' ? 'SCALE 升级回滚' : 'SCALE Upgrade Rollback');
|
|
464
|
+
console.log(lang === 'zh' ? ` 已回滚: ${result.applied}` : ` Applied: ${result.applied}`);
|
|
465
|
+
console.log(lang === 'zh' ? ` 原因: ${formatUpgradeApplyReason(result.reason, lang)}` : ` Reason: ${result.reason}`);
|
|
466
|
+
if (result.backup)
|
|
467
|
+
console.log(lang === 'zh' ? ` 备份: ${result.backup.manifestPath}` : ` Backup: ${result.backup.manifestPath}`);
|
|
468
|
+
for (const path of result.restoredFiles)
|
|
469
|
+
console.log(lang === 'zh' ? ` 已恢复: ${path}` : ` restored: ${path}`);
|
|
470
|
+
if (!result.ok)
|
|
471
|
+
process.exitCode = 1;
|
|
472
|
+
},
|
|
473
|
+
});
|
|
474
|
+
export const upgradeCommand = defineCommand({
|
|
475
|
+
meta: { name: 'upgrade', description: 'SCALE 工作流、模板、skills、MCP、CLI 工具的安全升级向导 / Safe update wizard for workflow assets' },
|
|
476
|
+
args: {
|
|
477
|
+
dir: { type: 'string', default: PROJECT_DIR, description: '项目目录 / Project directory' },
|
|
478
|
+
'target-version': { type: 'string', description: '目标 SCALE Engine 版本,默认使用当前 CLI 版本 / Target SCALE Engine version' },
|
|
479
|
+
apply: { type: 'boolean', default: false, description: '直接应用安全升级计划 / Apply safe upgrade plan' },
|
|
480
|
+
yes: { type: 'boolean', default: false, description: '非交互确认 / Confirm without prompting' },
|
|
481
|
+
html: { type: 'boolean', default: true, description: '写入 HTML 升级计划 / Write HTML plan' },
|
|
482
|
+
interactive: { type: 'boolean', default: true, description: '启用升级向导交互 / Enable upgrade wizard prompts' },
|
|
483
|
+
lang: { type: 'string', default: 'zh', description: '输出语言 zh/en / Output language' },
|
|
484
|
+
json: { type: 'boolean', default: false, description: '输出 JSON / Print JSON output' },
|
|
485
|
+
},
|
|
486
|
+
subCommands: { check: upgradeCheck, plan: upgradePlan, recommend: upgradeRecommend, apply: upgradeApply, rollback: upgradeRollback },
|
|
487
|
+
async run({ args }) {
|
|
488
|
+
if (isUpgradeSubcommandInvocation(process.argv))
|
|
489
|
+
return;
|
|
490
|
+
const lang = normalizeLangArg(args.lang);
|
|
491
|
+
const projectDir = resolve(String(args.dir ?? PROJECT_DIR));
|
|
492
|
+
const scaleDir = resolveScaleDirForProject(projectDir);
|
|
493
|
+
const targetScaleVersion = args['target-version'] ? String(args['target-version']) : undefined;
|
|
494
|
+
const plan = createUpgradePlanReport({ projectDir, scaleDir, targetScaleVersion });
|
|
495
|
+
const htmlPath = args.html ? writeUpgradePlanHtml(plan, undefined, lang) : undefined;
|
|
496
|
+
const canApply = plan.applyMode === 'safe' && plan.blockers.length === 0;
|
|
497
|
+
const interactive = isTruthyFlag(args.interactive) && !args.json && Boolean(process.stdin.isTTY) && !isTruthyFlag(args.yes);
|
|
498
|
+
let apply = isTruthyFlag(args.apply) || isTruthyFlag(args.yes);
|
|
499
|
+
let cancelled = false;
|
|
500
|
+
if (interactive && canApply && !apply) {
|
|
501
|
+
const answer = await askUpgradeWizardQuestion(lang === 'zh'
|
|
502
|
+
? '发现可安全应用的升级计划。现在应用吗?1=仅生成计划 2=应用 3=取消,默认 1: '
|
|
503
|
+
: 'Safe upgrade plan found. Apply now? 1=plan only 2=apply 3=cancel, default 1: ');
|
|
504
|
+
const normalized = answer.trim().toLowerCase();
|
|
505
|
+
apply = normalized === '2' || normalized === 'apply' || normalized === 'yes' || normalized === 'y';
|
|
506
|
+
cancelled = normalized === '3' || normalized === 'cancel' || normalized === 'c';
|
|
507
|
+
}
|
|
508
|
+
const applyResult = apply && !cancelled
|
|
509
|
+
? applyUpgradePlan({ projectDir, scaleDir, confirm: true })
|
|
510
|
+
: undefined;
|
|
511
|
+
const ok = !cancelled && (!applyResult || applyResult.ok);
|
|
512
|
+
const report = { ok, cancelled, htmlPath, plan, applyResult };
|
|
513
|
+
if (args.json) {
|
|
514
|
+
console.log(JSON.stringify(report, null, 2));
|
|
515
|
+
if (!ok)
|
|
516
|
+
process.exitCode = 1;
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
renderUpgradeWizardReport(report, lang);
|
|
520
|
+
if (!ok)
|
|
521
|
+
process.exitCode = 1;
|
|
522
|
+
},
|
|
523
|
+
});
|
|
524
|
+
// ============================================================================
|
|
525
|
+
// assets command - Resource lifecycle governance
|
|
526
|
+
// ============================================================================
|
|
527
|
+
const assetsScan = defineCommand({
|
|
528
|
+
meta: { name: 'scan', description: 'Classify project docs, reports, media, scripts, and temporary outputs' },
|
|
529
|
+
args: {
|
|
530
|
+
dir: { type: 'string', default: '.', description: 'Project directory' },
|
|
531
|
+
json: { type: 'boolean', default: false, description: 'Print JSON output' },
|
|
532
|
+
},
|
|
533
|
+
run({ args }) {
|
|
534
|
+
const report = scanResourceAssets({ projectDir: args.dir });
|
|
535
|
+
if (args.json) {
|
|
536
|
+
console.log(JSON.stringify(report, null, 2));
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
console.log('SCALE Asset Scan');
|
|
540
|
+
console.log(` Project: ${report.projectDir}`);
|
|
541
|
+
console.log(` Total resources: ${report.summary.total}`);
|
|
542
|
+
console.log(` Tracked forbidden: ${report.summary.trackedForbidden}`);
|
|
543
|
+
console.log(` Large tracked: ${report.summary.largeTracked}`);
|
|
544
|
+
console.log(` Expired: ${report.summary.expired}`);
|
|
545
|
+
console.log('\nBy type:');
|
|
546
|
+
for (const [type, count] of Object.entries(report.summary.byType)) {
|
|
547
|
+
if (count > 0)
|
|
548
|
+
console.log(` ${type}: ${count}`);
|
|
549
|
+
}
|
|
550
|
+
},
|
|
551
|
+
});
|
|
552
|
+
const assetsDoctor = defineCommand({
|
|
553
|
+
meta: { name: 'doctor', description: 'Find resource lifecycle and Git policy problems' },
|
|
554
|
+
args: {
|
|
555
|
+
dir: { type: 'string', default: '.', description: 'Project directory' },
|
|
556
|
+
json: { type: 'boolean', default: false, description: 'Print JSON output' },
|
|
557
|
+
},
|
|
558
|
+
run({ args }) {
|
|
559
|
+
const report = doctorResourceAssets({ projectDir: args.dir });
|
|
560
|
+
if (args.json) {
|
|
561
|
+
console.log(JSON.stringify(report, null, 2));
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
console.log(`SCALE Asset Doctor: ${report.ok ? 'OK' : 'FAILED'}`);
|
|
565
|
+
if (report.findings.length === 0) {
|
|
566
|
+
console.log(' No resource lifecycle findings.');
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
for (const finding of report.findings) {
|
|
570
|
+
const path = finding.path ? ` ${finding.path}` : '';
|
|
571
|
+
console.log(` [${finding.severity.toUpperCase()}] ${finding.code}${path}: ${finding.message}`);
|
|
572
|
+
if (finding.fix)
|
|
573
|
+
console.log(` fix: ${finding.fix}`);
|
|
574
|
+
}
|
|
575
|
+
if (!report.ok)
|
|
576
|
+
process.exitCode = 1;
|
|
577
|
+
},
|
|
578
|
+
});
|
|
579
|
+
const assetsSettle = defineCommand({
|
|
580
|
+
meta: { name: 'settle', description: 'Record resource lifecycle settlement evidence for a task' },
|
|
581
|
+
args: {
|
|
582
|
+
dir: { type: 'string', default: '.', description: 'Project directory' },
|
|
583
|
+
'task-id': { type: 'string', description: 'Task id for the settlement record' },
|
|
584
|
+
'artifact-dir': { type: 'string', description: 'Task artifact directory where resource-impact.md should be updated' },
|
|
585
|
+
json: { type: 'boolean', default: false, description: 'Print JSON output' },
|
|
586
|
+
},
|
|
587
|
+
run({ args }) {
|
|
588
|
+
const report = settleResourceAssets({
|
|
589
|
+
projectDir: args.dir,
|
|
590
|
+
taskId: args['task-id'],
|
|
591
|
+
artifactsDir: args['artifact-dir'],
|
|
592
|
+
});
|
|
593
|
+
if (args.json) {
|
|
594
|
+
console.log(JSON.stringify(report, null, 2));
|
|
595
|
+
}
|
|
596
|
+
else {
|
|
597
|
+
console.log(`SCALE Asset Settlement: ${report.ok ? 'OK' : 'FAILED'}`);
|
|
598
|
+
if (report.resourceImpactPath)
|
|
599
|
+
console.log(` Resource impact: ${report.resourceImpactPath}`);
|
|
600
|
+
if (report.doctor.findings.length > 0) {
|
|
601
|
+
for (const finding of report.doctor.findings) {
|
|
602
|
+
const path = finding.path ? ` ${finding.path}` : '';
|
|
603
|
+
console.log(` [${finding.severity.toUpperCase()}] ${finding.code}${path}: ${finding.message}`);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
if (!report.ok)
|
|
608
|
+
process.exitCode = 1;
|
|
609
|
+
},
|
|
610
|
+
});
|
|
611
|
+
export const assetsCommand = defineCommand({
|
|
612
|
+
meta: { name: 'assets', description: 'Resource lifecycle governance for generated and maintained project assets' },
|
|
613
|
+
subCommands: { scan: assetsScan, doctor: assetsDoctor, settle: assetsSettle },
|
|
614
|
+
});
|
|
615
|
+
// ============================================================================
|
|
616
|
+
// standards command - Engineering standards governance
|
|
617
|
+
// ============================================================================
|
|
618
|
+
const standardsScan = defineCommand({
|
|
619
|
+
meta: { name: 'scan', description: 'Scan source files for engineering standard violations' },
|
|
620
|
+
args: {
|
|
621
|
+
dir: { type: 'string', default: '.', description: 'Project directory' },
|
|
622
|
+
changed: { type: 'boolean', default: false, description: 'Scan changed Git files only' },
|
|
623
|
+
'changed-files': { type: 'string', description: 'Comma or newline separated file list to scan' },
|
|
624
|
+
json: { type: 'boolean', default: false, description: 'Print JSON output' },
|
|
625
|
+
},
|
|
626
|
+
run({ args }) {
|
|
627
|
+
const report = scanEngineeringStandards({ projectDir: args.dir, changedFiles: resolveChangedFilesArg(args) });
|
|
628
|
+
if (args.json) {
|
|
629
|
+
console.log(JSON.stringify(report, null, 2));
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
console.log('SCALE Standards Scan');
|
|
633
|
+
console.log(` Project: ${report.projectDir}`);
|
|
634
|
+
console.log(` Files scanned: ${report.summary.filesScanned}`);
|
|
635
|
+
console.log(` Findings: ${report.summary.totalFindings}`);
|
|
636
|
+
console.log(` Blocking findings: ${report.summary.blockingFindings}`);
|
|
637
|
+
for (const finding of report.findings.slice(0, 20)) {
|
|
638
|
+
const line = finding.line ? `:${finding.line}` : '';
|
|
639
|
+
console.log(` [${finding.severity.toUpperCase()}] ${finding.ruleId} ${finding.path}${line}: ${finding.message}`);
|
|
640
|
+
}
|
|
641
|
+
if (report.findings.length > 20)
|
|
642
|
+
console.log(` ... ${report.findings.length - 20} more finding(s)`);
|
|
643
|
+
},
|
|
644
|
+
});
|
|
645
|
+
const standardsDoctor = defineCommand({
|
|
646
|
+
meta: { name: 'doctor', description: 'Find blocking engineering standards problems' },
|
|
647
|
+
args: {
|
|
648
|
+
dir: { type: 'string', default: '.', description: 'Project directory' },
|
|
649
|
+
changed: { type: 'boolean', default: false, description: 'Scan changed Git files only' },
|
|
650
|
+
'changed-files': { type: 'string', description: 'Comma or newline separated file list to scan' },
|
|
651
|
+
json: { type: 'boolean', default: false, description: 'Print JSON output' },
|
|
652
|
+
},
|
|
653
|
+
run({ args }) {
|
|
654
|
+
const report = doctorEngineeringStandards({ projectDir: args.dir, changedFiles: resolveChangedFilesArg(args) });
|
|
655
|
+
if (args.json) {
|
|
656
|
+
console.log(JSON.stringify(report, null, 2));
|
|
657
|
+
if (!report.ok)
|
|
658
|
+
process.exitCode = 1;
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
console.log(`SCALE Standards Doctor: ${report.ok ? 'OK' : 'FAILED'}`);
|
|
662
|
+
if (report.findings.length === 0) {
|
|
663
|
+
console.log(' No engineering standards findings.');
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
for (const finding of report.findings) {
|
|
667
|
+
const line = finding.line ? `:${finding.line}` : '';
|
|
668
|
+
console.log(` [${finding.severity.toUpperCase()}] ${finding.ruleId} ${finding.path}${line}: ${finding.message}`);
|
|
669
|
+
if (finding.fix)
|
|
670
|
+
console.log(` fix: ${finding.fix}`);
|
|
671
|
+
}
|
|
672
|
+
if (!report.ok)
|
|
673
|
+
process.exitCode = 1;
|
|
674
|
+
},
|
|
675
|
+
});
|
|
676
|
+
const standardsSettle = defineCommand({
|
|
677
|
+
meta: { name: 'settle', description: 'Record engineering standards settlement evidence for a task' },
|
|
678
|
+
args: {
|
|
679
|
+
dir: { type: 'string', default: '.', description: 'Project directory' },
|
|
680
|
+
'task-id': { type: 'string', description: 'Task id for the settlement record' },
|
|
681
|
+
'artifact-dir': { type: 'string', description: 'Task artifact directory where standards-impact.md should be updated' },
|
|
682
|
+
changed: { type: 'boolean', default: false, description: 'Scan changed Git files only' },
|
|
683
|
+
'changed-files': { type: 'string', description: 'Comma or newline separated file list to scan' },
|
|
684
|
+
json: { type: 'boolean', default: false, description: 'Print JSON output' },
|
|
685
|
+
},
|
|
686
|
+
run({ args }) {
|
|
687
|
+
const report = settleEngineeringStandards({
|
|
688
|
+
projectDir: args.dir,
|
|
689
|
+
taskId: args['task-id'],
|
|
690
|
+
artifactsDir: args['artifact-dir'],
|
|
691
|
+
changedFiles: resolveChangedFilesArg(args),
|
|
692
|
+
});
|
|
693
|
+
if (args.json) {
|
|
694
|
+
console.log(JSON.stringify(report, null, 2));
|
|
695
|
+
}
|
|
696
|
+
else {
|
|
697
|
+
console.log(`SCALE Standards Settlement: ${report.ok ? 'OK' : 'FAILED'}`);
|
|
698
|
+
if (report.standardsImpactPath)
|
|
699
|
+
console.log(` Standards impact: ${report.standardsImpactPath}`);
|
|
700
|
+
for (const finding of report.doctor.findings) {
|
|
701
|
+
const line = finding.line ? `:${finding.line}` : '';
|
|
702
|
+
console.log(` [${finding.severity.toUpperCase()}] ${finding.ruleId} ${finding.path}${line}: ${finding.message}`);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
if (!report.ok)
|
|
706
|
+
process.exitCode = 1;
|
|
707
|
+
},
|
|
708
|
+
});
|
|
709
|
+
const standardsBaseline = defineCommand({
|
|
710
|
+
meta: { name: 'baseline', description: 'Generate a legacy standards baseline and classification report' },
|
|
711
|
+
args: {
|
|
712
|
+
dir: { type: 'string', default: '.', description: 'Project directory' },
|
|
713
|
+
write: { type: 'boolean', default: false, description: 'Write .scale/engineering-standards-baseline.json' },
|
|
714
|
+
'task-id': { type: 'string', description: 'Task id for the legacy debt report' },
|
|
715
|
+
'artifact-dir': { type: 'string', description: 'Directory where standards-legacy-debt.md should be written' },
|
|
716
|
+
reason: { type: 'string', default: 'legacy standards debt accepted for staged remediation', description: 'Reason recorded on generated baseline entries' },
|
|
717
|
+
json: { type: 'boolean', default: false, description: 'Print JSON output' },
|
|
718
|
+
},
|
|
719
|
+
run({ args }) {
|
|
720
|
+
const report = baselineEngineeringStandards({
|
|
721
|
+
projectDir: args.dir,
|
|
722
|
+
writeBaseline: args.write,
|
|
723
|
+
taskId: args['task-id'],
|
|
724
|
+
artifactsDir: args['artifact-dir'],
|
|
725
|
+
reason: args.reason,
|
|
726
|
+
});
|
|
727
|
+
if (args.json) {
|
|
728
|
+
console.log(JSON.stringify(report, null, 2));
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
console.log(`Standards baseline: ${report.wroteBaseline ? 'written' : 'dry-run'}`);
|
|
732
|
+
console.log(` Baseline entries: ${report.baselineEntries.length}`);
|
|
733
|
+
console.log(` Blocking findings: ${report.debt.blockingFindings}`);
|
|
734
|
+
console.log(` Baseline path: ${report.baselinePath}`);
|
|
735
|
+
if (report.legacyDebtPath)
|
|
736
|
+
console.log(` Legacy debt report: ${report.legacyDebtPath}`);
|
|
737
|
+
if (!report.wroteBaseline)
|
|
738
|
+
console.log(' Re-run with --write to update .scale/engineering-standards-baseline.json.');
|
|
739
|
+
},
|
|
740
|
+
});
|
|
741
|
+
export const standardsCommand = defineCommand({
|
|
742
|
+
meta: { name: 'standards', description: 'Engineering standards governance for logs, security, architecture, database, and code quality' },
|
|
743
|
+
subCommands: { scan: standardsScan, doctor: standardsDoctor, settle: standardsSettle, baseline: standardsBaseline },
|
|
744
|
+
});
|
|
745
|
+
// ============================================================================
|
|
746
|
+
// artifact command - Derived HTML artifacts for human review
|
|
747
|
+
// ============================================================================
|
|
748
|
+
const artifactRender = defineCommand({
|
|
749
|
+
meta: { name: 'render', description: 'Render a task Markdown source set into a governed HTML artifact' },
|
|
750
|
+
args: {
|
|
751
|
+
dir: { type: 'string', default: '.', description: 'Project directory' },
|
|
752
|
+
'task-id': { type: 'string', description: 'Task id under docs/worklog/tasks' },
|
|
753
|
+
'artifact-dir': { type: 'string', description: 'Task artifact directory override' },
|
|
754
|
+
type: { type: 'string', default: 'release-report', description: 'HTML artifact type' },
|
|
755
|
+
source: { type: 'string', description: 'Comma or newline separated source Markdown files relative to the task directory' },
|
|
756
|
+
theme: { type: 'string', default: 'auto', description: 'Theme mode: dark/light/auto' },
|
|
757
|
+
lang: { type: 'string', default: 'zh', description: 'HTML language: zh/en' },
|
|
758
|
+
title: { type: 'string', description: 'HTML document title override' },
|
|
759
|
+
json: { type: 'boolean', default: false, description: 'Print JSON output' },
|
|
760
|
+
},
|
|
761
|
+
run({ args }) {
|
|
762
|
+
const result = renderHtmlArtifact({
|
|
763
|
+
projectDir: args.dir,
|
|
764
|
+
taskId: args['task-id'],
|
|
765
|
+
artifactDir: args['artifact-dir'],
|
|
766
|
+
type: String(args.type ?? 'release-report'),
|
|
767
|
+
sourcePaths: splitChangedFiles(typeof args.source === 'string' ? args.source : undefined),
|
|
768
|
+
theme: normalizeThemeArg(args.theme),
|
|
769
|
+
lang: normalizeLangArg(args.lang),
|
|
770
|
+
title: typeof args.title === 'string' ? args.title : undefined,
|
|
771
|
+
});
|
|
772
|
+
if (args.json) {
|
|
773
|
+
console.log(JSON.stringify(result, null, 2));
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
console.log('SCALE HTML Artifact Render');
|
|
777
|
+
console.log(` Type: ${result.type}`);
|
|
778
|
+
console.log(` HTML: ${result.outputPath}`);
|
|
779
|
+
console.log(` Index: ${result.indexPath}`);
|
|
780
|
+
console.log(` Manifest: ${result.manifestPath}`);
|
|
781
|
+
if (result.missingSources.length > 0) {
|
|
782
|
+
console.log(` Missing sources: ${result.missingSources.join(', ')}`);
|
|
783
|
+
}
|
|
784
|
+
},
|
|
785
|
+
});
|
|
786
|
+
const artifactDoctor = defineCommand({
|
|
787
|
+
meta: { name: 'doctor', description: 'Check HTML artifacts for traceability, stale sources, remote assets, and secret-like content' },
|
|
788
|
+
args: {
|
|
789
|
+
dir: { type: 'string', default: '.', description: 'Project directory' },
|
|
790
|
+
'task-id': { type: 'string', description: 'Task id under docs/worklog/tasks' },
|
|
791
|
+
'artifact-dir': { type: 'string', description: 'Task artifact directory override' },
|
|
792
|
+
type: { type: 'string', description: 'Optional HTML artifact type to check' },
|
|
793
|
+
json: { type: 'boolean', default: false, description: 'Print JSON output' },
|
|
794
|
+
},
|
|
795
|
+
run({ args }) {
|
|
796
|
+
const report = doctorHtmlArtifacts({
|
|
797
|
+
projectDir: args.dir,
|
|
798
|
+
taskId: args['task-id'],
|
|
799
|
+
artifactDir: args['artifact-dir'],
|
|
800
|
+
type: typeof args.type === 'string' ? args.type : undefined,
|
|
801
|
+
});
|
|
802
|
+
if (args.json) {
|
|
803
|
+
console.log(JSON.stringify(report, null, 2));
|
|
804
|
+
if (!report.ok)
|
|
805
|
+
process.exitCode = 1;
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
console.log(`SCALE HTML Artifact Doctor: ${report.ok ? 'OK' : 'FAILED'}`);
|
|
809
|
+
console.log(` Manifest: ${report.manifestPath}`);
|
|
810
|
+
console.log(` Artifacts: ${report.artifacts.length}`);
|
|
811
|
+
if (report.findings.length === 0) {
|
|
812
|
+
console.log(' No HTML artifact findings.');
|
|
813
|
+
}
|
|
814
|
+
else {
|
|
815
|
+
for (const finding of report.findings) {
|
|
816
|
+
const path = finding.path ? ` ${finding.path}` : '';
|
|
817
|
+
console.log(` [${finding.severity.toUpperCase()}] ${finding.code}${path}: ${finding.message}`);
|
|
818
|
+
if (finding.fix)
|
|
819
|
+
console.log(` fix: ${finding.fix}`);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
if (!report.ok)
|
|
823
|
+
process.exitCode = 1;
|
|
824
|
+
},
|
|
825
|
+
});
|
|
826
|
+
const artifactSettle = defineCommand({
|
|
827
|
+
meta: { name: 'settle', description: 'Record HTML artifact settlement evidence for a task' },
|
|
828
|
+
args: {
|
|
829
|
+
dir: { type: 'string', default: '.', description: 'Project directory' },
|
|
830
|
+
'task-id': { type: 'string', description: 'Task id under docs/worklog/tasks' },
|
|
831
|
+
'artifact-dir': { type: 'string', description: 'Task artifact directory override' },
|
|
832
|
+
json: { type: 'boolean', default: false, description: 'Print JSON output' },
|
|
833
|
+
},
|
|
834
|
+
run({ args }) {
|
|
835
|
+
const report = settleHtmlArtifacts({
|
|
836
|
+
projectDir: args.dir,
|
|
837
|
+
taskId: args['task-id'],
|
|
838
|
+
artifactDir: args['artifact-dir'],
|
|
839
|
+
});
|
|
840
|
+
if (args.json) {
|
|
841
|
+
console.log(JSON.stringify(report, null, 2));
|
|
842
|
+
if (!report.ok)
|
|
843
|
+
process.exitCode = 1;
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
console.log(`SCALE HTML Artifact Settlement: ${report.ok ? 'OK' : 'FAILED'}`);
|
|
847
|
+
console.log(` HTML impact: ${report.htmlImpactPath}`);
|
|
848
|
+
for (const finding of report.doctor.findings) {
|
|
849
|
+
const path = finding.path ? ` ${finding.path}` : '';
|
|
850
|
+
console.log(` [${finding.severity.toUpperCase()}] ${finding.code}${path}: ${finding.message}`);
|
|
851
|
+
}
|
|
852
|
+
if (!report.ok)
|
|
853
|
+
process.exitCode = 1;
|
|
854
|
+
},
|
|
855
|
+
});
|
|
856
|
+
const artifactOpen = defineCommand({
|
|
857
|
+
meta: { name: 'open', description: 'Open or print the local file URL for a rendered HTML artifact' },
|
|
858
|
+
args: {
|
|
859
|
+
dir: { type: 'string', default: '.', description: 'Project directory' },
|
|
860
|
+
'task-id': { type: 'string', description: 'Task id under docs/worklog/tasks' },
|
|
861
|
+
'artifact-dir': { type: 'string', description: 'Task artifact directory override' },
|
|
862
|
+
type: { type: 'string', description: 'Optional HTML artifact type to open' },
|
|
863
|
+
'print-only': { type: 'boolean', default: false, description: 'Only print the file URL without launching a browser' },
|
|
864
|
+
json: { type: 'boolean', default: false, description: 'Print JSON output' },
|
|
865
|
+
},
|
|
866
|
+
run({ args }) {
|
|
867
|
+
const path = resolveHtmlArtifactForOpen({
|
|
868
|
+
projectDir: args.dir,
|
|
869
|
+
taskId: args['task-id'],
|
|
870
|
+
artifactDir: args['artifact-dir'],
|
|
871
|
+
type: typeof args.type === 'string' ? args.type : undefined,
|
|
872
|
+
});
|
|
873
|
+
const url = pathToFileURL(path).toString();
|
|
874
|
+
const exists = existsSync(path);
|
|
875
|
+
if (!args['print-only'] && exists)
|
|
876
|
+
launchLocalFile(path);
|
|
877
|
+
const output = { ok: exists, path, url, launched: Boolean(!args['print-only'] && exists) };
|
|
878
|
+
if (args.json) {
|
|
879
|
+
console.log(JSON.stringify(output, null, 2));
|
|
880
|
+
if (!exists)
|
|
881
|
+
process.exitCode = 1;
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
if (!exists) {
|
|
885
|
+
console.log(`HTML artifact not found: ${path}`);
|
|
886
|
+
process.exitCode = 1;
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
console.log(url);
|
|
890
|
+
},
|
|
891
|
+
});
|
|
892
|
+
const artifactDashboard = defineCommand({
|
|
893
|
+
meta: { name: 'dashboard', description: 'Render a governance HTML dashboard from runtime, eval, memory, resource, and artifact evidence' },
|
|
894
|
+
args: {
|
|
895
|
+
dir: { type: 'string', default: '.', description: 'Project directory' },
|
|
896
|
+
'task-id': { type: 'string', description: 'Optional task id to scope runtime/eval evidence and task HTML artifacts' },
|
|
897
|
+
output: { type: 'string', alias: 'o', description: 'Output HTML path; defaults to .scale/reports/governance-dashboard.html' },
|
|
898
|
+
theme: { type: 'string', default: 'auto', description: 'Theme mode: dark/light/auto' },
|
|
899
|
+
lang: { type: 'string', default: 'zh', description: 'HTML language: zh/en' },
|
|
900
|
+
json: { type: 'boolean', default: false, description: 'Print JSON output' },
|
|
901
|
+
},
|
|
902
|
+
run({ args }) {
|
|
903
|
+
const projectDir = resolve(String(args.dir ?? PROJECT_DIR));
|
|
904
|
+
const scaleDir = resolveScaleDirForProject(projectDir);
|
|
905
|
+
const report = renderGovernanceDashboard({
|
|
906
|
+
projectDir,
|
|
907
|
+
scaleDir,
|
|
908
|
+
taskId: typeof args['task-id'] === 'string' ? args['task-id'] : undefined,
|
|
909
|
+
output: typeof args.output === 'string' ? args.output : undefined,
|
|
910
|
+
theme: normalizeThemeArg(args.theme),
|
|
911
|
+
lang: normalizeLangArg(args.lang),
|
|
912
|
+
});
|
|
913
|
+
if (args.json) {
|
|
914
|
+
console.log(JSON.stringify(report, null, 2));
|
|
915
|
+
if (!report.ok)
|
|
916
|
+
process.exitCode = 1;
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
console.log(`SCALE Governance Dashboard: ${report.ok ? 'OK' : 'ATTENTION'}`);
|
|
920
|
+
console.log(` HTML: ${report.outputPath}`);
|
|
921
|
+
console.log(` Manifest: ${report.manifestPath}`);
|
|
922
|
+
for (const finding of report.findings) {
|
|
923
|
+
console.log(` [${finding.severity.toUpperCase()}] ${finding.code}: ${finding.message}`);
|
|
924
|
+
}
|
|
925
|
+
if (!report.ok)
|
|
926
|
+
process.exitCode = 1;
|
|
927
|
+
},
|
|
928
|
+
});
|
|
929
|
+
export const artifactCommand = defineCommand({
|
|
930
|
+
meta: { name: 'artifact', description: 'Derived HTML artifact rendering and safety checks' },
|
|
931
|
+
subCommands: { render: artifactRender, doctor: artifactDoctor, settle: artifactSettle, open: artifactOpen, dashboard: artifactDashboard },
|
|
932
|
+
});
|
|
933
|
+
//# sourceMappingURL=upgradeAssetsCommands.js.map
|