@fprad0/skill-master-mcp 0.0.11 → 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 +7 -0
- package/README.md +32 -3
- package/VERSION.md +3 -3
- package/bin/lib/client-config.mjs +25 -0
- package/bin/lib/menu-core.mjs +469 -101
- package/bin/lib/skill-installation.mjs +215 -0
- package/bin/skill-master-doctor.mjs +51 -4
- package/bin/skill-master-install-global-skills.mjs +3 -41
- 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 +58 -3
- package/docs/operations/GUIA_MULTI_COMPUTADOR.md +8 -1
- 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.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 +6 -6
- package/manifests/channels/stable.json +8 -8
- package/package.json +9 -1
- package/scripts/render-menu-evidence.mjs +130 -0
- package/scripts/verify-menu-actions.mjs +2 -0
package/bin/lib/menu-core.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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
6
|
import {
|
|
7
7
|
assessCodexConfigContent,
|
|
@@ -38,6 +38,18 @@ const REQUIRED_GLOBAL_SKILLS = [
|
|
|
38
38
|
'mcp-client-readiness',
|
|
39
39
|
'terminal-menu-operations',
|
|
40
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',
|
|
41
53
|
];
|
|
42
54
|
|
|
43
55
|
const BUNDLED_SKILL_DOMAIN_GROUPS = [
|
|
@@ -71,11 +83,14 @@ const BUNDLED_SKILL_DOMAIN_GROUPS = [
|
|
|
71
83
|
'frontend-dev-guidelines',
|
|
72
84
|
'frontend-design',
|
|
73
85
|
'frontend-ui-ux-systems',
|
|
86
|
+
'html-senior-master-engineering',
|
|
74
87
|
'nextjs',
|
|
75
88
|
'playwright',
|
|
89
|
+
'react-senior-master-engineering',
|
|
76
90
|
'screenshot',
|
|
77
91
|
'senior-fullstack',
|
|
78
92
|
'shadcn',
|
|
93
|
+
'css-senior-master-engineering',
|
|
79
94
|
'ui-component-primitives',
|
|
80
95
|
'webapp-testing',
|
|
81
96
|
'web-mobile-design-systems',
|
|
@@ -86,16 +101,25 @@ const BUNDLED_SKILL_DOMAIN_GROUPS = [
|
|
|
86
101
|
key: 'backend',
|
|
87
102
|
label: 'backend-data',
|
|
88
103
|
skills: [
|
|
104
|
+
'csharp-senior-master-engineering',
|
|
89
105
|
'go-engineering',
|
|
106
|
+
'go-senior-master-engineering',
|
|
90
107
|
'java-engineering',
|
|
91
108
|
'javascript-engineering',
|
|
109
|
+
'javascript-senior-master-engineering',
|
|
92
110
|
'json-contract-design',
|
|
111
|
+
'json-senior-master-engineering',
|
|
93
112
|
'mcp-builder',
|
|
94
113
|
'polyglot-backend-engineering',
|
|
95
114
|
'python-engineering',
|
|
115
|
+
'python-senior-master-engineering',
|
|
96
116
|
'ruby-engineering',
|
|
117
|
+
'ruby-senior-master-engineering',
|
|
118
|
+
'senior-master-code-optimizer',
|
|
97
119
|
'sql-postgresql-engineering',
|
|
120
|
+
'sql-senior-master-engineering',
|
|
98
121
|
'typescript-expert',
|
|
122
|
+
'typescript-senior-master-engineering',
|
|
99
123
|
],
|
|
100
124
|
},
|
|
101
125
|
{
|
|
@@ -138,6 +162,7 @@ const COMMAND_AREA_LABELS = {
|
|
|
138
162
|
activationBalanced: 'ativacao',
|
|
139
163
|
activationAlwaysOn: 'ativacao',
|
|
140
164
|
installGlobalSkills: 'skills-globais',
|
|
165
|
+
installProjectSkills: 'skills-projeto',
|
|
141
166
|
bootstrapGlobal: 'bootstrap-global',
|
|
142
167
|
registerClients: 'clientes-mcp',
|
|
143
168
|
promptRecommendation: 'roteamento',
|
|
@@ -168,6 +193,33 @@ function fitText(text, width) {
|
|
|
168
193
|
return `${value.slice(0, Math.max(0, width - 1))}~`;
|
|
169
194
|
}
|
|
170
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
|
+
|
|
171
223
|
function splitText(text, width, maxLines = 3) {
|
|
172
224
|
const words = String(text ?? '').split(/\s+/).filter(Boolean);
|
|
173
225
|
const lines = [];
|
|
@@ -390,9 +442,28 @@ function formatActionCommand(action) {
|
|
|
390
442
|
return [action.command, ...(action.args ?? [])].join(' ');
|
|
391
443
|
}
|
|
392
444
|
|
|
393
|
-
|
|
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() }) {
|
|
394
464
|
const isDevCheckout = existsSync(join(rootDir, 'tsconfig.json')) && existsSync(join(rootDir, 'src'));
|
|
395
465
|
const repoOnlyReason = 'Disponivel apenas no clone de desenvolvimento; no pacote npm use Doctor ou Status.';
|
|
466
|
+
const npmCommand = resolveNpmCommand(nodeExecPath);
|
|
396
467
|
|
|
397
468
|
return [
|
|
398
469
|
{
|
|
@@ -409,7 +480,7 @@ export function buildMenuCommands({ rootDir, currentFile, nodeExecPath = process
|
|
|
409
480
|
key: 'doctor',
|
|
410
481
|
aliases: ['doctor', 'diagnostico', 'verificar-menu', 'validar-menu'],
|
|
411
482
|
label: 'Doctor do menu e clientes MCP',
|
|
412
|
-
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.',
|
|
413
484
|
details: ['Seguro para outros notebooks.', 'Nao publica versao e nao mexe em configuracoes sem uma acao separada.'],
|
|
414
485
|
success: 'Mostra um relatorio GO/NO-GO com proximos comandos recomendados.',
|
|
415
486
|
command: nodeExecPath,
|
|
@@ -423,8 +494,9 @@ export function buildMenuCommands({ rootDir, currentFile, nodeExecPath = process
|
|
|
423
494
|
details: ['Exige clone do repositorio com src, testes e tsconfig.', 'Nao e uma acao indicada para instalacao global via npm.'],
|
|
424
495
|
success: 'Build, testes e manifestos passam no checkout de desenvolvimento.',
|
|
425
496
|
disabledReason: isDevCheckout ? null : repoOnlyReason,
|
|
426
|
-
command:
|
|
427
|
-
args: ['run', 'check'],
|
|
497
|
+
command: npmCommand.command,
|
|
498
|
+
args: [...npmCommand.argsPrefix, 'run', 'check'],
|
|
499
|
+
shell: npmCommand.shell,
|
|
428
500
|
},
|
|
429
501
|
{
|
|
430
502
|
key: 'build',
|
|
@@ -434,8 +506,9 @@ export function buildMenuCommands({ rootDir, currentFile, nodeExecPath = process
|
|
|
434
506
|
details: ['Exige clone do repositorio com tsconfig.', 'Pacotes npm publicados ja usam dist precompilado.'],
|
|
435
507
|
success: 'dist atualizado no checkout de desenvolvimento.',
|
|
436
508
|
disabledReason: isDevCheckout ? null : repoOnlyReason,
|
|
437
|
-
command:
|
|
438
|
-
args: ['run', 'build'],
|
|
509
|
+
command: npmCommand.command,
|
|
510
|
+
args: [...npmCommand.argsPrefix, 'run', 'build'],
|
|
511
|
+
shell: npmCommand.shell,
|
|
439
512
|
},
|
|
440
513
|
{
|
|
441
514
|
key: 'publicNpm',
|
|
@@ -444,13 +517,15 @@ export function buildMenuCommands({ rootDir, currentFile, nodeExecPath = process
|
|
|
444
517
|
description: 'Consulta a versao publicada em registry.npmjs.org.',
|
|
445
518
|
details: ['Usa somente leitura no registry publico.', 'Bom para confirmar se outro notebook consegue instalar por npm.'],
|
|
446
519
|
success: 'Retorna a versao publicada atualmente no npmjs.',
|
|
447
|
-
command:
|
|
520
|
+
command: npmCommand.command,
|
|
448
521
|
args: [
|
|
522
|
+
...npmCommand.argsPrefix,
|
|
449
523
|
'view',
|
|
450
524
|
'@fprad0/skill-master-mcp',
|
|
451
525
|
'version',
|
|
452
526
|
'--registry=https://registry.npmjs.org',
|
|
453
527
|
],
|
|
528
|
+
shell: npmCommand.shell,
|
|
454
529
|
},
|
|
455
530
|
{
|
|
456
531
|
key: 'updateGlobal',
|
|
@@ -516,6 +591,18 @@ export function buildMenuCommands({ rootDir, currentFile, nodeExecPath = process
|
|
|
516
591
|
args: [join(rootDir, 'bin', 'skill-master-install-global-skills.mjs')],
|
|
517
592
|
confirmMessage: 'Instalar as skills globais embutidas neste usuario?',
|
|
518
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
|
+
},
|
|
519
606
|
{
|
|
520
607
|
key: 'bootstrapGlobal',
|
|
521
608
|
aliases: ['bootstrap-global', 'global-bootstrap', 'ativar-global-completo'],
|
|
@@ -531,8 +618,8 @@ export function buildMenuCommands({ rootDir, currentFile, nodeExecPath = process
|
|
|
531
618
|
key: 'registerClients',
|
|
532
619
|
aliases: ['register-clients', 'registrar-clientes', 'codex-claude-gemini', 'antigravity'],
|
|
533
620
|
label: 'Registrar clientes MCP',
|
|
534
|
-
description: 'Gera/aplica configuracoes MCP para Codex, Claude, Gemini e Antigravity reconhecerem skill_master.',
|
|
535
|
-
details: ['Usa Node absoluto
|
|
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.'],
|
|
536
623
|
success: 'Arquivos de configuracao mesclados com o servidor skill_master.',
|
|
537
624
|
command: nodeExecPath,
|
|
538
625
|
args: [join(rootDir, 'bin', 'skill-master-register-clients.mjs'), '--apply-all'],
|
|
@@ -679,9 +766,9 @@ export function formatStatusReport(status) {
|
|
|
679
766
|
`Bundle de skills embutidas: ${bundledCatalog.total}`,
|
|
680
767
|
`Dominios do bundle: ${formatBundledCategoryLine(bundledCatalog)}`,
|
|
681
768
|
`Codex MCP robusto: ${readiness.codex.globalCommand ? 'sim' : readiness.codex.kind}`,
|
|
682
|
-
`Claude MCP robusto: ${readiness.claude.globalCommand ? 'sim' : readiness.claude.kind}`,
|
|
683
|
-
`Gemini MCP robusto: ${readiness.gemini.globalCommand ? 'sim' : readiness.gemini.kind}`,
|
|
684
|
-
`Antigravity MCP robusto: ${readiness.antigravity.globalCommand ? 'sim' : readiness.antigravity.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'}`,
|
|
685
772
|
];
|
|
686
773
|
|
|
687
774
|
if (status.versionText) {
|
|
@@ -698,8 +785,8 @@ function formatGlobalAlert(status, { useColor = false } = {}) {
|
|
|
698
785
|
return renderPanelLines(
|
|
699
786
|
[
|
|
700
787
|
colorize('GLOBAL READY', ANSI.bold, useColor),
|
|
701
|
-
'O MCP esta registrado
|
|
702
|
-
'Codex, Claude, Gemini e Antigravity podem iniciar
|
|
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.',
|
|
703
790
|
'A instalacao global e as skills embutidas ja estao prontas para uso.',
|
|
704
791
|
],
|
|
705
792
|
{ color: ANSI.green, useColor },
|
|
@@ -710,9 +797,10 @@ function formatGlobalAlert(status, { useColor = false } = {}) {
|
|
|
710
797
|
renderPanelLines(
|
|
711
798
|
[
|
|
712
799
|
colorize('ALERTA GLOBAL', ANSI.bold, useColor),
|
|
713
|
-
'Este computador ainda nao esta pronto para uso
|
|
800
|
+
'Este computador ainda nao esta pronto para uso robusto do Skill Master.',
|
|
714
801
|
'Execute agora: skill-master-menu --run bootstrap-global --yes',
|
|
715
|
-
'Isso instala as skills globais e registra Codex, Claude, Gemini e Antigravity com Node absoluto.',
|
|
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',
|
|
716
804
|
],
|
|
717
805
|
{ color: ANSI.red, useColor },
|
|
718
806
|
),
|
|
@@ -728,11 +816,165 @@ function formatGlobalAlert(status, { useColor = false } = {}) {
|
|
|
728
816
|
}
|
|
729
817
|
|
|
730
818
|
function renderPanelLines(lines, { color = ANSI.cyan, useColor = false } = {}) {
|
|
731
|
-
const width = Math.max(...lines.map((line) => line
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
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(''));
|
|
736
978
|
}
|
|
737
979
|
|
|
738
980
|
export function formatMenuBanner(status, { useColor = false } = {}) {
|
|
@@ -752,71 +994,83 @@ export function formatMenuBanner(status, { useColor = false } = {}) {
|
|
|
752
994
|
].join('\n\n');
|
|
753
995
|
}
|
|
754
996
|
|
|
755
|
-
function dnaPanelLines(tick, width, height, status, { useColor = false, compact = false } = {}) {
|
|
997
|
+
function dnaPanelLines(tick, width, height, status, selected, { useColor = false, compact = false } = {}) {
|
|
756
998
|
const lines = [];
|
|
757
|
-
const
|
|
758
|
-
const
|
|
759
|
-
const
|
|
760
|
-
const
|
|
761
|
-
const
|
|
762
|
-
const
|
|
763
|
-
const
|
|
764
|
-
const
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
}
|
|
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);
|
|
771
1012
|
|
|
772
|
-
|
|
773
|
-
if (col >= 0 && col < width) cells[col] = colorize(char, color, useColor);
|
|
774
|
-
};
|
|
1013
|
+
lines.push(fitText(colorize(`DNA CYBER HELIX / ${actionArea}`, ANSI.bold, useColor), width));
|
|
775
1014
|
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
const rightColor = front ? ANSI.amber : ANSI.gray;
|
|
786
|
-
const bondColor = front ? ANSI.gray : ANSI.blue;
|
|
787
|
-
const scanActive = row === scanRow;
|
|
788
|
-
|
|
789
|
-
place(chars, graphLeft, row === 0 ? '┌' : row === bodyHeight - 1 ? '└' : '│', ANSI.gray);
|
|
790
|
-
place(chars, graphRight, row === 0 ? '┐' : row === bodyHeight - 1 ? '┘' : '│', ANSI.gray);
|
|
791
|
-
|
|
792
|
-
if (row === 0 || row === bodyHeight - 1) {
|
|
793
|
-
for (let col = graphLeft + 1; col < graphRight; col += 1) {
|
|
794
|
-
place(chars, col, '─', ANSI.gray);
|
|
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));
|
|
1019
|
+
|
|
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);
|
|
795
1024
|
}
|
|
796
1025
|
}
|
|
797
1026
|
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
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);
|
|
801
1053
|
}
|
|
802
1054
|
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
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
|
+
}
|
|
812
1066
|
}
|
|
1067
|
+
}
|
|
813
1068
|
|
|
814
|
-
|
|
1069
|
+
for (const line of renderBrailleCanvas(canvas, { useColor })) {
|
|
1070
|
+
lines.push(line);
|
|
815
1071
|
}
|
|
816
1072
|
|
|
817
|
-
lines.push(fitText(
|
|
818
|
-
lines.push(fitText(colorize(compact ? 'helix stable' : 'helix stable lattice aligned', ANSI.gray, useColor), width));
|
|
819
|
-
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));
|
|
820
1074
|
|
|
821
1075
|
return lines.slice(0, height).map((line) => fitText(line, width));
|
|
822
1076
|
}
|
|
@@ -889,6 +1143,23 @@ function telemetryPanelLines(status, selected, width, height, { useColor = false
|
|
|
889
1143
|
return lines.slice(0, height).map((line) => fitText(line, width));
|
|
890
1144
|
}
|
|
891
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
|
+
|
|
892
1163
|
function renderBox(lines, width, title, { useColor = false, color = ANSI.cyan, style = 'normal' } = {}) {
|
|
893
1164
|
const innerWidth = Math.max(10, width - 4);
|
|
894
1165
|
const titleText = title ? ` ${title.toUpperCase()} ` : '';
|
|
@@ -998,7 +1269,7 @@ export function formatCyberMenuFrame(status, commands, selectedIndex, tick = 0,
|
|
|
998
1269
|
colorize('up/down move enter run q exit', ANSI.dim, useColor),
|
|
999
1270
|
...visibleCommands.map((command, offset) => {
|
|
1000
1271
|
const index = scrollStart + offset;
|
|
1001
|
-
const marker = index === selectedIndex ? '
|
|
1272
|
+
const marker = index === selectedIndex ? '>' : '.';
|
|
1002
1273
|
const disabled = command.disabledReason ? ' [off]' : '';
|
|
1003
1274
|
const label = `${marker} ${command.label}${disabled}`;
|
|
1004
1275
|
return index === selectedIndex
|
|
@@ -1015,7 +1286,7 @@ export function formatCyberMenuFrame(status, commands, selectedIndex, tick = 0,
|
|
|
1015
1286
|
colorize('DETAIL FOCUS', ANSI.amber, useColor),
|
|
1016
1287
|
colorize(selected?.label ?? 'Nenhuma acao', ANSI.bold, useColor),
|
|
1017
1288
|
fitText(colorize(`area ${actionArea} | action ${Math.min(selectedIndex + 1, commands.length)}/${commands.length}`, ANSI.gray, useColor), detailInnerWidth),
|
|
1018
|
-
fitText(colorize('
|
|
1289
|
+
fitText(colorize(patternFill('-=', 32), ANSI.amber, useColor), detailInnerWidth),
|
|
1019
1290
|
...splitText(selected?.description, detailInnerWidth, 1),
|
|
1020
1291
|
...splitText(selected?.success ? `resultado: ${selected.success}` : '', detailInnerWidth, 1),
|
|
1021
1292
|
...splitText(`exec: ${formatActionCommand(selected)}`, detailInnerWidth, 1),
|
|
@@ -1027,28 +1298,28 @@ export function formatCyberMenuFrame(status, commands, selectedIndex, tick = 0,
|
|
|
1027
1298
|
descriptionLines.push(fitText('', detailInnerWidth));
|
|
1028
1299
|
}
|
|
1029
1300
|
|
|
1030
|
-
const rightTopLines = dnaPanelLines(tick, rightInnerWidth, rightTopInnerHeight, status, { useColor, compact: compactMode });
|
|
1301
|
+
const rightTopLines = dnaPanelLines(tick, rightInnerWidth, rightTopInnerHeight, status, selected, { useColor, compact: compactMode });
|
|
1031
1302
|
const rightBottomLines = telemetryPanelLines(status, selected, rightInnerWidth, rightBottomInnerHeight, { useColor, tick, compact: compactMode });
|
|
1032
|
-
const summaryBox =
|
|
1303
|
+
const summaryBox = renderCyberFrame(
|
|
1033
1304
|
summaryLines.slice(0, summaryInnerHeight).map((line) => fitText(line, summaryInnerWidth)),
|
|
1034
1305
|
width,
|
|
1035
1306
|
'overview',
|
|
1036
|
-
{ useColor, color: ANSI.white, style: 'hud' },
|
|
1307
|
+
{ useColor, color: ANSI.white, style: 'hud', tick },
|
|
1037
1308
|
);
|
|
1038
|
-
const actionBox =
|
|
1309
|
+
const actionBox = renderCyberFrame(
|
|
1039
1310
|
actionLines.slice(0, actionInnerHeight).map((line) => fitText(line, actionInnerWidth)),
|
|
1040
1311
|
leftWidth,
|
|
1041
1312
|
'actions',
|
|
1042
|
-
{ useColor, color: ANSI.gray, style: '
|
|
1313
|
+
{ useColor, color: ANSI.gray, style: 'thin', tick },
|
|
1043
1314
|
);
|
|
1044
|
-
const detailBox =
|
|
1315
|
+
const detailBox = renderCyberFrame(
|
|
1045
1316
|
descriptionLines.slice(0, detailInnerHeight).map((line) => fitText(line, detailInnerWidth)),
|
|
1046
1317
|
leftWidth,
|
|
1047
1318
|
'details',
|
|
1048
|
-
{ useColor, color: ANSI.amber, style: 'focus' },
|
|
1319
|
+
{ useColor, color: ANSI.amber, style: 'focus', tick },
|
|
1049
1320
|
);
|
|
1050
|
-
const dnaBox =
|
|
1051
|
-
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 });
|
|
1052
1323
|
const bodyRows = joinHorizontalBoxes([
|
|
1053
1324
|
{ lines: [...actionBox, ...detailBox], width: leftWidth },
|
|
1054
1325
|
{ lines: [...dnaBox, ...telemetryBox], width: rightWidth },
|
|
@@ -1067,27 +1338,97 @@ export function formatRunningActionFrame(status, action, tick = 0, {
|
|
|
1067
1338
|
const innerWidth = width - 4;
|
|
1068
1339
|
const commandArea = resolveCommandArea(action);
|
|
1069
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));
|
|
1070
1342
|
const lines = [
|
|
1071
|
-
colorize('
|
|
1343
|
+
colorize('SKILL_MASTER WORKFLOW ONLINE', ANSI.bold, useColor),
|
|
1072
1344
|
fitText(colorize(`area ${commandArea} | launching ${action?.label ?? 'acao'}`, ANSI.amber, useColor), innerWidth),
|
|
1073
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 }),
|
|
1074
1348
|
fitText('', innerWidth),
|
|
1075
1349
|
...splitText(action?.description ?? '', innerWidth, 2),
|
|
1076
1350
|
...splitText(action?.success ? `resultado esperado: ${action.success}` : '', innerWidth, 2),
|
|
1077
1351
|
...splitText(`exec: ${formatActionCommand(action)}`, innerWidth, 2),
|
|
1078
1352
|
fitText('', innerWidth),
|
|
1079
|
-
fitText(colorize('
|
|
1353
|
+
fitText(colorize('handoff: HUD -> stream real do comando', ANSI.dim, useColor), innerWidth),
|
|
1080
1354
|
];
|
|
1081
1355
|
|
|
1082
1356
|
while (lines.length < Math.max(8, height - 2)) {
|
|
1083
1357
|
lines.push(fitText('', innerWidth));
|
|
1084
1358
|
}
|
|
1085
1359
|
|
|
1086
|
-
return
|
|
1360
|
+
return renderCyberFrame(
|
|
1087
1361
|
lines.slice(0, Math.max(8, height - 2)).map((line) => fitText(line, innerWidth)),
|
|
1088
1362
|
width,
|
|
1089
1363
|
'running',
|
|
1090
|
-
{ 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 },
|
|
1091
1432
|
).join('\n');
|
|
1092
1433
|
}
|
|
1093
1434
|
|
|
@@ -1100,13 +1441,32 @@ export function formatActionHeader(action, { useColor = false } = {}) {
|
|
|
1100
1441
|
action.disabledReason ? `Indisponivel: ${action.disabledReason}` : null,
|
|
1101
1442
|
].filter(Boolean);
|
|
1102
1443
|
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
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');
|
|
1107
1451
|
}
|
|
1108
1452
|
|
|
1109
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 } = {}) {
|
|
1110
1470
|
if (code === 0) {
|
|
1111
1471
|
return colorize('Comando concluido com sucesso.', ANSI.green, useColor);
|
|
1112
1472
|
}
|
|
@@ -1139,14 +1499,22 @@ O comando de menu e voltado para operacao humana. O binario MCP stdio continua s
|
|
|
1139
1499
|
}
|
|
1140
1500
|
|
|
1141
1501
|
export function runCommand(action, { cwd, env = process.env } = {}) {
|
|
1142
|
-
return new Promise((resolve) => {
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
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
|
+
}
|
|
1149
1516
|
|
|
1517
|
+
child.on('error', reject);
|
|
1150
1518
|
child.on('close', (code) => {
|
|
1151
1519
|
resolve(code ?? 1);
|
|
1152
1520
|
});
|