@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.
Files changed (108) hide show
  1. package/CHANGELOG.md +12 -1
  2. package/README.md +59 -10
  3. package/VERSION.md +3 -3
  4. package/bin/lib/client-config.mjs +293 -0
  5. package/bin/lib/menu-core.mjs +509 -131
  6. package/bin/lib/skill-installation.mjs +215 -0
  7. package/bin/skill-master-bootstrap-global.mjs +2 -1
  8. package/bin/skill-master-doctor.mjs +92 -32
  9. package/bin/skill-master-install-global-skills.mjs +4 -42
  10. package/bin/skill-master-install-project-skills.mjs +97 -0
  11. package/bin/skill-master-menu.mjs +91 -6
  12. package/bin/skill-master-register-clients.mjs +91 -115
  13. package/docs/operations/GUIA_MULTI_COMPUTADOR.md +262 -0
  14. package/docs/operations/GUIA_NPM_PUBLICO.md +147 -0
  15. package/docs/operations/MENU_VISUAL_EVIDENCE_2026-06-28.md +66 -0
  16. package/docs/operations/assets/menu-frame-compact.html +76 -0
  17. package/docs/operations/assets/menu-frame-compact.png +0 -0
  18. package/docs/operations/assets/menu-frame-large.html +84 -0
  19. package/docs/operations/assets/menu-frame-large.png +0 -0
  20. package/docs/operations/assets/menu-frame-running.html +80 -0
  21. package/docs/operations/assets/menu-frame-running.png +0 -0
  22. package/docs/operations/cross-platform-auth-transfer/ANALISE_COMPATIBILIDADE_MCP_2026-06-28.md +140 -0
  23. package/docs/operations/cross-platform-auth-transfer/README_TRANSFERENCIA.md +85 -0
  24. package/docs/operations/reborn-menu-cyberpunk-transfer/ANALISE_MENU_REBORN_CYBERPUNK_2026-06-28.md +174 -0
  25. package/docs/operations/reborn-menu-cyberpunk-transfer/HANDOFF_IMPLEMENTACAO_REBORN_CYBERPUNK_2026-06-28.md +119 -0
  26. package/docs/operations/reborn-menu-cyberpunk-transfer/ORDEM_DE_EXECUCAO_MENU_REBORN_CYBERPUNK.md +134 -0
  27. package/docs/operations/reborn-menu-cyberpunk-transfer/README_TRANSFERENCIA.md +84 -0
  28. package/docs/operations/reborn-menu-cyberpunk-transfer/README_TRANSFERENCIA_REBORN_PACKAGE.md +56 -0
  29. package/docs/operations/reborn-menu-cyberpunk-transfer/references/cyan-hud-frame-sheet.jpg +0 -0
  30. package/docs/operations/reborn-menu-cyberpunk-transfer/references/cyberpunk-pattern-sheet.jpg +0 -0
  31. package/docs/operations/reborn-menu-cyberpunk-transfer/references/fluid-workflow-windows.gif +0 -0
  32. package/docs/prompt-tasks/PROMPT_TASK_001_BOOTSTRAP_SKILL_MASTER_MCP.md +6 -0
  33. package/docs/prompt-tasks/PROMPT_TASK_002_AUTO_UPDATE_LAUNCHER.md +6 -0
  34. package/docs/prompt-tasks/PROMPT_TASK_003_REMOTE_MANIFEST_AND_RELEASES.md +6 -0
  35. package/docs/prompt-tasks/PROMPT_TASK_004_MULTI_USER_DISTRIBUTION.md +6 -0
  36. package/docs/prompt-tasks/PROMPT_TASK_005_SECURITY_AND_QUALITY_GATE.md +6 -0
  37. package/docs/prompt-tasks/PROMPT_TASK_006_MASTER_ACIONAMENTO_APRENDIZADO.md +83 -0
  38. package/docs/prompt-tasks/PROMPT_TASK_007_PERSONA_ORQUESTRADORA.md +88 -0
  39. package/docs/prompt-tasks/PROMPT_TASK_008_PROMPT_ROUTER_MODOS_ATIVACAO.md +156 -0
  40. package/docs/prompt-tasks/PROMPT_TASK_009_PIPELINE_APRENDIZADO_SUCESSO.md +105 -0
  41. package/docs/prompt-tasks/PROMPT_TASK_010_EVALS_GOVERNANCA_ATIVACAO.md +119 -0
  42. package/docs/prompt-tasks/PROMPT_TASK_011_MENU_NOTIFICACOES_NOTION.md +120 -0
  43. package/docs/prompt-tasks/PROMPT_TASK_012_MENU_CYBERPUNK_PIXEL_FRAME.md +123 -0
  44. package/docs/prompt-tasks/PROMPT_TASK_013_MENU_FLUID_DNA_ANIMATION.md +114 -0
  45. package/docs/prompt-tasks/PROMPT_TASK_014_MENU_FUNCTIONAL_PARITY_QA.md +157 -0
  46. package/docs/prompt-tasks/PROMPT_TASK_015_TRANSFER_RELEASE_HANDOFF.md +127 -0
  47. package/docs/prompt-tasks/PROMPT_TASK_016_CROSS_PLATFORM_MCP_AUTH_REGISTRATION.md +107 -0
  48. package/docs/prompt-tasks/PROMPT_TASK_018_NPM_PUBLISH_2FA_SETUP.md +80 -0
  49. package/docs/prompt-tasks/PROMPT_TASK_MASTER_EXECUTOR.md +6 -0
  50. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/SKILL.md +399 -0
  51. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/common-patterns.md +331 -0
  52. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/complete-examples.md +872 -0
  53. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/component-patterns.md +502 -0
  54. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/data-fetching.md +767 -0
  55. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/file-organization.md +502 -0
  56. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/loading-and-error-states.md +501 -0
  57. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/performance.md +406 -0
  58. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/routing-guide.md +364 -0
  59. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/styling-guide.md +428 -0
  60. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/typescript-standards.md +418 -0
  61. package/docs/skill-candidates/v0.0.11/git-version-control-ops/SKILL.md +34 -0
  62. package/docs/skill-candidates/v0.0.11/go-engineering/SKILL.md +34 -0
  63. package/docs/skill-candidates/v0.0.11/java-engineering/SKILL.md +34 -0
  64. package/docs/skill-candidates/v0.0.11/javascript-engineering/SKILL.md +34 -0
  65. package/docs/skill-candidates/v0.0.11/json-contract-design/SKILL.md +34 -0
  66. package/docs/skill-candidates/v0.0.11/multi-client-mcp-ops/SKILL.md +36 -0
  67. package/docs/skill-candidates/v0.0.11/nextjs/SKILL.md +745 -0
  68. package/docs/skill-candidates/v0.0.11/nextjs/agents/openai.yaml +3 -0
  69. package/docs/skill-candidates/v0.0.11/nextjs/references/app-router-files.md +94 -0
  70. package/docs/skill-candidates/v0.0.11/python-engineering/SKILL.md +34 -0
  71. package/docs/skill-candidates/v0.0.11/ruby-engineering/SKILL.md +34 -0
  72. package/docs/skill-candidates/v0.0.11/senior-fullstack/SKILL.md +209 -0
  73. package/docs/skill-candidates/v0.0.11/senior-fullstack/references/architecture_patterns.md +103 -0
  74. package/docs/skill-candidates/v0.0.11/senior-fullstack/references/development_workflows.md +103 -0
  75. package/docs/skill-candidates/v0.0.11/senior-fullstack/references/tech_stack_guide.md +103 -0
  76. package/docs/skill-candidates/v0.0.11/senior-fullstack/scripts/code_quality_analyzer.py +114 -0
  77. package/docs/skill-candidates/v0.0.11/senior-fullstack/scripts/fullstack_scaffolder.py +114 -0
  78. package/docs/skill-candidates/v0.0.11/senior-fullstack/scripts/project_scaffolder.py +114 -0
  79. package/docs/skill-candidates/v0.0.11/shadcn/SKILL.md +573 -0
  80. package/docs/skill-candidates/v0.0.11/shadcn/agents/openai.yaml +3 -0
  81. package/docs/skill-candidates/v0.0.11/sql-postgresql-engineering/SKILL.md +34 -0
  82. package/docs/skill-candidates/v0.0.11/terminal-shell-ops/SKILL.md +34 -0
  83. package/docs/skill-candidates/v0.0.11/typescript-expert/SKILL.md +429 -0
  84. package/docs/skill-candidates/v0.0.11/typescript-expert/references/tsconfig-strict.json +92 -0
  85. package/docs/skill-candidates/v0.0.11/typescript-expert/references/typescript-cheatsheet.md +383 -0
  86. package/docs/skill-candidates/v0.0.11/typescript-expert/references/utility-types.ts +335 -0
  87. package/docs/skill-candidates/v0.0.11/typescript-expert/scripts/ts_diagnostic.py +203 -0
  88. package/docs/skill-candidates/v0.0.11/ui-component-primitives/SKILL.md +34 -0
  89. package/docs/skill-candidates/v0.0.11/web-mobile-design-systems/SKILL.md +34 -0
  90. package/docs/skill-candidates/v0.0.11/windows-linux-platform-ops/SKILL.md +34 -0
  91. package/docs/skill-candidates/v0.0.12/csharp-senior-master-engineering/SKILL.md +32 -0
  92. package/docs/skill-candidates/v0.0.12/css-senior-master-engineering/SKILL.md +32 -0
  93. package/docs/skill-candidates/v0.0.12/go-senior-master-engineering/SKILL.md +32 -0
  94. package/docs/skill-candidates/v0.0.12/html-senior-master-engineering/SKILL.md +32 -0
  95. package/docs/skill-candidates/v0.0.12/javascript-senior-master-engineering/SKILL.md +32 -0
  96. package/docs/skill-candidates/v0.0.12/json-senior-master-engineering/SKILL.md +32 -0
  97. package/docs/skill-candidates/v0.0.12/python-senior-master-engineering/SKILL.md +32 -0
  98. package/docs/skill-candidates/v0.0.12/react-senior-master-engineering/SKILL.md +32 -0
  99. package/docs/skill-candidates/v0.0.12/ruby-senior-master-engineering/SKILL.md +32 -0
  100. package/docs/skill-candidates/v0.0.12/senior-master-code-optimizer/SKILL.md +48 -0
  101. package/docs/skill-candidates/v0.0.12/sql-senior-master-engineering/SKILL.md +31 -0
  102. package/docs/skill-candidates/v0.0.12/typescript-senior-master-engineering/SKILL.md +35 -0
  103. package/examples/client-configs/claude-code.commands.md +11 -7
  104. package/manifests/channels/beta.json +7 -7
  105. package/manifests/channels/stable.json +8 -8
  106. package/package.json +14 -2
  107. package/scripts/render-menu-evidence.mjs +130 -0
  108. package/scripts/verify-menu-actions.mjs +117 -0
@@ -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 hasSkillMaster = content.includes('skill_master');
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: hasGlobalCommand ? 'global' : hasLauncher ? 'launcher' : hasSkillMaster ? 'custom' : 'unknown',
184
- globalCommand: hasGlobalCommand,
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
- globalCommand: server?.command === 'skill-master-mcp',
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 codexConfigPath = join(process.env.CODEX_HOME ?? join(os.homedir(), '.codex'), 'config.toml');
222
- const claudeConfigPath = defaultClaudeConfigPath();
223
- const geminiConfigPath = join(os.homedir(), '.gemini', 'settings.json');
224
- const antigravityConfigPath = join(os.homedir(), '.gemini', 'config', 'mcp_config.json');
225
- const codex = readClientConfigState(codexConfigPath);
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.kind === 'launcher' || claude.present || gemini.present || antigravity.present
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
- export function buildMenuCommands({ rootDir, currentFile, nodeExecPath = process.execPath }) {
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: 'npm',
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: 'npm',
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: 'npm',
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 binario global skill-master-mcp.', 'Preserva outros servidores MCP ja existentes nos JSONs.'],
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 global: ${readiness.codex.globalCommand ? 'sim' : readiness.codex.kind}`,
672
- `Claude global: ${readiness.claude.globalCommand ? 'sim' : readiness.claude.present ? 'nao-global' : 'ausente'}`,
673
- `Gemini global: ${readiness.gemini.globalCommand ? 'sim' : readiness.gemini.present ? 'nao-global' : 'ausente'}`,
674
- `Antigravity global: ${readiness.antigravity.globalCommand ? 'sim' : readiness.antigravity.present ? 'nao-global' : 'ausente'}`,
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 reconhecido como comando global neste computador.',
692
- 'Codex, Claude, Gemini e Antigravity podem ser apontados para skill-master-mcp.',
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 global do Skill Master.',
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.length), 24);
722
- const border = `+${'-'.repeat(width + 2)}+`;
723
- const paintedBorder = colorize(border, color, useColor);
724
- const body = lines.map((line) => `| ${line.padEnd(width)} |`);
725
- return [paintedBorder, ...body, paintedBorder].join('\n');
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 center = Math.floor(width / 2);
748
- const graphLeft = 2;
749
- const graphRight = Math.max(graphLeft + 8, width - 3);
750
- const graphWidth = Math.max(10, graphRight - graphLeft + 1);
751
- const radius = Math.max(3, Math.floor((graphWidth - 4) / 3.35));
752
- const phase = tick / 6;
753
- const bodyHeight = Math.max(compact ? 6 : 8, height - (compact ? 5 : 6));
754
- const scanRow = bodyHeight > 0 ? tick % bodyHeight : 0;
755
-
756
- lines.push(fitText(colorize('GENOME MATRIX', ANSI.bold, useColor), width));
757
- lines.push(fitText(colorize(compact ? 'lab helix / scan stable' : 'procedural intelligence helix', ANSI.dim, useColor), width));
758
- if (!compact) {
759
- lines.push(fitText(colorize('signal map / sync locked / low flicker', ANSI.gray, useColor), width));
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
- const place = (cells, col, char, color) => {
763
- if (col >= 0 && col < width) cells[col] = colorize(char, color, useColor);
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 row = 0; row < bodyHeight; row += 1) {
767
- const angle = row * 0.49 + phase;
768
- const left = Math.round(center + Math.sin(angle) * radius);
769
- const right = Math.round(center + Math.sin(angle + Math.PI) * radius);
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
- for (let col = min + 2; col < max - 1; col += 1) {
789
- const rungChar = row % 4 < 2 ? '═' : '─';
790
- place(chars, col, scanActive ? '█' : rungChar, scanActive ? ANSI.white : bondColor);
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
- place(chars, left - 1, '▓', leftColor);
794
- place(chars, left, scanActive ? '█' : row % 2 === 0 ? '█' : '▓', scanActive ? ANSI.white : leftColor);
795
- place(chars, left + 1, '▓', leftColor);
796
- place(chars, right - 1, '▓', rightColor);
797
- place(chars, right, scanActive ? '█' : row % 2 === 0 ? '' : '', scanActive ? ANSI.white : rightColor);
798
- place(chars, right + 1, '▓', rightColor);
799
-
800
- if (row % 4 === 1 || row % 4 === 2) {
801
- place(chars, center, scanActive ? '◆' : '◆', scanActive ? ANSI.white : ANSI.gray);
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
- lines.push(chars.join(''));
1069
+ for (const line of renderBrailleCanvas(canvas, { useColor })) {
1070
+ lines.push(line);
805
1071
  }
806
1072
 
807
- lines.push(fitText('', width));
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('────────────────────────────────', ANSI.amber, useColor), detailInnerWidth),
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 = renderBox(
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 = renderBox(
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: 'normal' },
1313
+ { useColor, color: ANSI.gray, style: 'thin', tick },
1033
1314
  );
1034
- const detailBox = renderBox(
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 = renderBox(rightTopLines, rightWidth, 'dna-core', { useColor, color: ANSI.white, style: 'hud' });
1041
- const telemetryBox = renderBox(rightBottomLines, rightWidth, 'telemetry', { useColor, color: ANSI.blue, style: 'normal' });
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('EXECUTION TRANSFER', ANSI.bold, useColor),
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('alternando do HUD para o stream real do comando...', ANSI.dim, useColor), innerWidth),
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 renderBox(
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: 'focus' },
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
- return renderPanelLines(
1094
- lines,
1095
- { color: ANSI.green, useColor },
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
- const child = spawn(action.command, action.args, {
1134
- cwd,
1135
- env,
1136
- shell: process.platform === 'win32',
1137
- stdio: 'inherit',
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
  });