@hongmaple0820/scale-engine 0.33.0 → 0.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +86 -376
- package/README.md +95 -540
- package/dist/api/cli.js +293 -18
- package/dist/api/cli.js.map +1 -1
- package/dist/api/doctor.d.ts +38 -3
- package/dist/api/doctor.js +269 -44
- package/dist/api/doctor.js.map +1 -1
- package/dist/api/mcp.js +2 -2
- package/dist/api/mcp.js.map +1 -1
- package/dist/api/quickstart.d.ts +34 -4
- package/dist/api/quickstart.js +90 -73
- package/dist/api/quickstart.js.map +1 -1
- package/dist/bootstrap/DependencyBootstrap.d.ts +110 -0
- package/dist/bootstrap/DependencyBootstrap.js +829 -0
- package/dist/bootstrap/DependencyBootstrap.js.map +1 -0
- package/dist/bootstrap/DependencyBootstrapRenderer.d.ts +3 -0
- package/dist/bootstrap/DependencyBootstrapRenderer.js +140 -0
- package/dist/bootstrap/DependencyBootstrapRenderer.js.map +1 -0
- package/dist/capabilities/InstalledSkillsIntegration.js +14 -6
- package/dist/capabilities/InstalledSkillsIntegration.js.map +1 -1
- package/dist/cli/gateStatusCommands.d.ts +1 -0
- package/dist/cli/gateStatusCommands.js +52 -0
- package/dist/cli/gateStatusCommands.js.map +1 -0
- package/dist/cli/phaseCommands.js +15 -3
- package/dist/cli/phaseCommands.js.map +1 -1
- package/dist/cli/promptCommands.d.ts +1 -0
- package/dist/cli/promptCommands.js +57 -0
- package/dist/cli/promptCommands.js.map +1 -0
- package/dist/cli/scoreCommands.d.ts +1 -0
- package/dist/cli/scoreCommands.js +112 -0
- package/dist/cli/scoreCommands.js.map +1 -0
- package/dist/codegraph/CodeIntelligence.d.ts +12 -0
- package/dist/codegraph/CodeIntelligence.js +251 -30
- package/dist/codegraph/CodeIntelligence.js.map +1 -1
- package/dist/config/profiles.d.ts +12 -0
- package/dist/config/profiles.js +39 -4
- package/dist/config/profiles.js.map +1 -1
- package/dist/context/SessionStartSequence.js +13 -4
- package/dist/context/SessionStartSequence.js.map +1 -1
- package/dist/core/ExternalCommand.d.ts +9 -0
- package/dist/core/ExternalCommand.js +70 -0
- package/dist/core/ExternalCommand.js.map +1 -0
- package/dist/env/EnvironmentDoctor.d.ts +66 -0
- package/dist/env/EnvironmentDoctor.js +365 -0
- package/dist/env/EnvironmentDoctor.js.map +1 -0
- package/dist/i18n/Language.d.ts +9 -0
- package/dist/i18n/Language.js +38 -0
- package/dist/i18n/Language.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/knowledge/CerebrumManager.d.ts +2 -2
- package/dist/knowledge/CerebrumManager.js.map +1 -1
- package/dist/knowledge/GraphifyKnowledgeBase.d.ts +38 -0
- package/dist/knowledge/GraphifyKnowledgeBase.js +409 -0
- package/dist/knowledge/GraphifyKnowledgeBase.js.map +1 -0
- package/dist/memory/MemoryFabric.js +1 -0
- package/dist/memory/MemoryFabric.js.map +1 -1
- package/dist/memory/MemoryIntelligence.d.ts +42 -0
- package/dist/memory/MemoryIntelligence.js +215 -0
- package/dist/memory/MemoryIntelligence.js.map +1 -0
- package/dist/memory/MemoryProviders.d.ts +22 -0
- package/dist/memory/MemoryProviders.js +171 -5
- package/dist/memory/MemoryProviders.js.map +1 -1
- package/dist/memory/index.d.ts +1 -0
- package/dist/memory/index.js +1 -0
- package/dist/memory/index.js.map +1 -1
- package/dist/prompts/PromptOptimizer.d.ts +42 -0
- package/dist/prompts/PromptOptimizer.js +309 -0
- package/dist/prompts/PromptOptimizer.js.map +1 -0
- package/dist/runtime/AiOsRuntime.d.ts +2 -0
- package/dist/runtime/AiOsRuntime.js +2 -0
- package/dist/runtime/AiOsRuntime.js.map +1 -1
- package/dist/runtime/ExecutionLedger.d.ts +46 -0
- package/dist/runtime/ExecutionLedger.js +71 -0
- package/dist/runtime/ExecutionLedger.js.map +1 -0
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/index.js +1 -0
- package/dist/runtime/index.js.map +1 -1
- package/dist/setup/SetupWizard.d.ts +42 -0
- package/dist/setup/SetupWizard.js +156 -0
- package/dist/setup/SetupWizard.js.map +1 -0
- package/dist/skills/SkillRepository.js +7 -7
- package/dist/skills/SkillRepository.js.map +1 -1
- package/dist/skills/routing/SkillPolicy.js +2 -2
- package/dist/skills/routing/SkillPolicy.js.map +1 -1
- package/dist/testing/DiffTestSelector.js +1 -1
- package/dist/testing/DiffTestSelector.js.map +1 -1
- package/dist/tools/RtkRuntime.d.ts +9 -0
- package/dist/tools/RtkRuntime.js +43 -0
- package/dist/tools/RtkRuntime.js.map +1 -0
- package/dist/tools/ToolCapabilityRegistry.d.ts +5 -0
- package/dist/tools/ToolCapabilityRegistry.js +75 -13
- package/dist/tools/ToolCapabilityRegistry.js.map +1 -1
- package/dist/tools/ToolOrchestrator.js +6 -4
- package/dist/tools/ToolOrchestrator.js.map +1 -1
- package/dist/tools/ToolPolicy.js +16 -1
- package/dist/tools/ToolPolicy.js.map +1 -1
- package/dist/workflow/AdaptiveWorkflowRouter.d.ts +1 -0
- package/dist/workflow/AdaptiveWorkflowRouter.js +3 -0
- package/dist/workflow/AdaptiveWorkflowRouter.js.map +1 -1
- package/dist/workflow/CommitDiscipline.d.ts +68 -0
- package/dist/workflow/CommitDiscipline.js +328 -0
- package/dist/workflow/CommitDiscipline.js.map +1 -0
- package/dist/workflow/CrossRepoOrchestrator.d.ts +92 -0
- package/dist/workflow/CrossRepoOrchestrator.js +408 -0
- package/dist/workflow/CrossRepoOrchestrator.js.map +1 -0
- package/dist/workflow/GateCatalog.d.ts +61 -0
- package/dist/workflow/GateCatalog.js +212 -0
- package/dist/workflow/GateCatalog.js.map +1 -0
- package/dist/workflow/GovernanceRoi.d.ts +52 -0
- package/dist/workflow/GovernanceRoi.js +204 -0
- package/dist/workflow/GovernanceRoi.js.map +1 -0
- package/dist/workflow/GovernanceTemplatePacks.js +19 -4
- package/dist/workflow/GovernanceTemplatePacks.js.map +1 -1
- package/dist/workflow/GovernanceTemplates.js +2 -2
- package/dist/workflow/McpGovernance.d.ts +63 -0
- package/dist/workflow/McpGovernance.js +198 -0
- package/dist/workflow/McpGovernance.js.map +1 -0
- package/dist/workflow/SessionCoordinator.d.ts +103 -0
- package/dist/workflow/SessionCoordinator.js +401 -0
- package/dist/workflow/SessionCoordinator.js.map +1 -0
- package/dist/workflow/SessionPreamble.js +7 -2
- package/dist/workflow/SessionPreamble.js.map +1 -1
- package/dist/workflow/TaskDependencyGraph.d.ts +73 -0
- package/dist/workflow/TaskDependencyGraph.js +245 -0
- package/dist/workflow/TaskDependencyGraph.js.map +1 -0
- package/dist/workflow/TaskScoreEngine.d.ts +42 -0
- package/dist/workflow/TaskScoreEngine.js +181 -0
- package/dist/workflow/TaskScoreEngine.js.map +1 -0
- package/dist/workflow/WorkflowTemplates.d.ts +38 -0
- package/dist/workflow/WorkflowTemplates.js +371 -0
- package/dist/workflow/WorkflowTemplates.js.map +1 -0
- package/dist/workflow/WorkspacePolicy.d.ts +46 -0
- package/dist/workflow/WorkspacePolicy.js +141 -0
- package/dist/workflow/WorkspacePolicy.js.map +1 -0
- package/dist/workflow/WorkspaceTopology.d.ts +3 -0
- package/dist/workflow/WorkspaceTopology.js +40 -3
- package/dist/workflow/WorkspaceTopology.js.map +1 -1
- package/dist/workflow/gates/GateSystem.js +14 -11
- package/dist/workflow/gates/GateSystem.js.map +1 -1
- package/dist/workflow/index.d.ts +9 -0
- package/dist/workflow/index.js +9 -0
- package/dist/workflow/index.js.map +1 -1
- package/docs/CODE_INTELLIGENCE.md +48 -6
- package/docs/EXTERNAL_REFERENCES.md +5 -2
- package/docs/MEMORY_FABRIC.md +28 -3
- package/docs/SKILL-REPOSITORY.md +3 -3
- package/docs/THIRD_PARTY_SKILLS.md +50 -1
- package/docs/guides/GETTING_STARTED.md +24 -0
- package/docs/start/quickstart.md +107 -69
- package/docs/workflow/GATES_AND_SCORE.md +56 -0
- package/docs/workflow/PROMPT_OPTIMIZATION.md +44 -0
- package/docs/workflow/README.md +7 -0
- package/docs/workflow/node-library.md +3 -3
- package/docs/workflow/templates/skill-plan.md +1 -1
- package/package.json +13 -5
- package/scripts/workflow/provider-rehearsal.mjs +425 -0
- package/scripts/workflow/setup-smoke.mjs +299 -0
|
@@ -0,0 +1,829 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join, resolve } from 'node:path';
|
|
4
|
+
import { execa, execaSync } from 'execa';
|
|
5
|
+
import { inspectCodeIntelligence, writeCodeIntelligenceConfig } from '../codegraph/CodeIntelligence.js';
|
|
6
|
+
import { externalCommandExists } from '../core/ExternalCommand.js';
|
|
7
|
+
import { inspectMemoryProviders, useMemoryProvider, writeMemoryProvidersConfig } from '../memory/MemoryProviders.js';
|
|
8
|
+
import { wrapShellCommandWithRtk } from '../tools/RtkRuntime.js';
|
|
9
|
+
import { inspectToolCapabilities } from '../tools/ToolCapabilityRegistry.js';
|
|
10
|
+
const UI_SKILL_INSTALLS = {
|
|
11
|
+
'awesome-design-md': (ctx) => {
|
|
12
|
+
const installDir = quotePath(join(ctx.homeDir, '.scale', 'vendor', 'awesome-design-md'));
|
|
13
|
+
return `npx degit VoltAgent/awesome-design-md ${installDir}`;
|
|
14
|
+
},
|
|
15
|
+
'ui-ux-pro-max': 'npx uipro-cli init --ai codex',
|
|
16
|
+
'frontend-design': 'npx skills add anthropics/skills --skill frontend-design',
|
|
17
|
+
};
|
|
18
|
+
const RTK_INSTALL = 'cargo install --git https://github.com/rtk-ai/rtk';
|
|
19
|
+
const CODEGRAPH_INSTALL = 'npm install -g @colbymchenry/codegraph';
|
|
20
|
+
const GRAPHIFY_UV_INSTALL = 'uv tool install graphify && graphify install --platform codex';
|
|
21
|
+
const GRAPHIFY_PIPX_INSTALL = 'pipx install graphify && graphify install --platform codex';
|
|
22
|
+
const GRAPHIFY_PIP_INSTALL = 'pip install graphify && graphify install --platform codex';
|
|
23
|
+
const GRAPHIFY_PIP3_INSTALL = 'pip3 install graphify && graphify install --platform codex';
|
|
24
|
+
const GRAPHIFY_PYTHON_INSTALL = 'python -m pip install graphify && graphify install --platform codex';
|
|
25
|
+
const GRAPHIFY_PYTHON3_INSTALL = 'python3 -m pip install graphify && graphify install --platform codex';
|
|
26
|
+
const GBRAIN_INSTALL = 'bun install -g github:garrytan/gbrain && gbrain init --pglite';
|
|
27
|
+
const GBRAIN_SOURCE = 'https://github.com/garrytan/gbrain';
|
|
28
|
+
const DEPENDENCY_BOOTSTRAP_DEFINITIONS = [
|
|
29
|
+
{
|
|
30
|
+
id: 'awesome-design-md',
|
|
31
|
+
name: 'Awesome Design.md',
|
|
32
|
+
kind: 'skill',
|
|
33
|
+
packs: ['ui'],
|
|
34
|
+
source: 'https://github.com/VoltAgent/awesome-design-md',
|
|
35
|
+
detectSkillId: 'awesome-design-md',
|
|
36
|
+
detectPaths: ctx => [join(ctx.homeDir, '.scale', 'vendor', 'awesome-design-md', 'README.md')],
|
|
37
|
+
prerequisites: ['npx'],
|
|
38
|
+
manualReason: 'Requires npm/npx to sync the upstream DESIGN.md catalog that drives brand and visual-language decisions.',
|
|
39
|
+
installCommand: ctx => ctx.commandExists('npx') ? UI_SKILL_INSTALLS['awesome-design-md'](ctx) : null,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
id: 'ui-ux-pro-max',
|
|
43
|
+
name: 'UI/UX Pro Max',
|
|
44
|
+
kind: 'skill',
|
|
45
|
+
packs: ['ui'],
|
|
46
|
+
source: 'https://github.com/nextlevelbuilder/ui-ux-pro-max-skill',
|
|
47
|
+
detectSkillId: 'ui-ux-pro-max',
|
|
48
|
+
prerequisites: ['npx'],
|
|
49
|
+
manualReason: 'Requires npm/npx so the official uipro-cli installer can configure the UX, state, accessibility, and responsive-review skill.',
|
|
50
|
+
installCommand: ctx => ctx.commandExists('npx') ? UI_SKILL_INSTALLS['ui-ux-pro-max'] : null,
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: 'frontend-design',
|
|
54
|
+
name: 'Frontend Design',
|
|
55
|
+
kind: 'skill',
|
|
56
|
+
packs: [],
|
|
57
|
+
source: 'https://github.com/anthropics/skills/tree/main/skills/frontend-design',
|
|
58
|
+
detectSkillId: 'frontend-design',
|
|
59
|
+
prerequisites: ['npx'],
|
|
60
|
+
manualReason: 'Optional implementation companion only; awesome-design-md and ui-ux-pro-max are the default UI stack.',
|
|
61
|
+
installCommand: ctx => ctx.commandExists('npx') ? UI_SKILL_INSTALLS['frontend-design'] : null,
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: 'rtk',
|
|
65
|
+
name: 'RTK',
|
|
66
|
+
kind: 'cli',
|
|
67
|
+
packs: ['external-cli'],
|
|
68
|
+
source: 'https://github.com/rtk-ai/rtk',
|
|
69
|
+
detectCommand: 'rtk',
|
|
70
|
+
prerequisites: ['cargo'],
|
|
71
|
+
manualReason: 'RTK currently installs from the official Rust toolchain path and needs Cargo on PATH.',
|
|
72
|
+
installCommand: ctx => ctx.commandExists('cargo') ? RTK_INSTALL : null,
|
|
73
|
+
healthCheck: checkRtkHealth,
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
id: 'gbrain',
|
|
77
|
+
name: 'GBrain',
|
|
78
|
+
kind: 'cli',
|
|
79
|
+
packs: ['memory'],
|
|
80
|
+
source: GBRAIN_SOURCE,
|
|
81
|
+
detectCommand: 'gbrain',
|
|
82
|
+
prerequisites: ['bun'],
|
|
83
|
+
manualReason: 'The official standalone GBrain install needs Bun and then a configured brain (`gbrain init --pglite`) before cross-session recall is usable.',
|
|
84
|
+
installCommand: ctx => buildGbrainInstallCommand(ctx),
|
|
85
|
+
healthCheck: checkGbrainHealth,
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
id: 'graphify',
|
|
89
|
+
name: 'Graphify',
|
|
90
|
+
kind: 'cli',
|
|
91
|
+
packs: ['knowledge'],
|
|
92
|
+
source: 'https://github.com/safishamsi/graphify',
|
|
93
|
+
detectCommand: 'graphify',
|
|
94
|
+
prerequisites: ['uv|pipx|pip|pip3|python|python3'],
|
|
95
|
+
manualReason: 'Graphify requires Python 3.10+ and a supported installer; uv tool install is preferred to avoid polluting project virtualenvs.',
|
|
96
|
+
installCommand: ctx => buildGraphifyInstallCommand(ctx),
|
|
97
|
+
healthCheck: checkGraphifyHealth,
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
id: 'codegraph',
|
|
101
|
+
name: 'CodeGraph',
|
|
102
|
+
kind: 'cli',
|
|
103
|
+
packs: ['knowledge'],
|
|
104
|
+
source: 'https://github.com/colbymchenry/codegraph',
|
|
105
|
+
detectCommand: 'codegraph',
|
|
106
|
+
prerequisites: ['npm'],
|
|
107
|
+
manualReason: 'CodeGraph installs through npm and needs Node.js/npm available on PATH.',
|
|
108
|
+
installCommand: ctx => ctx.commandExists('npm') ? CODEGRAPH_INSTALL : null,
|
|
109
|
+
healthCheck: checkCodeGraphHealth,
|
|
110
|
+
},
|
|
111
|
+
];
|
|
112
|
+
export async function bootstrapDependencies(options = {}) {
|
|
113
|
+
const projectDir = resolve(options.projectDir ?? process.cwd());
|
|
114
|
+
const scaleDir = resolveScaleRoot(projectDir, options.scaleDir);
|
|
115
|
+
const homeDir = homedir();
|
|
116
|
+
const packIds = normalizePackIds(options.packIds);
|
|
117
|
+
const includeIds = unique((options.includeIds ?? []).map(value => value.trim()).filter(Boolean));
|
|
118
|
+
const definitions = selectDefinitions(packIds, includeIds);
|
|
119
|
+
const context = {
|
|
120
|
+
projectDir,
|
|
121
|
+
homeDir,
|
|
122
|
+
commandExists: externalCommandExists,
|
|
123
|
+
};
|
|
124
|
+
const reports = definitions.map(definition => inspectDefinition(definition, context));
|
|
125
|
+
const runtimeChecks = buildRuntimeChecks(reports);
|
|
126
|
+
if (options.apply) {
|
|
127
|
+
for (const item of reports.filter(entry => entry.status === 'ready')) {
|
|
128
|
+
if (!item.installCommand)
|
|
129
|
+
continue;
|
|
130
|
+
const result = await runInstallCommand(item.installCommand);
|
|
131
|
+
item.output = result.output;
|
|
132
|
+
item.error = result.error;
|
|
133
|
+
const definition = definitions.find(entry => entry.id === item.id);
|
|
134
|
+
const rechecked = definition ? inspectDefinition(definition, context) : item;
|
|
135
|
+
item.installed = rechecked.installed;
|
|
136
|
+
item.detectedBy = rechecked.detectedBy;
|
|
137
|
+
item.health = rechecked.health;
|
|
138
|
+
item.status = result.ok && rechecked.installed
|
|
139
|
+
? rechecked.status === 'installed' ? 'installed-now' : rechecked.status
|
|
140
|
+
: 'failed';
|
|
141
|
+
if (!result.ok && !item.error)
|
|
142
|
+
item.error = `Installation command failed: ${item.installCommand}`;
|
|
143
|
+
if (!result.ok)
|
|
144
|
+
item.installSupported = true;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const postActions = options.apply ? applyDependencyBootstrapPostActions(projectDir, scaleDir, reports) : [];
|
|
148
|
+
const postChecks = options.apply
|
|
149
|
+
? runDependencyBootstrapPostChecks({ projectDir, scaleDir, packIds, items: reports, homeDir })
|
|
150
|
+
: [];
|
|
151
|
+
return buildReport(projectDir, scaleDir, packIds, includeIds, Boolean(options.apply), runtimeChecks, reports, postActions, postChecks);
|
|
152
|
+
}
|
|
153
|
+
function buildReport(projectDir, scaleDir, packIds, includeIds, apply, runtimeChecks, items, postActions, postChecks) {
|
|
154
|
+
const summary = {
|
|
155
|
+
total: items.length,
|
|
156
|
+
installed: items.filter(item => item.status === 'installed').length,
|
|
157
|
+
ready: items.filter(item => item.status === 'ready').length,
|
|
158
|
+
manualReview: items.filter(item => item.status === 'manual-review').length,
|
|
159
|
+
needsInit: items.filter(item => item.status === 'needs-init').length,
|
|
160
|
+
versionDrift: items.filter(item => item.status === 'version-drift').length,
|
|
161
|
+
installedNow: items.filter(item => item.status === 'installed-now').length,
|
|
162
|
+
failed: items.filter(item => item.status === 'failed').length,
|
|
163
|
+
};
|
|
164
|
+
const complete = items.length > 0 && items.every(item => item.status === 'installed' || item.status === 'installed-now');
|
|
165
|
+
const postCheckSummary = {
|
|
166
|
+
total: postChecks.length,
|
|
167
|
+
passed: postChecks.filter(item => item.status === 'passed').length,
|
|
168
|
+
warned: postChecks.filter(item => item.status === 'warn').length,
|
|
169
|
+
failed: postChecks.filter(item => item.status === 'failed').length,
|
|
170
|
+
};
|
|
171
|
+
const recommendations = [];
|
|
172
|
+
const postCheckCommands = buildPostCheckCommands(packIds, items);
|
|
173
|
+
const rollbackHints = buildRollbackHints(items);
|
|
174
|
+
if (!apply && summary.ready > 0)
|
|
175
|
+
recommendations.push('Re-run with --apply to install all ready dependencies in one pass.');
|
|
176
|
+
if (runtimeChecks.some(check => check.status === 'missing'))
|
|
177
|
+
recommendations.push('Install missing runtime dependencies before running --apply.');
|
|
178
|
+
if (runtimeChecks.some(check => check.status === 'warn'))
|
|
179
|
+
recommendations.push('Review runtime warnings; some tools may install but fail initialization or post-checks.');
|
|
180
|
+
if (summary.manualReview > 0)
|
|
181
|
+
recommendations.push('Review manual-review items for missing package managers, PATH setup, or platform-specific configuration.');
|
|
182
|
+
if (summary.needsInit > 0)
|
|
183
|
+
recommendations.push('Run the listed initialization commands before treating installed CLIs as ready for autonomous use.');
|
|
184
|
+
if (summary.versionDrift > 0)
|
|
185
|
+
recommendations.push('Resolve version-drift items before relying on their generated skills, hooks, or artifacts.');
|
|
186
|
+
if (items.some(item => item.id === 'frontend-design'))
|
|
187
|
+
recommendations.push('frontend-design is optional; keep it as an explicit companion only when implementation ideation is needed.');
|
|
188
|
+
if (items.some(item => item.id === 'awesome-design-md'))
|
|
189
|
+
recommendations.push('Use awesome-design-md as the source of DESIGN.md, brand direction, and visual-language selection.');
|
|
190
|
+
if (items.some(item => item.id === 'ui-ux-pro-max'))
|
|
191
|
+
recommendations.push('Use ui-ux-pro-max for UX flow, UI state, accessibility, and responsive acceptance checks.');
|
|
192
|
+
if (items.some(item => item.id === 'gbrain'))
|
|
193
|
+
recommendations.push('After GBrain is installed, validate remote or thin-client health with `scale memory provider status --json`.');
|
|
194
|
+
if (items.some(item => item.id === 'graphify' || item.id === 'codegraph'))
|
|
195
|
+
recommendations.push('After knowledge tools are installed, run `scale codegraph status --json` and initialize the project index or graph artifacts as needed.');
|
|
196
|
+
if (postCheckSummary.failed > 0)
|
|
197
|
+
recommendations.push('Resolve failed post-checks before treating the dependency bootstrap as production-ready.');
|
|
198
|
+
if (postCheckSummary.warned > 0)
|
|
199
|
+
recommendations.push('Review warned post-checks; they usually indicate provider initialization or project index/artifact setup is still pending.');
|
|
200
|
+
return {
|
|
201
|
+
ok: summary.failed === 0 && postCheckSummary.failed === 0,
|
|
202
|
+
complete,
|
|
203
|
+
projectDir,
|
|
204
|
+
scaleDir,
|
|
205
|
+
packIds,
|
|
206
|
+
includeIds,
|
|
207
|
+
apply,
|
|
208
|
+
runtimeChecks,
|
|
209
|
+
items,
|
|
210
|
+
summary,
|
|
211
|
+
postActions,
|
|
212
|
+
postChecks,
|
|
213
|
+
postCheckSummary,
|
|
214
|
+
postCheckCommands,
|
|
215
|
+
rollbackHints,
|
|
216
|
+
recommendations,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
function buildRuntimeChecks(items) {
|
|
220
|
+
const ids = new Set(items.map(item => item.id));
|
|
221
|
+
const requirements = new Map();
|
|
222
|
+
const requireRuntime = (runtimeId, requiredFor) => {
|
|
223
|
+
const existing = requirements.get(runtimeId) ?? new Set();
|
|
224
|
+
for (const value of requiredFor)
|
|
225
|
+
existing.add(value);
|
|
226
|
+
requirements.set(runtimeId, existing);
|
|
227
|
+
};
|
|
228
|
+
const uiIds = ['awesome-design-md', 'ui-ux-pro-max', 'frontend-design'].filter(id => ids.has(id));
|
|
229
|
+
if (uiIds.length > 0) {
|
|
230
|
+
requireRuntime('node', uiIds);
|
|
231
|
+
requireRuntime('npx', uiIds);
|
|
232
|
+
}
|
|
233
|
+
if (ids.has('rtk'))
|
|
234
|
+
requireRuntime('cargo', ['rtk']);
|
|
235
|
+
if (ids.has('gbrain'))
|
|
236
|
+
requireRuntime('bun', ['gbrain']);
|
|
237
|
+
if (ids.has('graphify')) {
|
|
238
|
+
requireRuntime('python', ['graphify']);
|
|
239
|
+
requireRuntime('python-installer', ['graphify']);
|
|
240
|
+
}
|
|
241
|
+
if (ids.has('codegraph')) {
|
|
242
|
+
requireRuntime('node', ['codegraph']);
|
|
243
|
+
requireRuntime('npm', ['codegraph']);
|
|
244
|
+
}
|
|
245
|
+
const checks = [];
|
|
246
|
+
const requiredFor = (runtimeId) => [...(requirements.get(runtimeId) ?? [])];
|
|
247
|
+
if (requirements.has('node'))
|
|
248
|
+
checks.push(nodeRuntimeCheck(requiredFor('node')));
|
|
249
|
+
if (requirements.has('npx'))
|
|
250
|
+
checks.push(commandRuntimeCheck({
|
|
251
|
+
id: 'npx',
|
|
252
|
+
label: 'npx',
|
|
253
|
+
candidates: [{ command: 'npx', args: ['--version'], display: 'npx' }],
|
|
254
|
+
requiredFor: requiredFor('npx'),
|
|
255
|
+
installHint: 'Install Node.js 20+; npm/npx are bundled with the official Node.js installer.',
|
|
256
|
+
}));
|
|
257
|
+
if (requirements.has('npm'))
|
|
258
|
+
checks.push(commandRuntimeCheck({
|
|
259
|
+
id: 'npm',
|
|
260
|
+
label: 'npm',
|
|
261
|
+
candidates: [{ command: 'npm', args: ['--version'], display: 'npm' }],
|
|
262
|
+
requiredFor: requiredFor('npm'),
|
|
263
|
+
installHint: 'Install Node.js 20+; npm is bundled with the official Node.js installer.',
|
|
264
|
+
}));
|
|
265
|
+
if (requirements.has('cargo'))
|
|
266
|
+
checks.push(commandRuntimeCheck({
|
|
267
|
+
id: 'cargo',
|
|
268
|
+
label: 'Rust/Cargo',
|
|
269
|
+
candidates: [{ command: 'cargo', args: ['--version'], display: 'cargo' }],
|
|
270
|
+
requiredFor: requiredFor('cargo'),
|
|
271
|
+
installHint: 'Install Rust with rustup, then re-open the shell so cargo is on PATH.',
|
|
272
|
+
}));
|
|
273
|
+
if (requirements.has('bun'))
|
|
274
|
+
checks.push(commandRuntimeCheck({
|
|
275
|
+
id: 'bun',
|
|
276
|
+
label: 'Bun',
|
|
277
|
+
candidates: [{ command: 'bun', args: ['--version'], display: 'bun' }],
|
|
278
|
+
requiredFor: requiredFor('bun'),
|
|
279
|
+
installHint: 'Install Bun from https://bun.sh and re-open the shell so bun is on PATH.',
|
|
280
|
+
}));
|
|
281
|
+
if (requirements.has('python'))
|
|
282
|
+
checks.push(pythonRuntimeCheck(requiredFor('python')));
|
|
283
|
+
if (requirements.has('python-installer'))
|
|
284
|
+
checks.push(pythonInstallerRuntimeCheck(requiredFor('python-installer')));
|
|
285
|
+
return checks;
|
|
286
|
+
}
|
|
287
|
+
function commandRuntimeCheck(input) {
|
|
288
|
+
const detected = firstAvailableRuntimeTool(input.candidates);
|
|
289
|
+
if (!detected) {
|
|
290
|
+
return {
|
|
291
|
+
id: input.id,
|
|
292
|
+
label: input.label,
|
|
293
|
+
commands: input.candidates.map(candidate => candidate.display),
|
|
294
|
+
status: 'missing',
|
|
295
|
+
requiredFor: input.requiredFor,
|
|
296
|
+
reason: `${input.label} was not detected on PATH.`,
|
|
297
|
+
installHint: input.installHint,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
return {
|
|
301
|
+
id: input.id,
|
|
302
|
+
label: input.label,
|
|
303
|
+
commands: input.candidates.map(candidate => candidate.display),
|
|
304
|
+
status: 'ok',
|
|
305
|
+
requiredFor: input.requiredFor,
|
|
306
|
+
detectedCommand: detected.display,
|
|
307
|
+
version: firstLine(detected.output),
|
|
308
|
+
reason: `${input.label} is available via ${detected.display}.`,
|
|
309
|
+
installHint: input.installHint,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
function nodeRuntimeCheck(requiredFor) {
|
|
313
|
+
const detected = firstAvailableRuntimeTool([{ command: 'node', args: ['--version'], display: 'node' }]);
|
|
314
|
+
const commands = ['node'];
|
|
315
|
+
const installHint = 'Install Node.js 20+ from https://nodejs.org.';
|
|
316
|
+
if (!detected) {
|
|
317
|
+
return {
|
|
318
|
+
id: 'node',
|
|
319
|
+
label: 'Node.js',
|
|
320
|
+
commands,
|
|
321
|
+
status: 'missing',
|
|
322
|
+
requiredFor,
|
|
323
|
+
reason: 'Node.js was not detected on PATH.',
|
|
324
|
+
installHint,
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
const version = firstLine(detected.output);
|
|
328
|
+
const parsed = parseSemver(version);
|
|
329
|
+
if (parsed && parsed.major < 20) {
|
|
330
|
+
return {
|
|
331
|
+
id: 'node',
|
|
332
|
+
label: 'Node.js',
|
|
333
|
+
commands,
|
|
334
|
+
status: 'warn',
|
|
335
|
+
requiredFor,
|
|
336
|
+
detectedCommand: detected.display,
|
|
337
|
+
version,
|
|
338
|
+
reason: `Node.js ${version} is installed, but SCALE setup expects Node.js 20+ for current third-party installers.`,
|
|
339
|
+
installHint,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
return {
|
|
343
|
+
id: 'node',
|
|
344
|
+
label: 'Node.js',
|
|
345
|
+
commands,
|
|
346
|
+
status: 'ok',
|
|
347
|
+
requiredFor,
|
|
348
|
+
detectedCommand: detected.display,
|
|
349
|
+
version,
|
|
350
|
+
reason: `Node.js ${version} is available.`,
|
|
351
|
+
installHint,
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
function pythonRuntimeCheck(requiredFor) {
|
|
355
|
+
const candidates = [
|
|
356
|
+
{ command: 'python', args: ['--version'], display: 'python' },
|
|
357
|
+
{ command: 'python3', args: ['--version'], display: 'python3' },
|
|
358
|
+
{ command: 'py', args: ['--version'], display: 'py' },
|
|
359
|
+
];
|
|
360
|
+
const detected = firstAvailableRuntimeTool(candidates);
|
|
361
|
+
const installHint = 'Install Python 3.10+ and prefer uv or pipx for graphify installation.';
|
|
362
|
+
if (!detected) {
|
|
363
|
+
return {
|
|
364
|
+
id: 'python',
|
|
365
|
+
label: 'Python',
|
|
366
|
+
commands: candidates.map(candidate => candidate.display),
|
|
367
|
+
status: 'missing',
|
|
368
|
+
requiredFor,
|
|
369
|
+
reason: 'Python 3.10+ was not detected on PATH.',
|
|
370
|
+
installHint,
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
const version = firstLine(detected.output);
|
|
374
|
+
const parsed = parseSemver(version);
|
|
375
|
+
if (!parsed || parsed.major < 3 || (parsed.major === 3 && parsed.minor < 10)) {
|
|
376
|
+
return {
|
|
377
|
+
id: 'python',
|
|
378
|
+
label: 'Python',
|
|
379
|
+
commands: candidates.map(candidate => candidate.display),
|
|
380
|
+
status: 'warn',
|
|
381
|
+
requiredFor,
|
|
382
|
+
detectedCommand: detected.display,
|
|
383
|
+
version,
|
|
384
|
+
reason: `${version} is detected, but Graphify requires Python 3.10+.`,
|
|
385
|
+
installHint,
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
return {
|
|
389
|
+
id: 'python',
|
|
390
|
+
label: 'Python',
|
|
391
|
+
commands: candidates.map(candidate => candidate.display),
|
|
392
|
+
status: 'ok',
|
|
393
|
+
requiredFor,
|
|
394
|
+
detectedCommand: detected.display,
|
|
395
|
+
version,
|
|
396
|
+
reason: `${version} is available for Graphify.`,
|
|
397
|
+
installHint,
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
function pythonInstallerRuntimeCheck(requiredFor) {
|
|
401
|
+
const candidates = [
|
|
402
|
+
{ command: 'uv', args: ['--version'], display: 'uv' },
|
|
403
|
+
{ command: 'pipx', args: ['--version'], display: 'pipx' },
|
|
404
|
+
{ command: 'pip', args: ['--version'], display: 'pip' },
|
|
405
|
+
{ command: 'pip3', args: ['--version'], display: 'pip3' },
|
|
406
|
+
{ command: 'python', args: ['-m', 'pip', '--version'], display: 'python -m pip' },
|
|
407
|
+
{ command: 'python3', args: ['-m', 'pip', '--version'], display: 'python3 -m pip' },
|
|
408
|
+
];
|
|
409
|
+
const detected = firstAvailableRuntimeTool(candidates);
|
|
410
|
+
const installHint = 'Install uv or pipx first; pip is supported only as a fallback for graphify.';
|
|
411
|
+
if (!detected) {
|
|
412
|
+
return {
|
|
413
|
+
id: 'python-installer',
|
|
414
|
+
label: 'Python installer',
|
|
415
|
+
commands: candidates.map(candidate => candidate.display),
|
|
416
|
+
status: 'missing',
|
|
417
|
+
requiredFor,
|
|
418
|
+
reason: 'No supported Python installer was detected for graphify.',
|
|
419
|
+
installHint,
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
const version = firstLine(detected.output);
|
|
423
|
+
const preferred = detected.display === 'uv' || detected.display === 'pipx';
|
|
424
|
+
return {
|
|
425
|
+
id: 'python-installer',
|
|
426
|
+
label: 'Python installer',
|
|
427
|
+
commands: candidates.map(candidate => candidate.display),
|
|
428
|
+
status: preferred ? 'ok' : 'warn',
|
|
429
|
+
requiredFor,
|
|
430
|
+
detectedCommand: detected.display,
|
|
431
|
+
version,
|
|
432
|
+
reason: preferred
|
|
433
|
+
? `${detected.display} is available for isolated graphify installation.`
|
|
434
|
+
: `${detected.display} is available, but uv or pipx is preferred to avoid polluting project environments.`,
|
|
435
|
+
installHint,
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
function firstAvailableRuntimeTool(candidates) {
|
|
439
|
+
for (const candidate of candidates) {
|
|
440
|
+
const result = runHealthCommand(candidate.command, candidate.args);
|
|
441
|
+
if (!result.ok)
|
|
442
|
+
continue;
|
|
443
|
+
return {
|
|
444
|
+
display: candidate.display,
|
|
445
|
+
output: `${result.stdout}\n${result.stderr}`.trim(),
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
return undefined;
|
|
449
|
+
}
|
|
450
|
+
function parseSemver(value) {
|
|
451
|
+
const match = value.match(/(\d+)\.(\d+)(?:\.(\d+))?/);
|
|
452
|
+
if (!match)
|
|
453
|
+
return null;
|
|
454
|
+
return {
|
|
455
|
+
major: Number.parseInt(match[1], 10),
|
|
456
|
+
minor: Number.parseInt(match[2], 10),
|
|
457
|
+
patch: Number.parseInt(match[3] ?? '0', 10),
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
export function runDependencyBootstrapPostChecks(input, deps = {}) {
|
|
461
|
+
const inspectTools = deps.inspectTools ?? inspectToolCapabilities;
|
|
462
|
+
const inspectMemory = deps.inspectMemory ?? inspectMemoryProviders;
|
|
463
|
+
const inspectCode = deps.inspectCode ?? inspectCodeIntelligence;
|
|
464
|
+
const toolIds = unique(input.items.map(item => item.id).filter(id => ['awesome-design-md', 'ui-ux-pro-max', 'frontend-design', 'rtk', 'gbrain', 'codegraph', 'graphify'].includes(id)));
|
|
465
|
+
const results = [];
|
|
466
|
+
if (toolIds.length > 0) {
|
|
467
|
+
const toolReport = inspectTools({
|
|
468
|
+
projectDir: input.projectDir,
|
|
469
|
+
homeDir: input.homeDir,
|
|
470
|
+
toolIds,
|
|
471
|
+
});
|
|
472
|
+
const missing = toolReport.tools.filter(tool => !tool.installed).map(tool => tool.id);
|
|
473
|
+
results.push({
|
|
474
|
+
id: 'tool-capabilities',
|
|
475
|
+
label: 'Tool Doctor',
|
|
476
|
+
command: `scale tool doctor --tools ${toolIds.join(',')} --json`,
|
|
477
|
+
status: toolReport.ok ? 'passed' : 'failed',
|
|
478
|
+
summary: `${toolReport.summary.installed}/${toolReport.summary.total} selected tools are available${missing.length > 0 ? `; missing: ${missing.join(', ')}` : ''}`,
|
|
479
|
+
details: {
|
|
480
|
+
toolIds,
|
|
481
|
+
missing,
|
|
482
|
+
},
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
if (input.items.some(item => item.id === 'gbrain')) {
|
|
486
|
+
const memoryReport = inspectMemory({ projectDir: input.projectDir, scaleDir: input.scaleDir });
|
|
487
|
+
const gbrain = memoryReport.providers.find(provider => provider.id === 'gbrain');
|
|
488
|
+
const warnings = [...memoryReport.warnings];
|
|
489
|
+
const status = !gbrain?.available ? 'failed' : warnings.length > 0 ? 'warn' : 'passed';
|
|
490
|
+
results.push({
|
|
491
|
+
id: 'memory-provider',
|
|
492
|
+
label: 'Memory Provider',
|
|
493
|
+
command: 'scale memory provider status --json',
|
|
494
|
+
status,
|
|
495
|
+
summary: gbrain
|
|
496
|
+
? `mode=${memoryReport.routing.mode}; order=${memoryReport.routing.defaultOrder.join(' -> ')}; gbrain=${gbrain.available ? 'available' : 'unavailable'}`
|
|
497
|
+
: 'gbrain provider entry is missing from routing policy',
|
|
498
|
+
details: {
|
|
499
|
+
warnings,
|
|
500
|
+
gbrainReason: gbrain?.reason,
|
|
501
|
+
},
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
if (input.items.some(item => item.id === 'codegraph' || item.id === 'graphify')) {
|
|
505
|
+
const codeReport = inspectCode({ projectDir: input.projectDir, scaleDir: input.scaleDir });
|
|
506
|
+
const codegraph = codeReport.providers.find(provider => provider.id === 'codegraph');
|
|
507
|
+
const graphify = codeReport.providers.find(provider => provider.id === 'graphify');
|
|
508
|
+
const warnings = [];
|
|
509
|
+
let failed = false;
|
|
510
|
+
if (input.items.some(item => item.id === 'codegraph')) {
|
|
511
|
+
if (!codegraph?.available)
|
|
512
|
+
failed = true;
|
|
513
|
+
else if (!codeReport.projectIndexExists)
|
|
514
|
+
warnings.push('codegraph CLI is installed but the project index (.codegraph/) is not initialized yet');
|
|
515
|
+
}
|
|
516
|
+
if (input.items.some(item => item.id === 'graphify') && !graphify?.available) {
|
|
517
|
+
warnings.push('graphify CLI is installed but graphify-out/graph.json is not present yet');
|
|
518
|
+
}
|
|
519
|
+
results.push({
|
|
520
|
+
id: 'code-intelligence',
|
|
521
|
+
label: 'Code Intelligence',
|
|
522
|
+
command: 'scale codegraph status --json',
|
|
523
|
+
status: failed ? 'failed' : warnings.length > 0 ? 'warn' : 'passed',
|
|
524
|
+
summary: [
|
|
525
|
+
codegraph ? `codegraph=${codegraph.available ? 'available' : 'missing'}` : undefined,
|
|
526
|
+
graphify ? `graphify-artifact=${graphify.available ? 'available' : 'missing'}` : undefined,
|
|
527
|
+
`projectIndex=${codeReport.projectIndexExists ? 'present' : 'missing'}`,
|
|
528
|
+
].filter(Boolean).join('; '),
|
|
529
|
+
details: {
|
|
530
|
+
warnings,
|
|
531
|
+
},
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
return results;
|
|
535
|
+
}
|
|
536
|
+
function inspectDefinition(definition, context) {
|
|
537
|
+
const detectedBy = detectDefinition(definition, context.projectDir, context.homeDir);
|
|
538
|
+
const installed = detectedBy !== 'missing';
|
|
539
|
+
const prerequisites = definition.prerequisites.map(requirement => ({
|
|
540
|
+
command: requirement,
|
|
541
|
+
present: requirementSatisfied(requirement, context.commandExists),
|
|
542
|
+
}));
|
|
543
|
+
const health = installed && definition.healthCheck ? definition.healthCheck(context) : undefined;
|
|
544
|
+
const installCommand = installed ? undefined : definition.installCommand(context) ?? undefined;
|
|
545
|
+
const installSupported = Boolean(installCommand);
|
|
546
|
+
return {
|
|
547
|
+
id: definition.id,
|
|
548
|
+
name: definition.name,
|
|
549
|
+
kind: definition.kind,
|
|
550
|
+
packs: definition.packs,
|
|
551
|
+
source: definition.source,
|
|
552
|
+
installed,
|
|
553
|
+
status: installed
|
|
554
|
+
? health?.bootstrapStatus ?? 'installed'
|
|
555
|
+
: installSupported ? 'ready' : 'manual-review',
|
|
556
|
+
installCommand,
|
|
557
|
+
installSupported,
|
|
558
|
+
detectedBy,
|
|
559
|
+
prerequisites,
|
|
560
|
+
manualReason: installed ? undefined : definition.manualReason,
|
|
561
|
+
health,
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
function detectDefinition(definition, projectDir, homeDir) {
|
|
565
|
+
const context = { projectDir, homeDir, commandExists: externalCommandExists };
|
|
566
|
+
const detectedPath = definition.detectPaths?.(context).find(candidate => existsSync(candidate));
|
|
567
|
+
if (detectedPath)
|
|
568
|
+
return detectedPath;
|
|
569
|
+
if (definition.detectSkillId) {
|
|
570
|
+
const path = skillCandidatePaths(projectDir, homeDir, definition.detectSkillId).find(candidate => existsSync(candidate));
|
|
571
|
+
return path ?? 'missing';
|
|
572
|
+
}
|
|
573
|
+
if (definition.detectCommand && externalCommandExists(definition.detectCommand))
|
|
574
|
+
return `PATH:${definition.detectCommand}`;
|
|
575
|
+
return 'missing';
|
|
576
|
+
}
|
|
577
|
+
function skillCandidatePaths(projectDir, homeDir, skillId) {
|
|
578
|
+
return [
|
|
579
|
+
join(projectDir, '.agents', 'skills', skillId, 'SKILL.md'),
|
|
580
|
+
join(projectDir, '.codex', 'skills', skillId, 'SKILL.md'),
|
|
581
|
+
join(projectDir, '.claude', 'skills', skillId, 'SKILL.md'),
|
|
582
|
+
join(homeDir, '.agents', 'skills', skillId, 'SKILL.md'),
|
|
583
|
+
join(homeDir, '.codex', 'skills', skillId, 'SKILL.md'),
|
|
584
|
+
join(homeDir, '.claude', 'skills', skillId, 'SKILL.md'),
|
|
585
|
+
];
|
|
586
|
+
}
|
|
587
|
+
function requirementSatisfied(requirement, commandExists) {
|
|
588
|
+
return requirement.split('|').some(command => commandExists(command));
|
|
589
|
+
}
|
|
590
|
+
function buildGraphifyInstallCommand(context) {
|
|
591
|
+
if (context.commandExists('uv'))
|
|
592
|
+
return GRAPHIFY_UV_INSTALL;
|
|
593
|
+
if (context.commandExists('pipx'))
|
|
594
|
+
return GRAPHIFY_PIPX_INSTALL;
|
|
595
|
+
if (context.commandExists('pip'))
|
|
596
|
+
return GRAPHIFY_PIP_INSTALL;
|
|
597
|
+
if (context.commandExists('pip3'))
|
|
598
|
+
return GRAPHIFY_PIP3_INSTALL;
|
|
599
|
+
if (context.commandExists('python'))
|
|
600
|
+
return GRAPHIFY_PYTHON_INSTALL;
|
|
601
|
+
if (context.commandExists('python3'))
|
|
602
|
+
return GRAPHIFY_PYTHON3_INSTALL;
|
|
603
|
+
return null;
|
|
604
|
+
}
|
|
605
|
+
function buildGbrainInstallCommand(context) {
|
|
606
|
+
if (!context.commandExists('bun'))
|
|
607
|
+
return null;
|
|
608
|
+
return GBRAIN_INSTALL;
|
|
609
|
+
}
|
|
610
|
+
function checkRtkHealth() {
|
|
611
|
+
const gain = runHealthCommand('rtk', ['gain']);
|
|
612
|
+
if (!gain.ok) {
|
|
613
|
+
return {
|
|
614
|
+
status: 'warn',
|
|
615
|
+
bootstrapStatus: 'needs-init',
|
|
616
|
+
reason: 'rtk CLI is installed but `rtk gain` did not run successfully; token-savings evidence is not available yet.',
|
|
617
|
+
nextCommands: ['rtk init -g --codex', 'rtk gain'],
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
const output = `${gain.stdout}\n${gain.stderr}`;
|
|
621
|
+
if (/no hook installed/i.test(output)) {
|
|
622
|
+
return {
|
|
623
|
+
status: 'warn',
|
|
624
|
+
bootstrapStatus: 'needs-init',
|
|
625
|
+
reason: 'rtk CLI is installed, but the shell hook is not installed so command-output compression is not automatic yet.',
|
|
626
|
+
nextCommands: ['rtk init -g --codex', 'rtk gain'],
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
return { status: 'ok', reason: 'rtk CLI and gain evidence are available.' };
|
|
630
|
+
}
|
|
631
|
+
function checkGbrainHealth() {
|
|
632
|
+
const doctor = runHealthCommand('gbrain', ['doctor', '--json'], 10_000);
|
|
633
|
+
if (doctor.ok)
|
|
634
|
+
return { status: 'ok', reason: 'gbrain doctor passed; provider can be used for default memory routing.' };
|
|
635
|
+
const output = `${doctor.stdout}\n${doctor.stderr}`;
|
|
636
|
+
return {
|
|
637
|
+
status: 'warn',
|
|
638
|
+
bootstrapStatus: 'needs-init',
|
|
639
|
+
reason: /no brain configured/i.test(output)
|
|
640
|
+
? 'gbrain CLI is installed but no brain is configured yet; cross-session recall will fail until initialized.'
|
|
641
|
+
: `gbrain CLI is installed but doctor failed: ${firstLine(output)}`,
|
|
642
|
+
nextCommands: ['gbrain init --pglite', 'gbrain doctor --json', 'scale memory provider status --json'],
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
function checkGraphifyHealth() {
|
|
646
|
+
const version = runHealthCommand('graphify', ['--version']);
|
|
647
|
+
const hook = runHealthCommand('graphify', ['hook', 'status'], 10_000);
|
|
648
|
+
const output = `${version.stdout}\n${version.stderr}\n${hook.stdout}\n${hook.stderr}`;
|
|
649
|
+
if (/skill.*version|package.*version|drift|outdated/i.test(output)) {
|
|
650
|
+
return {
|
|
651
|
+
status: 'warn',
|
|
652
|
+
bootstrapStatus: 'version-drift',
|
|
653
|
+
reason: 'graphify CLI is installed but its generated skill/hook assets appear out of sync with the package.',
|
|
654
|
+
nextCommands: ['graphify install --platform codex', 'graphify hook status', 'scale codegraph status --json'],
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
if (!hook.ok || /not installed|missing/i.test(output)) {
|
|
658
|
+
return {
|
|
659
|
+
status: 'warn',
|
|
660
|
+
bootstrapStatus: 'needs-init',
|
|
661
|
+
reason: 'graphify CLI is installed but Codex hooks are not fully configured.',
|
|
662
|
+
nextCommands: ['graphify install --platform codex', 'graphify hook status'],
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
return { status: 'ok', reason: 'graphify CLI and hook status are available.' };
|
|
666
|
+
}
|
|
667
|
+
function checkCodeGraphHealth(context) {
|
|
668
|
+
const indexPath = join(context.projectDir, '.codegraph');
|
|
669
|
+
if (!existsSync(indexPath)) {
|
|
670
|
+
return {
|
|
671
|
+
status: 'warn',
|
|
672
|
+
bootstrapStatus: 'needs-init',
|
|
673
|
+
reason: 'codegraph CLI is installed but this project has no .codegraph index yet.',
|
|
674
|
+
nextCommands: ['codegraph init -i', 'scale codegraph status --json'],
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
const status = runHealthCommand('codegraph', ['status', context.projectDir], 10_000);
|
|
678
|
+
if (!status.ok) {
|
|
679
|
+
return {
|
|
680
|
+
status: 'warn',
|
|
681
|
+
bootstrapStatus: 'needs-init',
|
|
682
|
+
reason: `codegraph index exists but status check failed: ${firstLine(`${status.stdout}\n${status.stderr}`)}`,
|
|
683
|
+
nextCommands: ['codegraph init -i', 'codegraph status .'],
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
return { status: 'ok', reason: 'codegraph CLI and project index are available.' };
|
|
687
|
+
}
|
|
688
|
+
function runHealthCommand(command, args, timeout = 5_000) {
|
|
689
|
+
try {
|
|
690
|
+
const result = execaSync(command, args, {
|
|
691
|
+
reject: false,
|
|
692
|
+
timeout,
|
|
693
|
+
});
|
|
694
|
+
return {
|
|
695
|
+
ok: (result.exitCode ?? 1) === 0,
|
|
696
|
+
stdout: result.stdout ?? '',
|
|
697
|
+
stderr: result.stderr ?? '',
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
catch (error) {
|
|
701
|
+
const err = error;
|
|
702
|
+
return {
|
|
703
|
+
ok: false,
|
|
704
|
+
stdout: String(err.stdout ?? ''),
|
|
705
|
+
stderr: String(err.stderr ?? err.message ?? ''),
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
function firstLine(value) {
|
|
710
|
+
return value.split(/\r?\n/).map(line => line.trim()).find(Boolean) ?? 'unknown error';
|
|
711
|
+
}
|
|
712
|
+
function quotePath(path) {
|
|
713
|
+
return `"${path.replace(/"/g, '\\"')}"`;
|
|
714
|
+
}
|
|
715
|
+
async function runInstallCommand(shellCommand) {
|
|
716
|
+
const wrapped = wrapShellCommandWithRtk(shellCommand);
|
|
717
|
+
const result = wrapped
|
|
718
|
+
? await execa(wrapped.command, wrapped.args, { reject: false, timeout: 300_000, all: false })
|
|
719
|
+
: await execa(shellCommand, { shell: true, reject: false, timeout: 300_000, all: false });
|
|
720
|
+
return {
|
|
721
|
+
ok: (result.exitCode ?? 1) === 0,
|
|
722
|
+
output: result.stdout ?? '',
|
|
723
|
+
error: result.stderr ?? '',
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
export function applyDependencyBootstrapPostActions(projectDir, scaleDir, items, deps = {}) {
|
|
727
|
+
const writeMemoryConfig = deps.writeMemoryConfig ?? writeMemoryProvidersConfig;
|
|
728
|
+
const switchMemoryProvider = deps.switchMemoryProvider ?? useMemoryProvider;
|
|
729
|
+
const writeCodeConfig = deps.writeCodeConfig ?? writeCodeIntelligenceConfig;
|
|
730
|
+
const ids = new Set(items.map(item => item.id));
|
|
731
|
+
const actions = [];
|
|
732
|
+
if (ids.has('gbrain')) {
|
|
733
|
+
const memoryConfig = writeMemoryConfig({ projectDir, scaleDir });
|
|
734
|
+
actions.push(`${memoryConfig.written ? 'Wrote' : 'Reused'} ${memoryConfig.path}`);
|
|
735
|
+
const provider = switchMemoryProvider({ projectDir, scaleDir, provider: 'gbrain' });
|
|
736
|
+
const previousOrder = provider.previousOrder.join(' -> ');
|
|
737
|
+
const nextOrder = provider.nextOrder.join(' -> ');
|
|
738
|
+
actions.push(previousOrder === nextOrder
|
|
739
|
+
? `Memory provider order unchanged: ${nextOrder}`
|
|
740
|
+
: `Memory provider order: ${previousOrder} => ${nextOrder}`);
|
|
741
|
+
}
|
|
742
|
+
if (ids.has('graphify') || ids.has('codegraph')) {
|
|
743
|
+
const codeIntelligence = writeCodeConfig({ projectDir, scaleDir });
|
|
744
|
+
actions.push(`${codeIntelligence.written ? 'Wrote' : 'Reused'} ${codeIntelligence.path}`);
|
|
745
|
+
}
|
|
746
|
+
return actions;
|
|
747
|
+
}
|
|
748
|
+
function selectDefinitions(packIds, includeIds) {
|
|
749
|
+
const selectedPacks = new Set(packIds.includes('full') ? ['ui', 'memory', 'knowledge', 'external-cli'] : packIds);
|
|
750
|
+
const selectedIds = new Set(includeIds);
|
|
751
|
+
return DEPENDENCY_BOOTSTRAP_DEFINITIONS.filter(definition => definition.packs.some(pack => selectedPacks.has(pack)) || selectedIds.has(definition.id));
|
|
752
|
+
}
|
|
753
|
+
function buildPostCheckCommands(packIds, items) {
|
|
754
|
+
const commands = new Set();
|
|
755
|
+
const selectedPacks = new Set(packIds.includes('full') ? ['ui', 'memory', 'knowledge', 'external-cli'] : packIds);
|
|
756
|
+
const ids = new Set(items.map(item => item.id));
|
|
757
|
+
if (selectedPacks.has('ui')) {
|
|
758
|
+
const uiToolIds = items
|
|
759
|
+
.filter(item => item.kind === 'skill' && item.packs.includes('ui'))
|
|
760
|
+
.map(item => item.id);
|
|
761
|
+
if (uiToolIds.length > 0)
|
|
762
|
+
commands.add(`scale tool doctor --tools ${uiToolIds.join(',')} --json`);
|
|
763
|
+
commands.add('scale skill doctor --json');
|
|
764
|
+
}
|
|
765
|
+
if (selectedPacks.has('memory') || ids.has('gbrain')) {
|
|
766
|
+
commands.add('scale memory provider status --json');
|
|
767
|
+
}
|
|
768
|
+
if (selectedPacks.has('knowledge') || ids.has('codegraph') || ids.has('graphify')) {
|
|
769
|
+
commands.add('scale tool doctor --tools codegraph,graphify --json');
|
|
770
|
+
commands.add('scale codegraph status --json');
|
|
771
|
+
}
|
|
772
|
+
if (selectedPacks.has('external-cli') || ids.has('rtk')) {
|
|
773
|
+
commands.add('scale tool doctor --tools rtk --json');
|
|
774
|
+
}
|
|
775
|
+
commands.add('scale doctor');
|
|
776
|
+
return [...commands];
|
|
777
|
+
}
|
|
778
|
+
function buildRollbackHints(items) {
|
|
779
|
+
const hints = new Set();
|
|
780
|
+
for (const item of items) {
|
|
781
|
+
switch (item.id) {
|
|
782
|
+
case 'rtk':
|
|
783
|
+
hints.add('RTK rollback: cargo uninstall rtk');
|
|
784
|
+
break;
|
|
785
|
+
case 'codegraph':
|
|
786
|
+
hints.add('CodeGraph rollback: npm uninstall -g @colbymchenry/codegraph');
|
|
787
|
+
break;
|
|
788
|
+
case 'graphify':
|
|
789
|
+
hints.add('Graphify rollback: pip uninstall graphify # or pip3/python -m pip uninstall graphify');
|
|
790
|
+
break;
|
|
791
|
+
case 'gbrain':
|
|
792
|
+
hints.add('GBrain rollback: bun unlink gbrain, then remove ~/.scale/vendor/gbrain if you want a full local cleanup');
|
|
793
|
+
break;
|
|
794
|
+
case 'awesome-design-md':
|
|
795
|
+
case 'ui-ux-pro-max':
|
|
796
|
+
case 'frontend-design':
|
|
797
|
+
hints.add(`Skill rollback (${item.id}): remove the installed skill directory under ~/.agents/skills/${item.id} after review`);
|
|
798
|
+
break;
|
|
799
|
+
default:
|
|
800
|
+
break;
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
return [...hints];
|
|
804
|
+
}
|
|
805
|
+
function normalizePackIds(input) {
|
|
806
|
+
const requested = unique((input ?? ['full']).map(value => value.trim()).filter(Boolean));
|
|
807
|
+
const packIds = requested
|
|
808
|
+
.flatMap(value => value.split(','))
|
|
809
|
+
.map(value => value.trim())
|
|
810
|
+
.filter(Boolean)
|
|
811
|
+
.map(value => normalizePackId(value))
|
|
812
|
+
.filter((value) => value !== null);
|
|
813
|
+
return packIds.length > 0 ? unique(packIds) : ['full'];
|
|
814
|
+
}
|
|
815
|
+
function normalizePackId(value) {
|
|
816
|
+
if (value === 'ui' || value === 'memory' || value === 'knowledge' || value === 'external-cli' || value === 'full')
|
|
817
|
+
return value;
|
|
818
|
+
if (value === 'external' || value === 'cli')
|
|
819
|
+
return 'external-cli';
|
|
820
|
+
return null;
|
|
821
|
+
}
|
|
822
|
+
function unique(values) {
|
|
823
|
+
return [...new Set(values)];
|
|
824
|
+
}
|
|
825
|
+
function resolveScaleRoot(projectDir, scaleDir) {
|
|
826
|
+
const candidate = scaleDir ?? '.scale';
|
|
827
|
+
return resolve(projectDir, candidate);
|
|
828
|
+
}
|
|
829
|
+
//# sourceMappingURL=DependencyBootstrap.js.map
|