@hongmaple0820/scale-engine 0.15.1 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/agents/LeadershipPresets.d.ts +16 -0
  2. package/dist/agents/LeadershipPresets.js +152 -0
  3. package/dist/agents/LeadershipPresets.js.map +1 -0
  4. package/dist/api/cli.js +494 -6
  5. package/dist/api/cli.js.map +1 -1
  6. package/dist/artifact/types.d.ts +4 -0
  7. package/dist/artifact/types.js.map +1 -1
  8. package/dist/cli/phaseCommands.d.ts +14 -0
  9. package/dist/cli/phaseCommands.js +153 -2
  10. package/dist/cli/phaseCommands.js.map +1 -1
  11. package/dist/cli/vibeCommands.d.ts +20 -0
  12. package/dist/cli/vibeCommands.js +150 -173
  13. package/dist/cli/vibeCommands.js.map +1 -1
  14. package/dist/index.d.ts +4 -0
  15. package/dist/index.js +5 -0
  16. package/dist/index.js.map +1 -1
  17. package/dist/prompts/VibeTemplateGallery.d.ts +25 -0
  18. package/dist/prompts/VibeTemplateGallery.js +295 -0
  19. package/dist/prompts/VibeTemplateGallery.js.map +1 -0
  20. package/dist/skills/SkillRepository.d.ts +63 -0
  21. package/dist/skills/SkillRepository.js +365 -0
  22. package/dist/skills/SkillRepository.js.map +1 -0
  23. package/dist/tools/ToolCapabilityRegistry.d.ts +46 -0
  24. package/dist/tools/ToolCapabilityRegistry.js +223 -0
  25. package/dist/tools/ToolCapabilityRegistry.js.map +1 -0
  26. package/dist/tools/ToolEvidenceGate.d.ts +39 -0
  27. package/dist/tools/ToolEvidenceGate.js +117 -0
  28. package/dist/tools/ToolEvidenceGate.js.map +1 -0
  29. package/dist/tools/ToolEvidenceStore.d.ts +58 -0
  30. package/dist/tools/ToolEvidenceStore.js +129 -0
  31. package/dist/tools/ToolEvidenceStore.js.map +1 -0
  32. package/dist/tools/ToolOrchestrator.d.ts +67 -0
  33. package/dist/tools/ToolOrchestrator.js +193 -0
  34. package/dist/tools/ToolOrchestrator.js.map +1 -0
  35. package/dist/tools/ToolPolicy.d.ts +33 -0
  36. package/dist/tools/ToolPolicy.js +157 -0
  37. package/dist/tools/ToolPolicy.js.map +1 -0
  38. package/dist/tools/index.d.ts +5 -0
  39. package/dist/tools/index.js +6 -0
  40. package/dist/tools/index.js.map +1 -0
  41. package/dist/workflow/EngineeringStandards.d.ts +69 -0
  42. package/dist/workflow/EngineeringStandards.js +348 -6
  43. package/dist/workflow/EngineeringStandards.js.map +1 -1
  44. package/dist/workflow/GovernanceTemplatePacks.js +11 -9
  45. package/dist/workflow/GovernanceTemplatePacks.js.map +1 -1
  46. package/dist/workflow/GovernanceTemplates.js +15 -4
  47. package/dist/workflow/GovernanceTemplates.js.map +1 -1
  48. package/package.json +2 -2
package/dist/api/cli.js CHANGED
@@ -23,6 +23,8 @@ import { Doctor } from './doctor.js';
23
23
  import { quickStart, detectPlatform } from './quickstart.js';
24
24
  import { SkillDiscovery } from '../skills/SkillDiscovery.js';
25
25
  import { inspectRequiredWorkflowSkills, inspectWorkflowSkills } from '../skills/SkillDoctor.js';
26
+ import { evaluateSkillInstallSafety, listSkillRepositoryEntries, recommendSkillWorkflow, renderSkillRepositoryMarkdown, } from '../skills/SkillRepository.js';
27
+ import { listLeadershipPresets, renderLeadershipPresetsMarkdown } from '../agents/LeadershipPresets.js';
26
28
  import { listWorkflowPresets, getPresetsByScenario } from '../workflows/presets.js';
27
29
  import { EvidenceStore } from '../workflow/EvidenceStore.js';
28
30
  import { OutOfScopeStore } from '../workflow/OutOfScopeStore.js';
@@ -31,15 +33,21 @@ import { WorkflowEngine } from '../workflow/WorkflowEngine.js';
31
33
  import { resolveVerificationTargets } from '../workflow/VerificationProfile.js';
32
34
  import { writeGovernanceTemplates } from '../workflow/GovernanceTemplates.js';
33
35
  import { computeGovernanceDrift } from '../workflow/GovernanceLock.js';
34
- import { doctorEngineeringStandards, scanEngineeringStandards, settleEngineeringStandards, } from '../workflow/EngineeringStandards.js';
36
+ import { baselineEngineeringStandards, doctorEngineeringStandards, scanEngineeringStandards, settleEngineeringStandards, } from '../workflow/EngineeringStandards.js';
35
37
  import { doctorResourceAssets, scanResourceAssets, settleResourceAssets } from '../workflow/ResourceGovernance.js';
36
38
  import { TaskMetricsStore } from '../workflow/TaskMetricsStore.js';
37
39
  import { checkTaskArtifactCompleteness } from '../workflow/TaskArtifactScaffolder.js';
38
40
  import { WorkflowArtifactWriter } from '../workflow/WorkflowArtifactWriter.js';
41
+ import { inspectToolCapabilities } from '../tools/ToolCapabilityRegistry.js';
42
+ import { evaluateToolEvidenceGate } from '../tools/ToolEvidenceGate.js';
43
+ import { ToolEvidenceStore } from '../tools/ToolEvidenceStore.js';
44
+ import { ToolOrchestrator } from '../tools/ToolOrchestrator.js';
45
+ import { loadToolPolicy, toolPolicyTemplate } from '../tools/ToolPolicy.js';
39
46
  import { cleanupWorkspaceLifecycle, inspectWorkspaceLifecycle, } from '../workflow/WorkspaceLifecycle.js';
40
47
  import { resolveWorkspaceTopology, workspaceTopologyPath, workspaceTopologyTemplate, } from '../workflow/WorkspaceTopology.js';
41
48
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
42
49
  import { join, resolve } from 'node:path';
50
+ import { execFileSync } from 'node:child_process';
43
51
  import { SCALE_ENGINE_VERSION } from '../version.js';
44
52
  // ============================================================================
45
53
  // Engine bootstrap (单例 + lazy init)
@@ -893,6 +901,52 @@ function printWorkspaceLifecycle(report) {
893
901
  for (const action of report.finish.nextActions)
894
902
  console.log(` [NEXT] ${action}`);
895
903
  }
904
+ function compactList(values, limit = 5) {
905
+ if (values.length <= limit)
906
+ return values.join(', ');
907
+ return `${values.slice(0, limit).join(', ')} (+${values.length - limit} more)`;
908
+ }
909
+ function printWorkspaceSummary(report) {
910
+ const dirtyChildren = report.childRepositories
911
+ .filter(child => !child.clean)
912
+ .map(child => child.relativePath);
913
+ const unpushedChildren = report.childRepositories
914
+ .filter(child => child.ahead > 0 || (report.topology.finishPolicy.requirePushedBranches && report.topology.topology === 'moe' && !child.upstream && Boolean(child.branch)))
915
+ .map(child => child.relativePath);
916
+ const noUpstreamChildren = report.childRepositories
917
+ .filter(child => !child.upstream && Boolean(child.branch))
918
+ .map(child => child.relativePath);
919
+ const rootStatus = report.root.clean
920
+ ? 'clean'
921
+ : `dirty (staged=${report.root.staged}, unstaged=${report.root.unstaged}, untracked=${report.root.untracked})`;
922
+ const status = report.finish.blockers.length > 0 ? 'BLOCKED' : 'READY';
923
+ console.log('\nSCALE Workspace Summary');
924
+ console.log(` Status: ${status}`);
925
+ console.log(` Topology: ${report.topology.topology}${report.topology.configured ? '' : ' (default)'}`);
926
+ console.log(` Root: ${rootStatus}`);
927
+ console.log(` Children: ${report.childRepositories.length} total, ${dirtyChildren.length} dirty, ${unpushedChildren.length} unpushed, ${noUpstreamChildren.length} no upstream`);
928
+ if (dirtyChildren.length > 0)
929
+ console.log(` Dirty child repositories: ${compactList(dirtyChildren)}`);
930
+ if (unpushedChildren.length > 0)
931
+ console.log(` Unpushed child repositories: ${compactList(unpushedChildren)}`);
932
+ if (report.finish.blockers.length > 0) {
933
+ console.log('\n Blockers:');
934
+ for (const blocker of report.finish.blockers.slice(0, 8))
935
+ console.log(` - ${blocker}`);
936
+ if (report.finish.blockers.length > 8)
937
+ console.log(` - ... ${report.finish.blockers.length - 8} more blocker(s)`);
938
+ }
939
+ if (report.finish.warnings.length > 0) {
940
+ console.log(`\n Warnings: ${report.finish.warnings.length} warning(s); run scale workspace finish --json for details`);
941
+ }
942
+ console.log('\n Next:');
943
+ const nextActions = report.finish.blockers.length > 0
944
+ ? report.finish.nextActions
945
+ : ['Proceed with scale ship <task-id> or cleanup when the branch policy is satisfied'];
946
+ for (const action of nextActions.slice(0, 3))
947
+ console.log(` - ${action}`);
948
+ console.log(' - Run scale workspace finish --json for full details');
949
+ }
896
950
  function printWorkspaceTopology(topology, written) {
897
951
  console.log('\nSCALE Workspace Topology');
898
952
  console.log(` Topology: ${topology.topology}${topology.configured ? '' : ' (default)'}`);
@@ -925,6 +979,7 @@ const workspaceStatus = defineCommand({
925
979
  meta: { name: 'status', description: 'Inspect root worktree and child repository lifecycle state' },
926
980
  args: {
927
981
  dir: { type: 'string', description: 'Repository or worktree directory; defaults to current project directory' },
982
+ summary: { type: 'boolean', default: false, description: 'Print concise human summary instead of the full repository listing' },
928
983
  json: { type: 'boolean', default: false },
929
984
  },
930
985
  async run({ args }) {
@@ -932,6 +987,9 @@ const workspaceStatus = defineCommand({
932
987
  if (args.json) {
933
988
  console.log(JSON.stringify(report, null, 2));
934
989
  }
990
+ else if (isTruthyFlag(args.summary)) {
991
+ printWorkspaceSummary(report);
992
+ }
935
993
  else {
936
994
  printWorkspaceLifecycle(report);
937
995
  }
@@ -972,6 +1030,7 @@ const workspaceFinish = defineCommand({
972
1030
  meta: { name: 'finish', description: 'Check whether a temporary worktree can be safely finished or cleaned up' },
973
1031
  args: {
974
1032
  dir: { type: 'string', description: 'Repository or worktree directory; defaults to current project directory' },
1033
+ summary: { type: 'boolean', default: false, description: 'Print concise human summary instead of the full repository listing' },
975
1034
  json: { type: 'boolean', default: false },
976
1035
  },
977
1036
  async run({ args }) {
@@ -985,6 +1044,9 @@ const workspaceFinish = defineCommand({
985
1044
  if (args.json) {
986
1045
  console.log(JSON.stringify(result, null, 2));
987
1046
  }
1047
+ else if (isTruthyFlag(args.summary)) {
1048
+ printWorkspaceSummary(report);
1049
+ }
988
1050
  else {
989
1051
  printWorkspaceLifecycle(report);
990
1052
  }
@@ -999,6 +1061,7 @@ const workspaceCleanup = defineCommand({
999
1061
  'dry-run': { type: 'boolean', default: false, description: 'Preview cleanup; this is the default unless --apply is set' },
1000
1062
  apply: { type: 'boolean', default: false, description: 'Actually run git worktree remove after safety checks' },
1001
1063
  confirm: { type: 'string', description: 'Required confirmation token for --apply, usually the worktree branch name' },
1064
+ summary: { type: 'boolean', default: false, description: 'Print concise human summary before the cleanup plan' },
1002
1065
  json: { type: 'boolean', default: false },
1003
1066
  },
1004
1067
  async run({ args }) {
@@ -1010,6 +1073,14 @@ const workspaceCleanup = defineCommand({
1010
1073
  if (args.json) {
1011
1074
  console.log(JSON.stringify(result, null, 2));
1012
1075
  }
1076
+ else if (isTruthyFlag(args.summary)) {
1077
+ printWorkspaceSummary(result.report);
1078
+ console.log('\n Cleanup:');
1079
+ console.log(` Mode: ${result.mode}`);
1080
+ console.log(` Can apply: ${result.canApply ? 'yes' : 'no'}`);
1081
+ console.log(` Applied: ${result.applied ? 'yes' : 'no'}`);
1082
+ console.log(` Confirmation token: ${result.confirmationToken ?? '(unavailable)'}`);
1083
+ }
1013
1084
  else {
1014
1085
  printWorkspaceCleanup(result);
1015
1086
  }
@@ -1559,14 +1630,48 @@ const assets = defineCommand({
1559
1630
  // ============================================================================
1560
1631
  // standards command - Engineering standards governance
1561
1632
  // ============================================================================
1633
+ function resolveChangedFilesArg(args) {
1634
+ const explicit = splitChangedFiles(args['changed-files']);
1635
+ if (explicit.length > 0)
1636
+ return explicit;
1637
+ if (!args.changed)
1638
+ return undefined;
1639
+ return readGitChangedFiles(args.dir ?? '.');
1640
+ }
1641
+ function splitChangedFiles(value) {
1642
+ if (!value)
1643
+ return [];
1644
+ return value
1645
+ .split(/[\n,]/)
1646
+ .map(item => item.trim())
1647
+ .filter(Boolean);
1648
+ }
1649
+ function readGitChangedFiles(projectDir) {
1650
+ const tracked = readGitPathList(projectDir, ['diff', '--name-only', '--diff-filter=ACMRTUXB', 'HEAD', '--']);
1651
+ const untracked = readGitPathList(projectDir, ['ls-files', '--others', '--exclude-standard']);
1652
+ return Array.from(new Set([...tracked, ...untracked]));
1653
+ }
1654
+ function readGitPathList(projectDir, args) {
1655
+ try {
1656
+ return execFileSync('git', ['-C', projectDir, ...args], { encoding: 'utf-8' })
1657
+ .split(/\r?\n/)
1658
+ .map(item => item.trim())
1659
+ .filter(Boolean);
1660
+ }
1661
+ catch {
1662
+ return [];
1663
+ }
1664
+ }
1562
1665
  const standardsScan = defineCommand({
1563
1666
  meta: { name: 'scan', description: 'Scan source files for engineering standard violations' },
1564
1667
  args: {
1565
1668
  dir: { type: 'string', default: '.', description: 'Project directory' },
1669
+ changed: { type: 'boolean', default: false, description: 'Scan changed Git files only' },
1670
+ 'changed-files': { type: 'string', description: 'Comma or newline separated file list to scan' },
1566
1671
  json: { type: 'boolean', default: false, description: 'Print JSON output' },
1567
1672
  },
1568
1673
  run({ args }) {
1569
- const report = scanEngineeringStandards({ projectDir: args.dir });
1674
+ const report = scanEngineeringStandards({ projectDir: args.dir, changedFiles: resolveChangedFilesArg(args) });
1570
1675
  if (args.json) {
1571
1676
  console.log(JSON.stringify(report, null, 2));
1572
1677
  return;
@@ -1588,10 +1693,12 @@ const standardsDoctor = defineCommand({
1588
1693
  meta: { name: 'doctor', description: 'Find blocking engineering standards problems' },
1589
1694
  args: {
1590
1695
  dir: { type: 'string', default: '.', description: 'Project directory' },
1696
+ changed: { type: 'boolean', default: false, description: 'Scan changed Git files only' },
1697
+ 'changed-files': { type: 'string', description: 'Comma or newline separated file list to scan' },
1591
1698
  json: { type: 'boolean', default: false, description: 'Print JSON output' },
1592
1699
  },
1593
1700
  run({ args }) {
1594
- const report = doctorEngineeringStandards({ projectDir: args.dir });
1701
+ const report = doctorEngineeringStandards({ projectDir: args.dir, changedFiles: resolveChangedFilesArg(args) });
1595
1702
  if (args.json) {
1596
1703
  console.log(JSON.stringify(report, null, 2));
1597
1704
  if (!report.ok)
@@ -1619,6 +1726,8 @@ const standardsSettle = defineCommand({
1619
1726
  dir: { type: 'string', default: '.', description: 'Project directory' },
1620
1727
  'task-id': { type: 'string', description: 'Task id for the settlement record' },
1621
1728
  'artifact-dir': { type: 'string', description: 'Task artifact directory where standards-impact.md should be updated' },
1729
+ changed: { type: 'boolean', default: false, description: 'Scan changed Git files only' },
1730
+ 'changed-files': { type: 'string', description: 'Comma or newline separated file list to scan' },
1622
1731
  json: { type: 'boolean', default: false, description: 'Print JSON output' },
1623
1732
  },
1624
1733
  run({ args }) {
@@ -1626,6 +1735,7 @@ const standardsSettle = defineCommand({
1626
1735
  projectDir: args.dir,
1627
1736
  taskId: args['task-id'],
1628
1737
  artifactsDir: args['artifact-dir'],
1738
+ changedFiles: resolveChangedFilesArg(args),
1629
1739
  });
1630
1740
  if (args.json) {
1631
1741
  console.log(JSON.stringify(report, null, 2));
@@ -1643,9 +1753,41 @@ const standardsSettle = defineCommand({
1643
1753
  process.exitCode = 1;
1644
1754
  },
1645
1755
  });
1756
+ const standardsBaseline = defineCommand({
1757
+ meta: { name: 'baseline', description: 'Generate a legacy standards baseline and classification report' },
1758
+ args: {
1759
+ dir: { type: 'string', default: '.', description: 'Project directory' },
1760
+ write: { type: 'boolean', default: false, description: 'Write .scale/engineering-standards-baseline.json' },
1761
+ 'task-id': { type: 'string', description: 'Task id for the legacy debt report' },
1762
+ 'artifact-dir': { type: 'string', description: 'Directory where standards-legacy-debt.md should be written' },
1763
+ reason: { type: 'string', default: 'legacy standards debt accepted for staged remediation', description: 'Reason recorded on generated baseline entries' },
1764
+ json: { type: 'boolean', default: false, description: 'Print JSON output' },
1765
+ },
1766
+ run({ args }) {
1767
+ const report = baselineEngineeringStandards({
1768
+ projectDir: args.dir,
1769
+ writeBaseline: args.write,
1770
+ taskId: args['task-id'],
1771
+ artifactsDir: args['artifact-dir'],
1772
+ reason: args.reason,
1773
+ });
1774
+ if (args.json) {
1775
+ console.log(JSON.stringify(report, null, 2));
1776
+ return;
1777
+ }
1778
+ console.log(`Standards baseline: ${report.wroteBaseline ? 'written' : 'dry-run'}`);
1779
+ console.log(` Baseline entries: ${report.baselineEntries.length}`);
1780
+ console.log(` Blocking findings: ${report.debt.blockingFindings}`);
1781
+ console.log(` Baseline path: ${report.baselinePath}`);
1782
+ if (report.legacyDebtPath)
1783
+ console.log(` Legacy debt report: ${report.legacyDebtPath}`);
1784
+ if (!report.wroteBaseline)
1785
+ console.log(' Re-run with --write to update .scale/engineering-standards-baseline.json.');
1786
+ },
1787
+ });
1646
1788
  const standards = defineCommand({
1647
1789
  meta: { name: 'standards', description: 'Engineering standards governance for logs, security, architecture, database, and code quality' },
1648
- subCommands: { scan: standardsScan, doctor: standardsDoctor, settle: standardsSettle },
1790
+ subCommands: { scan: standardsScan, doctor: standardsDoctor, settle: standardsSettle, baseline: standardsBaseline },
1649
1791
  });
1650
1792
  // ============================================================================
1651
1793
  // evolve command
@@ -2083,9 +2225,331 @@ const skillCheckCommand = defineCommand({
2083
2225
  process.exitCode = 1;
2084
2226
  },
2085
2227
  });
2228
+ const skillRepoCommand = defineCommand({
2229
+ meta: { name: 'repo', description: 'Show SCALE progressive skill repository guide' },
2230
+ args: {
2231
+ category: { type: 'string', description: 'Filter by category: ui/browser/desktop/testing/review/docs/agent-cli/role-library/discovery' },
2232
+ output: { type: 'string', alias: 'o', description: 'Write markdown guide to file' },
2233
+ json: { type: 'boolean', default: false },
2234
+ },
2235
+ run({ args }) {
2236
+ if (args.json) {
2237
+ console.log(JSON.stringify(listSkillRepositoryEntries(args.category ? { category: args.category } : undefined), null, 2));
2238
+ return;
2239
+ }
2240
+ const markdown = renderSkillRepositoryMarkdown();
2241
+ if (args.output) {
2242
+ const outputPath = resolve(PROJECT_DIR, args.output);
2243
+ ensureDir(resolve(outputPath, '..'));
2244
+ writeFileSync(outputPath, markdown, 'utf-8');
2245
+ console.log(`[OK] Skill 仓库指南已生成: ${outputPath}`);
2246
+ return;
2247
+ }
2248
+ console.log(markdown);
2249
+ },
2250
+ });
2251
+ const skillSafetyCommand = defineCommand({
2252
+ meta: { name: 'safety', description: 'Evaluate skill install command and source safety' },
2253
+ args: {
2254
+ source: { type: 'string', description: 'Skill source URL' },
2255
+ command: { type: 'string', description: 'Install command to review' },
2256
+ json: { type: 'boolean', default: false },
2257
+ },
2258
+ run({ args }) {
2259
+ const report = evaluateSkillInstallSafety({
2260
+ sourceUrl: args.source,
2261
+ installCommand: args.command,
2262
+ });
2263
+ if (args.json) {
2264
+ console.log(JSON.stringify(report, null, 2));
2265
+ return;
2266
+ }
2267
+ console.log('\nSCALE Skill Safety');
2268
+ console.log(` Risk: ${report.risk}`);
2269
+ console.log(` Blocked: ${report.blocked}`);
2270
+ for (const finding of report.findings) {
2271
+ console.log(` [${finding.severity.toUpperCase()}] ${finding.rule}: ${finding.message}`);
2272
+ }
2273
+ console.log(' Required checks:');
2274
+ for (const check of report.requiredChecks)
2275
+ console.log(` - ${check}`);
2276
+ if (report.blocked)
2277
+ process.exitCode = 1;
2278
+ },
2279
+ });
2280
+ const skillRecommendCommand = defineCommand({
2281
+ meta: { name: 'recommend', description: 'Recommend a composable skill workflow for a task' },
2282
+ args: {
2283
+ task: { type: 'string', required: true, description: 'Task description' },
2284
+ phase: { type: 'string', description: 'Workflow phase' },
2285
+ json: { type: 'boolean', default: false },
2286
+ },
2287
+ run({ args }) {
2288
+ const plan = recommendSkillWorkflow({
2289
+ description: args.task,
2290
+ phase: args.phase,
2291
+ });
2292
+ if (args.json) {
2293
+ console.log(JSON.stringify(plan, null, 2));
2294
+ return;
2295
+ }
2296
+ console.log('\nSCALE Skill Recommendation');
2297
+ console.log(` Primary: ${plan.primarySkills.join(', ') || 'none'}`);
2298
+ console.log(` Supporting: ${plan.supportingSkills.join(', ') || 'none'}`);
2299
+ console.log(` Safety required: ${plan.safetyRequired}`);
2300
+ console.log(` Evidence: ${plan.requiredEvidence.join(', ') || 'none'}`);
2301
+ for (const reason of plan.rationale)
2302
+ console.log(` - ${reason}`);
2303
+ },
2304
+ });
2086
2305
  const skill = defineCommand({
2087
2306
  meta: { name: 'skill', description: 'Skill discovery and management' },
2088
- subCommands: { scan: skillScan, doctor: skillDoctorCommand, plan: skillPlanCommand, check: skillCheckCommand },
2307
+ subCommands: {
2308
+ scan: skillScan,
2309
+ doctor: skillDoctorCommand,
2310
+ plan: skillPlanCommand,
2311
+ check: skillCheckCommand,
2312
+ repo: skillRepoCommand,
2313
+ safety: skillSafetyCommand,
2314
+ recommend: skillRecommendCommand,
2315
+ },
2316
+ });
2317
+ // ============================================================================
2318
+ // tool command - Skills/MCP/CLI orchestration governance
2319
+ // ============================================================================
2320
+ function normalizeToolMode(value) {
2321
+ const normalized = String(value ?? 'evidence-required');
2322
+ if (normalized === 'off' || normalized === 'advisory' || normalized === 'evidence-required' || normalized === 'block')
2323
+ return normalized;
2324
+ return 'evidence-required';
2325
+ }
2326
+ function parseToolIds(value) {
2327
+ const raw = String(value ?? '').trim();
2328
+ if (!raw)
2329
+ return undefined;
2330
+ return raw.split(',').map(item => item.trim()).filter(Boolean);
2331
+ }
2332
+ function parseCommaList(value) {
2333
+ return parseToolIds(value) ?? [];
2334
+ }
2335
+ function createToolExecutionPlanFromArgs(args) {
2336
+ const projectDir = resolve(String(args.dir ?? PROJECT_DIR));
2337
+ const level = normalizeTaskArtifactLevel(args.level ?? 'M');
2338
+ const skillPolicy = loadSkillRoutingPolicy(projectDir, SCALE_DIR);
2339
+ const skillPlan = createSkillPlan({
2340
+ taskId: String(args['task-id'] ?? `TOOL-${Date.now()}`),
2341
+ taskName: String(args.task ?? 'Tool orchestration task'),
2342
+ description: String(args.task ?? ''),
2343
+ level,
2344
+ files: parseCommaList(args.files),
2345
+ services: parseCommaList(args.services),
2346
+ policy: skillPolicy,
2347
+ });
2348
+ const toolPolicy = loadToolPolicy(projectDir, SCALE_DIR);
2349
+ const toolIds = uniqueStrings([
2350
+ ...skillPlan.requiredSkills,
2351
+ ...skillPlan.recommendedSkills,
2352
+ ...Object.keys(toolPolicy.tools).filter(toolId => {
2353
+ const config = toolPolicy.tools[toolId];
2354
+ const domains = new Set(skillPlan.intents.map(intent => intent.domain));
2355
+ return config.enabled && (config.requiredFor.some(domain => domains.has(domain)) ||
2356
+ (config.recommendedFor ?? []).some(domain => domains.has(domain)));
2357
+ }),
2358
+ ]);
2359
+ const capabilityReport = inspectToolCapabilities({
2360
+ projectDir,
2361
+ toolIds,
2362
+ });
2363
+ const orchestrator = new ToolOrchestrator({
2364
+ projectDir,
2365
+ policy: toolPolicy,
2366
+ capabilityReport,
2367
+ evidenceStore: new ToolEvidenceStore({ projectDir, scaleDir: SCALE_DIR }),
2368
+ });
2369
+ return {
2370
+ projectDir,
2371
+ skillPlan,
2372
+ orchestrator,
2373
+ plan: orchestrator.plan({ skillPlan }),
2374
+ capabilityReport,
2375
+ };
2376
+ }
2377
+ function uniqueStrings(items) {
2378
+ return [...new Set(items)];
2379
+ }
2380
+ const toolPolicyCommand = defineCommand({
2381
+ meta: { name: 'policy', description: 'Show resolved tool orchestration policy' },
2382
+ args: {
2383
+ dir: { type: 'string', default: '.', description: 'Project directory' },
2384
+ mode: { type: 'string', description: 'Render a starter policy mode instead of reading .scale/tools.json' },
2385
+ json: { type: 'boolean', default: false },
2386
+ },
2387
+ run({ args }) {
2388
+ const policy = args.mode
2389
+ ? JSON.parse(toolPolicyTemplate(normalizeToolMode(args.mode)))
2390
+ : loadToolPolicy(args.dir, SCALE_DIR);
2391
+ if (args.json) {
2392
+ console.log(JSON.stringify(policy, null, 2));
2393
+ return;
2394
+ }
2395
+ console.log('\nSCALE Tool Policy');
2396
+ console.log(` Mode: ${policy.mode}`);
2397
+ console.log(` Tools: ${Object.keys(policy.tools).length}`);
2398
+ for (const [id, config] of Object.entries(policy.tools)) {
2399
+ const state = config.enabled ? '[ON]' : '[OFF]';
2400
+ console.log(` ${state} ${id}: requiredFor=${config.requiredFor.join(',') || 'none'}`);
2401
+ }
2402
+ },
2403
+ });
2404
+ const toolDoctorCommand = defineCommand({
2405
+ meta: { name: 'doctor', description: 'Check skill, MCP, and CLI tool availability' },
2406
+ args: {
2407
+ dir: { type: 'string', default: '.', description: 'Project directory' },
2408
+ tools: { type: 'string', description: 'Comma-separated tool ids to check' },
2409
+ json: { type: 'boolean', default: false },
2410
+ },
2411
+ run({ args }) {
2412
+ const report = inspectToolCapabilities({
2413
+ projectDir: args.dir,
2414
+ toolIds: parseToolIds(args.tools),
2415
+ });
2416
+ if (args.json) {
2417
+ console.log(JSON.stringify(report, null, 2));
2418
+ }
2419
+ else {
2420
+ console.log('\nSCALE Tool Doctor');
2421
+ console.log(` Installed: ${report.summary.installed}/${report.summary.total}`);
2422
+ for (const entry of report.tools) {
2423
+ console.log(` ${entry.installed ? '[OK]' : '[MISSING]'} ${entry.id}`);
2424
+ if (entry.detectedPath)
2425
+ console.log(` path: ${entry.detectedPath}`);
2426
+ if (entry.version)
2427
+ console.log(` version: ${entry.version}`);
2428
+ if (entry.missingReason)
2429
+ console.log(` reason: ${entry.missingReason}`);
2430
+ }
2431
+ }
2432
+ if (!report.ok)
2433
+ process.exitCode = 1;
2434
+ },
2435
+ });
2436
+ const toolPlanCommand = defineCommand({
2437
+ meta: { name: 'plan', description: 'Create a tool execution plan from task intent' },
2438
+ args: {
2439
+ dir: { type: 'string', default: '.', description: 'Project directory' },
2440
+ 'task-id': { type: 'string', required: true, description: 'Task id for evidence linkage' },
2441
+ task: { type: 'string', required: true, description: 'Task description' },
2442
+ level: { type: 'string', default: 'M', description: 'Task level: S, M, L, or CRITICAL' },
2443
+ files: { type: 'string', description: 'Comma-separated changed or target files' },
2444
+ services: { type: 'string', description: 'Comma-separated affected services' },
2445
+ json: { type: 'boolean', default: false },
2446
+ },
2447
+ run({ args }) {
2448
+ const result = createToolExecutionPlanFromArgs(args);
2449
+ if (args.json) {
2450
+ console.log(JSON.stringify(result.plan, null, 2));
2451
+ return;
2452
+ }
2453
+ console.log('\nSCALE Tool Plan');
2454
+ console.log(` Task: ${result.plan.taskId}`);
2455
+ console.log(` Mode: ${result.plan.mode}`);
2456
+ console.log(` Steps: ${result.plan.steps.length}`);
2457
+ for (const step of result.plan.steps) {
2458
+ console.log(` ${step.status === 'ready' ? '[READY]' : '[MISSING]'} ${step.toolId} (${step.adapter}) required=${step.required}`);
2459
+ }
2460
+ for (const blocker of result.plan.blockers)
2461
+ console.log(` [BLOCKER] ${blocker}`);
2462
+ for (const warning of result.plan.warnings)
2463
+ console.log(` [WARN] ${warning}`);
2464
+ },
2465
+ });
2466
+ const toolRunCommand = defineCommand({
2467
+ meta: { name: 'run', description: 'Run or dry-run a tool execution plan and write tool evidence' },
2468
+ args: {
2469
+ dir: { type: 'string', default: '.', description: 'Project directory' },
2470
+ 'task-id': { type: 'string', required: true, description: 'Task id for evidence linkage' },
2471
+ task: { type: 'string', required: true, description: 'Task description' },
2472
+ level: { type: 'string', default: 'M', description: 'Task level: S, M, L, or CRITICAL' },
2473
+ files: { type: 'string', description: 'Comma-separated changed or target files' },
2474
+ services: { type: 'string', description: 'Comma-separated affected services' },
2475
+ 'dry-run': { type: 'boolean', default: false, description: 'Plan and record skipped evidence without executing tools' },
2476
+ json: { type: 'boolean', default: false },
2477
+ },
2478
+ async run({ args }) {
2479
+ const result = createToolExecutionPlanFromArgs(args);
2480
+ const report = await result.orchestrator.run(result.plan, {
2481
+ dryRun: isTruthyFlag(args['dry-run']),
2482
+ });
2483
+ if (args.json) {
2484
+ console.log(JSON.stringify(report, null, 2));
2485
+ }
2486
+ else {
2487
+ console.log('\nSCALE Tool Run');
2488
+ console.log(` Task: ${report.taskId}`);
2489
+ console.log(` Dry-run: ${report.dryRun}`);
2490
+ console.log(` Evidence: ${report.evidence.length}`);
2491
+ for (const record of report.evidence) {
2492
+ console.log(` [${record.status.toUpperCase()}] ${record.tool} -> ${record.id}`);
2493
+ }
2494
+ for (const blocker of report.blockers)
2495
+ console.log(` [BLOCKER] ${blocker}`);
2496
+ for (const warning of report.warnings)
2497
+ console.log(` [WARN] ${warning}`);
2498
+ }
2499
+ if (!report.ok)
2500
+ process.exitCode = 1;
2501
+ },
2502
+ });
2503
+ const toolEvidenceCommand = defineCommand({
2504
+ meta: { name: 'evidence', description: 'Check required tool execution evidence for a task' },
2505
+ args: {
2506
+ dir: { type: 'string', default: '.', description: 'Project directory' },
2507
+ 'task-id': { type: 'string', required: true, description: 'Task id for evidence linkage' },
2508
+ task: { type: 'string', required: true, description: 'Task description' },
2509
+ level: { type: 'string', default: 'M', description: 'Task level: S, M, L, or CRITICAL' },
2510
+ files: { type: 'string', description: 'Comma-separated changed or target files' },
2511
+ services: { type: 'string', description: 'Comma-separated affected services' },
2512
+ mode: { type: 'string', description: 'Override tool gate mode: off, advisory, evidence-required, or block' },
2513
+ 'allow-skipped': { type: 'boolean', default: false, description: 'Allow skipped/manual fallback evidence to satisfy required tools' },
2514
+ json: { type: 'boolean', default: false },
2515
+ },
2516
+ run({ args }) {
2517
+ const result = createToolExecutionPlanFromArgs(args);
2518
+ const gate = evaluateToolEvidenceGate({
2519
+ projectDir: result.projectDir,
2520
+ level: normalizeTaskArtifactLevel(args.level ?? 'M'),
2521
+ plan: result.plan,
2522
+ evidenceStore: new ToolEvidenceStore({ projectDir: result.projectDir, scaleDir: SCALE_DIR }),
2523
+ mode: args.mode ? normalizeToolMode(args.mode) : result.plan.mode,
2524
+ allowSkipped: isTruthyFlag(args['allow-skipped']),
2525
+ });
2526
+ if (args.json) {
2527
+ console.log(JSON.stringify(gate, null, 2));
2528
+ }
2529
+ else {
2530
+ console.log('\nSCALE Tool Evidence Gate');
2531
+ console.log(` Task: ${gate.taskId ?? args['task-id']}`);
2532
+ console.log(` Mode: ${gate.mode}`);
2533
+ console.log(` Complete: ${gate.complete}`);
2534
+ console.log(` Required tools: ${gate.requiredTools.join(', ') || 'none'}`);
2535
+ for (const item of gate.missing)
2536
+ console.log(` [MISSING] ${item.toolId}: ${item.reason}`);
2537
+ for (const item of gate.failed)
2538
+ console.log(` [FAILED] ${item.toolId}: ${item.reason}`);
2539
+ for (const item of gate.skipped)
2540
+ console.log(` [SKIPPED] ${item.toolId}: ${item.reason}`);
2541
+ for (const item of gate.passed)
2542
+ console.log(` [PASS] ${item.toolId}: ${item.evidenceId ?? 'evidence'}`);
2543
+ for (const warning of gate.warnings)
2544
+ console.log(` [WARN] ${warning}`);
2545
+ }
2546
+ if (gate.blocked)
2547
+ process.exitCode = 1;
2548
+ },
2549
+ });
2550
+ const tool = defineCommand({
2551
+ meta: { name: 'tool', description: 'Skills, MCP, browser, desktop, and external CLI governance' },
2552
+ subCommands: { policy: toolPolicyCommand, doctor: toolDoctorCommand, plan: toolPlanCommand, run: toolRunCommand, evidence: toolEvidenceCommand },
2089
2553
  });
2090
2554
  // ============================================================================
2091
2555
  // agent commands — Multi-Agent 协作系统 (Phase 9)
@@ -2142,9 +2606,32 @@ const agentProfiles = defineCommand({
2142
2606
  }
2143
2607
  },
2144
2608
  });
2609
+ const agentLeaders = defineCommand({
2610
+ meta: { name: 'leaders', description: 'List SCALE leader presets such as CEO and CTO' },
2611
+ args: {
2612
+ output: { type: 'string', alias: 'o', description: 'Write markdown guide to file' },
2613
+ json: { type: 'boolean', default: false },
2614
+ },
2615
+ async run({ args }) {
2616
+ const presets = listLeadershipPresets();
2617
+ if (args.json) {
2618
+ console.log(JSON.stringify(presets, null, 2));
2619
+ return;
2620
+ }
2621
+ const markdown = renderLeadershipPresetsMarkdown();
2622
+ if (args.output) {
2623
+ const outputPath = resolve(PROJECT_DIR, args.output);
2624
+ ensureDir(resolve(outputPath, '..'));
2625
+ writeFileSync(outputPath, markdown, 'utf-8');
2626
+ console.log(`[OK] 领导者角色指南已生成: ${outputPath}`);
2627
+ return;
2628
+ }
2629
+ console.log(markdown);
2630
+ },
2631
+ });
2145
2632
  const agent = defineCommand({
2146
2633
  meta: { name: 'agent', description: 'Multi-Agent system management' },
2147
- subCommands: { spawn: agentSpawn, list: agentList, profiles: agentProfiles },
2634
+ subCommands: { spawn: agentSpawn, list: agentList, profiles: agentProfiles, leaders: agentLeaders },
2148
2635
  });
2149
2636
  // ============================================================================
2150
2637
  // team commands — 团队协作 (Phase 9)
@@ -2250,6 +2737,7 @@ const main = defineCommand({
2250
2737
  status,
2251
2738
  workflow,
2252
2739
  evidence,
2740
+ tool,
2253
2741
  skill,
2254
2742
  skills: skill,
2255
2743
  agent,