@hongmaple0820/scale-engine 0.12.3 → 0.14.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/dist/adapters/AiderAdapter.js +1 -1
- package/dist/adapters/AiderAdapter.js.map +1 -1
- package/dist/adapters/ClaudeCodeAdapter.d.ts +1 -0
- package/dist/adapters/ClaudeCodeAdapter.js +5 -3
- package/dist/adapters/ClaudeCodeAdapter.js.map +1 -1
- package/dist/adapters/CodexAdapter.js +1 -1
- package/dist/adapters/CodexAdapter.js.map +1 -1
- package/dist/adapters/CursorAdapter.js +1 -1
- package/dist/adapters/CursorAdapter.js.map +1 -1
- package/dist/adapters/DeepSeekTuiAdapter.js +5 -3
- package/dist/adapters/DeepSeekTuiAdapter.js.map +1 -1
- package/dist/adapters/DoubaoAdapter.js +1 -1
- package/dist/adapters/DoubaoAdapter.js.map +1 -1
- package/dist/adapters/GeminiAdapter.js +1 -1
- package/dist/adapters/GeminiAdapter.js.map +1 -1
- package/dist/adapters/HermesAdapter.js +1 -1
- package/dist/adapters/HermesAdapter.js.map +1 -1
- package/dist/adapters/KimiAdapter.js +1 -1
- package/dist/adapters/KimiAdapter.js.map +1 -1
- package/dist/adapters/KiroAdapter.d.ts +14 -0
- package/dist/adapters/KiroAdapter.js +180 -0
- package/dist/adapters/KiroAdapter.js.map +1 -0
- package/dist/adapters/OpenClawAdapter.js +1 -1
- package/dist/adapters/OpenClawAdapter.js.map +1 -1
- package/dist/adapters/OpenCodeAdapter.js +1 -1
- package/dist/adapters/OpenCodeAdapter.js.map +1 -1
- package/dist/adapters/QCoderAdapter.js +1 -1
- package/dist/adapters/QCoderAdapter.js.map +1 -1
- package/dist/adapters/TraeAdapter.js +1 -1
- package/dist/adapters/TraeAdapter.js.map +1 -1
- package/dist/adapters/VSCAdapter.js +1 -1
- package/dist/adapters/VSCAdapter.js.map +1 -1
- package/dist/adapters/WindsurfAdapter.js +1 -1
- package/dist/adapters/WindsurfAdapter.js.map +1 -1
- package/dist/adapters/WorkBuddyAdapter.js +1 -1
- package/dist/adapters/WorkBuddyAdapter.js.map +1 -1
- package/dist/adapters/index.d.ts +1 -0
- package/dist/adapters/index.js +3 -0
- package/dist/adapters/index.js.map +1 -1
- package/dist/api/cli.js +690 -9
- package/dist/api/cli.js.map +1 -1
- package/dist/api/doctor.d.ts +13 -0
- package/dist/api/doctor.js +261 -5
- package/dist/api/doctor.js.map +1 -1
- package/dist/api/quickstart.d.ts +20 -1
- package/dist/api/quickstart.js +104 -3
- package/dist/api/quickstart.js.map +1 -1
- package/dist/artifact/types.d.ts +16 -2
- package/dist/artifact/types.js.map +1 -1
- package/dist/cli/phaseCommands.d.ts +66 -0
- package/dist/cli/phaseCommands.js +695 -51
- package/dist/cli/phaseCommands.js.map +1 -1
- package/dist/guardrails/detectors.d.ts +9 -0
- package/dist/guardrails/detectors.js +102 -0
- package/dist/guardrails/detectors.js.map +1 -1
- package/dist/hooks/HookGeneratorEnhanced.js +29 -0
- package/dist/hooks/HookGeneratorEnhanced.js.map +1 -1
- package/dist/hooks/WorkflowHooksManager.js +20 -1
- package/dist/hooks/WorkflowHooksManager.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/output/BrandThemeLoader.d.ts +54 -0
- package/dist/output/BrandThemeLoader.js +340 -0
- package/dist/output/BrandThemeLoader.js.map +1 -0
- package/dist/output/HTMLDocumentRenderer.d.ts +83 -0
- package/dist/output/HTMLDocumentRenderer.js +717 -0
- package/dist/output/HTMLDocumentRenderer.js.map +1 -0
- package/dist/output/UIPrototypeRenderer.d.ts +61 -0
- package/dist/output/UIPrototypeRenderer.js +500 -0
- package/dist/output/UIPrototypeRenderer.js.map +1 -0
- package/dist/output/index.d.ts +6 -0
- package/dist/output/index.js +6 -0
- package/dist/output/index.js.map +1 -0
- package/dist/skills/ExternalSkills.js +2 -0
- package/dist/skills/ExternalSkills.js.map +1 -1
- package/dist/skills/SkillCatalog.d.ts +13 -0
- package/dist/skills/SkillCatalog.js +184 -0
- package/dist/skills/SkillCatalog.js.map +1 -0
- package/dist/skills/SkillDiscovery.js +2 -1
- package/dist/skills/SkillDiscovery.js.map +1 -1
- package/dist/skills/SkillDoctor.d.ts +37 -0
- package/dist/skills/SkillDoctor.js +90 -0
- package/dist/skills/SkillDoctor.js.map +1 -0
- package/dist/skills/index.d.ts +2 -0
- package/dist/skills/index.js +2 -0
- package/dist/skills/index.js.map +1 -1
- package/dist/skills/routing/SkillGate.d.ts +12 -0
- package/dist/skills/routing/SkillGate.js +93 -0
- package/dist/skills/routing/SkillGate.js.map +1 -0
- package/dist/skills/routing/SkillPlanner.d.ts +8 -0
- package/dist/skills/routing/SkillPlanner.js +91 -0
- package/dist/skills/routing/SkillPlanner.js.map +1 -0
- package/dist/skills/routing/SkillPolicy.d.ts +6 -0
- package/dist/skills/routing/SkillPolicy.js +173 -0
- package/dist/skills/routing/SkillPolicy.js.map +1 -0
- package/dist/skills/routing/SkillRoutingTypes.d.ts +72 -0
- package/dist/skills/routing/SkillRoutingTypes.js +2 -0
- package/dist/skills/routing/SkillRoutingTypes.js.map +1 -0
- package/dist/skills/routing/TaskIntentClassifier.d.ts +6 -0
- package/dist/skills/routing/TaskIntentClassifier.js +79 -0
- package/dist/skills/routing/TaskIntentClassifier.js.map +1 -0
- package/dist/skills/routing/index.d.ts +5 -0
- package/dist/skills/routing/index.js +6 -0
- package/dist/skills/routing/index.js.map +1 -0
- package/dist/workflow/GovernanceLock.d.ts +35 -0
- package/dist/workflow/GovernanceLock.js +58 -0
- package/dist/workflow/GovernanceLock.js.map +1 -0
- package/dist/workflow/GovernanceTemplatePacks.d.ts +24 -0
- package/dist/workflow/GovernanceTemplatePacks.js +83 -0
- package/dist/workflow/GovernanceTemplatePacks.js.map +1 -0
- package/dist/workflow/GovernanceTemplates.d.ts +17 -0
- package/dist/workflow/GovernanceTemplates.js +686 -0
- package/dist/workflow/GovernanceTemplates.js.map +1 -0
- package/dist/workflow/PhaseMarkerTracker.d.ts +63 -0
- package/dist/workflow/PhaseMarkerTracker.js +291 -0
- package/dist/workflow/PhaseMarkerTracker.js.map +1 -0
- package/dist/workflow/SessionStateTracker.d.ts +74 -0
- package/dist/workflow/SessionStateTracker.js +270 -0
- package/dist/workflow/SessionStateTracker.js.map +1 -0
- package/dist/workflow/TaskArtifactScaffolder.d.ts +47 -0
- package/dist/workflow/TaskArtifactScaffolder.js +237 -0
- package/dist/workflow/TaskArtifactScaffolder.js.map +1 -0
- package/dist/workflow/TaskMetricsStore.d.ts +49 -0
- package/dist/workflow/TaskMetricsStore.js +149 -0
- package/dist/workflow/TaskMetricsStore.js.map +1 -0
- package/dist/workflow/VerificationCommands.d.ts +2 -0
- package/dist/workflow/VerificationCommands.js +8 -12
- package/dist/workflow/VerificationCommands.js.map +1 -1
- package/dist/workflow/VerificationProfile.d.ts +56 -0
- package/dist/workflow/VerificationProfile.js +170 -0
- package/dist/workflow/VerificationProfile.js.map +1 -0
- package/dist/workflow/WorkflowArtifactWriter.d.ts +113 -0
- package/dist/workflow/WorkflowArtifactWriter.js +241 -0
- package/dist/workflow/WorkflowArtifactWriter.js.map +1 -0
- package/dist/workflow/WorkflowEngine.d.ts +26 -5
- package/dist/workflow/WorkflowEngine.js +38 -9
- package/dist/workflow/WorkflowEngine.js.map +1 -1
- package/dist/workflow/WorkspaceLifecycle.d.ts +51 -0
- package/dist/workflow/WorkspaceLifecycle.js +259 -0
- package/dist/workflow/WorkspaceLifecycle.js.map +1 -0
- package/dist/workflow/autonomous/AutonomousDevLoop.d.ts +88 -0
- package/dist/workflow/autonomous/AutonomousDevLoop.js +381 -0
- package/dist/workflow/autonomous/AutonomousDevLoop.js.map +1 -0
- package/dist/workflow/autonomous/WorklogManager.d.ts +50 -0
- package/dist/workflow/autonomous/WorklogManager.js +264 -0
- package/dist/workflow/autonomous/WorklogManager.js.map +1 -0
- package/dist/workflow/autonomous/index.d.ts +2 -0
- package/dist/workflow/autonomous/index.js +4 -0
- package/dist/workflow/autonomous/index.js.map +1 -0
- package/dist/workflow/gates/GateSystem.d.ts +12 -3
- package/dist/workflow/gates/GateSystem.js +185 -41
- package/dist/workflow/gates/GateSystem.js.map +1 -1
- package/dist/workflow/index.d.ts +10 -0
- package/dist/workflow/index.js +10 -0
- package/dist/workflow/index.js.map +1 -1
- package/package.json +3 -3
package/dist/api/cli.js
CHANGED
|
@@ -12,26 +12,62 @@ import { DangerousCommandDetector, SecretLeakDetector, RoleGateDetector, ScopeCr
|
|
|
12
12
|
import { SQLiteKnowledgeBase } from '../knowledge/SQLiteKnowledgeBase.js';
|
|
13
13
|
import { ContextBuilder } from '../context/ContextBuilder.js';
|
|
14
14
|
import { FSMAgentBridge } from '../fsm/FSMAgentBridge.js';
|
|
15
|
+
import { CapabilityRegistry } from '../capabilities/CapabilityRegistry.js';
|
|
16
|
+
import { SkillRegistry } from '../skills/SkillRegistry.js';
|
|
17
|
+
import { registerCoreSkills } from '../skills/coreSkills.js';
|
|
18
|
+
import { registerExternalSkills } from '../skills/ExternalSkills.js';
|
|
19
|
+
import { createSkillPlan, evaluateSkillGate, loadSkillRoutingPolicy, skillPlanMarkdown } from '../skills/routing/index.js';
|
|
15
20
|
import { createAdapter, SUPPORTED_AGENTS } from '../adapters/index.js';
|
|
16
21
|
import { LessonExtractor, RuleProposer, HookGenerator, EvolutionEngine } from '../evolution/EvolutionEngine.js';
|
|
17
22
|
import { Doctor } from './doctor.js';
|
|
18
23
|
import { quickStart, detectPlatform } from './quickstart.js';
|
|
19
24
|
import { SkillDiscovery } from '../skills/SkillDiscovery.js';
|
|
25
|
+
import { inspectRequiredWorkflowSkills, inspectWorkflowSkills } from '../skills/SkillDoctor.js';
|
|
20
26
|
import { listWorkflowPresets, getPresetsByScenario } from '../workflows/presets.js';
|
|
21
27
|
import { EvidenceStore } from '../workflow/EvidenceStore.js';
|
|
22
28
|
import { OutOfScopeStore } from '../workflow/OutOfScopeStore.js';
|
|
23
29
|
import { ReviewStore } from '../workflow/ReviewStore.js';
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
30
|
+
import { WorkflowEngine } from '../workflow/WorkflowEngine.js';
|
|
31
|
+
import { resolveVerificationTargets } from '../workflow/VerificationProfile.js';
|
|
32
|
+
import { writeGovernanceTemplates } from '../workflow/GovernanceTemplates.js';
|
|
33
|
+
import { computeGovernanceDrift } from '../workflow/GovernanceLock.js';
|
|
34
|
+
import { TaskMetricsStore } from '../workflow/TaskMetricsStore.js';
|
|
35
|
+
import { checkTaskArtifactCompleteness } from '../workflow/TaskArtifactScaffolder.js';
|
|
36
|
+
import { WorkflowArtifactWriter } from '../workflow/WorkflowArtifactWriter.js';
|
|
37
|
+
import { cleanupWorkspaceLifecycle, inspectWorkspaceLifecycle, } from '../workflow/WorkspaceLifecycle.js';
|
|
38
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
39
|
+
import { join, resolve } from 'node:path';
|
|
26
40
|
// ============================================================================
|
|
27
41
|
// Engine bootstrap (单例 + lazy init)
|
|
28
42
|
// ============================================================================
|
|
29
43
|
const SCALE_DIR = process.env.SCALE_DIR ?? '.scale';
|
|
44
|
+
const PROJECT_DIR = process.env.SCALE_PROJECT_DIR ?? process.cwd();
|
|
30
45
|
const DB_PATH = join(SCALE_DIR, 'scale.db');
|
|
46
|
+
function governanceModeFromScenario(scenario) {
|
|
47
|
+
if (scenario === 'critical')
|
|
48
|
+
return 'critical';
|
|
49
|
+
if (scenario === 'sandbox')
|
|
50
|
+
return 'minimal';
|
|
51
|
+
return 'standard';
|
|
52
|
+
}
|
|
31
53
|
function ensureDir(dir) {
|
|
32
54
|
if (!existsSync(dir))
|
|
33
55
|
mkdirSync(dir, { recursive: true });
|
|
34
56
|
}
|
|
57
|
+
function isTruthyFlag(value) {
|
|
58
|
+
return value === true || value === '' || value === 'true' || value === '1';
|
|
59
|
+
}
|
|
60
|
+
function normalizePreflightProfile(value) {
|
|
61
|
+
const normalized = String(value ?? 'quick').trim().toLowerCase();
|
|
62
|
+
if (normalized === 'full' || normalized === 'ci')
|
|
63
|
+
return normalized;
|
|
64
|
+
return 'quick';
|
|
65
|
+
}
|
|
66
|
+
function gatesForPreflightProfile(profile) {
|
|
67
|
+
if (profile === 'quick')
|
|
68
|
+
return ['G3', 'G0', 'G4', 'G5'];
|
|
69
|
+
return ['G3', 'G0', 'G4', 'G5', 'G6', 'G7'];
|
|
70
|
+
}
|
|
35
71
|
let _engine = null;
|
|
36
72
|
function getEngine() {
|
|
37
73
|
if (!_engine)
|
|
@@ -60,7 +96,17 @@ function createEngine() {
|
|
|
60
96
|
const kb = new SQLiteKnowledgeBase(eventBus, { dbPath: join(SCALE_DIR, 'knowledge.db') });
|
|
61
97
|
const ctx = new ContextBuilder(store, kb, eventBus);
|
|
62
98
|
const fsmAgentBridge = new FSMAgentBridge(fsm, store);
|
|
63
|
-
|
|
99
|
+
const capabilityRegistry = new CapabilityRegistry(eventBus);
|
|
100
|
+
const skillRegistry = new SkillRegistry(eventBus);
|
|
101
|
+
registerCoreSkills(skillRegistry);
|
|
102
|
+
registerExternalSkills(skillRegistry, eventBus);
|
|
103
|
+
const workflowEngine = new WorkflowEngine({
|
|
104
|
+
eventBus,
|
|
105
|
+
capabilityRegistry,
|
|
106
|
+
skillRegistry,
|
|
107
|
+
scaleDir: SCALE_DIR,
|
|
108
|
+
});
|
|
109
|
+
return { eventBus, store, fsm, gateway, roleGate, kb, ctx, fsmAgentBridge, workflowEngine };
|
|
64
110
|
}
|
|
65
111
|
// ============================================================================
|
|
66
112
|
// session commands
|
|
@@ -699,6 +745,286 @@ const stats = defineCommand({
|
|
|
699
745
|
console.log(JSON.stringify({ ...s, eventCount: events.length }, null, 2));
|
|
700
746
|
},
|
|
701
747
|
});
|
|
748
|
+
const metricsList = defineCommand({
|
|
749
|
+
meta: { name: 'list', description: 'List M/L task workflow metrics' },
|
|
750
|
+
args: {
|
|
751
|
+
json: { type: 'boolean', default: false },
|
|
752
|
+
},
|
|
753
|
+
async run({ args }) {
|
|
754
|
+
const store = new TaskMetricsStore(SCALE_DIR);
|
|
755
|
+
const records = store.list();
|
|
756
|
+
const summary = store.summarize();
|
|
757
|
+
if (args.json) {
|
|
758
|
+
console.log(JSON.stringify({ summary, records }, null, 2));
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
console.log('\nWorkflow Metrics');
|
|
762
|
+
console.log(` Total tasks: ${summary.total}`);
|
|
763
|
+
console.log(` First-pass verification rate: ${(summary.firstPassRate * 100).toFixed(1)}%`);
|
|
764
|
+
console.log(` Average fix iterations: ${summary.averageFixIterations.toFixed(2)}`);
|
|
765
|
+
console.log(` Artifact completeness: ${(summary.artifactCompletenessRate * 100).toFixed(1)}%`);
|
|
766
|
+
for (const record of records.slice(-10)) {
|
|
767
|
+
console.log(` - ${record.date} ${record.level} ${record.taskName}: ${record.finalGateStatus}`);
|
|
768
|
+
}
|
|
769
|
+
},
|
|
770
|
+
});
|
|
771
|
+
const metrics = defineCommand({
|
|
772
|
+
meta: { name: 'metrics', description: 'Inspect workflow task metrics' },
|
|
773
|
+
subCommands: { list: metricsList },
|
|
774
|
+
});
|
|
775
|
+
function normalizeTaskArtifactLevel(value) {
|
|
776
|
+
const normalized = String(value ?? 'M').trim().toUpperCase();
|
|
777
|
+
if (normalized === 'S' || normalized === 'M' || normalized === 'L' || normalized === 'CRITICAL') {
|
|
778
|
+
return normalized;
|
|
779
|
+
}
|
|
780
|
+
throw new Error(`Invalid task level "${String(value)}"; expected S, M, L, or CRITICAL.`);
|
|
781
|
+
}
|
|
782
|
+
const taskArtifactsCheck = defineCommand({
|
|
783
|
+
meta: { name: 'check', description: 'Check task artifact completeness' },
|
|
784
|
+
args: {
|
|
785
|
+
dir: { type: 'string', description: 'Task artifact directory; defaults to .scale/state/current.json artifactsDir' },
|
|
786
|
+
level: { type: 'string', description: 'Task level: S, M, L, or CRITICAL; defaults to current state level or M' },
|
|
787
|
+
'warn-only': { type: 'boolean', default: false, description: 'Return zero even when artifacts are incomplete' },
|
|
788
|
+
json: { type: 'boolean', default: false },
|
|
789
|
+
},
|
|
790
|
+
run({ args }) {
|
|
791
|
+
const state = new WorkflowArtifactWriter(SCALE_DIR).readCurrentState();
|
|
792
|
+
let level;
|
|
793
|
+
try {
|
|
794
|
+
level = normalizeTaskArtifactLevel(args.level ?? state?.level ?? 'M');
|
|
795
|
+
}
|
|
796
|
+
catch (e) {
|
|
797
|
+
console.error(e.message);
|
|
798
|
+
process.exit(1);
|
|
799
|
+
}
|
|
800
|
+
const result = checkTaskArtifactCompleteness({
|
|
801
|
+
projectDir: PROJECT_DIR,
|
|
802
|
+
artifactsDir: args.dir ?? state?.artifactsDir,
|
|
803
|
+
level,
|
|
804
|
+
skillRequiredArtifacts: state?.requiredSkillArtifacts,
|
|
805
|
+
});
|
|
806
|
+
if (args.json) {
|
|
807
|
+
console.log(JSON.stringify(result, null, 2));
|
|
808
|
+
}
|
|
809
|
+
else {
|
|
810
|
+
console.log(`\nTask Artifacts: ${result.complete ? 'COMPLETE' : 'INCOMPLETE'}`);
|
|
811
|
+
if (result.artifactsDir)
|
|
812
|
+
console.log(` Directory: ${result.artifactsDir}`);
|
|
813
|
+
console.log(` Required: ${result.required.join(', ') || 'none'}`);
|
|
814
|
+
for (const file of result.missing)
|
|
815
|
+
console.log(` [MISSING] ${file}`);
|
|
816
|
+
for (const item of result.incomplete)
|
|
817
|
+
console.log(` [INCOMPLETE] ${item.file}: ${item.reason}`);
|
|
818
|
+
}
|
|
819
|
+
if (!result.complete && !args['warn-only'])
|
|
820
|
+
process.exitCode = 1;
|
|
821
|
+
},
|
|
822
|
+
});
|
|
823
|
+
const taskArtifacts = defineCommand({
|
|
824
|
+
meta: { name: 'task-artifacts', description: 'Inspect task artifact completeness' },
|
|
825
|
+
subCommands: { check: taskArtifactsCheck },
|
|
826
|
+
});
|
|
827
|
+
function printWorkspaceLifecycle(report) {
|
|
828
|
+
console.log('\nSCALE Workspace Lifecycle');
|
|
829
|
+
console.log(` Root: ${report.root.path}`);
|
|
830
|
+
console.log(` Branch: ${report.root.branch ?? '(detached)'}`);
|
|
831
|
+
console.log(` Linked worktree: ${report.root.isLinkedWorktree ? 'yes' : 'no'}`);
|
|
832
|
+
console.log(` Root status: ${report.root.clean ? 'clean' : 'dirty'}`);
|
|
833
|
+
if (!report.root.clean) {
|
|
834
|
+
console.log(` staged=${report.root.staged} unstaged=${report.root.unstaged} untracked=${report.root.untracked}`);
|
|
835
|
+
}
|
|
836
|
+
if (report.childRepositories.length) {
|
|
837
|
+
console.log('\n Child repositories:');
|
|
838
|
+
for (const child of report.childRepositories) {
|
|
839
|
+
console.log(` ${child.clean ? '[CLEAN]' : '[DIRTY]'} ${child.relativePath} (${child.kind}) branch=${child.branch ?? '(detached)'}`);
|
|
840
|
+
if (!child.clean)
|
|
841
|
+
console.log(` staged=${child.staged} unstaged=${child.unstaged} untracked=${child.untracked}`);
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
else {
|
|
845
|
+
console.log('\n Child repositories: none');
|
|
846
|
+
}
|
|
847
|
+
console.log(`\n Cleanup candidate: ${report.finish.canCleanup ? 'yes' : 'no'}`);
|
|
848
|
+
for (const blocker of report.finish.blockers)
|
|
849
|
+
console.log(` [BLOCKER] ${blocker}`);
|
|
850
|
+
for (const warning of report.finish.warnings)
|
|
851
|
+
console.log(` [WARN] ${warning}`);
|
|
852
|
+
for (const action of report.finish.nextActions)
|
|
853
|
+
console.log(` [NEXT] ${action}`);
|
|
854
|
+
}
|
|
855
|
+
function printWorkspaceCleanup(result) {
|
|
856
|
+
printWorkspaceLifecycle(result.report);
|
|
857
|
+
console.log('\n Cleanup plan:');
|
|
858
|
+
console.log(` Mode: ${result.mode}`);
|
|
859
|
+
console.log(` Target: ${result.targetPath}`);
|
|
860
|
+
console.log(` Can apply: ${result.canApply ? 'yes' : 'no'}`);
|
|
861
|
+
console.log(` Applied: ${result.applied ? 'yes' : 'no'}`);
|
|
862
|
+
console.log(` Confirmation token: ${result.confirmationToken ?? '(unavailable)'}`);
|
|
863
|
+
for (const command of result.commands)
|
|
864
|
+
console.log(` Command: ${command}`);
|
|
865
|
+
for (const blocker of result.blockers)
|
|
866
|
+
console.log(` [BLOCKER] ${blocker}`);
|
|
867
|
+
for (const warning of result.warnings)
|
|
868
|
+
console.log(` [WARN] ${warning}`);
|
|
869
|
+
}
|
|
870
|
+
const workspaceStatus = defineCommand({
|
|
871
|
+
meta: { name: 'status', description: 'Inspect root worktree and child repository lifecycle state' },
|
|
872
|
+
args: {
|
|
873
|
+
dir: { type: 'string', description: 'Repository or worktree directory; defaults to current project directory' },
|
|
874
|
+
json: { type: 'boolean', default: false },
|
|
875
|
+
},
|
|
876
|
+
async run({ args }) {
|
|
877
|
+
const report = await inspectWorkspaceLifecycle({ projectDir: args.dir ?? PROJECT_DIR });
|
|
878
|
+
if (args.json) {
|
|
879
|
+
console.log(JSON.stringify(report, null, 2));
|
|
880
|
+
}
|
|
881
|
+
else {
|
|
882
|
+
printWorkspaceLifecycle(report);
|
|
883
|
+
}
|
|
884
|
+
if (report.finish.blockers.length > 0)
|
|
885
|
+
process.exitCode = 1;
|
|
886
|
+
},
|
|
887
|
+
});
|
|
888
|
+
const workspaceFinish = defineCommand({
|
|
889
|
+
meta: { name: 'finish', description: 'Check whether a temporary worktree can be safely finished or cleaned up' },
|
|
890
|
+
args: {
|
|
891
|
+
dir: { type: 'string', description: 'Repository or worktree directory; defaults to current project directory' },
|
|
892
|
+
json: { type: 'boolean', default: false },
|
|
893
|
+
},
|
|
894
|
+
async run({ args }) {
|
|
895
|
+
const report = await inspectWorkspaceLifecycle({ projectDir: args.dir ?? PROJECT_DIR });
|
|
896
|
+
const result = {
|
|
897
|
+
root: report.root,
|
|
898
|
+
childRepositories: report.childRepositories,
|
|
899
|
+
finish: report.finish,
|
|
900
|
+
};
|
|
901
|
+
if (args.json) {
|
|
902
|
+
console.log(JSON.stringify(result, null, 2));
|
|
903
|
+
}
|
|
904
|
+
else {
|
|
905
|
+
printWorkspaceLifecycle(report);
|
|
906
|
+
}
|
|
907
|
+
if (report.finish.blockers.length > 0)
|
|
908
|
+
process.exitCode = 1;
|
|
909
|
+
},
|
|
910
|
+
});
|
|
911
|
+
const workspaceCleanup = defineCommand({
|
|
912
|
+
meta: { name: 'cleanup', description: 'Dry-run or apply safe removal of a linked temporary worktree' },
|
|
913
|
+
args: {
|
|
914
|
+
dir: { type: 'string', description: 'Linked worktree directory; defaults to current project directory' },
|
|
915
|
+
'dry-run': { type: 'boolean', default: false, description: 'Preview cleanup; this is the default unless --apply is set' },
|
|
916
|
+
apply: { type: 'boolean', default: false, description: 'Actually run git worktree remove after safety checks' },
|
|
917
|
+
confirm: { type: 'string', description: 'Required confirmation token for --apply, usually the worktree branch name' },
|
|
918
|
+
json: { type: 'boolean', default: false },
|
|
919
|
+
},
|
|
920
|
+
async run({ args }) {
|
|
921
|
+
const result = await cleanupWorkspaceLifecycle({
|
|
922
|
+
projectDir: args.dir ?? PROJECT_DIR,
|
|
923
|
+
apply: isTruthyFlag(args.apply),
|
|
924
|
+
confirm: args.confirm,
|
|
925
|
+
});
|
|
926
|
+
if (args.json) {
|
|
927
|
+
console.log(JSON.stringify(result, null, 2));
|
|
928
|
+
}
|
|
929
|
+
else {
|
|
930
|
+
printWorkspaceCleanup(result);
|
|
931
|
+
}
|
|
932
|
+
if (!result.canApply || (isTruthyFlag(args.apply) && !result.applied))
|
|
933
|
+
process.exitCode = 1;
|
|
934
|
+
},
|
|
935
|
+
});
|
|
936
|
+
const workspace = defineCommand({
|
|
937
|
+
meta: { name: 'workspace', description: 'Inspect worktree, branch, and child repository lifecycle safety' },
|
|
938
|
+
subCommands: {
|
|
939
|
+
status: workspaceStatus,
|
|
940
|
+
finish: workspaceFinish,
|
|
941
|
+
cleanup: workspaceCleanup,
|
|
942
|
+
},
|
|
943
|
+
});
|
|
944
|
+
const preflight = defineCommand({
|
|
945
|
+
meta: { name: 'preflight', description: 'Run service-aware verification without a task artifact' },
|
|
946
|
+
args: {
|
|
947
|
+
'build-cmd': { type: 'string', description: 'Override build command' },
|
|
948
|
+
'lint-cmd': { type: 'string', description: 'Override lint command' },
|
|
949
|
+
'test-cmd': { type: 'string', description: 'Override test command' },
|
|
950
|
+
'coverage-cmd': { type: 'string', description: 'Override coverage command' },
|
|
951
|
+
profile: { type: 'string', description: 'Verification profile from .scale/verification.json' },
|
|
952
|
+
'preflight-profile': { type: 'string', default: 'quick', description: 'Gate intensity profile (quick/full/ci); quick skips coverage and security' },
|
|
953
|
+
service: { type: 'string', description: 'Service name from .scale/verification.json; use all for required services' },
|
|
954
|
+
'tdd-evidence': { type: 'string', description: 'Path to JSON TDD evidence with red/green/refactor/testFirst=true' },
|
|
955
|
+
'tdd-strict': { type: 'boolean', default: false, description: 'Require TDD evidence before other gates' },
|
|
956
|
+
json: { type: 'boolean', default: false },
|
|
957
|
+
},
|
|
958
|
+
async run({ args }) {
|
|
959
|
+
const { workflowEngine } = getEngine();
|
|
960
|
+
const preflightProfile = normalizePreflightProfile(args['preflight-profile']);
|
|
961
|
+
const gateStages = gatesForPreflightProfile(preflightProfile);
|
|
962
|
+
const resolved = resolveVerificationTargets({
|
|
963
|
+
projectDir: PROJECT_DIR,
|
|
964
|
+
scaleDir: SCALE_DIR,
|
|
965
|
+
profile: args.profile,
|
|
966
|
+
service: args.service,
|
|
967
|
+
});
|
|
968
|
+
const targetResults = [];
|
|
969
|
+
if (!args.json) {
|
|
970
|
+
console.log('\nSCALE Preflight');
|
|
971
|
+
for (const warning of resolved.warnings)
|
|
972
|
+
console.log(` [WARN] ${warning}`);
|
|
973
|
+
console.log(` Profile: ${resolved.profileName}`);
|
|
974
|
+
console.log(` Preflight profile: ${preflightProfile}`);
|
|
975
|
+
console.log(` Gates: ${gateStages.join(', ')}`);
|
|
976
|
+
}
|
|
977
|
+
for (const target of resolved.targets) {
|
|
978
|
+
if (!args.json) {
|
|
979
|
+
const label = target.service ? `${target.service.name} (${target.service.path})` : 'root';
|
|
980
|
+
console.log(`\n Target: ${label}`);
|
|
981
|
+
}
|
|
982
|
+
const gates = await workflowEngine.verify({
|
|
983
|
+
cwd: target.config.cwd,
|
|
984
|
+
build: args['build-cmd'] ?? target.config.build,
|
|
985
|
+
lint: args['lint-cmd'] ?? target.config.lint,
|
|
986
|
+
test: args['test-cmd'] ?? target.config.test,
|
|
987
|
+
coverage: args['coverage-cmd'] ?? target.config.coverage,
|
|
988
|
+
tddEvidence: args['tdd-evidence'],
|
|
989
|
+
tddStrict: isTruthyFlag(args['tdd-strict']),
|
|
990
|
+
gates: gateStages,
|
|
991
|
+
});
|
|
992
|
+
const passed = gates.every(gate => gate.passed);
|
|
993
|
+
targetResults.push({
|
|
994
|
+
service: target.service?.name,
|
|
995
|
+
cwd: target.config.cwd ?? PROJECT_DIR,
|
|
996
|
+
gates,
|
|
997
|
+
passed,
|
|
998
|
+
});
|
|
999
|
+
if (!args.json) {
|
|
1000
|
+
for (const gate of gates) {
|
|
1001
|
+
console.log(` ${gate.passed ? '[PASS]' : '[FAIL]'} ${gate.gate}: ${gate.evidence.slice(0, 80)}`);
|
|
1002
|
+
for (const blocker of gate.blockers)
|
|
1003
|
+
console.log(` [BLOCKER] ${blocker.slice(0, 120)}`);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
const passed = targetResults.length > 0 && targetResults.every(target => target.passed);
|
|
1008
|
+
const result = {
|
|
1009
|
+
phase: 'PREFLIGHT',
|
|
1010
|
+
profile: resolved.profileName,
|
|
1011
|
+
preflightProfile,
|
|
1012
|
+
gates: gateStages,
|
|
1013
|
+
services: targetResults.map(target => target.service).filter(Boolean),
|
|
1014
|
+
policy: resolved.policy,
|
|
1015
|
+
targets: targetResults,
|
|
1016
|
+
passed,
|
|
1017
|
+
};
|
|
1018
|
+
if (args.json) {
|
|
1019
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1020
|
+
}
|
|
1021
|
+
else {
|
|
1022
|
+
console.log(`\nPREFLIGHT: ${passed ? 'PASSED' : 'FAILED'}\n`);
|
|
1023
|
+
}
|
|
1024
|
+
if (!passed)
|
|
1025
|
+
process.exitCode = 1;
|
|
1026
|
+
},
|
|
1027
|
+
});
|
|
702
1028
|
const status = defineCommand({
|
|
703
1029
|
meta: { name: 'status', description: 'Show current SCALE workflow status' },
|
|
704
1030
|
args: {
|
|
@@ -819,13 +1145,116 @@ const init = defineCommand({
|
|
|
819
1145
|
args: {
|
|
820
1146
|
agent: { type: 'string', default: '', description: `Agent type (${SUPPORTED_AGENTS.join('/')}) - auto-detected if not specified` },
|
|
821
1147
|
dir: { type: 'string', default: '.', description: 'Project directory' },
|
|
1148
|
+
json: { type: 'boolean', default: false, description: 'Output initialization result as JSON' },
|
|
822
1149
|
scenario: { type: 'string', default: 'standard', description: 'Scenario mode (sandbox/standard/critical)' },
|
|
1150
|
+
'governance-pack': {
|
|
1151
|
+
type: 'string',
|
|
1152
|
+
default: 'standard',
|
|
1153
|
+
description: 'Governance template pack (standard/project-scaffold/go-service-matrix/node-library/frontend-app)',
|
|
1154
|
+
},
|
|
823
1155
|
quick: { type: 'boolean', default: false, description: 'Quick start with auto-detection' },
|
|
1156
|
+
interactive: { type: 'boolean', default: false, description: 'Interactive configuration mode with prompts' },
|
|
1157
|
+
'coverage-threshold': { type: 'string', default: '80', description: 'Coverage threshold (default 80%)' },
|
|
1158
|
+
'retry-threshold': { type: 'string', default: '3', description: 'Brute retry threshold (default 3)' },
|
|
1159
|
+
'block-severity': { type: 'string', default: 'CRITICAL', description: 'Block severity level (CRITICAL/HIGH/MEDIUM)' },
|
|
824
1160
|
},
|
|
825
1161
|
async run({ args }) {
|
|
1162
|
+
// Interactive configuration mode
|
|
1163
|
+
if (args.interactive) {
|
|
1164
|
+
console.log('\n🔧 SCALE Engine Interactive Configuration\n');
|
|
1165
|
+
console.log('='.repeat(50));
|
|
1166
|
+
// Step 1: Detect and suggest agent platform
|
|
1167
|
+
const detection = detectPlatform(args.dir);
|
|
1168
|
+
console.log('\n📋 Step 1: Agent Platform Selection');
|
|
1169
|
+
console.log(` Detected suggestions: ${detection.suggestions.join(', ') || 'none'}`);
|
|
1170
|
+
const agentType = args.agent || detection.suggestions[0] || 'claude-code';
|
|
1171
|
+
console.log(` Using: ${agentType}`);
|
|
1172
|
+
// Step 2: Scenario mode
|
|
1173
|
+
console.log('\n📋 Step 2: Scenario Mode');
|
|
1174
|
+
console.log(' sandbox - No quality gates (POC/prototype)');
|
|
1175
|
+
console.log(' standard - Default quality gates');
|
|
1176
|
+
console.log(' critical - Hardened gates + manual approval');
|
|
1177
|
+
const scenarioMode = args.scenario;
|
|
1178
|
+
console.log(` Using: ${scenarioMode}`);
|
|
1179
|
+
// Step 3: Quality Gate Thresholds (quantified)
|
|
1180
|
+
console.log('\n📋 Step 3: Quality Gate Thresholds');
|
|
1181
|
+
const coverageThreshold = parseInt(args['coverage-threshold'], 10) || 80;
|
|
1182
|
+
const retryThreshold = parseInt(args['retry-threshold'], 10) || 3;
|
|
1183
|
+
const blockSeverity = args['block-severity'] || 'CRITICAL';
|
|
1184
|
+
console.log(` Coverage threshold: ${coverageThreshold}%`);
|
|
1185
|
+
console.log(` Retry threshold: ${retryThreshold} (brute retry block)`);
|
|
1186
|
+
console.log(` Block severity: ${blockSeverity}`);
|
|
1187
|
+
// Step 4: Write thresholds to .scale/thresholds.json
|
|
1188
|
+
const thresholdsPath = join(args.dir, '.scale', 'thresholds.json');
|
|
1189
|
+
ensureDir(join(args.dir, '.scale'));
|
|
1190
|
+
writeFileSync(thresholdsPath, JSON.stringify({
|
|
1191
|
+
coverage: { minimum: coverageThreshold, unit: 'percent' },
|
|
1192
|
+
retry: { bruteMaximum: retryThreshold, unit: 'count' },
|
|
1193
|
+
severity: { blockLevel: blockSeverity },
|
|
1194
|
+
gates: {
|
|
1195
|
+
G3_build: { required: scenarioMode !== 'sandbox', exitCode: 0 },
|
|
1196
|
+
G4_lint: { required: scenarioMode !== 'sandbox', exitCode: 0 },
|
|
1197
|
+
G5_tests: { required: scenarioMode !== 'sandbox', allPass: true },
|
|
1198
|
+
G6_coverage: { required: scenarioMode !== 'sandbox', minimum: coverageThreshold },
|
|
1199
|
+
G7_security: { required: scenarioMode === 'critical', noCritical: true },
|
|
1200
|
+
},
|
|
1201
|
+
}, null, 2));
|
|
1202
|
+
console.log(`\n ✓ Thresholds written to: ${thresholdsPath}`);
|
|
1203
|
+
// Initialize with adapter
|
|
1204
|
+
const adapter = createAdapter(agentType);
|
|
1205
|
+
const result = await adapter.init({
|
|
1206
|
+
projectDir: args.dir,
|
|
1207
|
+
agentType: agentType,
|
|
1208
|
+
scenarioMode,
|
|
1209
|
+
thresholdsPath,
|
|
1210
|
+
});
|
|
1211
|
+
const projectName = args.dir.split(/[/\\]/).pop() || 'Project';
|
|
1212
|
+
const governance = writeGovernanceTemplates(args.dir, {
|
|
1213
|
+
mode: governanceModeFromScenario(scenarioMode),
|
|
1214
|
+
projectName,
|
|
1215
|
+
pack: args['governance-pack'],
|
|
1216
|
+
});
|
|
1217
|
+
result.created.push(...governance.created);
|
|
1218
|
+
result.skipped.push(...governance.skipped);
|
|
1219
|
+
console.log(`\n✅ SCALE Engine initialized for ${agentType} (interactive mode)`);
|
|
1220
|
+
console.log(`\n📁 Created:`);
|
|
1221
|
+
for (const f of result.created)
|
|
1222
|
+
console.log(` + ${f}`);
|
|
1223
|
+
if (result.skipped.length > 0) {
|
|
1224
|
+
console.log(`\n⏭️ Skipped (already exist):`);
|
|
1225
|
+
for (const f of result.skipped)
|
|
1226
|
+
console.log(` - ${f}`);
|
|
1227
|
+
}
|
|
1228
|
+
console.log(`\n🔧 Configuration Summary:`);
|
|
1229
|
+
console.log(` Settings: ${result.settingsPath}`);
|
|
1230
|
+
console.log(` Knowledge: ${result.knowledgeDocPath}`);
|
|
1231
|
+
console.log(` Thresholds: ${thresholdsPath}`);
|
|
1232
|
+
console.log(` Data dir: ${result.scaleDir}`);
|
|
1233
|
+
console.log(` Scenario: ${scenarioMode}`);
|
|
1234
|
+
console.log(`\n📋 Next steps:`);
|
|
1235
|
+
console.log(` → scale doctor`);
|
|
1236
|
+
console.log(` → scale create Spec "<feature name>"`);
|
|
1237
|
+
return;
|
|
1238
|
+
}
|
|
826
1239
|
// One-click quick start mode
|
|
827
|
-
if (
|
|
828
|
-
const qsResult = await quickStart(args.dir);
|
|
1240
|
+
if (!args.agent) {
|
|
1241
|
+
const qsResult = await quickStart(args.dir, { governancePack: args['governance-pack'] });
|
|
1242
|
+
if (args.json) {
|
|
1243
|
+
const detection = qsResult.success ? undefined : detectPlatform(args.dir);
|
|
1244
|
+
console.log(JSON.stringify({
|
|
1245
|
+
ok: qsResult.success,
|
|
1246
|
+
mode: 'quick',
|
|
1247
|
+
platform: qsResult.platform,
|
|
1248
|
+
created: qsResult.created,
|
|
1249
|
+
skipped: qsResult.skipped,
|
|
1250
|
+
constraintsApplied: qsResult.constraintsApplied,
|
|
1251
|
+
capabilitiesEnabled: qsResult.capabilitiesEnabled,
|
|
1252
|
+
knowledgeGraph: qsResult.knowledgeGraph,
|
|
1253
|
+
nextSteps: qsResult.nextSteps,
|
|
1254
|
+
suggestions: detection?.suggestions ?? [],
|
|
1255
|
+
}, null, 2));
|
|
1256
|
+
return;
|
|
1257
|
+
}
|
|
829
1258
|
if (qsResult.success && qsResult.platform) {
|
|
830
1259
|
console.log(`\n✅ SCALE Engine Quick Start completed for ${qsResult.platform}`);
|
|
831
1260
|
console.log(`\n📁 Created (${qsResult.created.length}):`);
|
|
@@ -853,6 +1282,30 @@ const init = defineCommand({
|
|
|
853
1282
|
// Manual agent specification mode
|
|
854
1283
|
const adapter = createAdapter(args.agent);
|
|
855
1284
|
const result = await adapter.init({ projectDir: args.dir, agentType: args.agent, scenarioMode: args.scenario });
|
|
1285
|
+
const projectName = args.dir.split(/[/\\]/).pop() || 'Project';
|
|
1286
|
+
const governance = writeGovernanceTemplates(args.dir, {
|
|
1287
|
+
mode: governanceModeFromScenario(args.scenario),
|
|
1288
|
+
projectName,
|
|
1289
|
+
pack: args['governance-pack'],
|
|
1290
|
+
});
|
|
1291
|
+
result.created.push(...governance.created);
|
|
1292
|
+
result.skipped.push(...governance.skipped);
|
|
1293
|
+
if (args.json) {
|
|
1294
|
+
console.log(JSON.stringify({
|
|
1295
|
+
ok: true,
|
|
1296
|
+
mode: args.quick ? 'quick-agent' : 'manual',
|
|
1297
|
+
agent: args.agent,
|
|
1298
|
+
scenario: args.scenario,
|
|
1299
|
+
governancePack: args['governance-pack'],
|
|
1300
|
+
settingsPath: result.settingsPath,
|
|
1301
|
+
knowledgeDocPath: result.knowledgeDocPath,
|
|
1302
|
+
scaleDir: result.scaleDir,
|
|
1303
|
+
created: result.created,
|
|
1304
|
+
skipped: result.skipped,
|
|
1305
|
+
nextSteps: ['scale doctor', 'scale create Spec "<feature name>"'],
|
|
1306
|
+
}, null, 2));
|
|
1307
|
+
return;
|
|
1308
|
+
}
|
|
856
1309
|
console.log(`\n✅ SCALE Engine initialized for ${args.agent} (scenario: ${args.scenario})`);
|
|
857
1310
|
console.log(`\n📁 Created:`);
|
|
858
1311
|
for (const f of result.created)
|
|
@@ -871,6 +1324,39 @@ const init = defineCommand({
|
|
|
871
1324
|
},
|
|
872
1325
|
});
|
|
873
1326
|
// ============================================================================
|
|
1327
|
+
// governance command — Generated governance asset tooling
|
|
1328
|
+
// ============================================================================
|
|
1329
|
+
const governanceDiff = defineCommand({
|
|
1330
|
+
meta: { name: 'diff', description: 'Check generated governance files for drift' },
|
|
1331
|
+
args: {
|
|
1332
|
+
dir: { type: 'string', default: '.', description: 'Project directory' },
|
|
1333
|
+
json: { type: 'boolean', default: false, description: 'Print JSON output' },
|
|
1334
|
+
},
|
|
1335
|
+
run({ args }) {
|
|
1336
|
+
const report = computeGovernanceDrift(args.dir);
|
|
1337
|
+
if (args.json) {
|
|
1338
|
+
console.log(JSON.stringify(report, null, 2));
|
|
1339
|
+
return;
|
|
1340
|
+
}
|
|
1341
|
+
if (!report.lockExists) {
|
|
1342
|
+
console.log('No governance lock found. Run: scale init --governance-pack <pack>');
|
|
1343
|
+
return;
|
|
1344
|
+
}
|
|
1345
|
+
if (report.missing.length === 0 && report.changed.length === 0) {
|
|
1346
|
+
console.log('Governance generated files are clean.');
|
|
1347
|
+
return;
|
|
1348
|
+
}
|
|
1349
|
+
for (const item of report.missing)
|
|
1350
|
+
console.log(`missing: ${item.path}`);
|
|
1351
|
+
for (const item of report.changed)
|
|
1352
|
+
console.log(`changed: ${item.path}`);
|
|
1353
|
+
},
|
|
1354
|
+
});
|
|
1355
|
+
const governance = defineCommand({
|
|
1356
|
+
meta: { name: 'governance', description: 'Governance template pack tools' },
|
|
1357
|
+
subCommands: { diff: governanceDiff },
|
|
1358
|
+
});
|
|
1359
|
+
// ============================================================================
|
|
874
1360
|
// evolve command
|
|
875
1361
|
// ============================================================================
|
|
876
1362
|
const evolve = defineCommand({
|
|
@@ -914,11 +1400,29 @@ const workflowList = defineCommand({
|
|
|
914
1400
|
meta: { name: 'list', description: 'List all workflow presets' },
|
|
915
1401
|
args: {
|
|
916
1402
|
scenario: { type: 'string', description: 'Filter by scenario mode (sandbox/standard/critical)' },
|
|
1403
|
+
json: { type: 'boolean', default: false, description: 'Output workflow presets as JSON' },
|
|
917
1404
|
},
|
|
918
1405
|
async run({ args }) {
|
|
919
1406
|
const presets = args.scenario
|
|
920
1407
|
? getPresetsByScenario(args.scenario)
|
|
921
1408
|
: listWorkflowPresets();
|
|
1409
|
+
if (args.json) {
|
|
1410
|
+
console.log(JSON.stringify({
|
|
1411
|
+
ok: true,
|
|
1412
|
+
scenario: args.scenario ?? null,
|
|
1413
|
+
count: presets.length,
|
|
1414
|
+
presets: presets.map(preset => ({
|
|
1415
|
+
id: preset.id,
|
|
1416
|
+
name: preset.name,
|
|
1417
|
+
nameZh: preset.nameZh,
|
|
1418
|
+
description: preset.description,
|
|
1419
|
+
scenarioMode: preset.scenarioMode,
|
|
1420
|
+
requiredArtifacts: preset.requiredArtifacts,
|
|
1421
|
+
steps: preset.steps,
|
|
1422
|
+
})),
|
|
1423
|
+
}, null, 2));
|
|
1424
|
+
return;
|
|
1425
|
+
}
|
|
922
1426
|
if (presets.length === 0) {
|
|
923
1427
|
console.log('No workflow presets found.');
|
|
924
1428
|
return;
|
|
@@ -1092,15 +1596,34 @@ const skillScan = defineCommand({
|
|
|
1092
1596
|
meta: { name: 'scan', description: 'Scan for installed skills' },
|
|
1093
1597
|
args: {
|
|
1094
1598
|
dir: { type: 'string', default: '.', description: 'Project directory' },
|
|
1599
|
+
json: { type: 'boolean', default: false, description: 'Output scan result as JSON' },
|
|
1095
1600
|
},
|
|
1096
1601
|
async run({ args }) {
|
|
1097
1602
|
const discovery = new SkillDiscovery(args.dir);
|
|
1098
1603
|
const platform = discovery.detectPlatform();
|
|
1604
|
+
if (!platform && args.json) {
|
|
1605
|
+
console.log(JSON.stringify({
|
|
1606
|
+
ok: false,
|
|
1607
|
+
platform: null,
|
|
1608
|
+
skills: [],
|
|
1609
|
+
message: 'No agent platform detected. Run `scale init` first.',
|
|
1610
|
+
}, null, 2));
|
|
1611
|
+
return;
|
|
1612
|
+
}
|
|
1099
1613
|
if (!platform) {
|
|
1100
1614
|
console.log('\n⚠️ No agent platform detected. Run `scale init` first.');
|
|
1101
1615
|
return;
|
|
1102
1616
|
}
|
|
1103
1617
|
const result = discovery.scanSkills(platform);
|
|
1618
|
+
if (args.json) {
|
|
1619
|
+
console.log(JSON.stringify({
|
|
1620
|
+
ok: true,
|
|
1621
|
+
platform: result.platform,
|
|
1622
|
+
count: result.skills.length,
|
|
1623
|
+
skills: result.skills,
|
|
1624
|
+
}, null, 2));
|
|
1625
|
+
return;
|
|
1626
|
+
}
|
|
1104
1627
|
console.log(`\n🔍 Platform: ${result.platform}`);
|
|
1105
1628
|
console.log(`📦 Skills found: ${result.skills.length}`);
|
|
1106
1629
|
if (result.skills.length > 0) {
|
|
@@ -1115,18 +1638,170 @@ const skillScan = defineCommand({
|
|
|
1115
1638
|
}
|
|
1116
1639
|
},
|
|
1117
1640
|
});
|
|
1641
|
+
const skillPlanCommand = defineCommand({
|
|
1642
|
+
meta: { name: 'plan', description: 'Create or refresh a task skill plan' },
|
|
1643
|
+
args: {
|
|
1644
|
+
'task-id': { type: 'positional', required: true },
|
|
1645
|
+
dir: { type: 'string', description: 'Task artifact directory; defaults to current state artifactsDir' },
|
|
1646
|
+
json: { type: 'boolean', default: false },
|
|
1647
|
+
},
|
|
1648
|
+
async run({ args }) {
|
|
1649
|
+
const { store } = getEngine();
|
|
1650
|
+
const task = await store.get(args['task-id']);
|
|
1651
|
+
if (!task || task.type !== 'Task') {
|
|
1652
|
+
console.error(`Task not found: ${args['task-id']}`);
|
|
1653
|
+
process.exit(1);
|
|
1654
|
+
}
|
|
1655
|
+
const payload = task.payload;
|
|
1656
|
+
const level = normalizeTaskArtifactLevel(payload.workflowLevel ?? 'M');
|
|
1657
|
+
const policy = loadSkillRoutingPolicy(PROJECT_DIR, SCALE_DIR);
|
|
1658
|
+
const plan = createSkillPlan({
|
|
1659
|
+
taskId: task.id,
|
|
1660
|
+
taskName: task.title,
|
|
1661
|
+
description: payload.description,
|
|
1662
|
+
level,
|
|
1663
|
+
services: payload.servicesTouched ?? [],
|
|
1664
|
+
files: payload.filesInvolved ?? [],
|
|
1665
|
+
policy,
|
|
1666
|
+
});
|
|
1667
|
+
const updatedPayload = {
|
|
1668
|
+
...payload,
|
|
1669
|
+
skillIntents: plan.intents.map(intent => intent.domain),
|
|
1670
|
+
skillRoutingMode: plan.mode,
|
|
1671
|
+
skillPlanRequired: plan.required,
|
|
1672
|
+
requiredSkills: plan.requiredSkills,
|
|
1673
|
+
recommendedSkills: plan.recommendedSkills,
|
|
1674
|
+
requiredSkillArtifacts: plan.requiredArtifacts,
|
|
1675
|
+
requiredSkillVerification: plan.requiredVerification,
|
|
1676
|
+
};
|
|
1677
|
+
await store.update(task.id, { payload: updatedPayload });
|
|
1678
|
+
const state = new WorkflowArtifactWriter(SCALE_DIR).readCurrentState();
|
|
1679
|
+
const artifactsDir = args.dir ?? (state?.taskId === task.id ? state.artifactsDir : undefined);
|
|
1680
|
+
let writtenPath;
|
|
1681
|
+
if (artifactsDir) {
|
|
1682
|
+
const dir = resolve(PROJECT_DIR, artifactsDir);
|
|
1683
|
+
ensureDir(dir);
|
|
1684
|
+
writtenPath = join(dir, 'skill-plan.md');
|
|
1685
|
+
writeFileSync(writtenPath, skillPlanMarkdown(plan), 'utf-8');
|
|
1686
|
+
}
|
|
1687
|
+
new WorkflowArtifactWriter(SCALE_DIR).updateCurrentState({
|
|
1688
|
+
taskId: task.id,
|
|
1689
|
+
level,
|
|
1690
|
+
phase: 'plan',
|
|
1691
|
+
artifactsDir,
|
|
1692
|
+
skillIntents: plan.intents.map(intent => intent.domain),
|
|
1693
|
+
skillRoutingMode: plan.mode,
|
|
1694
|
+
skillPlanRequired: plan.required,
|
|
1695
|
+
skillPlanPath: writtenPath,
|
|
1696
|
+
requiredSkills: plan.requiredSkills,
|
|
1697
|
+
recommendedSkills: plan.recommendedSkills,
|
|
1698
|
+
requiredSkillArtifacts: plan.requiredArtifacts,
|
|
1699
|
+
requiredSkillVerification: plan.requiredVerification,
|
|
1700
|
+
});
|
|
1701
|
+
if (args.json) {
|
|
1702
|
+
console.log(JSON.stringify({ plan, writtenPath }, null, 2));
|
|
1703
|
+
return;
|
|
1704
|
+
}
|
|
1705
|
+
console.log('\nSkill Plan');
|
|
1706
|
+
console.log(` Task: ${task.id}`);
|
|
1707
|
+
console.log(` Intents: ${plan.intents.map(intent => intent.domain).join(', ') || 'none'}`);
|
|
1708
|
+
console.log(` Required skills: ${plan.requiredSkills.join(', ') || 'none'}`);
|
|
1709
|
+
console.log(` Recommended skills: ${plan.recommendedSkills.join(', ') || 'none'}`);
|
|
1710
|
+
console.log(` Required artifacts: ${plan.requiredArtifacts.join(', ') || 'none'}`);
|
|
1711
|
+
if (writtenPath)
|
|
1712
|
+
console.log(` Written: ${writtenPath}`);
|
|
1713
|
+
},
|
|
1714
|
+
});
|
|
1715
|
+
const skillDoctorCommand = defineCommand({
|
|
1716
|
+
meta: { name: 'doctor', description: 'Check workflow skill installation status' },
|
|
1717
|
+
args: {
|
|
1718
|
+
dir: { type: 'string', default: '.', description: 'Project directory' },
|
|
1719
|
+
json: { type: 'boolean', default: false, description: 'Output skill doctor report as JSON' },
|
|
1720
|
+
},
|
|
1721
|
+
run({ args }) {
|
|
1722
|
+
const report = inspectWorkflowSkills({ projectDir: args.dir });
|
|
1723
|
+
if (args.json) {
|
|
1724
|
+
console.log(JSON.stringify(report, null, 2));
|
|
1725
|
+
return;
|
|
1726
|
+
}
|
|
1727
|
+
console.log('\nSCALE Skill Doctor');
|
|
1728
|
+
console.log(` Installed: ${report.installed}/${report.total}`);
|
|
1729
|
+
for (const skill of report.skills) {
|
|
1730
|
+
console.log(` ${skill.installed ? '[OK]' : '[MISSING]'} ${skill.id}`);
|
|
1731
|
+
if (skill.detectedPath)
|
|
1732
|
+
console.log(` path: ${skill.detectedPath}`);
|
|
1733
|
+
if (!skill.installed)
|
|
1734
|
+
console.log(` install: ${skill.installCommand}`);
|
|
1735
|
+
}
|
|
1736
|
+
if (!report.ok)
|
|
1737
|
+
process.exitCode = 1;
|
|
1738
|
+
},
|
|
1739
|
+
});
|
|
1740
|
+
const skillCheckCommand = defineCommand({
|
|
1741
|
+
meta: { name: 'check', description: 'Check required skill evidence artifacts' },
|
|
1742
|
+
args: {
|
|
1743
|
+
dir: { type: 'string', description: 'Task artifact directory; defaults to current state artifactsDir' },
|
|
1744
|
+
level: { type: 'string', description: 'Task level: S, M, L, or CRITICAL; defaults to current state level or M' },
|
|
1745
|
+
'require-installed': { type: 'boolean', default: false, description: 'Fail when required workflow skills are not installed locally' },
|
|
1746
|
+
json: { type: 'boolean', default: false },
|
|
1747
|
+
},
|
|
1748
|
+
run({ args }) {
|
|
1749
|
+
const state = new WorkflowArtifactWriter(SCALE_DIR).readCurrentState();
|
|
1750
|
+
const level = normalizeTaskArtifactLevel(args.level ?? state?.level ?? 'M');
|
|
1751
|
+
const policy = loadSkillRoutingPolicy(PROJECT_DIR, SCALE_DIR);
|
|
1752
|
+
const result = evaluateSkillGate({
|
|
1753
|
+
projectDir: PROJECT_DIR,
|
|
1754
|
+
artifactsDir: args.dir ?? state?.artifactsDir,
|
|
1755
|
+
level,
|
|
1756
|
+
requiredArtifacts: state?.requiredSkillArtifacts,
|
|
1757
|
+
mode: state?.skillRoutingMode ?? policy.policy.mode,
|
|
1758
|
+
enforceLevels: policy.policy.enforceLevels,
|
|
1759
|
+
});
|
|
1760
|
+
const skillInstallation = inspectRequiredWorkflowSkills(state?.requiredSkills ?? [], { projectDir: PROJECT_DIR });
|
|
1761
|
+
const requireInstalled = isTruthyFlag(args['require-installed']);
|
|
1762
|
+
const blocked = result.blocked || (requireInstalled && !skillInstallation.ok);
|
|
1763
|
+
const output = {
|
|
1764
|
+
...result,
|
|
1765
|
+
complete: result.complete && (!requireInstalled || skillInstallation.ok),
|
|
1766
|
+
blocked,
|
|
1767
|
+
skillInstallation: {
|
|
1768
|
+
...skillInstallation,
|
|
1769
|
+
checked: requireInstalled,
|
|
1770
|
+
},
|
|
1771
|
+
};
|
|
1772
|
+
if (args.json) {
|
|
1773
|
+
console.log(JSON.stringify(output, null, 2));
|
|
1774
|
+
return;
|
|
1775
|
+
}
|
|
1776
|
+
console.log(`\nSkill Gate: ${output.complete ? 'COMPLETE' : 'INCOMPLETE'}`);
|
|
1777
|
+
console.log(` Mode: ${output.mode}`);
|
|
1778
|
+
console.log(` Required artifacts: ${output.required.join(', ') || 'none'}`);
|
|
1779
|
+
console.log(` Required skills: ${skillInstallation.required.join(', ') || 'none'}`);
|
|
1780
|
+
for (const file of output.missing)
|
|
1781
|
+
console.log(` [MISSING] ${file}`);
|
|
1782
|
+
for (const item of output.incomplete)
|
|
1783
|
+
console.log(` [INCOMPLETE] ${item.file}: ${item.reason}`);
|
|
1784
|
+
if (requireInstalled && !skillInstallation.ok) {
|
|
1785
|
+
for (const skill of skillInstallation.skills.filter(skill => !skill.installed)) {
|
|
1786
|
+
console.log(` [MISSING_SKILL] ${skill.id}: ${skill.installCommand}`);
|
|
1787
|
+
}
|
|
1788
|
+
for (const skill of skillInstallation.unknown)
|
|
1789
|
+
console.log(` [UNKNOWN_SKILL] ${skill}`);
|
|
1790
|
+
}
|
|
1791
|
+
if (blocked)
|
|
1792
|
+
process.exitCode = 1;
|
|
1793
|
+
},
|
|
1794
|
+
});
|
|
1118
1795
|
const skill = defineCommand({
|
|
1119
1796
|
meta: { name: 'skill', description: 'Skill discovery and management' },
|
|
1120
|
-
subCommands: { scan: skillScan },
|
|
1797
|
+
subCommands: { scan: skillScan, doctor: skillDoctorCommand, plan: skillPlanCommand, check: skillCheckCommand },
|
|
1121
1798
|
});
|
|
1122
1799
|
// ============================================================================
|
|
1123
1800
|
// agent commands — Multi-Agent 协作系统 (Phase 9)
|
|
1124
1801
|
// ============================================================================
|
|
1125
1802
|
import { AgentPool } from '../agents/AgentPool.js';
|
|
1126
|
-
import { AgentChannel } from '../agents/AgentChannel.js';
|
|
1127
1803
|
import { PROFESSIONAL_AGENTS, getProfile, listProfiles } from '../agents/profiles.js';
|
|
1128
1804
|
const agentPool = new AgentPool();
|
|
1129
|
-
const agentChannel = new AgentChannel(getEngine().eventBus);
|
|
1130
1805
|
const agentSpawn = defineCommand({
|
|
1131
1806
|
meta: { name: 'spawn', description: 'Spawn a new agent instance' },
|
|
1132
1807
|
args: {
|
|
@@ -1244,7 +1919,7 @@ import * as phaseCommands from '../cli/phaseCommands.js';
|
|
|
1244
1919
|
import * as liteCommands from '../cli/liteCommands.js';
|
|
1245
1920
|
import * as vibeCommands from '../cli/vibeCommands.js';
|
|
1246
1921
|
const main = defineCommand({
|
|
1247
|
-
meta: { name: 'scale', version: '0.
|
|
1922
|
+
meta: { name: 'scale', version: '0.13.0', description: 'SCALE Engine v0.13.0 CLI - hardened phase workflow gates, governance templates, platform adapters, skill routing, and verification automation' },
|
|
1248
1923
|
subCommands: {
|
|
1249
1924
|
// Lite Mode (agent-skills style interactive entry)
|
|
1250
1925
|
lite: liteCommands.liteCommand,
|
|
@@ -1274,10 +1949,16 @@ const main = defineCommand({
|
|
|
1274
1949
|
context,
|
|
1275
1950
|
evolve,
|
|
1276
1951
|
stats,
|
|
1952
|
+
preflight,
|
|
1953
|
+
governance,
|
|
1954
|
+
metrics,
|
|
1955
|
+
'task-artifacts': taskArtifacts,
|
|
1956
|
+
workspace,
|
|
1277
1957
|
status,
|
|
1278
1958
|
workflow,
|
|
1279
1959
|
evidence,
|
|
1280
1960
|
skill,
|
|
1961
|
+
skills: skill,
|
|
1281
1962
|
agent,
|
|
1282
1963
|
team,
|
|
1283
1964
|
'create-prd': createPRD,
|