@hongmaple0820/scale-engine 0.13.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/dist/adapters/AiderAdapter.js +53 -53
  2. package/dist/adapters/AiderAdapter.js.map +1 -1
  3. package/dist/adapters/ClaudeCodeAdapter.js +2 -0
  4. package/dist/adapters/ClaudeCodeAdapter.js.map +1 -1
  5. package/dist/adapters/CodexAdapter.js +1 -1
  6. package/dist/adapters/CodexAdapter.js.map +1 -1
  7. package/dist/adapters/CursorAdapter.js +1 -1
  8. package/dist/adapters/CursorAdapter.js.map +1 -1
  9. package/dist/adapters/DeepSeekTuiAdapter.js +2 -0
  10. package/dist/adapters/DeepSeekTuiAdapter.js.map +1 -1
  11. package/dist/adapters/DoubaoAdapter.js +34 -34
  12. package/dist/adapters/DoubaoAdapter.js.map +1 -1
  13. package/dist/adapters/GeminiAdapter.js +1 -1
  14. package/dist/adapters/GeminiAdapter.js.map +1 -1
  15. package/dist/adapters/HermesAdapter.js +1 -1
  16. package/dist/adapters/HermesAdapter.js.map +1 -1
  17. package/dist/adapters/KimiAdapter.js +33 -33
  18. package/dist/adapters/KimiAdapter.js.map +1 -1
  19. package/dist/adapters/KiroAdapter.js +27 -27
  20. package/dist/adapters/KiroAdapter.js.map +1 -1
  21. package/dist/adapters/OpenClawAdapter.js +1 -1
  22. package/dist/adapters/OpenClawAdapter.js.map +1 -1
  23. package/dist/adapters/OpenCodeAdapter.js +1 -1
  24. package/dist/adapters/OpenCodeAdapter.js.map +1 -1
  25. package/dist/adapters/QCoderAdapter.js +1 -1
  26. package/dist/adapters/QCoderAdapter.js.map +1 -1
  27. package/dist/adapters/TraeAdapter.js +1 -1
  28. package/dist/adapters/TraeAdapter.js.map +1 -1
  29. package/dist/adapters/VSCAdapter.js +1 -1
  30. package/dist/adapters/VSCAdapter.js.map +1 -1
  31. package/dist/adapters/WindsurfAdapter.js +33 -33
  32. package/dist/adapters/WindsurfAdapter.js.map +1 -1
  33. package/dist/adapters/WorkBuddyAdapter.js +1 -1
  34. package/dist/adapters/WorkBuddyAdapter.js.map +1 -1
  35. package/dist/api/cli.js +365 -13
  36. package/dist/api/cli.js.map +1 -1
  37. package/dist/api/doctor.d.ts +1 -0
  38. package/dist/api/doctor.js +29 -0
  39. package/dist/api/doctor.js.map +1 -1
  40. package/dist/api/quickstart.d.ts +1 -0
  41. package/dist/api/quickstart.js +2 -2
  42. package/dist/api/quickstart.js.map +1 -1
  43. package/dist/cli/phaseCommands.d.ts +5 -0
  44. package/dist/cli/phaseCommands.js +149 -25
  45. package/dist/cli/phaseCommands.js.map +1 -1
  46. package/dist/skills/ExternalSkills.js +2 -0
  47. package/dist/skills/ExternalSkills.js.map +1 -1
  48. package/dist/skills/SkillCatalog.d.ts +13 -0
  49. package/dist/skills/SkillCatalog.js +184 -0
  50. package/dist/skills/SkillCatalog.js.map +1 -0
  51. package/dist/skills/SkillDoctor.d.ts +37 -0
  52. package/dist/skills/SkillDoctor.js +90 -0
  53. package/dist/skills/SkillDoctor.js.map +1 -0
  54. package/dist/skills/index.d.ts +1 -0
  55. package/dist/skills/index.js +1 -0
  56. package/dist/skills/index.js.map +1 -1
  57. package/dist/skills/routing/SkillGate.d.ts +2 -1
  58. package/dist/skills/routing/SkillGate.js +19 -2
  59. package/dist/skills/routing/SkillGate.js.map +1 -1
  60. package/dist/skills/routing/SkillPlanner.js +40 -40
  61. package/dist/skills/routing/SkillPolicy.js +39 -12
  62. package/dist/skills/routing/SkillPolicy.js.map +1 -1
  63. package/dist/workflow/GovernanceLock.d.ts +35 -0
  64. package/dist/workflow/GovernanceLock.js +58 -0
  65. package/dist/workflow/GovernanceLock.js.map +1 -0
  66. package/dist/workflow/GovernanceTemplatePacks.d.ts +24 -0
  67. package/dist/workflow/GovernanceTemplatePacks.js +128 -0
  68. package/dist/workflow/GovernanceTemplatePacks.js.map +1 -0
  69. package/dist/workflow/GovernanceTemplates.d.ts +6 -1
  70. package/dist/workflow/GovernanceTemplates.js +585 -414
  71. package/dist/workflow/GovernanceTemplates.js.map +1 -1
  72. package/dist/workflow/TaskArtifactScaffolder.js +17 -17
  73. package/dist/workflow/TaskArtifactScaffolder.js.map +1 -1
  74. package/dist/workflow/VerificationCommands.js +1 -8
  75. package/dist/workflow/VerificationCommands.js.map +1 -1
  76. package/dist/workflow/VerificationProfile.d.ts +1 -0
  77. package/dist/workflow/VerificationProfile.js +42 -5
  78. package/dist/workflow/VerificationProfile.js.map +1 -1
  79. package/dist/workflow/WorkflowEngine.d.ts +6 -3
  80. package/dist/workflow/WorkflowEngine.js +1 -1
  81. package/dist/workflow/WorkflowEngine.js.map +1 -1
  82. package/dist/workflow/WorkspaceLifecycle.d.ts +53 -0
  83. package/dist/workflow/WorkspaceLifecycle.js +285 -0
  84. package/dist/workflow/WorkspaceLifecycle.js.map +1 -0
  85. package/dist/workflow/WorkspaceTopology.d.ts +49 -0
  86. package/dist/workflow/WorkspaceTopology.js +125 -0
  87. package/dist/workflow/WorkspaceTopology.js.map +1 -0
  88. package/dist/workflow/index.d.ts +4 -0
  89. package/dist/workflow/index.js +4 -0
  90. package/dist/workflow/index.js.map +1 -1
  91. package/package.json +2 -2
package/dist/api/cli.js CHANGED
@@ -22,6 +22,7 @@ import { LessonExtractor, RuleProposer, HookGenerator, EvolutionEngine } from '.
22
22
  import { Doctor } from './doctor.js';
23
23
  import { quickStart, detectPlatform } from './quickstart.js';
24
24
  import { SkillDiscovery } from '../skills/SkillDiscovery.js';
25
+ import { inspectRequiredWorkflowSkills, inspectWorkflowSkills } from '../skills/SkillDoctor.js';
25
26
  import { listWorkflowPresets, getPresetsByScenario } from '../workflows/presets.js';
26
27
  import { EvidenceStore } from '../workflow/EvidenceStore.js';
27
28
  import { OutOfScopeStore } from '../workflow/OutOfScopeStore.js';
@@ -29,9 +30,12 @@ import { ReviewStore } from '../workflow/ReviewStore.js';
29
30
  import { WorkflowEngine } from '../workflow/WorkflowEngine.js';
30
31
  import { resolveVerificationTargets } from '../workflow/VerificationProfile.js';
31
32
  import { writeGovernanceTemplates } from '../workflow/GovernanceTemplates.js';
33
+ import { computeGovernanceDrift } from '../workflow/GovernanceLock.js';
32
34
  import { TaskMetricsStore } from '../workflow/TaskMetricsStore.js';
33
35
  import { checkTaskArtifactCompleteness } from '../workflow/TaskArtifactScaffolder.js';
34
36
  import { WorkflowArtifactWriter } from '../workflow/WorkflowArtifactWriter.js';
37
+ import { cleanupWorkspaceLifecycle, inspectWorkspaceLifecycle, } from '../workflow/WorkspaceLifecycle.js';
38
+ import { resolveWorkspaceTopology, workspaceTopologyPath, workspaceTopologyTemplate, } from '../workflow/WorkspaceTopology.js';
35
39
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
36
40
  import { join, resolve } from 'node:path';
37
41
  // ============================================================================
@@ -54,6 +58,17 @@ function ensureDir(dir) {
54
58
  function isTruthyFlag(value) {
55
59
  return value === true || value === '' || value === 'true' || value === '1';
56
60
  }
61
+ function normalizePreflightProfile(value) {
62
+ const normalized = String(value ?? 'quick').trim().toLowerCase();
63
+ if (normalized === 'full' || normalized === 'ci')
64
+ return normalized;
65
+ return 'quick';
66
+ }
67
+ function gatesForPreflightProfile(profile) {
68
+ if (profile === 'quick')
69
+ return ['G3', 'G0', 'G4', 'G5'];
70
+ return ['G3', 'G0', 'G4', 'G5', 'G6', 'G7'];
71
+ }
57
72
  let _engine = null;
58
73
  function getEngine() {
59
74
  if (!_engine)
@@ -810,6 +825,179 @@ const taskArtifacts = defineCommand({
810
825
  meta: { name: 'task-artifacts', description: 'Inspect task artifact completeness' },
811
826
  subCommands: { check: taskArtifactsCheck },
812
827
  });
828
+ function printWorkspaceLifecycle(report) {
829
+ console.log('\nSCALE Workspace Lifecycle');
830
+ console.log(` Topology: ${report.topology.topology}${report.topology.configured ? '' : ' (default)'}`);
831
+ console.log(` Root: ${report.root.path}`);
832
+ console.log(` Branch: ${report.root.branch ?? '(detached)'}`);
833
+ console.log(` Linked worktree: ${report.root.isLinkedWorktree ? 'yes' : 'no'}`);
834
+ console.log(` Root status: ${report.root.clean ? 'clean' : 'dirty'}`);
835
+ if (!report.root.clean) {
836
+ console.log(` staged=${report.root.staged} unstaged=${report.root.unstaged} untracked=${report.root.untracked}`);
837
+ }
838
+ if (report.childRepositories.length) {
839
+ console.log('\n Child repositories:');
840
+ for (const child of report.childRepositories) {
841
+ console.log(` ${child.clean ? '[CLEAN]' : '[DIRTY]'} ${child.relativePath} (${child.kind}) branch=${child.branch ?? '(detached)'}`);
842
+ if (!child.clean)
843
+ console.log(` staged=${child.staged} unstaged=${child.unstaged} untracked=${child.untracked}`);
844
+ }
845
+ }
846
+ else {
847
+ console.log('\n Child repositories: none');
848
+ }
849
+ console.log(`\n Cleanup candidate: ${report.finish.canCleanup ? 'yes' : 'no'}`);
850
+ for (const blocker of report.finish.blockers)
851
+ console.log(` [BLOCKER] ${blocker}`);
852
+ for (const warning of report.finish.warnings)
853
+ console.log(` [WARN] ${warning}`);
854
+ for (const action of report.finish.nextActions)
855
+ console.log(` [NEXT] ${action}`);
856
+ }
857
+ function printWorkspaceTopology(topology, written) {
858
+ console.log('\nSCALE Workspace Topology');
859
+ console.log(` Topology: ${topology.topology}${topology.configured ? '' : ' (default)'}`);
860
+ console.log(` Config: ${topology.configPath}`);
861
+ if (written)
862
+ console.log(` Written: ${written}`);
863
+ console.log('\n Repositories:');
864
+ for (const repo of topology.repositories) {
865
+ console.log(` - ${repo.name}: ${repo.path} (${repo.role}) required=${repo.required !== false ? 'yes' : 'no'}`);
866
+ }
867
+ for (const warning of topology.warnings)
868
+ console.log(` [WARN] ${warning}`);
869
+ }
870
+ function printWorkspaceCleanup(result) {
871
+ printWorkspaceLifecycle(result.report);
872
+ console.log('\n Cleanup plan:');
873
+ console.log(` Mode: ${result.mode}`);
874
+ console.log(` Target: ${result.targetPath}`);
875
+ console.log(` Can apply: ${result.canApply ? 'yes' : 'no'}`);
876
+ console.log(` Applied: ${result.applied ? 'yes' : 'no'}`);
877
+ console.log(` Confirmation token: ${result.confirmationToken ?? '(unavailable)'}`);
878
+ for (const command of result.commands)
879
+ console.log(` Command: ${command}`);
880
+ for (const blocker of result.blockers)
881
+ console.log(` [BLOCKER] ${blocker}`);
882
+ for (const warning of result.warnings)
883
+ console.log(` [WARN] ${warning}`);
884
+ }
885
+ const workspaceStatus = defineCommand({
886
+ meta: { name: 'status', description: 'Inspect root worktree and child repository lifecycle state' },
887
+ args: {
888
+ dir: { type: 'string', description: 'Repository or worktree directory; defaults to current project directory' },
889
+ json: { type: 'boolean', default: false },
890
+ },
891
+ async run({ args }) {
892
+ const report = await inspectWorkspaceLifecycle({ projectDir: args.dir ?? PROJECT_DIR });
893
+ if (args.json) {
894
+ console.log(JSON.stringify(report, null, 2));
895
+ }
896
+ else {
897
+ printWorkspaceLifecycle(report);
898
+ }
899
+ if (report.finish.blockers.length > 0)
900
+ process.exitCode = 1;
901
+ },
902
+ });
903
+ const workspaceMap = defineCommand({
904
+ meta: { name: 'map', description: 'Resolve or write explicit workspace topology for single, monorepo, polyrepo, submodule, or MOE projects' },
905
+ args: {
906
+ dir: { type: 'string', description: 'Project directory; defaults to current project directory' },
907
+ topology: { type: 'string', default: 'moe', description: 'Starter topology for --write (single/monorepo/polyrepo/submodule-workspace/moe)' },
908
+ write: { type: 'boolean', default: false, description: 'Create .scale/workspace.json when it does not exist' },
909
+ json: { type: 'boolean', default: false },
910
+ },
911
+ run({ args }) {
912
+ const projectDir = resolve(args.dir ?? PROJECT_DIR);
913
+ const target = workspaceTopologyPath(projectDir);
914
+ let written = null;
915
+ if (isTruthyFlag(args.write) && !existsSync(target)) {
916
+ ensureDir(join(projectDir, '.scale'));
917
+ writeFileSync(target, workspaceTopologyTemplate({
918
+ topology: normalizeWorkspaceTopologyKind(args.topology),
919
+ }), 'utf-8');
920
+ written = target;
921
+ }
922
+ const topology = resolveWorkspaceTopology({ projectDir });
923
+ const result = { ...topology, written };
924
+ if (args.json) {
925
+ console.log(JSON.stringify(result, null, 2));
926
+ }
927
+ else {
928
+ printWorkspaceTopology(topology, written);
929
+ }
930
+ },
931
+ });
932
+ const workspaceFinish = defineCommand({
933
+ meta: { name: 'finish', description: 'Check whether a temporary worktree can be safely finished or cleaned up' },
934
+ args: {
935
+ dir: { type: 'string', description: 'Repository or worktree directory; defaults to current project directory' },
936
+ json: { type: 'boolean', default: false },
937
+ },
938
+ async run({ args }) {
939
+ const report = await inspectWorkspaceLifecycle({ projectDir: args.dir ?? PROJECT_DIR });
940
+ const result = {
941
+ root: report.root,
942
+ childRepositories: report.childRepositories,
943
+ topology: report.topology,
944
+ finish: report.finish,
945
+ };
946
+ if (args.json) {
947
+ console.log(JSON.stringify(result, null, 2));
948
+ }
949
+ else {
950
+ printWorkspaceLifecycle(report);
951
+ }
952
+ if (report.finish.blockers.length > 0)
953
+ process.exitCode = 1;
954
+ },
955
+ });
956
+ const workspaceCleanup = defineCommand({
957
+ meta: { name: 'cleanup', description: 'Dry-run or apply safe removal of a linked temporary worktree' },
958
+ args: {
959
+ dir: { type: 'string', description: 'Linked worktree directory; defaults to current project directory' },
960
+ 'dry-run': { type: 'boolean', default: false, description: 'Preview cleanup; this is the default unless --apply is set' },
961
+ apply: { type: 'boolean', default: false, description: 'Actually run git worktree remove after safety checks' },
962
+ confirm: { type: 'string', description: 'Required confirmation token for --apply, usually the worktree branch name' },
963
+ json: { type: 'boolean', default: false },
964
+ },
965
+ async run({ args }) {
966
+ const result = await cleanupWorkspaceLifecycle({
967
+ projectDir: args.dir ?? PROJECT_DIR,
968
+ apply: isTruthyFlag(args.apply),
969
+ confirm: args.confirm,
970
+ });
971
+ if (args.json) {
972
+ console.log(JSON.stringify(result, null, 2));
973
+ }
974
+ else {
975
+ printWorkspaceCleanup(result);
976
+ }
977
+ if (!result.canApply || (isTruthyFlag(args.apply) && !result.applied))
978
+ process.exitCode = 1;
979
+ },
980
+ });
981
+ const workspace = defineCommand({
982
+ meta: { name: 'workspace', description: 'Inspect worktree, branch, and child repository lifecycle safety' },
983
+ subCommands: {
984
+ map: workspaceMap,
985
+ status: workspaceStatus,
986
+ finish: workspaceFinish,
987
+ cleanup: workspaceCleanup,
988
+ },
989
+ });
990
+ function normalizeWorkspaceTopologyKind(value) {
991
+ const normalized = String(value ?? 'moe').trim();
992
+ if (normalized === 'single'
993
+ || normalized === 'monorepo'
994
+ || normalized === 'polyrepo'
995
+ || normalized === 'submodule-workspace'
996
+ || normalized === 'moe') {
997
+ return normalized;
998
+ }
999
+ return 'moe';
1000
+ }
813
1001
  const preflight = defineCommand({
814
1002
  meta: { name: 'preflight', description: 'Run service-aware verification without a task artifact' },
815
1003
  args: {
@@ -818,6 +1006,7 @@ const preflight = defineCommand({
818
1006
  'test-cmd': { type: 'string', description: 'Override test command' },
819
1007
  'coverage-cmd': { type: 'string', description: 'Override coverage command' },
820
1008
  profile: { type: 'string', description: 'Verification profile from .scale/verification.json' },
1009
+ 'preflight-profile': { type: 'string', default: 'quick', description: 'Gate intensity profile (quick/full/ci); quick skips coverage and security' },
821
1010
  service: { type: 'string', description: 'Service name from .scale/verification.json; use all for required services' },
822
1011
  'tdd-evidence': { type: 'string', description: 'Path to JSON TDD evidence with red/green/refactor/testFirst=true' },
823
1012
  'tdd-strict': { type: 'boolean', default: false, description: 'Require TDD evidence before other gates' },
@@ -825,6 +1014,8 @@ const preflight = defineCommand({
825
1014
  },
826
1015
  async run({ args }) {
827
1016
  const { workflowEngine } = getEngine();
1017
+ const preflightProfile = normalizePreflightProfile(args['preflight-profile']);
1018
+ const gateStages = gatesForPreflightProfile(preflightProfile);
828
1019
  const resolved = resolveVerificationTargets({
829
1020
  projectDir: PROJECT_DIR,
830
1021
  scaleDir: SCALE_DIR,
@@ -837,6 +1028,8 @@ const preflight = defineCommand({
837
1028
  for (const warning of resolved.warnings)
838
1029
  console.log(` [WARN] ${warning}`);
839
1030
  console.log(` Profile: ${resolved.profileName}`);
1031
+ console.log(` Preflight profile: ${preflightProfile}`);
1032
+ console.log(` Gates: ${gateStages.join(', ')}`);
840
1033
  }
841
1034
  for (const target of resolved.targets) {
842
1035
  if (!args.json) {
@@ -851,6 +1044,7 @@ const preflight = defineCommand({
851
1044
  coverage: args['coverage-cmd'] ?? target.config.coverage,
852
1045
  tddEvidence: args['tdd-evidence'],
853
1046
  tddStrict: isTruthyFlag(args['tdd-strict']),
1047
+ gates: gateStages,
854
1048
  });
855
1049
  const passed = gates.every(gate => gate.passed);
856
1050
  targetResults.push({
@@ -871,6 +1065,8 @@ const preflight = defineCommand({
871
1065
  const result = {
872
1066
  phase: 'PREFLIGHT',
873
1067
  profile: resolved.profileName,
1068
+ preflightProfile,
1069
+ gates: gateStages,
874
1070
  services: targetResults.map(target => target.service).filter(Boolean),
875
1071
  policy: resolved.policy,
876
1072
  targets: targetResults,
@@ -1006,7 +1202,13 @@ const init = defineCommand({
1006
1202
  args: {
1007
1203
  agent: { type: 'string', default: '', description: `Agent type (${SUPPORTED_AGENTS.join('/')}) - auto-detected if not specified` },
1008
1204
  dir: { type: 'string', default: '.', description: 'Project directory' },
1205
+ json: { type: 'boolean', default: false, description: 'Output initialization result as JSON' },
1009
1206
  scenario: { type: 'string', default: 'standard', description: 'Scenario mode (sandbox/standard/critical)' },
1207
+ 'governance-pack': {
1208
+ type: 'string',
1209
+ default: 'standard',
1210
+ description: 'Governance template pack (standard/project-scaffold/moe-workspace/go-service-matrix/node-library/frontend-app)',
1211
+ },
1010
1212
  quick: { type: 'boolean', default: false, description: 'Quick start with auto-detection' },
1011
1213
  interactive: { type: 'boolean', default: false, description: 'Interactive configuration mode with prompts' },
1012
1214
  'coverage-threshold': { type: 'string', default: '80', description: 'Coverage threshold (default 80%)' },
@@ -1067,6 +1269,7 @@ const init = defineCommand({
1067
1269
  const governance = writeGovernanceTemplates(args.dir, {
1068
1270
  mode: governanceModeFromScenario(scenarioMode),
1069
1271
  projectName,
1272
+ pack: args['governance-pack'],
1070
1273
  });
1071
1274
  result.created.push(...governance.created);
1072
1275
  result.skipped.push(...governance.skipped);
@@ -1091,8 +1294,24 @@ const init = defineCommand({
1091
1294
  return;
1092
1295
  }
1093
1296
  // One-click quick start mode
1094
- if (args.quick || !args.agent) {
1095
- const qsResult = await quickStart(args.dir);
1297
+ if (!args.agent) {
1298
+ const qsResult = await quickStart(args.dir, { governancePack: args['governance-pack'] });
1299
+ if (args.json) {
1300
+ const detection = qsResult.success ? undefined : detectPlatform(args.dir);
1301
+ console.log(JSON.stringify({
1302
+ ok: qsResult.success,
1303
+ mode: 'quick',
1304
+ platform: qsResult.platform,
1305
+ created: qsResult.created,
1306
+ skipped: qsResult.skipped,
1307
+ constraintsApplied: qsResult.constraintsApplied,
1308
+ capabilitiesEnabled: qsResult.capabilitiesEnabled,
1309
+ knowledgeGraph: qsResult.knowledgeGraph,
1310
+ nextSteps: qsResult.nextSteps,
1311
+ suggestions: detection?.suggestions ?? [],
1312
+ }, null, 2));
1313
+ return;
1314
+ }
1096
1315
  if (qsResult.success && qsResult.platform) {
1097
1316
  console.log(`\n✅ SCALE Engine Quick Start completed for ${qsResult.platform}`);
1098
1317
  console.log(`\n📁 Created (${qsResult.created.length}):`);
@@ -1124,9 +1343,26 @@ const init = defineCommand({
1124
1343
  const governance = writeGovernanceTemplates(args.dir, {
1125
1344
  mode: governanceModeFromScenario(args.scenario),
1126
1345
  projectName,
1346
+ pack: args['governance-pack'],
1127
1347
  });
1128
1348
  result.created.push(...governance.created);
1129
1349
  result.skipped.push(...governance.skipped);
1350
+ if (args.json) {
1351
+ console.log(JSON.stringify({
1352
+ ok: true,
1353
+ mode: args.quick ? 'quick-agent' : 'manual',
1354
+ agent: args.agent,
1355
+ scenario: args.scenario,
1356
+ governancePack: args['governance-pack'],
1357
+ settingsPath: result.settingsPath,
1358
+ knowledgeDocPath: result.knowledgeDocPath,
1359
+ scaleDir: result.scaleDir,
1360
+ created: result.created,
1361
+ skipped: result.skipped,
1362
+ nextSteps: ['scale doctor', 'scale create Spec "<feature name>"'],
1363
+ }, null, 2));
1364
+ return;
1365
+ }
1130
1366
  console.log(`\n✅ SCALE Engine initialized for ${args.agent} (scenario: ${args.scenario})`);
1131
1367
  console.log(`\n📁 Created:`);
1132
1368
  for (const f of result.created)
@@ -1145,6 +1381,39 @@ const init = defineCommand({
1145
1381
  },
1146
1382
  });
1147
1383
  // ============================================================================
1384
+ // governance command — Generated governance asset tooling
1385
+ // ============================================================================
1386
+ const governanceDiff = defineCommand({
1387
+ meta: { name: 'diff', description: 'Check generated governance files for drift' },
1388
+ args: {
1389
+ dir: { type: 'string', default: '.', description: 'Project directory' },
1390
+ json: { type: 'boolean', default: false, description: 'Print JSON output' },
1391
+ },
1392
+ run({ args }) {
1393
+ const report = computeGovernanceDrift(args.dir);
1394
+ if (args.json) {
1395
+ console.log(JSON.stringify(report, null, 2));
1396
+ return;
1397
+ }
1398
+ if (!report.lockExists) {
1399
+ console.log('No governance lock found. Run: scale init --governance-pack <pack>');
1400
+ return;
1401
+ }
1402
+ if (report.missing.length === 0 && report.changed.length === 0) {
1403
+ console.log('Governance generated files are clean.');
1404
+ return;
1405
+ }
1406
+ for (const item of report.missing)
1407
+ console.log(`missing: ${item.path}`);
1408
+ for (const item of report.changed)
1409
+ console.log(`changed: ${item.path}`);
1410
+ },
1411
+ });
1412
+ const governance = defineCommand({
1413
+ meta: { name: 'governance', description: 'Governance template pack tools' },
1414
+ subCommands: { diff: governanceDiff },
1415
+ });
1416
+ // ============================================================================
1148
1417
  // evolve command
1149
1418
  // ============================================================================
1150
1419
  const evolve = defineCommand({
@@ -1188,11 +1457,29 @@ const workflowList = defineCommand({
1188
1457
  meta: { name: 'list', description: 'List all workflow presets' },
1189
1458
  args: {
1190
1459
  scenario: { type: 'string', description: 'Filter by scenario mode (sandbox/standard/critical)' },
1460
+ json: { type: 'boolean', default: false, description: 'Output workflow presets as JSON' },
1191
1461
  },
1192
1462
  async run({ args }) {
1193
1463
  const presets = args.scenario
1194
1464
  ? getPresetsByScenario(args.scenario)
1195
1465
  : listWorkflowPresets();
1466
+ if (args.json) {
1467
+ console.log(JSON.stringify({
1468
+ ok: true,
1469
+ scenario: args.scenario ?? null,
1470
+ count: presets.length,
1471
+ presets: presets.map(preset => ({
1472
+ id: preset.id,
1473
+ name: preset.name,
1474
+ nameZh: preset.nameZh,
1475
+ description: preset.description,
1476
+ scenarioMode: preset.scenarioMode,
1477
+ requiredArtifacts: preset.requiredArtifacts,
1478
+ steps: preset.steps,
1479
+ })),
1480
+ }, null, 2));
1481
+ return;
1482
+ }
1196
1483
  if (presets.length === 0) {
1197
1484
  console.log('No workflow presets found.');
1198
1485
  return;
@@ -1366,15 +1653,34 @@ const skillScan = defineCommand({
1366
1653
  meta: { name: 'scan', description: 'Scan for installed skills' },
1367
1654
  args: {
1368
1655
  dir: { type: 'string', default: '.', description: 'Project directory' },
1656
+ json: { type: 'boolean', default: false, description: 'Output scan result as JSON' },
1369
1657
  },
1370
1658
  async run({ args }) {
1371
1659
  const discovery = new SkillDiscovery(args.dir);
1372
1660
  const platform = discovery.detectPlatform();
1661
+ if (!platform && args.json) {
1662
+ console.log(JSON.stringify({
1663
+ ok: false,
1664
+ platform: null,
1665
+ skills: [],
1666
+ message: 'No agent platform detected. Run `scale init` first.',
1667
+ }, null, 2));
1668
+ return;
1669
+ }
1373
1670
  if (!platform) {
1374
1671
  console.log('\n⚠️ No agent platform detected. Run `scale init` first.');
1375
1672
  return;
1376
1673
  }
1377
1674
  const result = discovery.scanSkills(platform);
1675
+ if (args.json) {
1676
+ console.log(JSON.stringify({
1677
+ ok: true,
1678
+ platform: result.platform,
1679
+ count: result.skills.length,
1680
+ skills: result.skills,
1681
+ }, null, 2));
1682
+ return;
1683
+ }
1378
1684
  console.log(`\n🔍 Platform: ${result.platform}`);
1379
1685
  console.log(`📦 Skills found: ${result.skills.length}`);
1380
1686
  if (result.skills.length > 0) {
@@ -1463,11 +1769,37 @@ const skillPlanCommand = defineCommand({
1463
1769
  console.log(` Written: ${writtenPath}`);
1464
1770
  },
1465
1771
  });
1772
+ const skillDoctorCommand = defineCommand({
1773
+ meta: { name: 'doctor', description: 'Check workflow skill installation status' },
1774
+ args: {
1775
+ dir: { type: 'string', default: '.', description: 'Project directory' },
1776
+ json: { type: 'boolean', default: false, description: 'Output skill doctor report as JSON' },
1777
+ },
1778
+ run({ args }) {
1779
+ const report = inspectWorkflowSkills({ projectDir: args.dir });
1780
+ if (args.json) {
1781
+ console.log(JSON.stringify(report, null, 2));
1782
+ return;
1783
+ }
1784
+ console.log('\nSCALE Skill Doctor');
1785
+ console.log(` Installed: ${report.installed}/${report.total}`);
1786
+ for (const skill of report.skills) {
1787
+ console.log(` ${skill.installed ? '[OK]' : '[MISSING]'} ${skill.id}`);
1788
+ if (skill.detectedPath)
1789
+ console.log(` path: ${skill.detectedPath}`);
1790
+ if (!skill.installed)
1791
+ console.log(` install: ${skill.installCommand}`);
1792
+ }
1793
+ if (!report.ok)
1794
+ process.exitCode = 1;
1795
+ },
1796
+ });
1466
1797
  const skillCheckCommand = defineCommand({
1467
1798
  meta: { name: 'check', description: 'Check required skill evidence artifacts' },
1468
1799
  args: {
1469
1800
  dir: { type: 'string', description: 'Task artifact directory; defaults to current state artifactsDir' },
1470
1801
  level: { type: 'string', description: 'Task level: S, M, L, or CRITICAL; defaults to current state level or M' },
1802
+ 'require-installed': { type: 'boolean', default: false, description: 'Fail when required workflow skills are not installed locally' },
1471
1803
  json: { type: 'boolean', default: false },
1472
1804
  },
1473
1805
  run({ args }) {
@@ -1482,33 +1814,51 @@ const skillCheckCommand = defineCommand({
1482
1814
  mode: state?.skillRoutingMode ?? policy.policy.mode,
1483
1815
  enforceLevels: policy.policy.enforceLevels,
1484
1816
  });
1817
+ const skillInstallation = inspectRequiredWorkflowSkills(state?.requiredSkills ?? [], { projectDir: PROJECT_DIR });
1818
+ const requireInstalled = isTruthyFlag(args['require-installed']);
1819
+ const blocked = result.blocked || (requireInstalled && !skillInstallation.ok);
1820
+ const output = {
1821
+ ...result,
1822
+ complete: result.complete && (!requireInstalled || skillInstallation.ok),
1823
+ blocked,
1824
+ skillInstallation: {
1825
+ ...skillInstallation,
1826
+ checked: requireInstalled,
1827
+ },
1828
+ };
1485
1829
  if (args.json) {
1486
- console.log(JSON.stringify(result, null, 2));
1830
+ console.log(JSON.stringify(output, null, 2));
1487
1831
  return;
1488
1832
  }
1489
- console.log(`\nSkill Gate: ${result.complete ? 'COMPLETE' : 'INCOMPLETE'}`);
1490
- console.log(` Mode: ${result.mode}`);
1491
- console.log(` Required: ${result.required.join(', ') || 'none'}`);
1492
- for (const file of result.missing)
1833
+ console.log(`\nSkill Gate: ${output.complete ? 'COMPLETE' : 'INCOMPLETE'}`);
1834
+ console.log(` Mode: ${output.mode}`);
1835
+ console.log(` Required artifacts: ${output.required.join(', ') || 'none'}`);
1836
+ console.log(` Required skills: ${skillInstallation.required.join(', ') || 'none'}`);
1837
+ for (const file of output.missing)
1493
1838
  console.log(` [MISSING] ${file}`);
1494
- for (const item of result.incomplete)
1839
+ for (const item of output.incomplete)
1495
1840
  console.log(` [INCOMPLETE] ${item.file}: ${item.reason}`);
1496
- if (result.blocked)
1841
+ if (requireInstalled && !skillInstallation.ok) {
1842
+ for (const skill of skillInstallation.skills.filter(skill => !skill.installed)) {
1843
+ console.log(` [MISSING_SKILL] ${skill.id}: ${skill.installCommand}`);
1844
+ }
1845
+ for (const skill of skillInstallation.unknown)
1846
+ console.log(` [UNKNOWN_SKILL] ${skill}`);
1847
+ }
1848
+ if (blocked)
1497
1849
  process.exitCode = 1;
1498
1850
  },
1499
1851
  });
1500
1852
  const skill = defineCommand({
1501
1853
  meta: { name: 'skill', description: 'Skill discovery and management' },
1502
- subCommands: { scan: skillScan, plan: skillPlanCommand, check: skillCheckCommand },
1854
+ subCommands: { scan: skillScan, doctor: skillDoctorCommand, plan: skillPlanCommand, check: skillCheckCommand },
1503
1855
  });
1504
1856
  // ============================================================================
1505
1857
  // agent commands — Multi-Agent 协作系统 (Phase 9)
1506
1858
  // ============================================================================
1507
1859
  import { AgentPool } from '../agents/AgentPool.js';
1508
- import { AgentChannel } from '../agents/AgentChannel.js';
1509
1860
  import { PROFESSIONAL_AGENTS, getProfile, listProfiles } from '../agents/profiles.js';
1510
1861
  const agentPool = new AgentPool();
1511
- const agentChannel = new AgentChannel(getEngine().eventBus);
1512
1862
  const agentSpawn = defineCommand({
1513
1863
  meta: { name: 'spawn', description: 'Spawn a new agent instance' },
1514
1864
  args: {
@@ -1626,7 +1976,7 @@ import * as phaseCommands from '../cli/phaseCommands.js';
1626
1976
  import * as liteCommands from '../cli/liteCommands.js';
1627
1977
  import * as vibeCommands from '../cli/vibeCommands.js';
1628
1978
  const main = defineCommand({
1629
- meta: { name: 'scale', version: '0.13.0', description: 'SCALE Engine v0.13.0 CLI - hardened phase workflow gates: define/plan/build/verify/review/ship; Vibe templates: scale vibe; 16 platform adapters; 12 agents; 10 workflows; 19 detectors' },
1979
+ 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' },
1630
1980
  subCommands: {
1631
1981
  // Lite Mode (agent-skills style interactive entry)
1632
1982
  lite: liteCommands.liteCommand,
@@ -1657,8 +2007,10 @@ const main = defineCommand({
1657
2007
  evolve,
1658
2008
  stats,
1659
2009
  preflight,
2010
+ governance,
1660
2011
  metrics,
1661
2012
  'task-artifacts': taskArtifacts,
2013
+ workspace,
1662
2014
  status,
1663
2015
  workflow,
1664
2016
  evidence,