@fprad0/skill-master-mcp 0.0.10 → 0.0.12
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/CHANGELOG.md +12 -1
- package/README.md +59 -10
- package/VERSION.md +3 -3
- package/bin/lib/client-config.mjs +293 -0
- package/bin/lib/menu-core.mjs +509 -131
- package/bin/lib/skill-installation.mjs +215 -0
- package/bin/skill-master-bootstrap-global.mjs +2 -1
- package/bin/skill-master-doctor.mjs +92 -32
- package/bin/skill-master-install-global-skills.mjs +4 -42
- package/bin/skill-master-install-project-skills.mjs +97 -0
- package/bin/skill-master-menu.mjs +91 -6
- package/bin/skill-master-register-clients.mjs +91 -115
- package/docs/operations/GUIA_MULTI_COMPUTADOR.md +262 -0
- package/docs/operations/GUIA_NPM_PUBLICO.md +147 -0
- package/docs/operations/MENU_VISUAL_EVIDENCE_2026-06-28.md +66 -0
- package/docs/operations/assets/menu-frame-compact.html +76 -0
- package/docs/operations/assets/menu-frame-compact.png +0 -0
- package/docs/operations/assets/menu-frame-large.html +84 -0
- package/docs/operations/assets/menu-frame-large.png +0 -0
- package/docs/operations/assets/menu-frame-running.html +80 -0
- package/docs/operations/assets/menu-frame-running.png +0 -0
- package/docs/operations/cross-platform-auth-transfer/ANALISE_COMPATIBILIDADE_MCP_2026-06-28.md +140 -0
- package/docs/operations/cross-platform-auth-transfer/README_TRANSFERENCIA.md +85 -0
- package/docs/operations/reborn-menu-cyberpunk-transfer/ANALISE_MENU_REBORN_CYBERPUNK_2026-06-28.md +174 -0
- package/docs/operations/reborn-menu-cyberpunk-transfer/HANDOFF_IMPLEMENTACAO_REBORN_CYBERPUNK_2026-06-28.md +119 -0
- package/docs/operations/reborn-menu-cyberpunk-transfer/ORDEM_DE_EXECUCAO_MENU_REBORN_CYBERPUNK.md +134 -0
- package/docs/operations/reborn-menu-cyberpunk-transfer/README_TRANSFERENCIA.md +84 -0
- package/docs/operations/reborn-menu-cyberpunk-transfer/README_TRANSFERENCIA_REBORN_PACKAGE.md +56 -0
- package/docs/operations/reborn-menu-cyberpunk-transfer/references/cyan-hud-frame-sheet.jpg +0 -0
- package/docs/operations/reborn-menu-cyberpunk-transfer/references/cyberpunk-pattern-sheet.jpg +0 -0
- package/docs/operations/reborn-menu-cyberpunk-transfer/references/fluid-workflow-windows.gif +0 -0
- package/docs/prompt-tasks/PROMPT_TASK_001_BOOTSTRAP_SKILL_MASTER_MCP.md +6 -0
- package/docs/prompt-tasks/PROMPT_TASK_002_AUTO_UPDATE_LAUNCHER.md +6 -0
- package/docs/prompt-tasks/PROMPT_TASK_003_REMOTE_MANIFEST_AND_RELEASES.md +6 -0
- package/docs/prompt-tasks/PROMPT_TASK_004_MULTI_USER_DISTRIBUTION.md +6 -0
- package/docs/prompt-tasks/PROMPT_TASK_005_SECURITY_AND_QUALITY_GATE.md +6 -0
- package/docs/prompt-tasks/PROMPT_TASK_006_MASTER_ACIONAMENTO_APRENDIZADO.md +83 -0
- package/docs/prompt-tasks/PROMPT_TASK_007_PERSONA_ORQUESTRADORA.md +88 -0
- package/docs/prompt-tasks/PROMPT_TASK_008_PROMPT_ROUTER_MODOS_ATIVACAO.md +156 -0
- package/docs/prompt-tasks/PROMPT_TASK_009_PIPELINE_APRENDIZADO_SUCESSO.md +105 -0
- package/docs/prompt-tasks/PROMPT_TASK_010_EVALS_GOVERNANCA_ATIVACAO.md +119 -0
- package/docs/prompt-tasks/PROMPT_TASK_011_MENU_NOTIFICACOES_NOTION.md +120 -0
- package/docs/prompt-tasks/PROMPT_TASK_012_MENU_CYBERPUNK_PIXEL_FRAME.md +123 -0
- package/docs/prompt-tasks/PROMPT_TASK_013_MENU_FLUID_DNA_ANIMATION.md +114 -0
- package/docs/prompt-tasks/PROMPT_TASK_014_MENU_FUNCTIONAL_PARITY_QA.md +157 -0
- package/docs/prompt-tasks/PROMPT_TASK_015_TRANSFER_RELEASE_HANDOFF.md +127 -0
- package/docs/prompt-tasks/PROMPT_TASK_016_CROSS_PLATFORM_MCP_AUTH_REGISTRATION.md +107 -0
- package/docs/prompt-tasks/PROMPT_TASK_018_NPM_PUBLISH_2FA_SETUP.md +80 -0
- package/docs/prompt-tasks/PROMPT_TASK_MASTER_EXECUTOR.md +6 -0
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/SKILL.md +399 -0
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/common-patterns.md +331 -0
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/complete-examples.md +872 -0
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/component-patterns.md +502 -0
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/data-fetching.md +767 -0
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/file-organization.md +502 -0
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/loading-and-error-states.md +501 -0
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/performance.md +406 -0
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/routing-guide.md +364 -0
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/styling-guide.md +428 -0
- package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/typescript-standards.md +418 -0
- package/docs/skill-candidates/v0.0.11/git-version-control-ops/SKILL.md +34 -0
- package/docs/skill-candidates/v0.0.11/go-engineering/SKILL.md +34 -0
- package/docs/skill-candidates/v0.0.11/java-engineering/SKILL.md +34 -0
- package/docs/skill-candidates/v0.0.11/javascript-engineering/SKILL.md +34 -0
- package/docs/skill-candidates/v0.0.11/json-contract-design/SKILL.md +34 -0
- package/docs/skill-candidates/v0.0.11/multi-client-mcp-ops/SKILL.md +36 -0
- package/docs/skill-candidates/v0.0.11/nextjs/SKILL.md +745 -0
- package/docs/skill-candidates/v0.0.11/nextjs/agents/openai.yaml +3 -0
- package/docs/skill-candidates/v0.0.11/nextjs/references/app-router-files.md +94 -0
- package/docs/skill-candidates/v0.0.11/python-engineering/SKILL.md +34 -0
- package/docs/skill-candidates/v0.0.11/ruby-engineering/SKILL.md +34 -0
- package/docs/skill-candidates/v0.0.11/senior-fullstack/SKILL.md +209 -0
- package/docs/skill-candidates/v0.0.11/senior-fullstack/references/architecture_patterns.md +103 -0
- package/docs/skill-candidates/v0.0.11/senior-fullstack/references/development_workflows.md +103 -0
- package/docs/skill-candidates/v0.0.11/senior-fullstack/references/tech_stack_guide.md +103 -0
- package/docs/skill-candidates/v0.0.11/senior-fullstack/scripts/code_quality_analyzer.py +114 -0
- package/docs/skill-candidates/v0.0.11/senior-fullstack/scripts/fullstack_scaffolder.py +114 -0
- package/docs/skill-candidates/v0.0.11/senior-fullstack/scripts/project_scaffolder.py +114 -0
- package/docs/skill-candidates/v0.0.11/shadcn/SKILL.md +573 -0
- package/docs/skill-candidates/v0.0.11/shadcn/agents/openai.yaml +3 -0
- package/docs/skill-candidates/v0.0.11/sql-postgresql-engineering/SKILL.md +34 -0
- package/docs/skill-candidates/v0.0.11/terminal-shell-ops/SKILL.md +34 -0
- package/docs/skill-candidates/v0.0.11/typescript-expert/SKILL.md +429 -0
- package/docs/skill-candidates/v0.0.11/typescript-expert/references/tsconfig-strict.json +92 -0
- package/docs/skill-candidates/v0.0.11/typescript-expert/references/typescript-cheatsheet.md +383 -0
- package/docs/skill-candidates/v0.0.11/typescript-expert/references/utility-types.ts +335 -0
- package/docs/skill-candidates/v0.0.11/typescript-expert/scripts/ts_diagnostic.py +203 -0
- package/docs/skill-candidates/v0.0.11/ui-component-primitives/SKILL.md +34 -0
- package/docs/skill-candidates/v0.0.11/web-mobile-design-systems/SKILL.md +34 -0
- package/docs/skill-candidates/v0.0.11/windows-linux-platform-ops/SKILL.md +34 -0
- package/docs/skill-candidates/v0.0.12/csharp-senior-master-engineering/SKILL.md +32 -0
- package/docs/skill-candidates/v0.0.12/css-senior-master-engineering/SKILL.md +32 -0
- package/docs/skill-candidates/v0.0.12/go-senior-master-engineering/SKILL.md +32 -0
- package/docs/skill-candidates/v0.0.12/html-senior-master-engineering/SKILL.md +32 -0
- package/docs/skill-candidates/v0.0.12/javascript-senior-master-engineering/SKILL.md +32 -0
- package/docs/skill-candidates/v0.0.12/json-senior-master-engineering/SKILL.md +32 -0
- package/docs/skill-candidates/v0.0.12/python-senior-master-engineering/SKILL.md +32 -0
- package/docs/skill-candidates/v0.0.12/react-senior-master-engineering/SKILL.md +32 -0
- package/docs/skill-candidates/v0.0.12/ruby-senior-master-engineering/SKILL.md +32 -0
- package/docs/skill-candidates/v0.0.12/senior-master-code-optimizer/SKILL.md +48 -0
- package/docs/skill-candidates/v0.0.12/sql-senior-master-engineering/SKILL.md +31 -0
- package/docs/skill-candidates/v0.0.12/typescript-senior-master-engineering/SKILL.md +35 -0
- package/examples/client-configs/claude-code.commands.md +11 -7
- package/manifests/channels/beta.json +7 -7
- package/manifests/channels/stable.json +8 -8
- package/package.json +14 -2
- package/scripts/render-menu-evidence.mjs +130 -0
- package/scripts/verify-menu-actions.mjs +117 -0
package/bin/lib/menu-core.mjs
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
3
3
|
import os from 'node:os';
|
|
4
|
-
import { join } from 'node:path';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
5
|
import process from 'node:process';
|
|
6
|
+
import {
|
|
7
|
+
assessCodexConfigContent,
|
|
8
|
+
assessMcpServerConfig,
|
|
9
|
+
defaultClientConfigPaths,
|
|
10
|
+
} from './client-config.mjs';
|
|
6
11
|
|
|
7
12
|
const ANSI = {
|
|
8
13
|
reset: '\x1b[0m',
|
|
@@ -33,6 +38,18 @@ const REQUIRED_GLOBAL_SKILLS = [
|
|
|
33
38
|
'mcp-client-readiness',
|
|
34
39
|
'terminal-menu-operations',
|
|
35
40
|
'terminal-pixel-art-tui',
|
|
41
|
+
'senior-master-code-optimizer',
|
|
42
|
+
'typescript-senior-master-engineering',
|
|
43
|
+
'python-senior-master-engineering',
|
|
44
|
+
'javascript-senior-master-engineering',
|
|
45
|
+
'go-senior-master-engineering',
|
|
46
|
+
'sql-senior-master-engineering',
|
|
47
|
+
'json-senior-master-engineering',
|
|
48
|
+
'ruby-senior-master-engineering',
|
|
49
|
+
'react-senior-master-engineering',
|
|
50
|
+
'html-senior-master-engineering',
|
|
51
|
+
'css-senior-master-engineering',
|
|
52
|
+
'csharp-senior-master-engineering',
|
|
36
53
|
];
|
|
37
54
|
|
|
38
55
|
const BUNDLED_SKILL_DOMAIN_GROUPS = [
|
|
@@ -63,11 +80,20 @@ const BUNDLED_SKILL_DOMAIN_GROUPS = [
|
|
|
63
80
|
'figma-generate-library',
|
|
64
81
|
'figma-implement-design',
|
|
65
82
|
'figma-use',
|
|
83
|
+
'frontend-dev-guidelines',
|
|
66
84
|
'frontend-design',
|
|
67
85
|
'frontend-ui-ux-systems',
|
|
86
|
+
'html-senior-master-engineering',
|
|
87
|
+
'nextjs',
|
|
68
88
|
'playwright',
|
|
89
|
+
'react-senior-master-engineering',
|
|
69
90
|
'screenshot',
|
|
91
|
+
'senior-fullstack',
|
|
92
|
+
'shadcn',
|
|
93
|
+
'css-senior-master-engineering',
|
|
94
|
+
'ui-component-primitives',
|
|
70
95
|
'webapp-testing',
|
|
96
|
+
'web-mobile-design-systems',
|
|
71
97
|
'winui-app',
|
|
72
98
|
],
|
|
73
99
|
},
|
|
@@ -75,8 +101,25 @@ const BUNDLED_SKILL_DOMAIN_GROUPS = [
|
|
|
75
101
|
key: 'backend',
|
|
76
102
|
label: 'backend-data',
|
|
77
103
|
skills: [
|
|
104
|
+
'csharp-senior-master-engineering',
|
|
105
|
+
'go-engineering',
|
|
106
|
+
'go-senior-master-engineering',
|
|
107
|
+
'java-engineering',
|
|
108
|
+
'javascript-engineering',
|
|
109
|
+
'javascript-senior-master-engineering',
|
|
110
|
+
'json-contract-design',
|
|
111
|
+
'json-senior-master-engineering',
|
|
78
112
|
'mcp-builder',
|
|
79
113
|
'polyglot-backend-engineering',
|
|
114
|
+
'python-engineering',
|
|
115
|
+
'python-senior-master-engineering',
|
|
116
|
+
'ruby-engineering',
|
|
117
|
+
'ruby-senior-master-engineering',
|
|
118
|
+
'senior-master-code-optimizer',
|
|
119
|
+
'sql-postgresql-engineering',
|
|
120
|
+
'sql-senior-master-engineering',
|
|
121
|
+
'typescript-expert',
|
|
122
|
+
'typescript-senior-master-engineering',
|
|
80
123
|
],
|
|
81
124
|
},
|
|
82
125
|
{
|
|
@@ -85,12 +128,16 @@ const BUNDLED_SKILL_DOMAIN_GROUPS = [
|
|
|
85
128
|
skills: [
|
|
86
129
|
'cli-creator',
|
|
87
130
|
'developer-workstation-ops',
|
|
131
|
+
'git-version-control-ops',
|
|
88
132
|
'github',
|
|
89
133
|
'mcp-client-readiness',
|
|
134
|
+
'multi-client-mcp-ops',
|
|
90
135
|
'openai-docs',
|
|
91
136
|
'skill-master-orchestrator',
|
|
137
|
+
'terminal-shell-ops',
|
|
92
138
|
'terminal-menu-operations',
|
|
93
139
|
'terminal-pixel-art-tui',
|
|
140
|
+
'windows-linux-platform-ops',
|
|
94
141
|
],
|
|
95
142
|
},
|
|
96
143
|
{
|
|
@@ -115,6 +162,7 @@ const COMMAND_AREA_LABELS = {
|
|
|
115
162
|
activationBalanced: 'ativacao',
|
|
116
163
|
activationAlwaysOn: 'ativacao',
|
|
117
164
|
installGlobalSkills: 'skills-globais',
|
|
165
|
+
installProjectSkills: 'skills-projeto',
|
|
118
166
|
bootstrapGlobal: 'bootstrap-global',
|
|
119
167
|
registerClients: 'clientes-mcp',
|
|
120
168
|
promptRecommendation: 'roteamento',
|
|
@@ -145,6 +193,33 @@ function fitText(text, width) {
|
|
|
145
193
|
return `${value.slice(0, Math.max(0, width - 1))}~`;
|
|
146
194
|
}
|
|
147
195
|
|
|
196
|
+
function truncateVisibleText(text, width) {
|
|
197
|
+
if (width <= 0) return '';
|
|
198
|
+
const value = String(text ?? '');
|
|
199
|
+
let output = '';
|
|
200
|
+
let visible = 0;
|
|
201
|
+
let hasOpenAnsi = false;
|
|
202
|
+
|
|
203
|
+
for (let index = 0; index < value.length && visible < width;) {
|
|
204
|
+
if (value[index] === '\x1b' && value[index + 1] === '[') {
|
|
205
|
+
const end = value.indexOf('m', index);
|
|
206
|
+
if (end === -1) break;
|
|
207
|
+
const sequence = value.slice(index, end + 1);
|
|
208
|
+
output += sequence;
|
|
209
|
+
hasOpenAnsi = sequence !== ANSI.reset;
|
|
210
|
+
index = end + 1;
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const [char] = Array.from(value.slice(index));
|
|
215
|
+
output += char;
|
|
216
|
+
visible += 1;
|
|
217
|
+
index += char.length;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return hasOpenAnsi ? `${output}${ANSI.reset}` : output;
|
|
221
|
+
}
|
|
222
|
+
|
|
148
223
|
function splitText(text, width, maxLines = 3) {
|
|
149
224
|
const words = String(text ?? '').split(/\s+/).filter(Boolean);
|
|
150
225
|
const lines = [];
|
|
@@ -174,58 +249,45 @@ function readClientConfigState(filePath) {
|
|
|
174
249
|
}
|
|
175
250
|
|
|
176
251
|
const content = readFileSync(filePath, 'utf8');
|
|
177
|
-
const
|
|
178
|
-
const hasGlobalCommand = content.includes('command = "skill-master-mcp"') || content.includes("command = 'skill-master-mcp'");
|
|
179
|
-
const hasLauncher = content.includes('skill-master-launcher');
|
|
252
|
+
const assessment = assessCodexConfigContent(content);
|
|
180
253
|
|
|
181
254
|
return {
|
|
182
255
|
present: true,
|
|
183
|
-
kind:
|
|
184
|
-
globalCommand:
|
|
256
|
+
kind: assessment.mode,
|
|
257
|
+
globalCommand: assessment.robust,
|
|
258
|
+
command: assessment.command,
|
|
185
259
|
};
|
|
186
260
|
}
|
|
187
261
|
|
|
188
262
|
function readJsonState(filePath) {
|
|
189
263
|
if (!existsSync(filePath)) {
|
|
190
|
-
return { present: false, globalCommand: false };
|
|
264
|
+
return { present: false, kind: 'missing', globalCommand: false };
|
|
191
265
|
}
|
|
192
266
|
|
|
193
267
|
try {
|
|
194
268
|
const parsed = JSON.parse(readFileSync(filePath, 'utf8'));
|
|
195
269
|
const server = parsed?.mcpServers?.skill_master;
|
|
270
|
+
const assessment = assessMcpServerConfig(server);
|
|
196
271
|
return {
|
|
197
272
|
present: true,
|
|
198
|
-
|
|
273
|
+
kind: assessment.mode,
|
|
274
|
+
globalCommand: assessment.robust,
|
|
275
|
+
command: assessment.command,
|
|
199
276
|
};
|
|
200
277
|
} catch {
|
|
201
|
-
return { present: true, globalCommand: false };
|
|
278
|
+
return { present: true, kind: 'invalid-json', globalCommand: false };
|
|
202
279
|
}
|
|
203
280
|
}
|
|
204
281
|
|
|
205
|
-
function defaultClaudeConfigPath() {
|
|
206
|
-
if (process.platform === 'win32') {
|
|
207
|
-
return join(process.env.APPDATA ?? join(os.homedir(), 'AppData', 'Roaming'), 'Claude', 'claude_desktop_config.json');
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
if (process.platform === 'darwin') {
|
|
211
|
-
return join(os.homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
return join(os.homedir(), '.config', 'Claude', 'claude_desktop_config.json');
|
|
215
|
-
}
|
|
216
|
-
|
|
217
282
|
function inspectGlobalReadiness() {
|
|
218
283
|
const root = globalSkillsRoot();
|
|
219
284
|
const installed = REQUIRED_GLOBAL_SKILLS.filter((name) => existsSync(join(root, name, 'SKILL.md')));
|
|
220
285
|
const missing = REQUIRED_GLOBAL_SKILLS.filter((name) => !installed.includes(name));
|
|
221
|
-
const
|
|
222
|
-
const
|
|
223
|
-
const
|
|
224
|
-
const
|
|
225
|
-
const
|
|
226
|
-
const claude = readJsonState(claudeConfigPath);
|
|
227
|
-
const gemini = readJsonState(geminiConfigPath);
|
|
228
|
-
const antigravity = readJsonState(antigravityConfigPath);
|
|
286
|
+
const clientPaths = defaultClientConfigPaths();
|
|
287
|
+
const codex = readClientConfigState(clientPaths.codex);
|
|
288
|
+
const claude = readJsonState(clientPaths.claude);
|
|
289
|
+
const gemini = readJsonState(clientPaths.gemini);
|
|
290
|
+
const antigravity = readJsonState(clientPaths.antigravity);
|
|
229
291
|
const ready = missing.length === 0
|
|
230
292
|
&& codex.globalCommand
|
|
231
293
|
&& claude.globalCommand
|
|
@@ -233,7 +295,7 @@ function inspectGlobalReadiness() {
|
|
|
233
295
|
&& antigravity.globalCommand;
|
|
234
296
|
const mode = ready
|
|
235
297
|
? 'ready'
|
|
236
|
-
: codex.
|
|
298
|
+
: codex.present || claude.present || gemini.present || antigravity.present
|
|
237
299
|
? 'partial'
|
|
238
300
|
: 'missing';
|
|
239
301
|
|
|
@@ -380,9 +442,28 @@ function formatActionCommand(action) {
|
|
|
380
442
|
return [action.command, ...(action.args ?? [])].join(' ');
|
|
381
443
|
}
|
|
382
444
|
|
|
383
|
-
|
|
445
|
+
function resolveNpmCommand(nodeExecPath) {
|
|
446
|
+
if (process.platform !== 'win32') {
|
|
447
|
+
return { command: 'npm', argsPrefix: [], shell: false };
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const candidates = [
|
|
451
|
+
process.env.npm_execpath,
|
|
452
|
+
join(dirname(nodeExecPath), 'node_modules', 'npm', 'bin', 'npm-cli.js'),
|
|
453
|
+
].filter(Boolean);
|
|
454
|
+
const npmCli = candidates.find((candidate) => existsSync(candidate));
|
|
455
|
+
|
|
456
|
+
if (npmCli) {
|
|
457
|
+
return { command: nodeExecPath, argsPrefix: [npmCli], shell: false };
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
return { command: 'npm.cmd', argsPrefix: [], shell: true };
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
export function buildMenuCommands({ rootDir, currentFile, nodeExecPath = process.execPath, invocationCwd = process.cwd() }) {
|
|
384
464
|
const isDevCheckout = existsSync(join(rootDir, 'tsconfig.json')) && existsSync(join(rootDir, 'src'));
|
|
385
465
|
const repoOnlyReason = 'Disponivel apenas no clone de desenvolvimento; no pacote npm use Doctor ou Status.';
|
|
466
|
+
const npmCommand = resolveNpmCommand(nodeExecPath);
|
|
386
467
|
|
|
387
468
|
return [
|
|
388
469
|
{
|
|
@@ -399,7 +480,7 @@ export function buildMenuCommands({ rootDir, currentFile, nodeExecPath = process
|
|
|
399
480
|
key: 'doctor',
|
|
400
481
|
aliases: ['doctor', 'diagnostico', 'verificar-menu', 'validar-menu'],
|
|
401
482
|
label: 'Doctor do menu e clientes MCP',
|
|
402
|
-
description: 'Valida pacote, binarios, skills globais e registros em Codex, Claude, Gemini e Antigravity.',
|
|
483
|
+
description: 'Valida pacote, binarios, skills globais e registros em Codex, Claude Desktop, Claude Code, Gemini e Antigravity.',
|
|
403
484
|
details: ['Seguro para outros notebooks.', 'Nao publica versao e nao mexe em configuracoes sem uma acao separada.'],
|
|
404
485
|
success: 'Mostra um relatorio GO/NO-GO com proximos comandos recomendados.',
|
|
405
486
|
command: nodeExecPath,
|
|
@@ -413,8 +494,9 @@ export function buildMenuCommands({ rootDir, currentFile, nodeExecPath = process
|
|
|
413
494
|
details: ['Exige clone do repositorio com src, testes e tsconfig.', 'Nao e uma acao indicada para instalacao global via npm.'],
|
|
414
495
|
success: 'Build, testes e manifestos passam no checkout de desenvolvimento.',
|
|
415
496
|
disabledReason: isDevCheckout ? null : repoOnlyReason,
|
|
416
|
-
command:
|
|
417
|
-
args: ['run', 'check'],
|
|
497
|
+
command: npmCommand.command,
|
|
498
|
+
args: [...npmCommand.argsPrefix, 'run', 'check'],
|
|
499
|
+
shell: npmCommand.shell,
|
|
418
500
|
},
|
|
419
501
|
{
|
|
420
502
|
key: 'build',
|
|
@@ -424,8 +506,9 @@ export function buildMenuCommands({ rootDir, currentFile, nodeExecPath = process
|
|
|
424
506
|
details: ['Exige clone do repositorio com tsconfig.', 'Pacotes npm publicados ja usam dist precompilado.'],
|
|
425
507
|
success: 'dist atualizado no checkout de desenvolvimento.',
|
|
426
508
|
disabledReason: isDevCheckout ? null : repoOnlyReason,
|
|
427
|
-
command:
|
|
428
|
-
args: ['run', 'build'],
|
|
509
|
+
command: npmCommand.command,
|
|
510
|
+
args: [...npmCommand.argsPrefix, 'run', 'build'],
|
|
511
|
+
shell: npmCommand.shell,
|
|
429
512
|
},
|
|
430
513
|
{
|
|
431
514
|
key: 'publicNpm',
|
|
@@ -434,13 +517,15 @@ export function buildMenuCommands({ rootDir, currentFile, nodeExecPath = process
|
|
|
434
517
|
description: 'Consulta a versao publicada em registry.npmjs.org.',
|
|
435
518
|
details: ['Usa somente leitura no registry publico.', 'Bom para confirmar se outro notebook consegue instalar por npm.'],
|
|
436
519
|
success: 'Retorna a versao publicada atualmente no npmjs.',
|
|
437
|
-
command:
|
|
520
|
+
command: npmCommand.command,
|
|
438
521
|
args: [
|
|
522
|
+
...npmCommand.argsPrefix,
|
|
439
523
|
'view',
|
|
440
524
|
'@fprad0/skill-master-mcp',
|
|
441
525
|
'version',
|
|
442
526
|
'--registry=https://registry.npmjs.org',
|
|
443
527
|
],
|
|
528
|
+
shell: npmCommand.shell,
|
|
444
529
|
},
|
|
445
530
|
{
|
|
446
531
|
key: 'updateGlobal',
|
|
@@ -506,6 +591,18 @@ export function buildMenuCommands({ rootDir, currentFile, nodeExecPath = process
|
|
|
506
591
|
args: [join(rootDir, 'bin', 'skill-master-install-global-skills.mjs')],
|
|
507
592
|
confirmMessage: 'Instalar as skills globais embutidas neste usuario?',
|
|
508
593
|
},
|
|
594
|
+
{
|
|
595
|
+
key: 'installProjectSkills',
|
|
596
|
+
aliases: ['install-project-skills', 'skills-projeto', 'project-skills', 'agents-skills'],
|
|
597
|
+
label: 'Instalar skills no projeto atual',
|
|
598
|
+
description: 'Copia o bundle para .agents/skills, .codex/skills, .claude/skills e .gemini/skills do projeto onde o menu foi aberto.',
|
|
599
|
+
details: ['Registra .skill-master/catalog.json e metadados em package.json quando existir.', `Projeto alvo: ${invocationCwd}`],
|
|
600
|
+
success: 'Skills de projeto copiadas e catalogo local atualizado.',
|
|
601
|
+
command: nodeExecPath,
|
|
602
|
+
args: [join(rootDir, 'bin', 'skill-master-install-project-skills.mjs'), '--project-root', invocationCwd, '--sync-package-json'],
|
|
603
|
+
cwd: invocationCwd,
|
|
604
|
+
confirmMessage: 'Instalar e catalogar as skills do Skill Master no projeto atual?',
|
|
605
|
+
},
|
|
509
606
|
{
|
|
510
607
|
key: 'bootstrapGlobal',
|
|
511
608
|
aliases: ['bootstrap-global', 'global-bootstrap', 'ativar-global-completo'],
|
|
@@ -521,8 +618,8 @@ export function buildMenuCommands({ rootDir, currentFile, nodeExecPath = process
|
|
|
521
618
|
key: 'registerClients',
|
|
522
619
|
aliases: ['register-clients', 'registrar-clientes', 'codex-claude-gemini', 'antigravity'],
|
|
523
620
|
label: 'Registrar clientes MCP',
|
|
524
|
-
description: 'Gera/aplica configuracoes MCP para Codex, Claude, Gemini e Antigravity reconhecerem skill_master.',
|
|
525
|
-
details: ['Usa o
|
|
621
|
+
description: 'Gera/aplica configuracoes MCP para Codex, Claude Desktop, Claude Code, Gemini e Antigravity reconhecerem skill_master.',
|
|
622
|
+
details: ['Usa Node absoluto e o entrypoint absoluto do pacote.', 'Claude Code e registrado via claude mcp add quando o CLI esta disponivel.'],
|
|
526
623
|
success: 'Arquivos de configuracao mesclados com o servidor skill_master.',
|
|
527
624
|
command: nodeExecPath,
|
|
528
625
|
args: [join(rootDir, 'bin', 'skill-master-register-clients.mjs'), '--apply-all'],
|
|
@@ -668,10 +765,10 @@ export function formatStatusReport(status) {
|
|
|
668
765
|
`Global skills instaladas: ${readiness.installed.length}/${readiness.required}`,
|
|
669
766
|
`Bundle de skills embutidas: ${bundledCatalog.total}`,
|
|
670
767
|
`Dominios do bundle: ${formatBundledCategoryLine(bundledCatalog)}`,
|
|
671
|
-
`Codex
|
|
672
|
-
`Claude
|
|
673
|
-
`Gemini
|
|
674
|
-
`Antigravity
|
|
768
|
+
`Codex MCP robusto: ${readiness.codex.globalCommand ? 'sim' : readiness.codex.kind}`,
|
|
769
|
+
`Claude MCP robusto: ${readiness.claude.globalCommand ? 'sim' : readiness.claude.present ? readiness.claude.kind : 'ausente'}`,
|
|
770
|
+
`Gemini MCP robusto: ${readiness.gemini.globalCommand ? 'sim' : readiness.gemini.present ? readiness.gemini.kind : 'ausente'}`,
|
|
771
|
+
`Antigravity MCP robusto: ${readiness.antigravity.globalCommand ? 'sim' : readiness.antigravity.present ? readiness.antigravity.kind : 'ausente'}`,
|
|
675
772
|
];
|
|
676
773
|
|
|
677
774
|
if (status.versionText) {
|
|
@@ -688,8 +785,8 @@ function formatGlobalAlert(status, { useColor = false } = {}) {
|
|
|
688
785
|
return renderPanelLines(
|
|
689
786
|
[
|
|
690
787
|
colorize('GLOBAL READY', ANSI.bold, useColor),
|
|
691
|
-
'O MCP esta
|
|
692
|
-
'Codex, Claude, Gemini e Antigravity podem
|
|
788
|
+
'O MCP esta registrado com Node absoluto neste computador.',
|
|
789
|
+
'Codex, Claude Desktop, Claude Code, Gemini e Antigravity podem iniciar o servidor sem depender do PATH.',
|
|
693
790
|
'A instalacao global e as skills embutidas ja estao prontas para uso.',
|
|
694
791
|
],
|
|
695
792
|
{ color: ANSI.green, useColor },
|
|
@@ -700,9 +797,10 @@ function formatGlobalAlert(status, { useColor = false } = {}) {
|
|
|
700
797
|
renderPanelLines(
|
|
701
798
|
[
|
|
702
799
|
colorize('ALERTA GLOBAL', ANSI.bold, useColor),
|
|
703
|
-
'Este computador ainda nao esta pronto para uso
|
|
800
|
+
'Este computador ainda nao esta pronto para uso robusto do Skill Master.',
|
|
704
801
|
'Execute agora: skill-master-menu --run bootstrap-global --yes',
|
|
705
|
-
'Isso instala as skills globais e registra Codex, Claude, Gemini e Antigravity.',
|
|
802
|
+
'Isso instala as skills globais e registra Codex, Claude Desktop, Claude Code, Gemini e Antigravity com Node absoluto.',
|
|
803
|
+
'Ou migre somente os clientes: skill-master-register-clients --apply-all --force',
|
|
706
804
|
],
|
|
707
805
|
{ color: ANSI.red, useColor },
|
|
708
806
|
),
|
|
@@ -718,11 +816,165 @@ function formatGlobalAlert(status, { useColor = false } = {}) {
|
|
|
718
816
|
}
|
|
719
817
|
|
|
720
818
|
function renderPanelLines(lines, { color = ANSI.cyan, useColor = false } = {}) {
|
|
721
|
-
const width = Math.max(...lines.map((line) => line
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
819
|
+
const width = Math.max(...lines.map((line) => visibleLength(line)), 24) + 4;
|
|
820
|
+
return renderCyberFrame(
|
|
821
|
+
lines.map((line) => fitText(line, width - 4)),
|
|
822
|
+
width,
|
|
823
|
+
'skill-master',
|
|
824
|
+
{ color, useColor, style: 'data' },
|
|
825
|
+
).join('\n');
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
function patternFill(pattern, width) {
|
|
829
|
+
if (width <= 0) return '';
|
|
830
|
+
return Array.from({ length: Math.ceil(width / pattern.length) }, () => pattern).join('').slice(0, width);
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
function frameTokens(style) {
|
|
834
|
+
if (style === 'focus') {
|
|
835
|
+
return { topLeft: '╭╼', topRight: '╾╮', bottomLeft: '╰╼', bottomRight: '╾╯', fill: '═', left: '▌ ', right: ' ▐', accent: '▰', micro: '▱' };
|
|
836
|
+
}
|
|
837
|
+
if (style === 'hud') {
|
|
838
|
+
return { topLeft: '┏╾', topRight: '╼┓', bottomLeft: '┗╾', bottomRight: '╼┛', fill: '─', left: '▌ ', right: ' ▐', accent: '▰', micro: '▱' };
|
|
839
|
+
}
|
|
840
|
+
if (style === 'running') {
|
|
841
|
+
return { topLeft: '╔╼', topRight: '╾╗', bottomLeft: '╚╼', bottomRight: '╾╝', fill: '═', left: '▌▌', right: '▐▐', accent: '▰', micro: '▱' };
|
|
842
|
+
}
|
|
843
|
+
if (style === 'thin') {
|
|
844
|
+
return { topLeft: '┌╴', topRight: '╶┐', bottomLeft: '└╴', bottomRight: '╶┘', fill: '─', left: '│ ', right: ' │', accent: '▱', micro: '╍' };
|
|
845
|
+
}
|
|
846
|
+
return { topLeft: '╭╴', topRight: '╶╮', bottomLeft: '╰╴', bottomRight: '╶╯', fill: '─', left: '│ ', right: ' │', accent: '▰', micro: '▱' };
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
function fillWithPattern(text, width, fill) {
|
|
850
|
+
const length = visibleLength(text);
|
|
851
|
+
if (length >= width) return fitText(text, width);
|
|
852
|
+
return `${text}${patternFill(fill, width - length)}`;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
function renderCyberRail(width, title, tokens, { bottom = false, tick = 0 } = {}) {
|
|
856
|
+
const left = bottom ? tokens.bottomLeft : tokens.topLeft;
|
|
857
|
+
const right = bottom ? tokens.bottomRight : tokens.topRight;
|
|
858
|
+
const innerWidth = Math.max(0, width - visibleLength(left) - visibleLength(right));
|
|
859
|
+
const phaseMicro = tick % 2 === 0 ? tokens.micro : tokens.fill;
|
|
860
|
+
const titleText = !bottom && title ? ` ${String(title).toUpperCase()} ` : '';
|
|
861
|
+
const titleWidth = titleText ? Math.min(visibleLength(titleText), Math.max(0, innerWidth - 8)) : 0;
|
|
862
|
+
const safeTitle = titleText ? fitText(titleText, titleWidth).trimEnd() : '';
|
|
863
|
+
|
|
864
|
+
if (bottom) {
|
|
865
|
+
const lead = patternFill(tokens.fill, Math.max(2, Math.floor(innerWidth * 0.18)));
|
|
866
|
+
const plate = innerWidth >= 34 ? `${tokens.accent}${tokens.accent}${tokens.accent}` : tokens.accent;
|
|
867
|
+
const vent = innerWidth >= 44 ? patternFill(`${phaseMicro}${tokens.fill}`, 8) : '';
|
|
868
|
+
return `${left}${fillWithPattern(`${lead}${plate}${tokens.fill}${vent}`, innerWidth, tokens.fill)}${right}`;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
const leadWidth = Math.max(2, Math.min(10, Math.floor(innerWidth * 0.16)));
|
|
872
|
+
const lead = patternFill(tokens.fill, leadWidth);
|
|
873
|
+
const chip = innerWidth >= 36 ? `${tokens.accent}${phaseMicro}${phaseMicro}` : tokens.accent;
|
|
874
|
+
const tab = innerWidth >= 48 ? `${tokens.fill}${tokens.fill}${tokens.accent}${tokens.fill}` : tokens.fill;
|
|
875
|
+
return `${left}${fillWithPattern(`${lead}${safeTitle}${chip}${tab}`, innerWidth, tokens.fill)}${right}`;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
function renderCyberFrame(lines, width, title, { useColor = false, color = ANSI.cyan, style = 'data', tick = 0 } = {}) {
|
|
879
|
+
const safeWidth = Math.max(16, width);
|
|
880
|
+
const innerWidth = safeWidth - 4;
|
|
881
|
+
const tokens = frameTokens(style);
|
|
882
|
+
const top = renderCyberRail(safeWidth, title, tokens, { tick });
|
|
883
|
+
const bottom = renderCyberRail(safeWidth, title, tokens, { bottom: true, tick });
|
|
884
|
+
const body = lines.map((line, index) => {
|
|
885
|
+
const marker = style === 'running' && index % 3 === Math.abs(tick % 3)
|
|
886
|
+
? tokens.accent
|
|
887
|
+
: index === 0 || index === lines.length - 1
|
|
888
|
+
? tokens.micro
|
|
889
|
+
: ' ';
|
|
890
|
+
const content = fitText(`${marker}${truncateVisibleText(line, Math.max(0, innerWidth - 1))}`, innerWidth);
|
|
891
|
+
return `${colorize(tokens.left, color, useColor)}${content}${colorize(tokens.right, color, useColor)}`;
|
|
892
|
+
});
|
|
893
|
+
|
|
894
|
+
return [
|
|
895
|
+
colorize(top, color, useColor),
|
|
896
|
+
...body,
|
|
897
|
+
colorize(bottom, color, useColor),
|
|
898
|
+
];
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
function renderCircuitRail(width, tick, { useColor = false, color = ANSI.teal } = {}) {
|
|
902
|
+
const patterns = ['<==/==>--', '<=/===/>-', '<<==--==>', '<==\\==>--'];
|
|
903
|
+
return colorize(patternFill(patterns[Math.abs(tick) % patterns.length], width), color, useColor);
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
function renderScanGrid(width, height, tick, { useColor = false } = {}) {
|
|
907
|
+
const lines = [];
|
|
908
|
+
const scanCol = width > 0 ? tick % width : 0;
|
|
909
|
+
const scanRow = height > 0 ? Math.floor(tick / 2) % height : 0;
|
|
910
|
+
|
|
911
|
+
for (let row = 0; row < height; row += 1) {
|
|
912
|
+
const cells = Array.from({ length: width }, (_, col) => {
|
|
913
|
+
if (row === scanRow && col % 3 === 0) return colorize('-', ANSI.white, useColor);
|
|
914
|
+
if (col === scanCol && row % 2 === 0) return colorize('|', ANSI.teal, useColor);
|
|
915
|
+
if ((row + col + tick) % 17 === 0) return colorize('+', ANSI.amber, useColor);
|
|
916
|
+
if (row % 4 === 0 && col % 8 === 0) return colorize('.', ANSI.gray, useColor);
|
|
917
|
+
return ' ';
|
|
918
|
+
});
|
|
919
|
+
lines.push(cells.join(''));
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
return lines;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
const BRAILLE_DOTS = [
|
|
926
|
+
[0x01, 0x02, 0x04, 0x40],
|
|
927
|
+
[0x08, 0x10, 0x20, 0x80],
|
|
928
|
+
];
|
|
929
|
+
|
|
930
|
+
function createBrailleCanvas(width, height) {
|
|
931
|
+
return {
|
|
932
|
+
width,
|
|
933
|
+
height,
|
|
934
|
+
dots: Array.from({ length: height }, () => Array.from({ length: width }, () => 0)),
|
|
935
|
+
colors: Array.from({ length: height }, () => Array.from({ length: width }, () => 'gray')),
|
|
936
|
+
priorities: Array.from({ length: height }, () => Array.from({ length: width }, () => -1)),
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
function plotBraille(canvas, x, y, color = 'gray', priority = 0) {
|
|
941
|
+
if (x < 0 || y < 0) return;
|
|
942
|
+
const cellX = Math.floor(x / 2);
|
|
943
|
+
const cellY = Math.floor(y / 4);
|
|
944
|
+
if (cellX < 0 || cellY < 0 || cellX >= canvas.width || cellY >= canvas.height) return;
|
|
945
|
+
canvas.dots[cellY][cellX] |= BRAILLE_DOTS[x % 2][y % 4];
|
|
946
|
+
if (priority >= canvas.priorities[cellY][cellX]) {
|
|
947
|
+
canvas.colors[cellY][cellX] = color;
|
|
948
|
+
canvas.priorities[cellY][cellX] = priority;
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
function plotBrailleThick(canvas, x, y, radius = 0, color = 'gray', priority = 0) {
|
|
953
|
+
for (let dy = -radius; dy <= radius; dy += 1) {
|
|
954
|
+
for (let dx = -radius; dx <= radius; dx += 1) {
|
|
955
|
+
if (Math.abs(dx) + Math.abs(dy) <= radius + 1) {
|
|
956
|
+
plotBraille(canvas, x + dx, y + dy, color, priority);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
function drawBrailleLine(canvas, x0, y0, x1, y1, thickness = 0, color = 'gray', priority = 0) {
|
|
963
|
+
const steps = Math.max(Math.abs(x1 - x0), Math.abs(y1 - y0), 1);
|
|
964
|
+
for (let step = 0; step <= steps; step += 1) {
|
|
965
|
+
const ratio = step / steps;
|
|
966
|
+
const x = Math.round(x0 + (x1 - x0) * ratio);
|
|
967
|
+
const y = Math.round(y0 + (y1 - y0) * ratio);
|
|
968
|
+
plotBrailleThick(canvas, x, y, thickness, color, priority);
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
function renderBrailleCanvas(canvas, { useColor = false } = {}) {
|
|
973
|
+
return canvas.dots.map((row, rowIndex) => row.map((value, colIndex) => {
|
|
974
|
+
if (value === 0) return ' ';
|
|
975
|
+
const color = ANSI[canvas.colors[rowIndex][colIndex]] ?? ANSI.teal;
|
|
976
|
+
return colorize(String.fromCodePoint(0x2800 + value), color, useColor);
|
|
977
|
+
}).join(''));
|
|
726
978
|
}
|
|
727
979
|
|
|
728
980
|
export function formatMenuBanner(status, { useColor = false } = {}) {
|
|
@@ -742,71 +994,83 @@ export function formatMenuBanner(status, { useColor = false } = {}) {
|
|
|
742
994
|
].join('\n\n');
|
|
743
995
|
}
|
|
744
996
|
|
|
745
|
-
function dnaPanelLines(tick, width, height, status, { useColor = false, compact = false } = {}) {
|
|
997
|
+
function dnaPanelLines(tick, width, height, status, selected, { useColor = false, compact = false } = {}) {
|
|
746
998
|
const lines = [];
|
|
747
|
-
const
|
|
748
|
-
const
|
|
749
|
-
const
|
|
750
|
-
const
|
|
751
|
-
const
|
|
752
|
-
const
|
|
753
|
-
const
|
|
754
|
-
const
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
}
|
|
999
|
+
const canvasWidth = Math.max(18, width);
|
|
1000
|
+
const canvasHeight = Math.max(compact ? 8 : 10, height - 2);
|
|
1001
|
+
const pixelWidth = canvasWidth * 2;
|
|
1002
|
+
const pixelHeight = canvasHeight * 4;
|
|
1003
|
+
const center = Math.floor(pixelWidth / 2);
|
|
1004
|
+
const amplitude = Math.max(8, Math.min(Math.floor(pixelWidth * 0.28), compact ? 14 : 19));
|
|
1005
|
+
const frameCount = 36;
|
|
1006
|
+
const frameRatio = (tick % frameCount) / frameCount;
|
|
1007
|
+
const phase = frameRatio * Math.PI * 2;
|
|
1008
|
+
const scanPhase = (1 - Math.cos(frameRatio * Math.PI * 2)) / 2;
|
|
1009
|
+
const pairEvery = compact ? 7 : 5;
|
|
1010
|
+
const actionArea = resolveCommandArea(selected);
|
|
1011
|
+
const canvas = createBrailleCanvas(canvasWidth, canvasHeight);
|
|
761
1012
|
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
1013
|
+
lines.push(fitText(colorize(`DNA CYBER HELIX / ${actionArea}`, ANSI.bold, useColor), width));
|
|
1014
|
+
|
|
1015
|
+
const xA = (y) => Math.round(center + Math.sin(y * 0.19 + phase) * amplitude);
|
|
1016
|
+
const xB = (y) => Math.round(center - Math.sin(y * 0.19 + phase) * amplitude);
|
|
1017
|
+
const frontA = (y) => Math.cos(y * 0.19 + phase) >= 0;
|
|
1018
|
+
const scanY = Math.floor(scanPhase * Math.max(1, pixelHeight - 1));
|
|
765
1019
|
|
|
766
|
-
for (let
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
const min = Math.min(left, right);
|
|
771
|
-
const max = Math.max(left, right);
|
|
772
|
-
const chars = Array.from({ length: width }, () => ' ');
|
|
773
|
-
const front = Math.cos(angle) >= 0;
|
|
774
|
-
const leftColor = front ? ANSI.teal : ANSI.blue;
|
|
775
|
-
const rightColor = front ? ANSI.amber : ANSI.gray;
|
|
776
|
-
const bondColor = front ? ANSI.gray : ANSI.blue;
|
|
777
|
-
const scanActive = row === scanRow;
|
|
778
|
-
|
|
779
|
-
place(chars, graphLeft, row === 0 ? '┌' : row === bodyHeight - 1 ? '└' : '│', ANSI.gray);
|
|
780
|
-
place(chars, graphRight, row === 0 ? '┐' : row === bodyHeight - 1 ? '┘' : '│', ANSI.gray);
|
|
781
|
-
|
|
782
|
-
if (row === 0 || row === bodyHeight - 1) {
|
|
783
|
-
for (let col = graphLeft + 1; col < graphRight; col += 1) {
|
|
784
|
-
place(chars, col, '─', ANSI.gray);
|
|
1020
|
+
for (let y = 0; y < pixelHeight; y += 1) {
|
|
1021
|
+
if (y % 8 === 0) {
|
|
1022
|
+
for (let x = 2; x < pixelWidth - 2; x += 12) {
|
|
1023
|
+
plotBraille(canvas, x, y, 'gray', 0);
|
|
785
1024
|
}
|
|
786
1025
|
}
|
|
787
1026
|
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
1027
|
+
const a = xA(y);
|
|
1028
|
+
const b = xB(y);
|
|
1029
|
+
const previousA = xA(Math.max(0, y - 1));
|
|
1030
|
+
const previousB = xB(Math.max(0, y - 1));
|
|
1031
|
+
const active = Math.abs(y - scanY) <= 2;
|
|
1032
|
+
const thicknessA = frontA(y) || active ? 1 : 0;
|
|
1033
|
+
const thicknessB = !frontA(y) || active ? 1 : 0;
|
|
1034
|
+
const strandAColor = active ? 'white' : 'teal';
|
|
1035
|
+
const strandBColor = active ? 'white' : 'amber';
|
|
1036
|
+
const strandAPriority = active ? 6 : frontA(y) ? 4 : 2;
|
|
1037
|
+
const strandBPriority = active ? 6 : !frontA(y) ? 4 : 2;
|
|
1038
|
+
|
|
1039
|
+
if (y > 0) {
|
|
1040
|
+
drawBrailleLine(canvas, previousA - 2, y - 1, a - 2, y, 0, 'blue', 1);
|
|
1041
|
+
drawBrailleLine(canvas, previousA + 2, y - 1, a + 2, y, 0, 'blue', 1);
|
|
1042
|
+
drawBrailleLine(canvas, previousB - 2, y - 1, b - 2, y, 0, 'gray', 1);
|
|
1043
|
+
drawBrailleLine(canvas, previousB + 2, y - 1, b + 2, y, 0, 'gray', 1);
|
|
1044
|
+
drawBrailleLine(canvas, previousA, y - 1, a, y, thicknessA, strandAColor, strandAPriority);
|
|
1045
|
+
drawBrailleLine(canvas, previousB, y - 1, b, y, thicknessB, strandBColor, strandBPriority);
|
|
1046
|
+
} else {
|
|
1047
|
+
plotBrailleThick(canvas, a - 2, y, 0, 'blue', 1);
|
|
1048
|
+
plotBrailleThick(canvas, a + 2, y, 0, 'blue', 1);
|
|
1049
|
+
plotBrailleThick(canvas, b - 2, y, 0, 'gray', 1);
|
|
1050
|
+
plotBrailleThick(canvas, b + 2, y, 0, 'gray', 1);
|
|
1051
|
+
plotBrailleThick(canvas, a, y, thicknessA, strandAColor, strandAPriority);
|
|
1052
|
+
plotBrailleThick(canvas, b, y, thicknessB, strandBColor, strandBPriority);
|
|
791
1053
|
}
|
|
792
1054
|
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
1055
|
+
if ((y + tick) % pairEvery === 0) {
|
|
1056
|
+
const left = Math.min(a, b);
|
|
1057
|
+
const right = Math.max(a, b);
|
|
1058
|
+
for (let x = left + 3; x < right - 2; x += active ? 2 : 4) {
|
|
1059
|
+
plotBraille(canvas, x, y, active ? 'white' : 'blue', active ? 5 : 1);
|
|
1060
|
+
}
|
|
1061
|
+
plotBrailleThick(canvas, a, y, active ? 1 : 0, strandAColor, strandAPriority + 1);
|
|
1062
|
+
plotBrailleThick(canvas, b, y, active ? 1 : 0, strandBColor, strandBPriority + 1);
|
|
1063
|
+
if (active) {
|
|
1064
|
+
drawBrailleLine(canvas, left + 4, y, right - 4, y, 0, 'white', 5);
|
|
1065
|
+
}
|
|
802
1066
|
}
|
|
1067
|
+
}
|
|
803
1068
|
|
|
804
|
-
|
|
1069
|
+
for (const line of renderBrailleCanvas(canvas, { useColor })) {
|
|
1070
|
+
lines.push(line);
|
|
805
1071
|
}
|
|
806
1072
|
|
|
807
|
-
lines.push(fitText(
|
|
808
|
-
lines.push(fitText(colorize(compact ? 'helix stable' : 'helix stable lattice aligned', ANSI.gray, useColor), width));
|
|
809
|
-
lines.push(fitText(colorize(compact ? `global ${status.globalReadiness.installed.length}/${status.globalReadiness.required}` : `global ${status.globalReadiness.installed.length}/${status.globalReadiness.required} clients linked`, ANSI.amber, useColor), width));
|
|
1073
|
+
lines.push(fitText(colorize(`frames ${tick % frameCount}/${frameCount} global ${status.globalReadiness.installed.length}/${status.globalReadiness.required}`, ANSI.amber, useColor), width));
|
|
810
1074
|
|
|
811
1075
|
return lines.slice(0, height).map((line) => fitText(line, width));
|
|
812
1076
|
}
|
|
@@ -879,6 +1143,23 @@ function telemetryPanelLines(status, selected, width, height, { useColor = false
|
|
|
879
1143
|
return lines.slice(0, height).map((line) => fitText(line, width));
|
|
880
1144
|
}
|
|
881
1145
|
|
|
1146
|
+
function cyberCorePanelLines(status, selected, width, height, { useColor = false, tick = 0, compact = false } = {}) {
|
|
1147
|
+
const actionArea = resolveCommandArea(selected);
|
|
1148
|
+
const gridHeight = Math.max(compact ? 4 : 6, height - (compact ? 5 : 6));
|
|
1149
|
+
const gridWidth = Math.max(12, width);
|
|
1150
|
+
const lines = [
|
|
1151
|
+
fitText(colorize('SKILL_MASTER CORE', ANSI.bold, useColor), width),
|
|
1152
|
+
fitText(colorize(compact ? 'cyber lattice / low flicker' : 'cyberpunk pixel art workflow engine', ANSI.dim, useColor), width),
|
|
1153
|
+
fitText(colorize(`area ${actionArea} | tick ${String(tick).padStart(3, '0')}`, ANSI.gray, useColor), width),
|
|
1154
|
+
fitText(renderCircuitRail(width, tick, { useColor, color: ANSI.teal }), width),
|
|
1155
|
+
...renderScanGrid(gridWidth, gridHeight, tick, { useColor }).map((line) => fitText(line, width)),
|
|
1156
|
+
fitText(renderCircuitRail(width, tick + 2, { useColor, color: ANSI.amber }), width),
|
|
1157
|
+
fitText(colorize(`global ${status.globalReadiness.installed.length}/${status.globalReadiness.required} | bundle ${status.bundledCatalog?.total ?? 0}`, ANSI.gray, useColor), width),
|
|
1158
|
+
];
|
|
1159
|
+
|
|
1160
|
+
return lines.slice(0, height).map((line) => fitText(line, width));
|
|
1161
|
+
}
|
|
1162
|
+
|
|
882
1163
|
function renderBox(lines, width, title, { useColor = false, color = ANSI.cyan, style = 'normal' } = {}) {
|
|
883
1164
|
const innerWidth = Math.max(10, width - 4);
|
|
884
1165
|
const titleText = title ? ` ${title.toUpperCase()} ` : '';
|
|
@@ -988,7 +1269,7 @@ export function formatCyberMenuFrame(status, commands, selectedIndex, tick = 0,
|
|
|
988
1269
|
colorize('up/down move enter run q exit', ANSI.dim, useColor),
|
|
989
1270
|
...visibleCommands.map((command, offset) => {
|
|
990
1271
|
const index = scrollStart + offset;
|
|
991
|
-
const marker = index === selectedIndex ? '
|
|
1272
|
+
const marker = index === selectedIndex ? '>' : '.';
|
|
992
1273
|
const disabled = command.disabledReason ? ' [off]' : '';
|
|
993
1274
|
const label = `${marker} ${command.label}${disabled}`;
|
|
994
1275
|
return index === selectedIndex
|
|
@@ -1005,7 +1286,7 @@ export function formatCyberMenuFrame(status, commands, selectedIndex, tick = 0,
|
|
|
1005
1286
|
colorize('DETAIL FOCUS', ANSI.amber, useColor),
|
|
1006
1287
|
colorize(selected?.label ?? 'Nenhuma acao', ANSI.bold, useColor),
|
|
1007
1288
|
fitText(colorize(`area ${actionArea} | action ${Math.min(selectedIndex + 1, commands.length)}/${commands.length}`, ANSI.gray, useColor), detailInnerWidth),
|
|
1008
|
-
fitText(colorize('
|
|
1289
|
+
fitText(colorize(patternFill('-=', 32), ANSI.amber, useColor), detailInnerWidth),
|
|
1009
1290
|
...splitText(selected?.description, detailInnerWidth, 1),
|
|
1010
1291
|
...splitText(selected?.success ? `resultado: ${selected.success}` : '', detailInnerWidth, 1),
|
|
1011
1292
|
...splitText(`exec: ${formatActionCommand(selected)}`, detailInnerWidth, 1),
|
|
@@ -1017,28 +1298,28 @@ export function formatCyberMenuFrame(status, commands, selectedIndex, tick = 0,
|
|
|
1017
1298
|
descriptionLines.push(fitText('', detailInnerWidth));
|
|
1018
1299
|
}
|
|
1019
1300
|
|
|
1020
|
-
const rightTopLines = dnaPanelLines(tick, rightInnerWidth, rightTopInnerHeight, status, { useColor, compact: compactMode });
|
|
1301
|
+
const rightTopLines = dnaPanelLines(tick, rightInnerWidth, rightTopInnerHeight, status, selected, { useColor, compact: compactMode });
|
|
1021
1302
|
const rightBottomLines = telemetryPanelLines(status, selected, rightInnerWidth, rightBottomInnerHeight, { useColor, tick, compact: compactMode });
|
|
1022
|
-
const summaryBox =
|
|
1303
|
+
const summaryBox = renderCyberFrame(
|
|
1023
1304
|
summaryLines.slice(0, summaryInnerHeight).map((line) => fitText(line, summaryInnerWidth)),
|
|
1024
1305
|
width,
|
|
1025
1306
|
'overview',
|
|
1026
|
-
{ useColor, color: ANSI.white, style: 'hud' },
|
|
1307
|
+
{ useColor, color: ANSI.white, style: 'hud', tick },
|
|
1027
1308
|
);
|
|
1028
|
-
const actionBox =
|
|
1309
|
+
const actionBox = renderCyberFrame(
|
|
1029
1310
|
actionLines.slice(0, actionInnerHeight).map((line) => fitText(line, actionInnerWidth)),
|
|
1030
1311
|
leftWidth,
|
|
1031
1312
|
'actions',
|
|
1032
|
-
{ useColor, color: ANSI.gray, style: '
|
|
1313
|
+
{ useColor, color: ANSI.gray, style: 'thin', tick },
|
|
1033
1314
|
);
|
|
1034
|
-
const detailBox =
|
|
1315
|
+
const detailBox = renderCyberFrame(
|
|
1035
1316
|
descriptionLines.slice(0, detailInnerHeight).map((line) => fitText(line, detailInnerWidth)),
|
|
1036
1317
|
leftWidth,
|
|
1037
1318
|
'details',
|
|
1038
|
-
{ useColor, color: ANSI.amber, style: 'focus' },
|
|
1319
|
+
{ useColor, color: ANSI.amber, style: 'focus', tick },
|
|
1039
1320
|
);
|
|
1040
|
-
const dnaBox =
|
|
1041
|
-
const telemetryBox =
|
|
1321
|
+
const dnaBox = renderCyberFrame(rightTopLines, rightWidth, 'dna-core', { useColor, color: ANSI.white, style: 'hud', tick });
|
|
1322
|
+
const telemetryBox = renderCyberFrame(rightBottomLines, rightWidth, 'telemetry', { useColor, color: ANSI.blue, style: 'thin', tick });
|
|
1042
1323
|
const bodyRows = joinHorizontalBoxes([
|
|
1043
1324
|
{ lines: [...actionBox, ...detailBox], width: leftWidth },
|
|
1044
1325
|
{ lines: [...dnaBox, ...telemetryBox], width: rightWidth },
|
|
@@ -1057,27 +1338,97 @@ export function formatRunningActionFrame(status, action, tick = 0, {
|
|
|
1057
1338
|
const innerWidth = width - 4;
|
|
1058
1339
|
const commandArea = resolveCommandArea(action);
|
|
1059
1340
|
const pulse = renderSparkline(buildTelemetrySeries((status.globalReadiness.installed.length || 1) + tick, Math.max(8, Math.min(18, Math.floor(innerWidth / 5))), tick), { useColor, color: ANSI.teal });
|
|
1341
|
+
const gridHeight = Math.max(4, Math.min(8, height - 14));
|
|
1060
1342
|
const lines = [
|
|
1061
|
-
colorize('
|
|
1343
|
+
colorize('SKILL_MASTER WORKFLOW ONLINE', ANSI.bold, useColor),
|
|
1062
1344
|
fitText(colorize(`area ${commandArea} | launching ${action?.label ?? 'acao'}`, ANSI.amber, useColor), innerWidth),
|
|
1063
1345
|
fitText(colorize(`pulse ${pulse}`, ANSI.gray, useColor), innerWidth),
|
|
1346
|
+
fitText(renderCircuitRail(innerWidth, tick, { useColor, color: ANSI.teal }), innerWidth),
|
|
1347
|
+
...renderScanGrid(innerWidth, gridHeight, tick, { useColor }),
|
|
1064
1348
|
fitText('', innerWidth),
|
|
1065
1349
|
...splitText(action?.description ?? '', innerWidth, 2),
|
|
1066
1350
|
...splitText(action?.success ? `resultado esperado: ${action.success}` : '', innerWidth, 2),
|
|
1067
1351
|
...splitText(`exec: ${formatActionCommand(action)}`, innerWidth, 2),
|
|
1068
1352
|
fitText('', innerWidth),
|
|
1069
|
-
fitText(colorize('
|
|
1353
|
+
fitText(colorize('handoff: HUD -> stream real do comando', ANSI.dim, useColor), innerWidth),
|
|
1070
1354
|
];
|
|
1071
1355
|
|
|
1072
1356
|
while (lines.length < Math.max(8, height - 2)) {
|
|
1073
1357
|
lines.push(fitText('', innerWidth));
|
|
1074
1358
|
}
|
|
1075
1359
|
|
|
1076
|
-
return
|
|
1360
|
+
return renderCyberFrame(
|
|
1077
1361
|
lines.slice(0, Math.max(8, height - 2)).map((line) => fitText(line, innerWidth)),
|
|
1078
1362
|
width,
|
|
1079
1363
|
'running',
|
|
1080
|
-
{ useColor, color: ANSI.teal, style: '
|
|
1364
|
+
{ useColor, color: ANSI.teal, style: 'running', tick },
|
|
1365
|
+
).join('\n');
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
export function formatSkillMasterIntroFrame(status, tick = 0, {
|
|
1369
|
+
columns = 120,
|
|
1370
|
+
rows = 32,
|
|
1371
|
+
useColor = false,
|
|
1372
|
+
message = 'skill_master assumindo o workflow',
|
|
1373
|
+
} = {}) {
|
|
1374
|
+
const width = Math.max(72, Math.min(columns, 150));
|
|
1375
|
+
const height = Math.max(18, Math.min(rows - 1, 34));
|
|
1376
|
+
const innerWidth = width - 4;
|
|
1377
|
+
const gridHeight = Math.max(6, Math.min(12, height - 12));
|
|
1378
|
+
const signal = renderSparkline(buildTelemetrySeries(status.pendingSuccessDrafts + status.studyCandidates + 5, Math.max(12, Math.min(28, Math.floor(innerWidth / 4))), tick), { useColor, color: ANSI.teal });
|
|
1379
|
+
const lines = [
|
|
1380
|
+
colorize('REBORN / SKILL_MASTER', ANSI.bold, useColor),
|
|
1381
|
+
fitText(colorize(message, ANSI.amber, useColor), innerWidth),
|
|
1382
|
+
fitText(colorize(`package ${status.packageName} | version ${status.semver} | channel ${status.manifestVersion}`, ANSI.gray, useColor), innerWidth),
|
|
1383
|
+
fitText(colorize(`signal ${signal}`, ANSI.teal, useColor), innerWidth),
|
|
1384
|
+
fitText(renderCircuitRail(innerWidth, tick, { useColor, color: ANSI.white }), innerWidth),
|
|
1385
|
+
...renderScanGrid(innerWidth, gridHeight, tick, { useColor }),
|
|
1386
|
+
fitText(renderCircuitRail(innerWidth, tick + 3, { useColor, color: ANSI.amber }), innerWidth),
|
|
1387
|
+
fitText(colorize(`global ${status.globalReadiness.installed.length}/${status.globalReadiness.required} | bundle ${status.bundledCatalog?.total ?? 0} | study ${status.studyCandidates}`, ANSI.gray, useColor), innerWidth),
|
|
1388
|
+
fitText(colorize('modo visual seguro: terminal humano ativo / stdio MCP preservado', ANSI.dim, useColor), innerWidth),
|
|
1389
|
+
];
|
|
1390
|
+
|
|
1391
|
+
while (lines.length < Math.max(8, height - 2)) {
|
|
1392
|
+
lines.push(fitText('', innerWidth));
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
return renderCyberFrame(
|
|
1396
|
+
lines.slice(0, Math.max(8, height - 2)).map((line) => fitText(line, innerWidth)),
|
|
1397
|
+
width,
|
|
1398
|
+
'boot-sequence',
|
|
1399
|
+
{ useColor, color: ANSI.teal, style: 'hud', tick },
|
|
1400
|
+
).join('\n');
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
export function formatCyberConfirmFrame(status, action, tick = 0, {
|
|
1404
|
+
columns = 120,
|
|
1405
|
+
rows = 32,
|
|
1406
|
+
useColor = false,
|
|
1407
|
+
} = {}) {
|
|
1408
|
+
const width = Math.max(72, Math.min(columns, 140));
|
|
1409
|
+
const height = Math.max(16, Math.min(rows - 1, 28));
|
|
1410
|
+
const innerWidth = width - 4;
|
|
1411
|
+
const commandArea = resolveCommandArea(action);
|
|
1412
|
+
const lines = [
|
|
1413
|
+
colorize('CONFIRMATION GATE', ANSI.bold, useColor),
|
|
1414
|
+
fitText(colorize(action?.confirmMessage ?? 'Confirmar acao?', ANSI.amber, useColor), innerWidth),
|
|
1415
|
+
fitText(`area ${commandArea} | action ${action?.key ?? 'unknown'}`, innerWidth),
|
|
1416
|
+
fitText(renderCircuitRail(innerWidth, tick, { useColor, color: ANSI.amber }), innerWidth),
|
|
1417
|
+
...splitText(action?.description ?? '', innerWidth, 2),
|
|
1418
|
+
...splitText(action?.success ? `resultado esperado: ${action.success}` : '', innerWidth, 2),
|
|
1419
|
+
fitText('', innerWidth),
|
|
1420
|
+
fitText(colorize('Y/Enter confirma N/Esc cancela', ANSI.teal, useColor), innerWidth),
|
|
1421
|
+
];
|
|
1422
|
+
|
|
1423
|
+
while (lines.length < Math.max(8, height - 2)) {
|
|
1424
|
+
lines.push(fitText('', innerWidth));
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
return renderCyberFrame(
|
|
1428
|
+
lines.slice(0, Math.max(8, height - 2)).map((line) => fitText(line, innerWidth)),
|
|
1429
|
+
width,
|
|
1430
|
+
'subroutine',
|
|
1431
|
+
{ useColor, color: ANSI.amber, style: 'focus', tick },
|
|
1081
1432
|
).join('\n');
|
|
1082
1433
|
}
|
|
1083
1434
|
|
|
@@ -1090,13 +1441,32 @@ export function formatActionHeader(action, { useColor = false } = {}) {
|
|
|
1090
1441
|
action.disabledReason ? `Indisponivel: ${action.disabledReason}` : null,
|
|
1091
1442
|
].filter(Boolean);
|
|
1092
1443
|
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1444
|
+
const width = Math.max(72, Math.min(120, Math.max(...lines.map((line) => visibleLength(line))) + 8));
|
|
1445
|
+
return renderCyberFrame(
|
|
1446
|
+
lines.map((line) => fitText(line, width - 4)),
|
|
1447
|
+
width,
|
|
1448
|
+
'skill-master action',
|
|
1449
|
+
{ color: ANSI.green, useColor, style: 'hud' },
|
|
1450
|
+
).join('\n');
|
|
1097
1451
|
}
|
|
1098
1452
|
|
|
1099
1453
|
export function formatResultMessage(code, { useColor = false } = {}) {
|
|
1454
|
+
const ok = code === 0;
|
|
1455
|
+
const lines = [
|
|
1456
|
+
colorize(ok ? 'SUBROUTINE COMPLETE' : 'SUBROUTINE FAILED', ok ? ANSI.green : ANSI.red, useColor),
|
|
1457
|
+
ok ? 'Comando concluido com sucesso.' : `Comando terminou com codigo ${code}.`,
|
|
1458
|
+
ok ? 'workflow liberado para a proxima acao' : 'verifique o log acima antes de continuar',
|
|
1459
|
+
];
|
|
1460
|
+
|
|
1461
|
+
return renderCyberFrame(
|
|
1462
|
+
lines.map((line) => fitText(line, 68)),
|
|
1463
|
+
72,
|
|
1464
|
+
ok ? 'result-ok' : 'result-error',
|
|
1465
|
+
{ color: ok ? ANSI.green : ANSI.red, useColor, style: ok ? 'hud' : 'focus' },
|
|
1466
|
+
).join('\n');
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
export function formatPlainResultMessage(code, { useColor = false } = {}) {
|
|
1100
1470
|
if (code === 0) {
|
|
1101
1471
|
return colorize('Comando concluido com sucesso.', ANSI.green, useColor);
|
|
1102
1472
|
}
|
|
@@ -1129,14 +1499,22 @@ O comando de menu e voltado para operacao humana. O binario MCP stdio continua s
|
|
|
1129
1499
|
}
|
|
1130
1500
|
|
|
1131
1501
|
export function runCommand(action, { cwd, env = process.env } = {}) {
|
|
1132
|
-
return new Promise((resolve) => {
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1502
|
+
return new Promise((resolve, reject) => {
|
|
1503
|
+
let child;
|
|
1504
|
+
|
|
1505
|
+
try {
|
|
1506
|
+
child = spawn(action.command, action.args, {
|
|
1507
|
+
cwd,
|
|
1508
|
+
env,
|
|
1509
|
+
shell: Boolean(action.shell),
|
|
1510
|
+
stdio: 'inherit',
|
|
1511
|
+
});
|
|
1512
|
+
} catch (error) {
|
|
1513
|
+
reject(error);
|
|
1514
|
+
return;
|
|
1515
|
+
}
|
|
1139
1516
|
|
|
1517
|
+
child.on('error', reject);
|
|
1140
1518
|
child.on('close', (code) => {
|
|
1141
1519
|
resolve(code ?? 1);
|
|
1142
1520
|
});
|