@fprad0/skill-master-mcp 1.0.0 → 1.0.1

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 (38) hide show
  1. package/CHANGELOG.md +9 -3
  2. package/README.md +2 -2
  3. package/VERSION.md +4 -4
  4. package/bin/lib/client-config.mjs +11 -9
  5. package/bin/lib/menu-core.mjs +241 -70
  6. package/bin/skill-master-activation.mjs +3 -1
  7. package/bin/skill-master-menu.mjs +127 -17
  8. package/bin/skill-master-success-skills.mjs +52 -2
  9. package/docs/operations/assets/menu-frame-compact.html +78 -75
  10. package/docs/operations/assets/menu-frame-dna-hero.html +87 -0
  11. package/docs/operations/assets/menu-frame-fine-helix.html +89 -0
  12. package/docs/operations/assets/menu-frame-large.html +86 -83
  13. package/docs/operations/assets/menu-frame-running.html +82 -79
  14. package/docs/operations/assets/menu-frame-score-10-contact-sheet.html +184 -0
  15. package/docs/planning/mcp-1.0.0/00_RESUMO_EXECUTIVO_AUDITORIA_MENU.md +118 -0
  16. package/docs/planning/mcp-1.0.0/01_MATRIZ_TESTES_MENU_E_RESULTADOS.md +250 -0
  17. package/docs/planning/mcp-1.0.0/02_PLANO_CORRECAO_ATIVAR_SKILL_APRENDIDA.md +200 -0
  18. package/docs/planning/mcp-1.0.0/03_PLANO_COMPATIBILIDADE_WINDOWS_LINUX_MACOS.md +167 -0
  19. package/docs/planning/mcp-1.0.0/04_PLANO_UI_CYBERPUNK_PIXEL_ART_E_PERFORMANCE.md +165 -0
  20. package/docs/planning/mcp-1.0.0/05_PROMPT_TASK_EXECUCAO_CORRECOES.md +151 -0
  21. package/docs/planning/mcp-1.0.0/06_CHECKLIST_REGRESSAO_PRE_RELEASE.md +159 -0
  22. package/docs/planning/mcp-1.0.0/07_RELATORIO_APLICACAO_CORRECOES_MENU_SKILL_MASTER.md +136 -0
  23. package/docs/planning/mcp-1.0.0/08_AUDITORIA_CRITICA_MENU_NOTA_E_DNA_REFINADO.md +184 -0
  24. package/docs/planning/mcp-1.0.0/prompt-tasks-nota-10-10/00_PROMPT_TASK_MASTER_NOTA_10_10.md +103 -0
  25. package/docs/planning/mcp-1.0.0/prompt-tasks-nota-10-10/01_PROMPT_TASK_FINE_HELIX_DNA.md +116 -0
  26. package/docs/planning/mcp-1.0.0/prompt-tasks-nota-10-10/02_PROMPT_TASK_DNA_HERO_BOOT_AND_MOTION.md +109 -0
  27. package/docs/planning/mcp-1.0.0/prompt-tasks-nota-10-10/03_PROMPT_TASK_MENU_UX_HELP_ERROR_COPY.md +99 -0
  28. package/docs/planning/mcp-1.0.0/prompt-tasks-nota-10-10/04_PROMPT_TASK_EVIDENCE_RENDERER_1_0_0.md +97 -0
  29. package/docs/planning/mcp-1.0.0/prompt-tasks-nota-10-10/05_PROMPT_TASK_CROSS_PLATFORM_UTF8_MOJIBAKE.md +99 -0
  30. package/docs/planning/mcp-1.0.0/prompt-tasks-nota-10-10/06_PROMPT_TASK_VISUAL_REGRESSION_QA.md +105 -0
  31. package/docs/planning/mcp-1.0.0/prompt-tasks-nota-10-10/07_PROMPT_TASK_PRE_RELEASE_SCORE_GATE_10_10.md +104 -0
  32. package/docs/planning/mcp-1.0.0/prompt-tasks-nota-10-10/README_ORDEM_EXECUCAO_NOTA_10_10.md +77 -0
  33. package/manifests/channels/beta.json +7 -7
  34. package/manifests/channels/stable.json +8 -8
  35. package/package.json +16 -14
  36. package/scripts/render-menu-evidence.mjs +115 -49
  37. package/scripts/verify-menu-actions.mjs +13 -8
  38. package/scripts/verify-menu-visual.mjs +90 -0
@@ -188,6 +188,44 @@ const SUPPORT_SYSTEM_SETUP_ACTIONS = new Set([
188
188
  'registerClients',
189
189
  ]);
190
190
 
191
+ const ASCII_SPARKLINE = ['.', ':', ':', '-', '=', '=', '#', '#'];
192
+ const UNICODE_SPARKLINE = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'];
193
+
194
+ function supportsUnicodeTerminal(env = process.env) {
195
+ if (env.SKILL_MASTER_ASCII === '1') return false;
196
+ if (env.TERM === 'dumb') return false;
197
+ if (env.CI === 'true') return false;
198
+ const locale = `${env.LC_ALL ?? ''} ${env.LC_CTYPE ?? ''} ${env.LANG ?? ''}`.toLowerCase();
199
+ if (locale && !locale.includes('utf') && !locale.includes('65001')) return false;
200
+ if (process.platform === 'win32') {
201
+ const termProgram = `${env.WT_SESSION ?? ''} ${env.TERM_PROGRAM ?? ''} ${env.ConEmuANSI ?? ''}`.toLowerCase();
202
+ const codepage = `${env.CMDCMDLINE ?? ''} ${env.TERM ?? ''}`.toLowerCase();
203
+ return Boolean(termProgram.trim()) || codepage.includes('65001') || locale.includes('utf');
204
+ }
205
+ return true;
206
+ }
207
+
208
+ export function resolveGlyphSet({ ascii = false, env = process.env } = {}) {
209
+ const useAscii = ascii || !supportsUnicodeTerminal(env);
210
+ return useAscii
211
+ ? {
212
+ ascii: true,
213
+ frame: 'ascii',
214
+ meterFilled: '#',
215
+ meterEmpty: '.',
216
+ sparkline: ASCII_SPARKLINE,
217
+ brailleFilled: '*',
218
+ }
219
+ : {
220
+ ascii: false,
221
+ frame: 'unicode',
222
+ meterFilled: '█',
223
+ meterEmpty: '░',
224
+ sparkline: UNICODE_SPARKLINE,
225
+ brailleFilled: null,
226
+ };
227
+ }
228
+
191
229
  function colorize(text, color, enabled) {
192
230
  return enabled ? `${color}${text}${ANSI.reset}` : text;
193
231
  }
@@ -208,6 +246,15 @@ function getActionAttentionBadge(command, status) {
208
246
  return '';
209
247
  }
210
248
 
249
+ function formatActionBadges(action) {
250
+ const badges = [];
251
+ if (action?.effect) badges.push(action.effect.toUpperCase());
252
+ if (action?.risk) badges.push(`RISK:${action.risk.toUpperCase()}`);
253
+ if (action?.confirmMessage) badges.push('CONFIRM');
254
+ if (action?.requiresTty) badges.push('TTY');
255
+ return badges.map((badge) => `[${badge}]`).join(' ');
256
+ }
257
+
211
258
  function getActionLineColor(command, selected, status) {
212
259
  const state = getActionAttentionState(command, status);
213
260
  if (selected && state === 'critical') return ANSI.rose;
@@ -225,8 +272,12 @@ function buildSetupAlertLine(status, compact) {
225
272
  : 'SETUP GLOBAL PENDENTE -> priorize [SETUP AGORA] e [SISTEMA] para autenticar o MCP no computador';
226
273
  }
227
274
 
275
+ function stripAnsi(text) {
276
+ return String(text).replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, '');
277
+ }
278
+
228
279
  function visibleLength(text) {
229
- return String(text).replace(/\x1b\[[0-9;]*m/g, '').length;
280
+ return stripAnsi(text).length;
230
281
  }
231
282
 
232
283
  function fitText(text, width) {
@@ -681,8 +732,13 @@ export function buildMenuCommands({ rootDir, currentFile, nodeExecPath = process
681
732
  description: 'Abre um fluxo interativo para avaliar um prompt pelo router local.',
682
733
  details: ['Ajuda a decidir se skill-master, skill_master ou skill-master-mcp deve agir.', 'Nao publica nem altera skills.'],
683
734
  success: 'Router imprime recomendacao, modo de ativacao e gates aplicaveis.',
735
+ effect: 'read-only',
736
+ risk: 'low',
737
+ requiresTty: true,
738
+ automationHint: 'skill-master-menu --run prompt-router --prompt "seu prompt".',
684
739
  command: nodeExecPath,
685
740
  args: [join(rootDir, 'bin', 'skill-master-activation.mjs'), '--route-prompt-interactive'],
741
+ cwd: invocationCwd,
686
742
  },
687
743
  {
688
744
  key: 'successNotifications',
@@ -711,6 +767,8 @@ export function buildMenuCommands({ rootDir, currentFile, nodeExecPath = process
711
767
  description: 'Cria um relatorio local para revisar e aprovar skills aprendidas.',
712
768
  details: ['Exige decisao humana antes de ativacao.', 'Inclui riscos, evidencias e recomendacao.'],
713
769
  success: 'Pacote local de aprovacao gerado.',
770
+ effect: 'writes-local',
771
+ risk: 'medium',
714
772
  command: nodeExecPath,
715
773
  args: [join(rootDir, 'bin', 'skill-master-success-skills.mjs'), '--approval-package'],
716
774
  },
@@ -742,8 +800,13 @@ export function buildMenuCommands({ rootDir, currentFile, nodeExecPath = process
742
800
  description: 'Instala uma skill aprendida em .codex/skills do projeto atual.',
743
801
  details: ['Escopo limitado ao workspace.', 'Mais conservador que ativar globalmente.'],
744
802
  success: 'Skill copiada para .codex/skills do projeto.',
803
+ effect: 'writes-local',
804
+ risk: 'medium',
805
+ requiresTty: true,
806
+ automationHint: 'use --manifest <manifest.json> --yes ou --yes quando houver exatamente um draft pronto.',
745
807
  command: nodeExecPath,
746
808
  args: [join(rootDir, 'bin', 'skill-master-success-skills.mjs'), '--activate-interactive', '--target', 'local'],
809
+ cwd: invocationCwd,
747
810
  confirmMessage: 'Selecionar e ativar uma skill aprendida no workspace atual?',
748
811
  },
749
812
  {
@@ -753,8 +816,13 @@ export function buildMenuCommands({ rootDir, currentFile, nodeExecPath = process
753
816
  description: 'Instala uma skill aprendida em CODEX_HOME/skills ou ~/.codex/skills.',
754
817
  details: ['Afeta todos os projetos deste usuario.', 'Use apenas depois de aprovacao humana.'],
755
818
  success: 'Skill instalada no diretorio global de skills.',
819
+ effect: 'writes-global',
820
+ risk: 'high',
821
+ requiresTty: true,
822
+ automationHint: 'use --manifest <manifest.json> --yes ou --yes quando houver exatamente um draft pronto.',
756
823
  command: nodeExecPath,
757
824
  args: [join(rootDir, 'bin', 'skill-master-success-skills.mjs'), '--activate-interactive', '--target', 'global'],
825
+ cwd: invocationCwd,
758
826
  confirmMessage: 'Selecionar e ativar uma skill aprendida como skill global deste usuario?',
759
827
  },
760
828
  {
@@ -887,7 +955,14 @@ function patternFill(pattern, width) {
887
955
  return Array.from({ length: Math.ceil(width / pattern.length) }, () => pattern).join('').slice(0, width);
888
956
  }
889
957
 
890
- function frameTokens(style) {
958
+ function frameTokens(style, glyphSet = resolveGlyphSet()) {
959
+ if (glyphSet.ascii) {
960
+ if (style === 'running') {
961
+ return { topLeft: '+-', topRight: '-+', bottomLeft: '+-', bottomRight: '-+', fill: '=', left: '||', right: '||', accent: '#', micro: '.' };
962
+ }
963
+ return { topLeft: '+-', topRight: '-+', bottomLeft: '+-', bottomRight: '-+', fill: style === 'focus' ? '=' : '-', left: '| ', right: ' |', accent: '#', micro: '.' };
964
+ }
965
+
891
966
  if (style === 'focus') {
892
967
  return { topLeft: '╭╼', topRight: '╾╮', bottomLeft: '╰╼', bottomRight: '╾╯', fill: '═', left: '▌ ', right: ' ▐', accent: '▰', micro: '▱' };
893
968
  }
@@ -932,10 +1007,10 @@ function renderCyberRail(width, title, tokens, { bottom = false, tick = 0 } = {}
932
1007
  return `${left}${fillWithPattern(`${lead}${safeTitle}${chip}${tab}`, innerWidth, tokens.fill)}${right}`;
933
1008
  }
934
1009
 
935
- function renderCyberFrame(lines, width, title, { useColor = false, color = ANSI.cyan, style = 'data', tick = 0 } = {}) {
1010
+ function renderCyberFrame(lines, width, title, { useColor = false, color = ANSI.cyan, style = 'data', tick = 0, glyphSet = resolveGlyphSet() } = {}) {
936
1011
  const safeWidth = Math.max(16, width);
937
1012
  const innerWidth = safeWidth - 4;
938
- const tokens = frameTokens(style);
1013
+ const tokens = frameTokens(style, glyphSet);
939
1014
  const top = renderCyberRail(safeWidth, title, tokens, { tick });
940
1015
  const bottom = renderCyberRail(safeWidth, title, tokens, { bottom: true, tick });
941
1016
  const body = lines.map((line, index) => {
@@ -1026,9 +1101,10 @@ function drawBrailleLine(canvas, x0, y0, x1, y1, thickness = 0, color = 'gray',
1026
1101
  }
1027
1102
  }
1028
1103
 
1029
- function renderBrailleCanvas(canvas, { useColor = false } = {}) {
1104
+ function renderBrailleCanvas(canvas, { useColor = false, glyphSet = resolveGlyphSet() } = {}) {
1030
1105
  return canvas.dots.map((row, rowIndex) => row.map((value, colIndex) => {
1031
1106
  if (value === 0) return ' ';
1107
+ if (glyphSet.ascii) return glyphSet.brailleFilled;
1032
1108
  const color = ANSI[canvas.colors[rowIndex][colIndex]] ?? ANSI.teal;
1033
1109
  return colorize(String.fromCodePoint(0x2800 + value), color, useColor);
1034
1110
  }).join(''));
@@ -1068,33 +1144,33 @@ export function formatMenuBanner(status, { useColor = false } = {}) {
1068
1144
  ].join('\n\n');
1069
1145
  }
1070
1146
 
1071
- function dnaPanelLines(tick, width, height, status, selected, { useColor = false, compact = false } = {}) {
1147
+ function dnaPanelLines(tick, width, height, status, selected, { useColor = false, compact = false, glyphSet = resolveGlyphSet() } = {}) {
1072
1148
  const lines = [];
1073
1149
  const canvasWidth = Math.max(18, width);
1074
1150
  const canvasHeight = Math.max(compact ? 8 : 10, height - 4);
1075
1151
  const pixelWidth = canvasWidth * 2;
1076
1152
  const pixelHeight = canvasHeight * 4;
1077
1153
  const center = Math.floor(pixelWidth / 2);
1078
- const amplitude = Math.max(8, Math.min(Math.floor(pixelWidth * 0.24), compact ? 12 : 18));
1154
+ const amplitude = Math.max(7, Math.min(Math.floor(pixelWidth * 0.2), compact ? 10 : 15));
1079
1155
  const frameCount = 48;
1080
1156
  const frameRatio = (tick % frameCount) / frameCount;
1081
1157
  const phase = frameRatio * Math.PI * 2;
1082
1158
  const scanPhase = (1 - Math.cos(frameRatio * Math.PI * 2)) / 2;
1083
- const pairEvery = compact ? 9 : 7;
1159
+ const pairEvery = compact ? 11 : 9;
1084
1160
  const actionArea = resolveCommandArea(selected);
1085
1161
  const canvas = createBrailleCanvas(canvasWidth, canvasHeight);
1086
1162
 
1087
1163
  lines.push(fitText(colorize(`DNA CYBER HELIX / ${actionArea}`, ANSI.bold, useColor), width));
1088
- lines.push(fitText(colorize(compact ? 'dual strand / focus' : 'dense helix / low flicker', ANSI.dim, useColor), width));
1164
+ lines.push(fitText(colorize(compact ? 'fine strand / focus' : 'luminous fine helix / low flicker', ANSI.dim, useColor), width));
1089
1165
 
1090
- const twist = compact ? 0.17 : 0.155;
1166
+ const twist = compact ? 0.19 : 0.168;
1091
1167
  const xA = (y) => Math.round(center + Math.sin(y * twist + phase) * amplitude);
1092
1168
  const xB = (y) => Math.round(center - Math.sin(y * twist + phase) * amplitude);
1093
1169
  const frontA = (y) => Math.cos(y * twist + phase) >= 0;
1094
1170
  const scanY = Math.floor(scanPhase * Math.max(1, pixelHeight - 1));
1095
- const pulse = 0.82 + ((Math.sin(phase * 2) + 1) / 2) * 0.28;
1171
+ const pulse = 0.72 + ((Math.sin(phase * 2) + 1) / 2) * 0.18;
1096
1172
 
1097
- for (let y = 0; y < pixelHeight; y += 8) {
1173
+ for (let y = 0; y < pixelHeight; y += 12) {
1098
1174
  plotBrailleThick(canvas, center, y, 0, 'gray', 0);
1099
1175
  }
1100
1176
 
@@ -1114,26 +1190,21 @@ function dnaPanelLines(tick, width, height, status, selected, { useColor = false
1114
1190
  const wave = y * twist + phase;
1115
1191
  const depthA = (Math.cos(wave) + 1) / 2;
1116
1192
  const depthB = 1 - depthA;
1117
- const strandAWidth = Math.max(1, Math.round((compact ? 1 : 2) + depthA * 2 * pulse));
1118
- const strandBWidth = Math.max(1, Math.round((compact ? 1 : 2) + depthB * 2 * pulse));
1119
- const edgeSpread = compact ? 2 : 3;
1193
+ const strandAWidth = Math.max(0, Math.min(1, Math.round(depthA * pulse)));
1194
+ const strandBWidth = Math.max(0, Math.min(1, Math.round(depthB * pulse)));
1120
1195
  const strandAColor = active ? 'white' : nearA ? 'rose' : 'violet';
1121
1196
  const strandBColor = active ? 'white' : nearA ? 'teal' : 'blue';
1122
1197
  const strandAPriority = active ? 7 : nearA ? 5 : 3;
1123
1198
  const strandBPriority = active ? 7 : nearA ? 3 : 5;
1124
1199
 
1125
1200
  if (y > 0) {
1126
- drawBrailleLine(canvas, previousA - edgeSpread, y - 1, a - edgeSpread, y, 0, nearA ? 'gray' : 'blue', 1);
1127
- drawBrailleLine(canvas, previousA + edgeSpread, y - 1, a + edgeSpread, y, 0, nearA ? 'gray' : 'blue', 1);
1128
- drawBrailleLine(canvas, previousB - edgeSpread, y - 1, b - edgeSpread, y, 0, nearA ? 'blue' : 'gray', 1);
1129
- drawBrailleLine(canvas, previousB + edgeSpread, y - 1, b + edgeSpread, y, 0, nearA ? 'blue' : 'gray', 1);
1201
+ if (y % 3 === 0) {
1202
+ drawBrailleLine(canvas, previousA + (nearA ? -2 : 2), y - 1, a + (nearA ? -2 : 2), y, 0, nearA ? 'gray' : 'blue', 1);
1203
+ drawBrailleLine(canvas, previousB + (nearA ? 2 : -2), y - 1, b + (nearA ? 2 : -2), y, 0, nearA ? 'blue' : 'gray', 1);
1204
+ }
1130
1205
  drawBrailleLine(canvas, previousA, y - 1, a, y, strandAWidth, strandAColor, strandAPriority);
1131
1206
  drawBrailleLine(canvas, previousB, y - 1, b, y, strandBWidth, strandBColor, strandBPriority);
1132
1207
  } else {
1133
- plotBrailleThick(canvas, a - edgeSpread, y, 0, nearA ? 'gray' : 'blue', 1);
1134
- plotBrailleThick(canvas, a + edgeSpread, y, 0, nearA ? 'gray' : 'blue', 1);
1135
- plotBrailleThick(canvas, b - edgeSpread, y, 0, nearA ? 'blue' : 'gray', 1);
1136
- plotBrailleThick(canvas, b + edgeSpread, y, 0, nearA ? 'blue' : 'gray', 1);
1137
1208
  plotBrailleThick(canvas, a, y, strandAWidth, strandAColor, strandAPriority);
1138
1209
  plotBrailleThick(canvas, b, y, strandBWidth, strandBColor, strandBPriority);
1139
1210
  }
@@ -1143,17 +1214,17 @@ function dnaPanelLines(tick, width, height, status, selected, { useColor = false
1143
1214
  const right = Math.max(a, b);
1144
1215
  const rungColor = active ? 'white' : nearA ? 'teal' : 'rose';
1145
1216
  const rungPriority = active ? 6 : 4;
1146
- drawBrailleLine(canvas, left + 3, y, right - 3, y, active ? 1 : 0, rungColor, rungPriority);
1147
- for (let x = left + 3; x < right - 2; x += active ? 2 : 3) {
1148
- plotBrailleThick(canvas, x, y, active ? 1 : 0, rungColor, rungPriority);
1217
+ drawBrailleLine(canvas, left + 3, y, right - 3, y, 0, rungColor, rungPriority);
1218
+ for (let x = left + 3; x < right - 2; x += active ? 4 : 5) {
1219
+ plotBrailleThick(canvas, x, y, 0, rungColor, rungPriority);
1149
1220
  }
1150
1221
  plotBrailleThick(canvas, Math.round((left + right) / 2), y, active ? 1 : 0, active ? 'white' : 'amber', rungPriority + 1);
1151
- plotBrailleThick(canvas, a, y, active ? 1 : 0, strandAColor, strandAPriority + 1);
1152
- plotBrailleThick(canvas, b, y, active ? 1 : 0, strandBColor, strandBPriority + 1);
1222
+ plotBrailleThick(canvas, a, y, 0, strandAColor, strandAPriority + 1);
1223
+ plotBrailleThick(canvas, b, y, 0, strandBColor, strandBPriority + 1);
1153
1224
  }
1154
1225
  }
1155
1226
 
1156
- for (const line of renderBrailleCanvas(canvas, { useColor })) {
1227
+ for (const line of renderBrailleCanvas(canvas, { useColor, glyphSet })) {
1157
1228
  lines.push(line);
1158
1229
  }
1159
1230
 
@@ -1162,17 +1233,17 @@ function dnaPanelLines(tick, width, height, status, selected, { useColor = false
1162
1233
  return lines.slice(0, height).map((line) => fitText(line, width));
1163
1234
  }
1164
1235
 
1165
- function renderMeterBar(value, total, width, { useColor = false, color = ANSI.teal } = {}) {
1236
+ function renderMeterBar(value, total, width, { useColor = false, color = ANSI.teal, glyphSet = resolveGlyphSet() } = {}) {
1166
1237
  const safeTotal = Math.max(1, total);
1167
1238
  const safeValue = Math.max(0, Math.min(value, safeTotal));
1168
1239
  const fill = Math.round((safeValue / safeTotal) * width);
1169
- const filled = '█'.repeat(fill);
1170
- const empty = '░'.repeat(Math.max(0, width - fill));
1240
+ const filled = glyphSet.meterFilled.repeat(fill);
1241
+ const empty = glyphSet.meterEmpty.repeat(Math.max(0, width - fill));
1171
1242
  return `${colorize(filled, color, useColor)}${colorize(empty, ANSI.gray, useColor)}`;
1172
1243
  }
1173
1244
 
1174
- function renderSparkline(series, { useColor = false, color = ANSI.teal } = {}) {
1175
- const blocks = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'];
1245
+ function renderSparkline(series, { useColor = false, color = ANSI.teal, glyphSet = resolveGlyphSet() } = {}) {
1246
+ const blocks = glyphSet.sparkline;
1176
1247
  return series.map((value) => colorize(blocks[Math.max(0, Math.min(blocks.length - 1, value))], color, useColor)).join('');
1177
1248
  }
1178
1249
 
@@ -1184,23 +1255,23 @@ function buildTelemetrySeries(seed, length, tick) {
1184
1255
  });
1185
1256
  }
1186
1257
 
1187
- function buildOverviewSignalLine(status, width, tick, { useColor = false, compact = false } = {}) {
1258
+ function buildOverviewSignalLine(status, width, tick, { useColor = false, compact = false, glyphSet = resolveGlyphSet() } = {}) {
1188
1259
  const signalWidth = Math.max(6, Math.min(compact ? 8 : 12, Math.floor(width / (compact ? 10 : 8))));
1189
1260
  const healthWidth = Math.max(6, Math.min(compact ? 8 : 12, Math.floor(width / (compact ? 10 : 8))));
1190
1261
  const signal = renderSparkline(
1191
1262
  buildTelemetrySeries(status.globalReadiness.installed.length + status.pendingSuccessDrafts, signalWidth, tick),
1192
- { useColor, color: ANSI.teal },
1263
+ { useColor, color: ANSI.teal, glyphSet },
1193
1264
  );
1194
1265
  const health = renderSparkline(
1195
1266
  buildTelemetrySeries(status.studyCandidates + (status.globalReadiness.ready ? 7 : 3), healthWidth, tick + 3),
1196
- { useColor, color: ANSI.amber },
1267
+ { useColor, color: ANSI.amber, glyphSet },
1197
1268
  );
1198
1269
  return compact
1199
1270
  ? `signal ${signal} health ${health}`
1200
1271
  : `signal ${signal} health ${health}`;
1201
1272
  }
1202
1273
 
1203
- function telemetryPanelLines(status, selected, width, height, { useColor = false, tick = 0, compact = false } = {}) {
1274
+ function telemetryPanelLines(status, selected, width, height, { useColor = false, tick = 0, compact = false, glyphSet = resolveGlyphSet() } = {}) {
1204
1275
  const commandArea = resolveCommandArea(selected);
1205
1276
  const bundledCatalog = status.bundledCatalog ?? { total: 0, categories: [] };
1206
1277
  const categoryLead = bundledCatalog.categories
@@ -1210,20 +1281,20 @@ function telemetryPanelLines(status, selected, width, height, { useColor = false
1210
1281
  const categoryCount = categoryLead?.count ?? 0;
1211
1282
  const meterWidth = Math.max(8, Math.min(16, width - 14));
1212
1283
  const sparkWidth = Math.max(8, Math.min(compact ? 8 : 12, width - 18));
1213
- const skillsSpark = renderSparkline(buildTelemetrySeries(status.globalReadiness.installed.length + categoryCount, sparkWidth, tick), { useColor, color: ANSI.amber });
1214
- const draftsSpark = renderSparkline(buildTelemetrySeries(status.pendingSuccessDrafts + 2, sparkWidth, tick + 2), { useColor, color: ANSI.teal });
1215
- const studySpark = renderSparkline(buildTelemetrySeries(status.studyCandidates + 4, sparkWidth, tick + 4), { useColor, color: ANSI.blue });
1284
+ const skillsSpark = renderSparkline(buildTelemetrySeries(status.globalReadiness.installed.length + categoryCount, sparkWidth, tick), { useColor, color: ANSI.amber, glyphSet });
1285
+ const draftsSpark = renderSparkline(buildTelemetrySeries(status.pendingSuccessDrafts + 2, sparkWidth, tick + 2), { useColor, color: ANSI.teal, glyphSet });
1286
+ const studySpark = renderSparkline(buildTelemetrySeries(status.studyCandidates + 4, sparkWidth, tick + 4), { useColor, color: ANSI.blue, glyphSet });
1216
1287
  const lines = [
1217
1288
  fitText(colorize('SYSTEM WINDOWS', ANSI.bold, useColor), width),
1218
1289
  renderStatusCapsule('AREA', commandArea, width, { useColor, color: ANSI.blue }),
1219
1290
  renderStatusCapsule('LEAD', `${categoryName} ${categoryCount}`, width, { useColor, color: ANSI.gray }),
1220
- fitText(`bundle ${String(bundledCatalog.total).padStart(2, '0')} ${renderMeterBar(bundledCatalog.total, Math.max(36, bundledCatalog.total), meterWidth, { useColor, color: ANSI.white })}`, width),
1221
- fitText(`global ${status.globalReadiness.installed.length}/${status.globalReadiness.required} ${renderMeterBar(status.globalReadiness.installed.length, status.globalReadiness.required, meterWidth, { useColor, color: ANSI.amber })}`, width),
1291
+ fitText(`bundle ${String(bundledCatalog.total).padStart(2, '0')} ${renderMeterBar(bundledCatalog.total, Math.max(36, bundledCatalog.total), meterWidth, { useColor, color: ANSI.white, glyphSet })}`, width),
1292
+ fitText(`global ${status.globalReadiness.installed.length}/${status.globalReadiness.required} ${renderMeterBar(status.globalReadiness.installed.length, status.globalReadiness.required, meterWidth, { useColor, color: ANSI.amber, glyphSet })}`, width),
1222
1293
  renderStatusCapsule('LINK', summarizeClientReadiness(status.globalReadiness), width, { useColor, color: ANSI.teal }),
1223
1294
  fitText(`skills ${skillsSpark}`, width),
1224
- fitText(`drafts ${String(status.pendingSuccessDrafts).padStart(2, '0')} ${renderMeterBar(status.pendingSuccessDrafts, Math.max(6, status.pendingSuccessDrafts || 1), meterWidth, { useColor, color: ANSI.teal })}`, width),
1295
+ fitText(`drafts ${String(status.pendingSuccessDrafts).padStart(2, '0')} ${renderMeterBar(status.pendingSuccessDrafts, Math.max(6, status.pendingSuccessDrafts || 1), meterWidth, { useColor, color: ANSI.teal, glyphSet })}`, width),
1225
1296
  fitText(`queue ${draftsSpark}`, width),
1226
- fitText(`study ${String(status.studyCandidates).padStart(2, '0')} ${renderMeterBar(status.studyCandidates, Math.max(8, status.studyCandidates || 1), meterWidth, { useColor, color: ANSI.blue })}`, width),
1297
+ fitText(`study ${String(status.studyCandidates).padStart(2, '0')} ${renderMeterBar(status.studyCandidates, Math.max(8, status.studyCandidates || 1), meterWidth, { useColor, color: ANSI.blue, glyphSet })}`, width),
1227
1298
  fitText(`links ${studySpark}`, width),
1228
1299
  fitText(colorize(compact ? 'codex / claude / gemini' : 'codex / claude / gemini / antigravity', ANSI.dim, useColor), width),
1229
1300
  ];
@@ -1295,11 +1366,13 @@ export function formatCyberMenuFrame(status, commands, selectedIndex, tick = 0,
1295
1366
  columns = 120,
1296
1367
  rows = 32,
1297
1368
  useColor = false,
1369
+ ascii = false,
1298
1370
  } = {}) {
1371
+ const glyphSet = resolveGlyphSet({ ascii });
1299
1372
  const width = Math.max(72, Math.min(columns, 150));
1300
1373
  const height = Math.max(22, Math.min(rows - 1, 42));
1301
1374
  const compactMode = width < 104 || height < 27;
1302
- const rightWidth = compactMode ? (width >= 92 ? 28 : 26) : width >= 132 ? 44 : width >= 118 ? 34 : width >= 108 ? 32 : 30;
1375
+ const rightWidth = compactMode ? (width >= 92 ? 28 : 26) : width >= 132 ? 44 : width >= 118 ? 40 : width >= 108 ? 36 : 34;
1303
1376
  const gutter = 2;
1304
1377
  const leftWidth = width - rightWidth - gutter;
1305
1378
  const selected = commands[selectedIndex] ?? commands[0];
@@ -1339,7 +1412,7 @@ export function formatCyberMenuFrame(status, commands, selectedIndex, tick = 0,
1339
1412
  `version ${status.semver} | channel ${status.manifestVersion} | readiness ${status.globalReadiness.ready ? 'ready' : status.globalReadiness.mode}`,
1340
1413
  colorize(`GLOBAL SKILLS ${status.globalReadiness.installed.length}/${status.globalReadiness.required}`, ANSI.amber, useColor),
1341
1414
  `bundle ${bundledCatalog.total} | drafts ${status.pendingSuccessDrafts} | study ${status.studyCandidates}`,
1342
- buildOverviewSignalLine(status, summaryInnerWidth, tick, { useColor, compact: true }),
1415
+ buildOverviewSignalLine(status, summaryInnerWidth, tick, { useColor, compact: true, glyphSet }),
1343
1416
  colorize(buildSetupAlertLine(status, true) ?? 'compact cyberpunk hud / operator safe', status.globalReadiness.ready ? ANSI.dim : ANSI.rose, useColor),
1344
1417
  ]
1345
1418
  : [
@@ -1348,7 +1421,7 @@ export function formatCyberMenuFrame(status, commands, selectedIndex, tick = 0,
1348
1421
  colorize(`GLOBAL SKILLS ${status.globalReadiness.installed.length}/${status.globalReadiness.required}`, ANSI.amber, useColor),
1349
1422
  `bundle ${bundledCatalog.total} | drafts ${status.pendingSuccessDrafts} | study ${status.studyCandidates}`,
1350
1423
  renderStatusCapsule('FOCUS', `${actionArea} / ${selected?.key ?? 'status'}`, summaryInnerWidth, { useColor, color: ANSI.teal }),
1351
- buildOverviewSignalLine(status, summaryInnerWidth, tick, { useColor, compact: false }),
1424
+ buildOverviewSignalLine(status, summaryInnerWidth, tick, { useColor, compact: false, glyphSet }),
1352
1425
  colorize(buildSetupAlertLine(status, false) ?? formatBundledCategoryLine(bundledCatalog), status.globalReadiness.ready ? ANSI.white : ANSI.rose, useColor),
1353
1426
  colorize(buildSetupAlertLine(status, false) ?? `clients ${summarizeClientReadiness(status.globalReadiness)}`, status.globalReadiness.ready ? ANSI.dim : ANSI.rose, useColor),
1354
1427
  ];
@@ -1359,9 +1432,9 @@ export function formatCyberMenuFrame(status, commands, selectedIndex, tick = 0,
1359
1432
  fitText(colorize(`window ${Math.min(selectedIndex + 1, commands.length)}/${commands.length} | visible ${visibleCommands.length}`, ANSI.gray, useColor), actionInnerWidth),
1360
1433
  ...visibleCommands.map((command, offset) => {
1361
1434
  const index = scrollStart + offset;
1362
- const marker = index === selectedIndex ? '>' : '·';
1435
+ const marker = index === selectedIndex ? '>' : glyphSet.ascii ? '-' : '·';
1363
1436
  const disabled = command.disabledReason ? ' [off]' : '';
1364
- const flair = index === selectedIndex && !compactMode ? ' ◢' : '';
1437
+ const flair = index === selectedIndex && !compactMode ? glyphSet.ascii ? ' >' : ' ◢' : '';
1365
1438
  const badge = getActionAttentionBadge(command, status);
1366
1439
  const label = `${marker} ${badge}${command.label}${disabled}${flair}`;
1367
1440
  const color = getActionLineColor(command, index === selectedIndex, status);
@@ -1378,6 +1451,7 @@ export function formatCyberMenuFrame(status, commands, selectedIndex, tick = 0,
1378
1451
  colorize('DETAIL FOCUS', ANSI.amber, useColor),
1379
1452
  colorize(selected?.label ?? 'Nenhuma acao', ANSI.bold, useColor),
1380
1453
  renderStatusCapsule('AREA', `${actionArea} | action ${Math.min(selectedIndex + 1, commands.length)}/${commands.length}`, detailInnerWidth, { useColor, color: ANSI.gray }),
1454
+ ...((selected?.effect || selected?.risk || selected?.requiresTty) && formatActionBadges(selected) ? [fitText(formatActionBadges(selected), detailInnerWidth)] : []),
1381
1455
  fitText(colorize(patternFill('=-', 32), ANSI.amber, useColor), detailInnerWidth),
1382
1456
  ...(() => {
1383
1457
  const attention = getActionAttentionState(selected, status);
@@ -1390,12 +1464,14 @@ export function formatCyberMenuFrame(status, commands, selectedIndex, tick = 0,
1390
1464
  return [];
1391
1465
  })(),
1392
1466
  ...splitText(selected?.description ? `desc: ${selected.description}` : '', detailInnerWidth, 1),
1467
+ ...((selected?.details ?? []).slice(0, 1).flatMap((detail) => splitText(`detail: ${detail}`, detailInnerWidth, 1))),
1393
1468
  ...(selected?.key === 'updateGlobal'
1394
1469
  ? splitText('recommended: se houver handoff ou aviso, feche o menu, rode o comando exibido e reabra os clientes MCP depois.', detailInnerWidth, 3)
1395
1470
  : []),
1396
- ...((selected?.details ?? []).flatMap((detail) => splitText(`detail: ${detail}`, detailInnerWidth, 2))),
1397
- ...splitText(`exec: ${formatActionCommand(selected)}`, detailInnerWidth, 1),
1471
+ ...(selected?.automationHint ? splitText(`automation: ${selected.automationHint}`, detailInnerWidth, 2) : []),
1472
+ ...splitText(`exec: ${formatActionCommand(selected)}`, detailInnerWidth, 1),
1398
1473
  ...splitText(selected?.success ? `result: ${selected.success}` : '', detailInnerWidth, 1),
1474
+ ...((selected?.details ?? []).slice(1).flatMap((detail) => splitText(`detail: ${detail}`, detailInnerWidth, 2))),
1399
1475
  ...(selected?.confirmMessage ? splitText(`confirmacao: ${selected.confirmMessage}`, detailInnerWidth, 2) : []),
1400
1476
  ...(selected?.disabledReason ? splitText(`Indisponivel: ${selected.disabledReason}`, detailInnerWidth, 2) : []),
1401
1477
  ];
@@ -1404,28 +1480,28 @@ export function formatCyberMenuFrame(status, commands, selectedIndex, tick = 0,
1404
1480
  descriptionLines.push(fitText('', detailInnerWidth));
1405
1481
  }
1406
1482
 
1407
- const rightTopLines = dnaPanelLines(tick, rightInnerWidth, rightTopInnerHeight, status, selected, { useColor, compact: compactMode });
1408
- const rightBottomLines = telemetryPanelLines(status, selected, rightInnerWidth, rightBottomInnerHeight, { useColor, tick, compact: compactMode });
1483
+ const rightTopLines = dnaPanelLines(tick, rightInnerWidth, rightTopInnerHeight, status, selected, { useColor, compact: compactMode, glyphSet });
1484
+ const rightBottomLines = telemetryPanelLines(status, selected, rightInnerWidth, rightBottomInnerHeight, { useColor, tick, compact: compactMode, glyphSet });
1409
1485
  const summaryBox = renderCyberFrame(
1410
1486
  summaryLines.slice(0, summaryInnerHeight).map((line) => fitText(line, summaryInnerWidth)),
1411
1487
  width,
1412
1488
  'overview',
1413
- { useColor, color: ANSI.white, style: 'hud', tick },
1489
+ { useColor, color: ANSI.white, style: 'hud', tick, glyphSet },
1414
1490
  );
1415
1491
  const actionBox = renderCyberFrame(
1416
1492
  actionLines.slice(0, actionInnerHeight).map((line) => fitText(line, actionInnerWidth)),
1417
1493
  leftWidth,
1418
1494
  'actions',
1419
- { useColor, color: ANSI.gray, style: 'thin', tick },
1495
+ { useColor, color: ANSI.gray, style: 'thin', tick, glyphSet },
1420
1496
  );
1421
1497
  const detailBox = renderCyberFrame(
1422
1498
  descriptionLines.slice(0, detailInnerHeight).map((line) => fitText(line, detailInnerWidth)),
1423
1499
  leftWidth,
1424
1500
  'details',
1425
- { useColor, color: ANSI.amber, style: 'focus', tick },
1501
+ { useColor, color: ANSI.amber, style: 'focus', tick, glyphSet },
1426
1502
  );
1427
- const dnaBox = renderCyberFrame(rightTopLines, rightWidth, 'dna-core', { useColor, color: ANSI.white, style: 'hud', tick });
1428
- const telemetryBox = renderCyberFrame(rightBottomLines, rightWidth, 'telemetry', { useColor, color: ANSI.blue, style: 'thin', tick });
1503
+ const dnaBox = renderCyberFrame(rightTopLines, rightWidth, 'dna-core', { useColor, color: ANSI.white, style: 'hud', tick, glyphSet });
1504
+ const telemetryBox = renderCyberFrame(rightBottomLines, rightWidth, 'telemetry', { useColor, color: ANSI.blue, style: 'thin', tick, glyphSet });
1429
1505
  const bodyRows = joinHorizontalBoxes([
1430
1506
  { lines: [...actionBox, ...detailBox], width: leftWidth },
1431
1507
  { lines: [...dnaBox, ...telemetryBox], width: rightWidth },
@@ -1438,12 +1514,14 @@ export function formatRunningActionFrame(status, action, tick = 0, {
1438
1514
  columns = 120,
1439
1515
  rows = 32,
1440
1516
  useColor = false,
1517
+ ascii = false,
1441
1518
  } = {}) {
1519
+ const glyphSet = resolveGlyphSet({ ascii });
1442
1520
  const width = Math.max(72, Math.min(columns, 150));
1443
1521
  const height = Math.max(18, Math.min(rows - 1, 30));
1444
1522
  const innerWidth = width - 4;
1445
1523
  const commandArea = resolveCommandArea(action);
1446
- 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 });
1524
+ 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, glyphSet });
1447
1525
  const gridHeight = Math.max(4, Math.min(8, height - 14));
1448
1526
  const lines = [
1449
1527
  colorize('SKILL_MASTER WORKFLOW ONLINE', ANSI.bold, useColor),
@@ -1467,7 +1545,7 @@ export function formatRunningActionFrame(status, action, tick = 0, {
1467
1545
  lines.slice(0, Math.max(8, height - 2)).map((line) => fitText(line, innerWidth)),
1468
1546
  width,
1469
1547
  'running',
1470
- { useColor, color: ANSI.teal, style: 'running', tick },
1548
+ { useColor, color: ANSI.teal, style: 'running', tick, glyphSet },
1471
1549
  ).join('\n');
1472
1550
  }
1473
1551
 
@@ -1475,13 +1553,15 @@ export function formatSkillMasterIntroFrame(status, tick = 0, {
1475
1553
  columns = 120,
1476
1554
  rows = 32,
1477
1555
  useColor = false,
1556
+ ascii = false,
1478
1557
  message = 'skill_master assumindo o workflow',
1479
1558
  } = {}) {
1559
+ const glyphSet = resolveGlyphSet({ ascii });
1480
1560
  const width = Math.max(72, Math.min(columns, 150));
1481
1561
  const height = Math.max(18, Math.min(rows - 1, 34));
1482
1562
  const innerWidth = width - 4;
1483
1563
  const gridHeight = Math.max(6, Math.min(12, height - 12));
1484
- const signal = renderSparkline(buildTelemetrySeries(status.pendingSuccessDrafts + status.studyCandidates + 5, Math.max(12, Math.min(28, Math.floor(innerWidth / 4))), tick), { useColor, color: ANSI.teal });
1564
+ const signal = renderSparkline(buildTelemetrySeries(status.pendingSuccessDrafts + status.studyCandidates + 5, Math.max(12, Math.min(28, Math.floor(innerWidth / 4))), tick), { useColor, color: ANSI.teal, glyphSet });
1485
1565
  const lines = [
1486
1566
  colorize('REBORN / SKILL_MASTER', ANSI.bold, useColor),
1487
1567
  fitText(colorize(message, ANSI.amber, useColor), innerWidth),
@@ -1502,7 +1582,57 @@ export function formatSkillMasterIntroFrame(status, tick = 0, {
1502
1582
  lines.slice(0, Math.max(8, height - 2)).map((line) => fitText(line, innerWidth)),
1503
1583
  width,
1504
1584
  'boot-sequence',
1505
- { useColor, color: ANSI.teal, style: 'hud', tick },
1585
+ { useColor, color: ANSI.teal, style: 'hud', tick, glyphSet },
1586
+ ).join('\n');
1587
+ }
1588
+
1589
+ export function formatSkillMasterDnaHeroFrame(status, tick = 0, {
1590
+ columns = 120,
1591
+ rows = 32,
1592
+ useColor = false,
1593
+ ascii = false,
1594
+ } = {}) {
1595
+ const glyphSet = resolveGlyphSet({ ascii });
1596
+ const width = Math.max(72, Math.min(columns, 150));
1597
+ const height = Math.max(18, Math.min(rows - 1, 34));
1598
+ const innerWidth = width - 4;
1599
+ const dnaHeight = Math.max(10, Math.min(16, height - 10));
1600
+ const dnaWidth = Math.max(34, Math.min(innerWidth, 72));
1601
+ const signal = renderSparkline(
1602
+ buildTelemetrySeries(status.globalReadiness.installed.length + status.studyCandidates + 9, Math.max(14, Math.min(30, Math.floor(innerWidth / 4))), tick),
1603
+ { useColor, color: ANSI.teal, glyphSet },
1604
+ );
1605
+ const dnaLines = dnaPanelLines(
1606
+ tick,
1607
+ dnaWidth,
1608
+ dnaHeight,
1609
+ status,
1610
+ { key: 'dnaHero', label: 'DNA hero' },
1611
+ { useColor, compact: false, glyphSet },
1612
+ );
1613
+ const centeredDna = dnaLines.map((line) => {
1614
+ const pad = Math.max(0, Math.floor((innerWidth - dnaWidth) / 2));
1615
+ return fitText(`${' '.repeat(pad)}${line.trimEnd()}`, innerWidth);
1616
+ });
1617
+ const lines = [
1618
+ colorize('SKILL_MASTER DNA ONLINE', ANSI.bold, useColor),
1619
+ fitText(colorize('boot hero / intelligence configuration / terminal-safe animation', ANSI.amber, useColor), innerWidth),
1620
+ fitText(colorize(`package ${status.packageName} | version ${status.semver} | channel ${status.manifestVersion}`, ANSI.gray, useColor), innerWidth),
1621
+ fitText(colorize(`DNA signal ${signal}`, ANSI.teal, useColor), innerWidth),
1622
+ ...centeredDna,
1623
+ fitText(renderCircuitRail(innerWidth, tick, { useColor, color: ANSI.teal }), innerWidth),
1624
+ fitText(colorize(`ONLINE | global ${status.globalReadiness.installed.length}/${status.globalReadiness.required} | bundle ${status.bundledCatalog?.total ?? 0}`, ANSI.gray, useColor), innerWidth),
1625
+ ];
1626
+
1627
+ while (lines.length < Math.max(8, height - 2)) {
1628
+ lines.push(fitText('', innerWidth));
1629
+ }
1630
+
1631
+ return renderCyberFrame(
1632
+ lines.slice(0, Math.max(8, height - 2)).map((line) => fitText(line, innerWidth)),
1633
+ width,
1634
+ 'dna-hero',
1635
+ { useColor, color: ANSI.teal, style: 'hud', tick, glyphSet },
1506
1636
  ).join('\n');
1507
1637
  }
1508
1638
 
@@ -1510,7 +1640,9 @@ export function formatCyberConfirmFrame(status, action, tick = 0, {
1510
1640
  columns = 120,
1511
1641
  rows = 32,
1512
1642
  useColor = false,
1643
+ ascii = false,
1513
1644
  } = {}) {
1645
+ const glyphSet = resolveGlyphSet({ ascii });
1514
1646
  const width = Math.max(72, Math.min(columns, 140));
1515
1647
  const height = Math.max(16, Math.min(rows - 1, 28));
1516
1648
  const innerWidth = width - 4;
@@ -1534,15 +1666,17 @@ export function formatCyberConfirmFrame(status, action, tick = 0, {
1534
1666
  lines.slice(0, Math.max(8, height - 2)).map((line) => fitText(line, innerWidth)),
1535
1667
  width,
1536
1668
  'subroutine',
1537
- { useColor, color: ANSI.amber, style: 'focus', tick },
1669
+ { useColor, color: ANSI.amber, style: 'focus', tick, glyphSet },
1538
1670
  ).join('\n');
1539
1671
  }
1540
1672
 
1541
1673
  export function formatActionHeader(action, { useColor = false } = {}) {
1542
1674
  const lines = [
1543
1675
  colorize(action.label, ANSI.bold, useColor),
1676
+ formatActionBadges(action),
1544
1677
  action.description,
1545
1678
  ...(action.details ?? []),
1679
+ action.automationHint ? `Automacao: ${action.automationHint}` : null,
1546
1680
  action.success ? `Resultado esperado: ${action.success}` : null,
1547
1681
  action.disabledReason ? `Indisponivel: ${action.disabledReason}` : null,
1548
1682
  ].filter(Boolean);
@@ -1585,21 +1719,58 @@ export function formatHelp(commands) {
1585
1719
  const actionList = commands.map((command) => {
1586
1720
  const aliases = command.aliases.length ? ` aliases: ${command.aliases.join(', ')}` : '';
1587
1721
  const disabled = command.disabledReason ? ` indisponivel: ${command.disabledReason}` : '';
1588
- return ` - ${command.key}: ${command.label}\n ${command.description}${aliases}${disabled}`;
1722
+ const badges = formatActionBadges(command);
1723
+ const hint = command.automationHint ? `\n ${command.automationHint}` : '';
1724
+ return ` - ${command.key}: ${command.label}${badges ? ` ${badges}` : ''}\n ${command.description}${aliases}${disabled}${hint}`;
1589
1725
  }).join('\n');
1726
+ const workspaceActions = commands
1727
+ .filter((command) => ['check', 'build', 'installProjectSkills', 'activateLearnedLocal'].includes(command.key))
1728
+ .map((command) => ` - ${command.key}: ${command.label}`)
1729
+ .join('\n');
1730
+ const globalActions = commands
1731
+ .filter((command) => ['updateGlobal', 'privateRegistry', 'activationAlwaysOn', 'installGlobalSkills', 'bootstrapGlobal', 'registerClients', 'rejectLearnedSkill', 'activateLearnedGlobal'].includes(command.key))
1732
+ .map((command) => ` - ${command.key}: ${command.label}`)
1733
+ .join('\n');
1590
1734
 
1591
1735
  return `Skill Master Menu
1592
1736
 
1593
- Uso:
1737
+ Uso humano:
1594
1738
  skill-master-menu
1595
- skill-master-menu --status
1596
- skill-master-menu --run <acao>
1597
- skill-master-menu --run update --yes
1598
1739
  skill-master-menu --help
1599
1740
 
1600
- Acoes disponiveis:
1741
+ Automacao sem TTY:
1742
+ skill-master-menu --status
1743
+ skill-master-menu --run <acao>
1744
+ skill-master-menu --run update --yes
1745
+ skill-master-menu --run activate-learned-local --manifest <manifest.json> --yes
1746
+ skill-master-menu --run activate-learned-global --manifest <manifest.json> --yes
1747
+ skill-master-menu --run prompt-router --prompt "validar menu nota 10"
1748
+ skill-master-menu --run activate-learned-local --list
1749
+ skill-master-menu --run activate-learned-global --list
1750
+ skill-master-menu --help
1751
+
1752
+ Diagnostico seguro:
1753
+ skill-master-menu --status
1754
+ skill-master-menu --run doctor
1755
+ skill-master-menu --run public-npm
1756
+ skill-master-menu --run notifications
1757
+
1758
+ Acoes que escrevem no workspace:
1759
+ ${workspaceActions}
1760
+
1761
+ Acoes globais ou de risco alto:
1762
+ ${globalActions}
1763
+
1764
+ Acoes disponiveis:
1601
1765
  ${actionList}
1602
1766
 
1767
+ Parametros uteis:
1768
+ --prompt "texto" passa prompt direto ao router sem abrir pergunta interativa
1769
+ --manifest <arquivo> seleciona manifest explicito de skill aprendida
1770
+ --list lista drafts quando usado com activate-learned-local/global
1771
+ --yes confirma acoes mutantes em automacao
1772
+ --overwrite permite substituir skill ja instalada quando usado com --manifest
1773
+
1603
1774
  O comando de menu e voltado para operacao humana. O binario MCP stdio continua sendo:
1604
1775
  skill-master-mcp
1605
1776
  `;
@@ -67,7 +67,9 @@ const loadSkills = async () => {
67
67
  };
68
68
 
69
69
  const routePrompt = async (prompt, { format }) => {
70
- if (!prompt) throw new Error('Missing prompt.');
70
+ if (!prompt) {
71
+ throw new Error('Missing prompt. Use: skill-master-menu --run prompt-router --prompt "validar menu nota 10"');
72
+ }
71
73
  const config = readActivationConfig();
72
74
  const skills = await loadSkills();
73
75
  const decision = routeSkillMasterPrompt(skills, prompt, {