@fprad0/skill-master-mcp 0.0.12 → 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 (353) hide show
  1. package/CHANGELOG.md +100 -88
  2. package/README.md +472 -472
  3. package/VERSION.md +9 -9
  4. package/bin/lib/bootstrap-global-core.mjs +34 -0
  5. package/bin/lib/client-config.mjs +287 -285
  6. package/bin/lib/doctor-core.mjs +202 -0
  7. package/bin/lib/menu-core.mjs +1792 -1514
  8. package/bin/lib/operation-result.mjs +59 -0
  9. package/bin/lib/register-clients-core.mjs +247 -0
  10. package/bin/lib/skill-installation.mjs +215 -215
  11. package/bin/lib/update-cli-core.mjs +117 -0
  12. package/bin/skill-master-activation.mjs +165 -163
  13. package/bin/skill-master-bootstrap-global.mjs +61 -49
  14. package/bin/skill-master-configure-private-registry.mjs +3 -3
  15. package/bin/skill-master-doctor.mjs +239 -228
  16. package/bin/skill-master-eval-activation.mjs +32 -32
  17. package/bin/skill-master-install-global-skills.mjs +59 -59
  18. package/bin/skill-master-install-project-skills.mjs +97 -97
  19. package/bin/skill-master-menu.mjs +489 -378
  20. package/bin/skill-master-register-clients.mjs +232 -153
  21. package/bin/skill-master-success-skills.mjs +357 -307
  22. package/bin/skill-master-update.mjs +121 -72
  23. package/bin/skill-master.mjs +3 -3
  24. package/dist/activation.d.ts.map +1 -1
  25. package/dist/activation.js +12 -0
  26. package/dist/activation.js.map +1 -1
  27. package/dist/prompt-router.d.ts.map +1 -1
  28. package/dist/prompt-router.js +19 -0
  29. package/dist/prompt-router.js.map +1 -1
  30. package/dist/recommender.d.ts.map +1 -1
  31. package/dist/recommender.js +4 -1
  32. package/dist/recommender.js.map +1 -1
  33. package/docs/architecture/APRENDIZADO_DE_IMPLEMENTACOES_BEM_SUCEDIDAS.md +125 -125
  34. package/docs/architecture/ARQUITETURA_AUTO_UPDATE.md +9 -9
  35. package/docs/architecture/PLANO_MASTER_ACIONAMENTO_AUTOMATICO_E_APRENDIZADO.md +341 -341
  36. package/docs/architecture/REDE_SEGURA_DE_SKILLS.md +148 -148
  37. package/docs/operations/GUIA_MULTI_COMPUTADOR.md +262 -262
  38. package/docs/operations/GUIA_NPM_PRIVADO.md +294 -294
  39. package/docs/operations/GUIA_NPM_PUBLICO.md +147 -147
  40. package/docs/operations/MENU_VISUAL_EVIDENCE_2026-06-28.md +66 -66
  41. package/docs/operations/assets/menu-frame-compact.html +36 -33
  42. package/docs/operations/assets/menu-frame-dna-hero.html +87 -0
  43. package/docs/operations/assets/menu-frame-fine-helix.html +89 -0
  44. package/docs/operations/assets/menu-frame-large.html +44 -41
  45. package/docs/operations/assets/menu-frame-running.html +41 -38
  46. package/docs/operations/assets/menu-frame-score-10-contact-sheet.html +184 -0
  47. package/docs/operations/cross-platform-auth-transfer/ANALISE_COMPATIBILIDADE_MCP_2026-06-28.md +140 -140
  48. package/docs/operations/cross-platform-auth-transfer/README_TRANSFERENCIA.md +85 -85
  49. package/docs/operations/reborn-menu-cyberpunk-transfer/ANALISE_MENU_REBORN_CYBERPUNK_2026-06-28.md +174 -174
  50. package/docs/operations/reborn-menu-cyberpunk-transfer/HANDOFF_IMPLEMENTACAO_REBORN_CYBERPUNK_2026-06-28.md +119 -119
  51. package/docs/operations/reborn-menu-cyberpunk-transfer/ORDEM_DE_EXECUCAO_MENU_REBORN_CYBERPUNK.md +134 -134
  52. package/docs/operations/reborn-menu-cyberpunk-transfer/README_TRANSFERENCIA.md +84 -84
  53. package/docs/operations/reborn-menu-cyberpunk-transfer/README_TRANSFERENCIA_REBORN_PACKAGE.md +56 -56
  54. package/docs/operations/token-economy-transfer/ANALISE_AVANCADA_ECONOMIA_TOKENS_2026-06-30.md +141 -0
  55. package/docs/operations/token-economy-transfer/PLANO_DEV_SENIOR_MASTER_TOKEN_ECONOMY_2026-06-30.md +171 -0
  56. package/docs/operations/token-economy-transfer/README_TRANSFERENCIA_TOKEN_ECONOMY.md +31 -0
  57. package/docs/planning/MENU_RUNTIME_CORRECTION_PLAN_2026-06-30.md +551 -0
  58. package/docs/planning/V0_0_9_APROVACAO_CRITICA_MENSAGENS_DE_VENDA.md +85 -85
  59. package/docs/planning/V0_0_9_FONTES_E_CRITERIOS_DE_AUTORIDADE.md +139 -139
  60. package/docs/planning/V0_0_9_MATRIZ_SKILLS_MULTIDISCIPLINARES.md +105 -105
  61. package/docs/planning/V0_0_9_POLITICA_MORAL_CATOLICA_PARA_IA.md +181 -181
  62. package/docs/planning/V0_0_9_PROMPTS_EXECUCAO.md +59 -59
  63. package/docs/planning/V0_0_9_ROADMAP_DISCERNIMENTO_E_CONHECIMENTO_AMPLO.md +181 -181
  64. package/docs/planning/mcp-1.0.0/00_RESUMO_EXECUTIVO_AUDITORIA_MENU.md +118 -0
  65. package/docs/planning/mcp-1.0.0/01_MATRIZ_TESTES_MENU_E_RESULTADOS.md +250 -0
  66. package/docs/planning/mcp-1.0.0/02_PLANO_CORRECAO_ATIVAR_SKILL_APRENDIDA.md +200 -0
  67. package/docs/planning/mcp-1.0.0/03_PLANO_COMPATIBILIDADE_WINDOWS_LINUX_MACOS.md +167 -0
  68. package/docs/planning/mcp-1.0.0/04_PLANO_UI_CYBERPUNK_PIXEL_ART_E_PERFORMANCE.md +165 -0
  69. package/docs/planning/mcp-1.0.0/05_PROMPT_TASK_EXECUCAO_CORRECOES.md +151 -0
  70. package/docs/planning/mcp-1.0.0/06_CHECKLIST_REGRESSAO_PRE_RELEASE.md +159 -0
  71. package/docs/planning/mcp-1.0.0/07_RELATORIO_APLICACAO_CORRECOES_MENU_SKILL_MASTER.md +136 -0
  72. package/docs/planning/mcp-1.0.0/08_AUDITORIA_CRITICA_MENU_NOTA_E_DNA_REFINADO.md +184 -0
  73. package/docs/planning/mcp-1.0.0/prompt-tasks-nota-10-10/00_PROMPT_TASK_MASTER_NOTA_10_10.md +103 -0
  74. package/docs/planning/mcp-1.0.0/prompt-tasks-nota-10-10/01_PROMPT_TASK_FINE_HELIX_DNA.md +116 -0
  75. package/docs/planning/mcp-1.0.0/prompt-tasks-nota-10-10/02_PROMPT_TASK_DNA_HERO_BOOT_AND_MOTION.md +109 -0
  76. package/docs/planning/mcp-1.0.0/prompt-tasks-nota-10-10/03_PROMPT_TASK_MENU_UX_HELP_ERROR_COPY.md +99 -0
  77. package/docs/planning/mcp-1.0.0/prompt-tasks-nota-10-10/04_PROMPT_TASK_EVIDENCE_RENDERER_1_0_0.md +97 -0
  78. package/docs/planning/mcp-1.0.0/prompt-tasks-nota-10-10/05_PROMPT_TASK_CROSS_PLATFORM_UTF8_MOJIBAKE.md +99 -0
  79. package/docs/planning/mcp-1.0.0/prompt-tasks-nota-10-10/06_PROMPT_TASK_VISUAL_REGRESSION_QA.md +105 -0
  80. package/docs/planning/mcp-1.0.0/prompt-tasks-nota-10-10/07_PROMPT_TASK_PRE_RELEASE_SCORE_GATE_10_10.md +104 -0
  81. package/docs/planning/mcp-1.0.0/prompt-tasks-nota-10-10/README_ORDEM_EXECUCAO_NOTA_10_10.md +77 -0
  82. package/docs/prompt-tasks/PROMPT_TASK_001_BOOTSTRAP_SKILL_MASTER_MCP.md +6 -6
  83. package/docs/prompt-tasks/PROMPT_TASK_002_AUTO_UPDATE_LAUNCHER.md +6 -6
  84. package/docs/prompt-tasks/PROMPT_TASK_003_REMOTE_MANIFEST_AND_RELEASES.md +6 -6
  85. package/docs/prompt-tasks/PROMPT_TASK_004_MULTI_USER_DISTRIBUTION.md +6 -6
  86. package/docs/prompt-tasks/PROMPT_TASK_005_SECURITY_AND_QUALITY_GATE.md +6 -6
  87. package/docs/prompt-tasks/PROMPT_TASK_006_MASTER_ACIONAMENTO_APRENDIZADO.md +83 -83
  88. package/docs/prompt-tasks/PROMPT_TASK_007_PERSONA_ORQUESTRADORA.md +88 -88
  89. package/docs/prompt-tasks/PROMPT_TASK_008_PROMPT_ROUTER_MODOS_ATIVACAO.md +156 -156
  90. package/docs/prompt-tasks/PROMPT_TASK_009_PIPELINE_APRENDIZADO_SUCESSO.md +105 -105
  91. package/docs/prompt-tasks/PROMPT_TASK_010_EVALS_GOVERNANCA_ATIVACAO.md +119 -119
  92. package/docs/prompt-tasks/PROMPT_TASK_011_MENU_NOTIFICACOES_NOTION.md +120 -120
  93. package/docs/prompt-tasks/PROMPT_TASK_012_MENU_CYBERPUNK_PIXEL_FRAME.md +123 -123
  94. package/docs/prompt-tasks/PROMPT_TASK_013_MENU_FLUID_DNA_ANIMATION.md +114 -114
  95. package/docs/prompt-tasks/PROMPT_TASK_014_MENU_FUNCTIONAL_PARITY_QA.md +157 -157
  96. package/docs/prompt-tasks/PROMPT_TASK_015_TRANSFER_RELEASE_HANDOFF.md +127 -127
  97. package/docs/prompt-tasks/PROMPT_TASK_016_CROSS_PLATFORM_MCP_AUTH_REGISTRATION.md +107 -107
  98. package/docs/prompt-tasks/PROMPT_TASK_018_NPM_PUBLISH_2FA_SETUP.md +80 -80
  99. package/docs/prompt-tasks/PROMPT_TASK_019_TOKEN_ECONOMY_GLOBAL_SKILLS.md +56 -0
  100. package/docs/prompt-tasks/PROMPT_TASK_MASTER_EXECUTOR.md +6 -6
  101. package/docs/skill-candidates/v0.0.10/cli-creator/LICENSE.txt +201 -201
  102. package/docs/skill-candidates/v0.0.10/cli-creator/SKILL.md +160 -160
  103. package/docs/skill-candidates/v0.0.10/cli-creator/agents/openai.yaml +4 -4
  104. package/docs/skill-candidates/v0.0.10/cli-creator/references/agent-cli-patterns.md +154 -154
  105. package/docs/skill-candidates/v0.0.10/developer-workstation-ops/SKILL.md +32 -32
  106. package/docs/skill-candidates/v0.0.10/figma/LICENSE.txt +1 -1
  107. package/docs/skill-candidates/v0.0.10/figma/SKILL.md +42 -42
  108. package/docs/skill-candidates/v0.0.10/figma/agents/openai.yaml +14 -14
  109. package/docs/skill-candidates/v0.0.10/figma/assets/figma-small.svg +3 -3
  110. package/docs/skill-candidates/v0.0.10/figma/assets/icon.svg +28 -28
  111. package/docs/skill-candidates/v0.0.10/figma/references/figma-mcp-config.md +35 -35
  112. package/docs/skill-candidates/v0.0.10/figma/references/figma-tools-and-prompts.md +34 -34
  113. package/docs/skill-candidates/v0.0.10/figma-code-connect-components/LICENSE.TXT +1 -1
  114. package/docs/skill-candidates/v0.0.10/figma-code-connect-components/SKILL.md +349 -349
  115. package/docs/skill-candidates/v0.0.10/figma-code-connect-components/agents/openai.yaml +14 -14
  116. package/docs/skill-candidates/v0.0.10/figma-code-connect-components/assets/figma-small.svg +3 -3
  117. package/docs/skill-candidates/v0.0.10/figma-code-connect-components/assets/icon.svg +28 -28
  118. package/docs/skill-candidates/v0.0.10/figma-code-connect-components/references/mapping-checklist.md +7 -7
  119. package/docs/skill-candidates/v0.0.10/figma-code-connect-components/scripts/normalize_node_id.py +25 -25
  120. package/docs/skill-candidates/v0.0.10/figma-create-design-system-rules/LICENSE.TXT +1 -1
  121. package/docs/skill-candidates/v0.0.10/figma-create-design-system-rules/SKILL.md +537 -537
  122. package/docs/skill-candidates/v0.0.10/figma-create-design-system-rules/agents/openai.yaml +14 -14
  123. package/docs/skill-candidates/v0.0.10/figma-create-design-system-rules/assets/figma-small.svg +3 -3
  124. package/docs/skill-candidates/v0.0.10/figma-create-design-system-rules/assets/icon.svg +28 -28
  125. package/docs/skill-candidates/v0.0.10/figma-create-design-system-rules/references/rule-template.md +15 -15
  126. package/docs/skill-candidates/v0.0.10/figma-create-design-system-rules/scripts/check_agents_md.sh +9 -9
  127. package/docs/skill-candidates/v0.0.10/figma-generate-design/LICENSE.TXT +1 -1
  128. package/docs/skill-candidates/v0.0.10/figma-generate-design/SKILL.md +341 -341
  129. package/docs/skill-candidates/v0.0.10/figma-generate-design/agents/openai.yaml +14 -14
  130. package/docs/skill-candidates/v0.0.10/figma-generate-design/assets/figma-small.svg +3 -3
  131. package/docs/skill-candidates/v0.0.10/figma-generate-design/assets/icon.svg +28 -28
  132. package/docs/skill-candidates/v0.0.10/figma-generate-design/maintainers.yml +1 -1
  133. package/docs/skill-candidates/v0.0.10/figma-generate-library/LICENSE.TXT +1 -1
  134. package/docs/skill-candidates/v0.0.10/figma-generate-library/SKILL.md +314 -314
  135. package/docs/skill-candidates/v0.0.10/figma-generate-library/agents/openai.yaml +14 -14
  136. package/docs/skill-candidates/v0.0.10/figma-generate-library/assets/figma-small.svg +3 -3
  137. package/docs/skill-candidates/v0.0.10/figma-generate-library/assets/icon.svg +28 -28
  138. package/docs/skill-candidates/v0.0.10/figma-generate-library/maintainers.yml +3 -3
  139. package/docs/skill-candidates/v0.0.10/figma-generate-library/references/code-connect-setup.md +260 -260
  140. package/docs/skill-candidates/v0.0.10/figma-generate-library/references/component-creation.md +1014 -1014
  141. package/docs/skill-candidates/v0.0.10/figma-generate-library/references/discovery-phase.md +518 -518
  142. package/docs/skill-candidates/v0.0.10/figma-generate-library/references/documentation-creation.md +834 -834
  143. package/docs/skill-candidates/v0.0.10/figma-generate-library/references/error-recovery.md +540 -540
  144. package/docs/skill-candidates/v0.0.10/figma-generate-library/references/naming-conventions.md +527 -527
  145. package/docs/skill-candidates/v0.0.10/figma-generate-library/references/token-creation.md +962 -962
  146. package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/bindVariablesToComponent.js +110 -110
  147. package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/cleanupOrphans.js +127 -127
  148. package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/createComponentWithVariants.js +148 -148
  149. package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/createDocumentationPage.js +139 -139
  150. package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/createSemanticTokens.js +108 -108
  151. package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/createVariableCollection.js +49 -49
  152. package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/inspectFileStructure.js +121 -121
  153. package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/rehydrateState.js +92 -92
  154. package/docs/skill-candidates/v0.0.10/figma-generate-library/scripts/validateCreation.js +83 -83
  155. package/docs/skill-candidates/v0.0.10/figma-implement-design/LICENSE.txt +1 -1
  156. package/docs/skill-candidates/v0.0.10/figma-implement-design/SKILL.md +258 -258
  157. package/docs/skill-candidates/v0.0.10/figma-implement-design/agents/openai.yaml +14 -14
  158. package/docs/skill-candidates/v0.0.10/figma-implement-design/assets/figma-small.svg +3 -3
  159. package/docs/skill-candidates/v0.0.10/figma-implement-design/assets/icon.svg +28 -28
  160. package/docs/skill-candidates/v0.0.10/figma-use/LICENSE.TXT +1 -1
  161. package/docs/skill-candidates/v0.0.10/figma-use/SKILL.md +233 -233
  162. package/docs/skill-candidates/v0.0.10/figma-use/agents/openai.yaml +14 -14
  163. package/docs/skill-candidates/v0.0.10/figma-use/assets/figma-small.svg +3 -3
  164. package/docs/skill-candidates/v0.0.10/figma-use/assets/icon.svg +28 -28
  165. package/docs/skill-candidates/v0.0.10/figma-use/maintainers.yml +1 -1
  166. package/docs/skill-candidates/v0.0.10/figma-use/references/api-reference.md +301 -301
  167. package/docs/skill-candidates/v0.0.10/figma-use/references/common-patterns.md +512 -512
  168. package/docs/skill-candidates/v0.0.10/figma-use/references/component-patterns.md +488 -488
  169. package/docs/skill-candidates/v0.0.10/figma-use/references/effect-style-patterns.md +123 -123
  170. package/docs/skill-candidates/v0.0.10/figma-use/references/gotchas.md +599 -599
  171. package/docs/skill-candidates/v0.0.10/figma-use/references/maintainers.yml +12 -12
  172. package/docs/skill-candidates/v0.0.10/figma-use/references/plugin-api-patterns.md +513 -513
  173. package/docs/skill-candidates/v0.0.10/figma-use/references/plugin-api-standalone.d.ts +11293 -11293
  174. package/docs/skill-candidates/v0.0.10/figma-use/references/plugin-api-standalone.index.md +441 -441
  175. package/docs/skill-candidates/v0.0.10/figma-use/references/text-style-patterns.md +203 -203
  176. package/docs/skill-candidates/v0.0.10/figma-use/references/validation-and-recovery.md +109 -109
  177. package/docs/skill-candidates/v0.0.10/figma-use/references/variable-patterns.md +354 -354
  178. package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/maintainers.yml +9 -9
  179. package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds-components--creating.md +17 -17
  180. package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds-components--using.md +17 -17
  181. package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds-components.md +50 -50
  182. package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds-effect-styles.md +52 -52
  183. package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds-text-styles.md +90 -90
  184. package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds-variables--creating.md +13 -13
  185. package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds-variables--using.md +13 -13
  186. package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds-variables.md +64 -64
  187. package/docs/skill-candidates/v0.0.10/figma-use/references/working-with-design-systems/wwds.md +41 -41
  188. package/docs/skill-candidates/v0.0.10/frontend-design/LICENSE.txt +177 -177
  189. package/docs/skill-candidates/v0.0.10/frontend-design/SKILL.md +55 -55
  190. package/docs/skill-candidates/v0.0.10/frontend-ui-ux-systems/SKILL.md +32 -32
  191. package/docs/skill-candidates/v0.0.10/github/SKILL.md +74 -74
  192. package/docs/skill-candidates/v0.0.10/github/agents/openai.yaml +6 -6
  193. package/docs/skill-candidates/v0.0.10/github/assets/github-small.svg +3 -3
  194. package/docs/skill-candidates/v0.0.10/image-graphic-design-rendering/SKILL.md +28 -28
  195. package/docs/skill-candidates/v0.0.10/language-quality-pt-en-fr-it-ru/SKILL.md +28 -28
  196. package/docs/skill-candidates/v0.0.10/math-physics-reasoning/SKILL.md +28 -28
  197. package/docs/skill-candidates/v0.0.10/mcp-builder/LICENSE.txt +201 -201
  198. package/docs/skill-candidates/v0.0.10/mcp-builder/SKILL.md +236 -236
  199. package/docs/skill-candidates/v0.0.10/mcp-builder/reference/evaluation.md +601 -601
  200. package/docs/skill-candidates/v0.0.10/mcp-builder/reference/mcp_best_practices.md +249 -249
  201. package/docs/skill-candidates/v0.0.10/mcp-builder/reference/node_mcp_server.md +969 -969
  202. package/docs/skill-candidates/v0.0.10/mcp-builder/reference/python_mcp_server.md +718 -718
  203. package/docs/skill-candidates/v0.0.10/mcp-builder/scripts/connections.py +151 -151
  204. package/docs/skill-candidates/v0.0.10/mcp-builder/scripts/evaluation.py +373 -373
  205. package/docs/skill-candidates/v0.0.10/mcp-builder/scripts/example_evaluation.xml +22 -22
  206. package/docs/skill-candidates/v0.0.10/mcp-builder/scripts/requirements.txt +2 -2
  207. package/docs/skill-candidates/v0.0.10/mcp-client-readiness/SKILL.md +31 -31
  208. package/docs/skill-candidates/v0.0.10/openai-docs/LICENSE.txt +201 -201
  209. package/docs/skill-candidates/v0.0.10/openai-docs/SKILL.md +161 -161
  210. package/docs/skill-candidates/v0.0.10/openai-docs/agents/openai.yaml +14 -14
  211. package/docs/skill-candidates/v0.0.10/openai-docs/assets/openai-small.svg +3 -3
  212. package/docs/skill-candidates/v0.0.10/openai-docs/references/latest-model.md +37 -37
  213. package/docs/skill-candidates/v0.0.10/openai-docs/references/prompting-guide.md +244 -244
  214. package/docs/skill-candidates/v0.0.10/openai-docs/references/upgrade-guide.md +181 -181
  215. package/docs/skill-candidates/v0.0.10/openai-docs/scripts/fetch-codex-manual.mjs +598 -598
  216. package/docs/skill-candidates/v0.0.10/openai-docs/scripts/resolve-latest-model-info.js +147 -147
  217. package/docs/skill-candidates/v0.0.10/playwright/NOTICE.txt +14 -14
  218. package/docs/skill-candidates/v0.0.10/playwright/SKILL.md +147 -147
  219. package/docs/skill-candidates/v0.0.10/playwright/agents/openai.yaml +6 -6
  220. package/docs/skill-candidates/v0.0.10/playwright/assets/playwright-small.svg +3 -3
  221. package/docs/skill-candidates/v0.0.10/playwright/references/cli.md +116 -116
  222. package/docs/skill-candidates/v0.0.10/playwright/references/workflows.md +95 -95
  223. package/docs/skill-candidates/v0.0.10/playwright/scripts/playwright_cli.sh +25 -25
  224. package/docs/skill-candidates/v0.0.10/polyglot-backend-engineering/SKILL.md +32 -32
  225. package/docs/skill-candidates/v0.0.10/screenshot/LICENSE.txt +201 -201
  226. package/docs/skill-candidates/v0.0.10/screenshot/SKILL.md +267 -267
  227. package/docs/skill-candidates/v0.0.10/screenshot/agents/openai.yaml +6 -6
  228. package/docs/skill-candidates/v0.0.10/screenshot/assets/screenshot-small.svg +5 -5
  229. package/docs/skill-candidates/v0.0.10/screenshot/scripts/ensure_macos_permissions.sh +54 -54
  230. package/docs/skill-candidates/v0.0.10/screenshot/scripts/macos_display_info.swift +22 -22
  231. package/docs/skill-candidates/v0.0.10/screenshot/scripts/macos_permissions.swift +40 -40
  232. package/docs/skill-candidates/v0.0.10/screenshot/scripts/macos_window_info.swift +126 -126
  233. package/docs/skill-candidates/v0.0.10/screenshot/scripts/take_screenshot.ps1 +163 -163
  234. package/docs/skill-candidates/v0.0.10/screenshot/scripts/take_screenshot.py +585 -585
  235. package/docs/skill-candidates/v0.0.10/skill-master-orchestrator/SKILL.md +62 -62
  236. package/docs/skill-candidates/v0.0.10/skill-master-orchestrator/agents/openai.yaml +4 -4
  237. package/docs/skill-candidates/v0.0.10/skill-master-orchestrator/references/activation-policy.md +77 -77
  238. package/docs/skill-candidates/v0.0.10/skill-master-orchestrator/references/human-approval-policy.md +83 -83
  239. package/docs/skill-candidates/v0.0.10/skill-master-orchestrator/references/persona-dev-senior-master.md +46 -46
  240. package/docs/skill-candidates/v0.0.10/terminal-menu-operations/SKILL.md +30 -30
  241. package/docs/skill-candidates/v0.0.10/terminal-pixel-art-tui/SKILL.md +43 -43
  242. package/docs/skill-candidates/v0.0.10/webapp-testing/LICENSE.txt +201 -201
  243. package/docs/skill-candidates/v0.0.10/webapp-testing/SKILL.md +95 -95
  244. package/docs/skill-candidates/v0.0.10/webapp-testing/examples/console_logging.py +34 -34
  245. package/docs/skill-candidates/v0.0.10/webapp-testing/examples/element_discovery.py +39 -39
  246. package/docs/skill-candidates/v0.0.10/webapp-testing/examples/static_html_automation.py +32 -32
  247. package/docs/skill-candidates/v0.0.10/webapp-testing/scripts/with_server.py +105 -105
  248. package/docs/skill-candidates/v0.0.10/winui-app/LICENSE.txt +201 -201
  249. package/docs/skill-candidates/v0.0.10/winui-app/SKILL.md +94 -94
  250. package/docs/skill-candidates/v0.0.10/winui-app/agents/openai.yaml +5 -5
  251. package/docs/skill-candidates/v0.0.10/winui-app/config.yaml +50 -50
  252. package/docs/skill-candidates/v0.0.10/winui-app/references/_sections.md +96 -96
  253. package/docs/skill-candidates/v0.0.10/winui-app/references/accessibility-input-and-localization.md +51 -51
  254. package/docs/skill-candidates/v0.0.10/winui-app/references/build-run-and-launch-verification.md +72 -72
  255. package/docs/skill-candidates/v0.0.10/winui-app/references/community-toolkit-controls-and-helpers.md +57 -57
  256. package/docs/skill-candidates/v0.0.10/winui-app/references/controls-layout-and-adaptive-ui.md +84 -84
  257. package/docs/skill-candidates/v0.0.10/winui-app/references/foundation-environment-audit-and-remediation.md +82 -82
  258. package/docs/skill-candidates/v0.0.10/winui-app/references/foundation-setup-and-project-selection.md +67 -67
  259. package/docs/skill-candidates/v0.0.10/winui-app/references/foundation-template-first-recovery.md +62 -62
  260. package/docs/skill-candidates/v0.0.10/winui-app/references/foundation-winui-app-structure.md +62 -62
  261. package/docs/skill-candidates/v0.0.10/winui-app/references/motion-animations-and-polish.md +45 -45
  262. package/docs/skill-candidates/v0.0.10/winui-app/references/performance-diagnostics-and-responsiveness.md +46 -46
  263. package/docs/skill-candidates/v0.0.10/winui-app/references/sample-source-map.md +37 -37
  264. package/docs/skill-candidates/v0.0.10/winui-app/references/shell-navigation-and-windowing.md +67 -67
  265. package/docs/skill-candidates/v0.0.10/winui-app/references/styling-theming-materials-and-icons.md +71 -71
  266. package/docs/skill-candidates/v0.0.10/winui-app/references/testing-debugging-and-review-checklists.md +77 -77
  267. package/docs/skill-candidates/v0.0.10/winui-app/references/windows-app-sdk-lifecycle-notifications-and-deployment.md +52 -52
  268. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/SKILL.md +398 -398
  269. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/common-patterns.md +330 -330
  270. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/complete-examples.md +871 -871
  271. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/component-patterns.md +501 -501
  272. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/data-fetching.md +766 -766
  273. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/file-organization.md +501 -501
  274. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/loading-and-error-states.md +500 -500
  275. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/performance.md +405 -405
  276. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/routing-guide.md +363 -363
  277. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/styling-guide.md +427 -427
  278. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/typescript-standards.md +417 -417
  279. package/docs/skill-candidates/v0.0.11/git-version-control-ops/SKILL.md +34 -34
  280. package/docs/skill-candidates/v0.0.11/go-engineering/SKILL.md +34 -34
  281. package/docs/skill-candidates/v0.0.11/java-engineering/SKILL.md +34 -34
  282. package/docs/skill-candidates/v0.0.11/javascript-engineering/SKILL.md +34 -34
  283. package/docs/skill-candidates/v0.0.11/json-contract-design/SKILL.md +34 -34
  284. package/docs/skill-candidates/v0.0.11/multi-client-mcp-ops/SKILL.md +36 -36
  285. package/docs/skill-candidates/v0.0.11/nextjs/SKILL.md +745 -745
  286. package/docs/skill-candidates/v0.0.11/nextjs/agents/openai.yaml +3 -3
  287. package/docs/skill-candidates/v0.0.11/nextjs/references/app-router-files.md +94 -94
  288. package/docs/skill-candidates/v0.0.11/python-engineering/SKILL.md +34 -34
  289. package/docs/skill-candidates/v0.0.11/ruby-engineering/SKILL.md +34 -34
  290. package/docs/skill-candidates/v0.0.11/senior-fullstack/SKILL.md +209 -209
  291. package/docs/skill-candidates/v0.0.11/senior-fullstack/references/architecture_patterns.md +103 -103
  292. package/docs/skill-candidates/v0.0.11/senior-fullstack/references/development_workflows.md +103 -103
  293. package/docs/skill-candidates/v0.0.11/senior-fullstack/references/tech_stack_guide.md +103 -103
  294. package/docs/skill-candidates/v0.0.11/senior-fullstack/scripts/code_quality_analyzer.py +114 -114
  295. package/docs/skill-candidates/v0.0.11/senior-fullstack/scripts/fullstack_scaffolder.py +114 -114
  296. package/docs/skill-candidates/v0.0.11/senior-fullstack/scripts/project_scaffolder.py +114 -114
  297. package/docs/skill-candidates/v0.0.11/shadcn/SKILL.md +573 -573
  298. package/docs/skill-candidates/v0.0.11/shadcn/agents/openai.yaml +3 -3
  299. package/docs/skill-candidates/v0.0.11/sql-postgresql-engineering/SKILL.md +34 -34
  300. package/docs/skill-candidates/v0.0.11/terminal-shell-ops/SKILL.md +34 -34
  301. package/docs/skill-candidates/v0.0.11/typescript-expert/SKILL.md +429 -429
  302. package/docs/skill-candidates/v0.0.11/typescript-expert/references/tsconfig-strict.json +91 -91
  303. package/docs/skill-candidates/v0.0.11/typescript-expert/references/typescript-cheatsheet.md +383 -383
  304. package/docs/skill-candidates/v0.0.11/typescript-expert/references/utility-types.ts +335 -335
  305. package/docs/skill-candidates/v0.0.11/typescript-expert/scripts/ts_diagnostic.py +203 -203
  306. package/docs/skill-candidates/v0.0.11/ui-component-primitives/SKILL.md +34 -34
  307. package/docs/skill-candidates/v0.0.11/web-mobile-design-systems/SKILL.md +34 -34
  308. package/docs/skill-candidates/v0.0.11/windows-linux-platform-ops/SKILL.md +34 -34
  309. package/docs/skill-candidates/v0.0.12/context-compression-handoff/SKILL.md +47 -0
  310. package/docs/skill-candidates/v0.0.12/csharp-senior-master-engineering/SKILL.md +32 -32
  311. package/docs/skill-candidates/v0.0.12/css-senior-master-engineering/SKILL.md +32 -32
  312. package/docs/skill-candidates/v0.0.12/go-senior-master-engineering/SKILL.md +32 -32
  313. package/docs/skill-candidates/v0.0.12/html-senior-master-engineering/SKILL.md +32 -32
  314. package/docs/skill-candidates/v0.0.12/javascript-senior-master-engineering/SKILL.md +32 -32
  315. package/docs/skill-candidates/v0.0.12/json-senior-master-engineering/SKILL.md +32 -32
  316. package/docs/skill-candidates/v0.0.12/prompt-budget-gate/SKILL.md +46 -0
  317. package/docs/skill-candidates/v0.0.12/python-senior-master-engineering/SKILL.md +32 -32
  318. package/docs/skill-candidates/v0.0.12/react-senior-master-engineering/SKILL.md +32 -32
  319. package/docs/skill-candidates/v0.0.12/ruby-senior-master-engineering/SKILL.md +32 -32
  320. package/docs/skill-candidates/v0.0.12/senior-master-code-optimizer/SKILL.md +48 -48
  321. package/docs/skill-candidates/v0.0.12/sql-senior-master-engineering/SKILL.md +31 -31
  322. package/docs/skill-candidates/v0.0.12/token-economy-orchestrator/SKILL.md +38 -0
  323. package/docs/skill-candidates/v0.0.12/typescript-senior-master-engineering/SKILL.md +35 -35
  324. package/docs/skill-candidates/v0.0.9/ai-ethics-human-dignity/SKILL.md +32 -32
  325. package/docs/skill-candidates/v0.0.9/broad-domain-router/SKILL.md +41 -41
  326. package/docs/skill-candidates/v0.0.9/catholic-moral-discernment/SKILL.md +31 -31
  327. package/docs/skill-candidates/v0.0.9/engineering-systems-master/SKILL.md +31 -31
  328. package/docs/skill-candidates/v0.0.9/language-quality-pt-en-fr/SKILL.md +28 -28
  329. package/docs/skill-candidates/v0.0.9/math-science-reasoning/SKILL.md +29 -29
  330. package/docs/skill-candidates/v0.0.9/philosophy-sociology-discernment/SKILL.md +28 -28
  331. package/docs/skill-candidates/v0.0.9/professional-boundary-triage/SKILL.md +40 -40
  332. package/docs/skill-candidates/v0.0.9/release-ethics-gate/SKILL.md +32 -32
  333. package/docs/skill-candidates/v0.0.9/source-authority-reviewer/SKILL.md +31 -31
  334. package/examples/client-configs/claude-code.commands.md +21 -21
  335. package/examples/client-configs/claude-code.project.mcp.json +18 -18
  336. package/examples/client-configs/claude-desktop.macos.json +18 -18
  337. package/examples/client-configs/claude-desktop.windows.json +20 -20
  338. package/examples/client-configs/codex.windows.toml +11 -11
  339. package/examples/client-configs/gemini-code-assist.intellij.mcp.json +18 -18
  340. package/examples/client-configs/gemini.linux.settings.json +21 -21
  341. package/examples/client-configs/gemini.windows.settings.json +23 -23
  342. package/examples/client-configs/generic-stdio.json +16 -16
  343. package/manifests/channels/beta.json +24 -24
  344. package/manifests/channels/stable.json +25 -25
  345. package/network/approved-skills.json +54 -54
  346. package/network/unapproved-skill-candidates.json +110 -110
  347. package/package.json +89 -86
  348. package/scripts/configure-private-registry.mjs +208 -208
  349. package/scripts/lib/private-registry.mjs +97 -97
  350. package/scripts/render-menu-evidence.mjs +196 -130
  351. package/scripts/verify-menu-actions.mjs +112 -107
  352. package/scripts/verify-menu-visual.mjs +90 -0
  353. package/sources.json +11 -11
@@ -1,1522 +1,1800 @@
1
- import { spawn } from 'node:child_process';
2
- import { existsSync, readFileSync, readdirSync } from 'node:fs';
3
- import os from 'node:os';
4
- import { dirname, join } from 'node:path';
5
- import process from 'node:process';
6
- import {
7
- assessCodexConfigContent,
8
- assessMcpServerConfig,
9
- defaultClientConfigPaths,
10
- } from './client-config.mjs';
11
-
12
- const ANSI = {
13
- reset: '\x1b[0m',
14
- bold: '\x1b[1m',
15
- dim: '\x1b[2m',
16
- cyan: '\x1b[36m',
17
- green: '\x1b[32m',
18
- yellow: '\x1b[33m',
19
- red: '\x1b[31m',
20
- white: '\x1b[37m',
21
- gray: '\x1b[90m',
22
- teal: '\x1b[38;5;80m',
23
- amber: '\x1b[38;5;179m',
24
- blue: '\x1b[38;5;75m',
25
- };
26
-
27
- const REQUIRED_GLOBAL_SKILLS = [
28
- 'catholic-moral-discernment',
29
- 'ai-ethics-human-dignity',
30
- 'professional-boundary-triage',
31
- 'broad-domain-router',
32
- 'language-quality-pt-en-fr',
33
- 'math-science-reasoning',
34
- 'philosophy-sociology-discernment',
35
- 'engineering-systems-master',
36
- 'source-authority-reviewer',
37
- 'release-ethics-gate',
38
- 'mcp-client-readiness',
39
- 'terminal-menu-operations',
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',
53
- ];
54
-
55
- const BUNDLED_SKILL_DOMAIN_GROUPS = [
56
- {
57
- key: 'moral',
58
- label: 'moral-core',
59
- skills: [
60
- 'ai-ethics-human-dignity',
61
- 'broad-domain-router',
62
- 'catholic-moral-discernment',
63
- 'engineering-systems-master',
64
- 'language-quality-pt-en-fr',
65
- 'math-science-reasoning',
66
- 'philosophy-sociology-discernment',
67
- 'professional-boundary-triage',
68
- 'release-ethics-gate',
69
- 'source-authority-reviewer',
70
- ],
71
- },
72
- {
73
- key: 'frontend',
74
- label: 'frontend-ui',
75
- skills: [
76
- 'figma',
77
- 'figma-code-connect-components',
78
- 'figma-create-design-system-rules',
79
- 'figma-generate-design',
80
- 'figma-generate-library',
81
- 'figma-implement-design',
82
- 'figma-use',
83
- 'frontend-dev-guidelines',
84
- 'frontend-design',
85
- 'frontend-ui-ux-systems',
86
- 'html-senior-master-engineering',
87
- 'nextjs',
88
- 'playwright',
89
- 'react-senior-master-engineering',
90
- 'screenshot',
91
- 'senior-fullstack',
92
- 'shadcn',
93
- 'css-senior-master-engineering',
94
- 'ui-component-primitives',
95
- 'webapp-testing',
96
- 'web-mobile-design-systems',
97
- 'winui-app',
98
- ],
99
- },
100
- {
101
- key: 'backend',
102
- label: 'backend-data',
103
- skills: [
104
- 'csharp-senior-master-engineering',
105
- 'go-engineering',
106
- 'go-senior-master-engineering',
107
- 'java-engineering',
108
- 'javascript-engineering',
109
- 'javascript-senior-master-engineering',
110
- 'json-contract-design',
111
- 'json-senior-master-engineering',
112
- 'mcp-builder',
113
- 'polyglot-backend-engineering',
114
- 'python-engineering',
115
- 'python-senior-master-engineering',
116
- 'ruby-engineering',
117
- 'ruby-senior-master-engineering',
118
- 'senior-master-code-optimizer',
119
- 'sql-postgresql-engineering',
120
- 'sql-senior-master-engineering',
121
- 'typescript-expert',
122
- 'typescript-senior-master-engineering',
123
- ],
124
- },
125
- {
126
- key: 'ops',
127
- label: 'ops-clients',
128
- skills: [
129
- 'cli-creator',
130
- 'developer-workstation-ops',
131
- 'git-version-control-ops',
132
- 'github',
133
- 'mcp-client-readiness',
134
- 'multi-client-mcp-ops',
135
- 'openai-docs',
136
- 'skill-master-orchestrator',
137
- 'terminal-shell-ops',
138
- 'terminal-menu-operations',
139
- 'terminal-pixel-art-tui',
140
- 'windows-linux-platform-ops',
141
- ],
142
- },
143
- {
144
- key: 'knowledge',
145
- label: 'knowledge',
146
- skills: [
147
- 'language-quality-pt-en-fr-it-ru',
148
- 'math-physics-reasoning',
149
- ],
150
- },
151
- ];
152
-
153
- const COMMAND_AREA_LABELS = {
154
- status: 'diagnostico',
155
- doctor: 'diagnostico',
156
- check: 'desenvolvimento',
157
- build: 'desenvolvimento',
158
- publicNpm: 'distribuicao',
159
- updateGlobal: 'distribuicao',
160
- privateRegistry: 'registry',
161
- activationStatus: 'ativacao',
162
- activationBalanced: 'ativacao',
163
- activationAlwaysOn: 'ativacao',
164
- installGlobalSkills: 'skills-globais',
165
- installProjectSkills: 'skills-projeto',
166
- bootstrapGlobal: 'bootstrap-global',
167
- registerClients: 'clientes-mcp',
168
- promptRecommendation: 'roteamento',
169
- successNotifications: 'aprendizado',
170
- studySkills: 'aprendizado',
171
- approvalPackage: 'aprovacao',
172
- markLearnedStudy: 'aprovacao',
173
- rejectLearnedSkill: 'aprovacao',
174
- activateLearnedLocal: 'ativacao-skill',
175
- activateLearnedGlobal: 'ativacao-skill',
176
- notionSummary: 'documentacao',
177
- };
178
-
179
- function colorize(text, color, enabled) {
180
- return enabled ? `${color}${text}${ANSI.reset}` : text;
181
- }
182
-
183
- function visibleLength(text) {
184
- return String(text).replace(/\x1b\[[0-9;]*m/g, '').length;
185
- }
186
-
187
- function fitText(text, width) {
188
- const value = String(text ?? '');
189
- if (visibleLength(value) <= width) {
190
- return `${value}${' '.repeat(width - visibleLength(value))}`;
191
- }
192
-
193
- return `${value.slice(0, Math.max(0, width - 1))}~`;
194
- }
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
-
223
- function splitText(text, width, maxLines = 3) {
224
- const words = String(text ?? '').split(/\s+/).filter(Boolean);
225
- const lines = [];
226
- let current = '';
227
-
228
- for (const word of words) {
229
- const next = current ? `${current} ${word}` : word;
230
- if (next.length <= width) {
231
- current = next;
232
- continue;
233
- }
234
- if (current) lines.push(current);
235
- current = word.length > width ? word.slice(0, width - 1) + '~' : word;
236
- if (lines.length >= maxLines) break;
237
- }
238
- if (current && lines.length < maxLines) lines.push(current);
239
- return lines.length ? lines : [''];
240
- }
241
-
242
- function globalSkillsRoot() {
243
- return join(process.env.CODEX_HOME ?? join(os.homedir(), '.codex'), 'skills');
244
- }
245
-
246
- function readClientConfigState(filePath) {
247
- if (!existsSync(filePath)) {
248
- return { present: false, kind: 'missing', globalCommand: false };
249
- }
250
-
251
- const content = readFileSync(filePath, 'utf8');
252
- const assessment = assessCodexConfigContent(content);
253
-
254
- return {
255
- present: true,
256
- kind: assessment.mode,
257
- globalCommand: assessment.robust,
258
- command: assessment.command,
259
- };
260
- }
261
-
262
- function readJsonState(filePath) {
263
- if (!existsSync(filePath)) {
264
- return { present: false, kind: 'missing', globalCommand: false };
265
- }
266
-
267
- try {
268
- const parsed = JSON.parse(readFileSync(filePath, 'utf8'));
269
- const server = parsed?.mcpServers?.skill_master;
270
- const assessment = assessMcpServerConfig(server);
271
- return {
272
- present: true,
273
- kind: assessment.mode,
274
- globalCommand: assessment.robust,
275
- command: assessment.command,
276
- };
277
- } catch {
278
- return { present: true, kind: 'invalid-json', globalCommand: false };
279
- }
280
- }
281
-
282
- function inspectGlobalReadiness() {
283
- const root = globalSkillsRoot();
284
- const installed = REQUIRED_GLOBAL_SKILLS.filter((name) => existsSync(join(root, name, 'SKILL.md')));
285
- const missing = REQUIRED_GLOBAL_SKILLS.filter((name) => !installed.includes(name));
286
- const clientPaths = defaultClientConfigPaths();
287
- const codex = readClientConfigState(clientPaths.codex);
288
- const claude = readJsonState(clientPaths.claude);
289
- const gemini = readJsonState(clientPaths.gemini);
290
- const antigravity = readJsonState(clientPaths.antigravity);
291
- const ready = missing.length === 0
292
- && codex.globalCommand
293
- && claude.globalCommand
294
- && gemini.globalCommand
295
- && antigravity.globalCommand;
296
- const mode = ready
297
- ? 'ready'
298
- : codex.present || claude.present || gemini.present || antigravity.present
299
- ? 'partial'
300
- : 'missing';
301
-
302
- return {
303
- root,
304
- required: REQUIRED_GLOBAL_SKILLS.length,
305
- installed,
306
- missing,
307
- ready,
308
- mode,
309
- codex,
310
- claude,
311
- gemini,
312
- antigravity,
313
- };
314
- }
315
-
316
- export function readJson(rootDir, relativePath) {
317
- const target = join(rootDir, relativePath);
318
- if (!existsSync(target)) {
319
- return null;
320
- }
321
-
322
- return JSON.parse(readFileSync(target, 'utf8'));
323
- }
324
-
325
- export function readText(rootDir, relativePath) {
326
- const target = join(rootDir, relativePath);
327
- if (!existsSync(target)) {
328
- return null;
329
- }
330
-
331
- return readFileSync(target, 'utf8').trim();
332
- }
333
-
334
- export function getMenuStatus(rootDir) {
335
- const packageJson = readJson(rootDir, 'package.json');
336
- const stableManifest = readJson(rootDir, 'manifests/channels/stable.json');
337
- const versionText = readText(rootDir, 'VERSION.md');
338
- const successLearningDir = process.env.SKILL_MASTER_SUCCESS_LEARNING_DIR
339
- ?? join(process.env.SKILL_MASTER_HOME ?? join(process.env.HOME ?? process.env.USERPROFILE ?? '', '.skill-master'), 'data', 'success-learning');
340
- const studyCandidates = readJson(rootDir, 'network/unapproved-skill-candidates.json');
341
- const globalReadiness = inspectGlobalReadiness();
342
- const bundledCatalog = inspectBundledSkillCatalog(rootDir);
343
-
344
- return {
345
- packageName: packageJson?.name ?? 'nao encontrado',
346
- semver: packageJson?.version ?? 'nao encontrado',
347
- manifestVersion: stableManifest?.version ?? 'nao encontrado',
348
- manifestSemver: stableManifest?.semver ?? 'nao encontrado',
349
- versionText,
350
- rootDir,
351
- pendingSuccessDrafts: countManifestFiles(successLearningDir),
352
- studyCandidates: Array.isArray(studyCandidates?.candidates) ? studyCandidates.candidates.length : 0,
353
- globalReadiness,
354
- bundledCatalog,
355
- };
356
- }
357
-
358
- function countManifestFiles(directory) {
359
- if (!directory || !existsSync(directory)) {
360
- return 0;
361
- }
362
-
363
- let count = 0;
364
- const visit = (current) => {
365
- for (const entry of readdirSync(current, { withFileTypes: true })) {
366
- const fullPath = join(current, entry.name);
367
- if (entry.isDirectory()) visit(fullPath);
368
- else if (entry.isFile() && entry.name === 'manifest.json') count += 1;
369
- }
370
- };
371
-
372
- visit(directory);
373
- return count;
374
- }
375
-
376
- function listBundledSkillNames(rootDir) {
377
- const source = join(rootDir, 'docs', 'skill-candidates');
378
- if (!existsSync(source)) {
379
- return [];
380
- }
381
-
382
- const entries = readdirSync(source, { withFileTypes: true });
383
- const directSkills = entries
384
- .filter((entry) => entry.isDirectory() && existsSync(join(source, entry.name, 'SKILL.md')))
385
- .map((entry) => entry.name);
386
-
387
- if (directSkills.length) {
388
- return Array.from(new Set(directSkills)).sort();
389
- }
390
-
391
- return Array.from(new Set(
392
- entries
393
- .filter((entry) => entry.isDirectory())
394
- .flatMap((entry) => {
395
- const versionPath = join(source, entry.name);
396
- return readdirSync(versionPath, { withFileTypes: true })
397
- .filter((skillEntry) => skillEntry.isDirectory() && existsSync(join(versionPath, skillEntry.name, 'SKILL.md')))
398
- .map((skillEntry) => skillEntry.name);
399
- }),
400
- )).sort();
401
- }
402
-
403
- function inspectBundledSkillCatalog(rootDir) {
404
- const names = listBundledSkillNames(rootDir);
405
- const categories = BUNDLED_SKILL_DOMAIN_GROUPS.map((group) => ({
406
- key: group.key,
407
- label: group.label,
408
- count: group.skills.filter((skill) => names.includes(skill)).length,
409
- }));
410
-
411
- return {
412
- total: names.length,
413
- names,
414
- categories,
415
- };
416
- }
417
-
418
- function formatBundledCategoryLine(catalog) {
419
- if (!catalog?.categories?.length) {
420
- return 'bundled skills: 0';
421
- }
422
-
423
- return catalog.categories
424
- .filter((category) => category.count > 0)
425
- .map((category) => `${category.label} ${category.count}`)
426
- .join(' | ');
427
- }
428
-
429
- function resolveCommandArea(action) {
430
- if (!action) {
431
- return 'geral';
432
- }
433
-
434
- return COMMAND_AREA_LABELS[action.key] ?? 'geral';
435
- }
436
-
437
- function formatActionCommand(action) {
438
- if (!action?.command) {
439
- return '';
440
- }
441
-
442
- return [action.command, ...(action.args ?? [])].join(' ');
443
- }
444
-
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() }) {
464
- const isDevCheckout = existsSync(join(rootDir, 'tsconfig.json')) && existsSync(join(rootDir, 'src'));
465
- const repoOnlyReason = 'Disponivel apenas no clone de desenvolvimento; no pacote npm use Doctor ou Status.';
466
- const npmCommand = resolveNpmCommand(nodeExecPath);
467
-
468
- return [
469
- {
470
- key: 'status',
471
- aliases: ['status'],
472
- label: 'Status local',
473
- description: 'Mostra versao local, manifesto e informacoes do pacote.',
474
- details: ['Nao altera arquivos.', 'Use para confirmar se o menu esta lendo a instalacao correta.'],
475
- success: 'Relatorio impresso com versao, canal, skills globais e clientes configurados.',
476
- command: nodeExecPath,
477
- args: [currentFile, '--status'],
478
- },
479
- {
480
- key: 'doctor',
481
- aliases: ['doctor', 'diagnostico', 'verificar-menu', 'validar-menu'],
482
- label: 'Doctor do menu e clientes MCP',
483
- description: 'Valida pacote, binarios, skills globais e registros em Codex, Claude Desktop, Claude Code, Gemini e Antigravity.',
484
- details: ['Seguro para outros notebooks.', 'Nao publica versao e nao mexe em configuracoes sem uma acao separada.'],
485
- success: 'Mostra um relatorio GO/NO-GO com proximos comandos recomendados.',
486
- command: nodeExecPath,
487
- args: [join(rootDir, 'bin', 'skill-master-doctor.mjs')],
488
- },
489
- {
490
- key: 'check',
491
- aliases: ['check', 'gate'],
492
- label: 'Rodar gate completo',
493
- description: 'Executa build, testes e validacao de manifestos.',
494
- details: ['Exige clone do repositorio com src, testes e tsconfig.', 'Nao e uma acao indicada para instalacao global via npm.'],
495
- success: 'Build, testes e manifestos passam no checkout de desenvolvimento.',
496
- disabledReason: isDevCheckout ? null : repoOnlyReason,
497
- command: npmCommand.command,
498
- args: [...npmCommand.argsPrefix, 'run', 'check'],
499
- shell: npmCommand.shell,
500
- },
501
- {
502
- key: 'build',
503
- aliases: ['build'],
504
- label: 'Rodar build',
505
- description: 'Compila o projeto localmente.',
506
- details: ['Exige clone do repositorio com tsconfig.', 'Pacotes npm publicados ja usam dist precompilado.'],
507
- success: 'dist atualizado no checkout de desenvolvimento.',
508
- disabledReason: isDevCheckout ? null : repoOnlyReason,
509
- command: npmCommand.command,
510
- args: [...npmCommand.argsPrefix, 'run', 'build'],
511
- shell: npmCommand.shell,
512
- },
513
- {
514
- key: 'publicNpm',
515
- aliases: ['public-npm', 'npm', 'registry'],
516
- label: 'Validar pacote no npm publico',
517
- description: 'Consulta a versao publicada em registry.npmjs.org.',
518
- details: ['Usa somente leitura no registry publico.', 'Bom para confirmar se outro notebook consegue instalar por npm.'],
519
- success: 'Retorna a versao publicada atualmente no npmjs.',
520
- command: npmCommand.command,
521
- args: [
522
- ...npmCommand.argsPrefix,
523
- 'view',
524
- '@fprad0/skill-master-mcp',
525
- 'version',
526
- '--registry=https://registry.npmjs.org',
527
- ],
528
- shell: npmCommand.shell,
529
- },
530
- {
531
- key: 'updateGlobal',
532
- aliases: ['update', 'update-global'],
533
- label: 'Atualizar pacote global via npm',
534
- description: 'Atualiza a instalacao global e exige reinicio do cliente MCP.',
535
- details: ['Roda npm install -g fora do processo MCP stdio.', 'Use antes de abrir Codex, Claude, Gemini ou Antigravity.'],
536
- success: 'Pacote global atualizado para a versao latest do npm publico.',
537
- command: nodeExecPath,
538
- args: [join(rootDir, 'bin', 'skill-master-update.mjs')],
539
- confirmMessage: 'Atualizar o pacote global agora?',
540
- },
541
- {
542
- key: 'privateRegistry',
543
- aliases: ['private-registry', 'private', 'github-packages'],
544
- label: 'Configurar registry privado GitHub Packages',
545
- description: 'Prepara o .npmrc e valida o acesso ao pacote privado.',
546
- details: ['Uso avancado para ambientes privados.', 'Nao e necessario para o pacote publico do npmjs.'],
547
- success: '.npmrc configurado e validacao npm view executada.',
548
- command: nodeExecPath,
549
- args: ['scripts/configure-private-registry.mjs', '--validate'],
550
- confirmMessage: 'Rodar a configuracao de registry privado agora?',
551
- },
552
- {
553
- key: 'activationStatus',
554
- aliases: ['activation-status', 'modo', 'modo-ativacao'],
555
- label: 'Modo de ativacao atual',
556
- description: 'Mostra o modo manual, balanced ou always-on-assisted configurado localmente.',
557
- details: ['Nao altera configuracao.', 'Ajuda a entender quando o Skill Master deve sugerir skills.'],
558
- success: 'Modo atual e caminho da configuracao impressos no terminal.',
559
- command: nodeExecPath,
560
- args: [join(rootDir, 'bin', 'skill-master-activation.mjs'), '--status'],
561
- },
562
- {
563
- key: 'activationBalanced',
564
- aliases: ['set-balanced', 'balanced'],
565
- label: 'Usar modo balanced',
566
- description: 'Define balanced como modo padrao de ativacao do Skill Master.',
567
- details: ['Recomendado para uso diario.', 'Ativa o MCP quando ha ganho claro para o prompt.'],
568
- success: 'Modo balanced salvo localmente.',
569
- command: nodeExecPath,
570
- args: [join(rootDir, 'bin', 'skill-master-activation.mjs'), '--set-mode', 'balanced'],
571
- },
572
- {
573
- key: 'activationAlwaysOn',
574
- aliases: ['set-always-on', 'always-on-assisted'],
575
- label: 'Usar modo always-on-assisted',
576
- description: 'Define avaliacao assistida quase sempre ativa, mantendo no-op quando nao houver ganho.',
577
- details: ['Mais agressivo que balanced.', 'Indicado para usuarios que querem o Skill Master avaliando quase todos os prompts.'],
578
- success: 'Modo always-on-assisted salvo localmente.',
579
- command: nodeExecPath,
580
- args: [join(rootDir, 'bin', 'skill-master-activation.mjs'), '--set-mode', 'always-on-assisted'],
581
- confirmMessage: 'Alterar o modo local para always-on-assisted?',
582
- },
583
- {
584
- key: 'installGlobalSkills',
585
- aliases: ['install-global-skills', 'skills-globais', 'global-skills'],
586
- label: 'Instalar skills globais do Skill Master',
587
- description: 'Copia as skills amplas e morais embutidas para CODEX_HOME/skills ou ~/.codex/skills.',
588
- details: ['Necessario para Codex descobrir as skills globais.', 'Pode ser reexecutado com seguranca pelo bootstrap.'],
589
- success: 'Skills globais copiadas para a pasta global do usuario.',
590
- command: nodeExecPath,
591
- args: [join(rootDir, 'bin', 'skill-master-install-global-skills.mjs')],
592
- confirmMessage: 'Instalar as skills globais embutidas neste usuario?',
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
- },
606
- {
607
- key: 'bootstrapGlobal',
608
- aliases: ['bootstrap-global', 'global-bootstrap', 'ativar-global-completo'],
609
- label: 'Ativar MCP global neste computador',
610
- description: 'Instala skills globais e registra Codex, Claude, Gemini e Antigravity no mesmo passo.',
611
- details: ['Acao principal para notebooks novos.', 'Depois reinicie os clientes para eles recarregarem MCP e skills.'],
612
- success: 'Skills instaladas e configuracoes MCP aplicadas nos clientes encontrados.',
613
- command: nodeExecPath,
614
- args: [join(rootDir, 'bin', 'skill-master-bootstrap-global.mjs')],
615
- confirmMessage: 'Executar o bootstrap global do Skill Master neste computador?',
616
- },
617
- {
618
- key: 'registerClients',
619
- aliases: ['register-clients', 'registrar-clientes', 'codex-claude-gemini', 'antigravity'],
620
- label: 'Registrar clientes MCP',
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.'],
623
- success: 'Arquivos de configuracao mesclados com o servidor skill_master.',
624
- command: nodeExecPath,
625
- args: [join(rootDir, 'bin', 'skill-master-register-clients.mjs'), '--apply-all'],
626
- confirmMessage: 'Aplicar registro do skill_master em Codex, Claude, Gemini e Antigravity?',
627
- },
628
- {
629
- key: 'promptRecommendation',
630
- aliases: ['recommend-prompt', 'recomendar-prompt', 'prompt-router'],
631
- label: 'Ver recomendacao para um prompt',
632
- description: 'Abre um fluxo interativo para avaliar um prompt pelo router local.',
633
- details: ['Ajuda a decidir se skill-master, skill_master ou skill-master-mcp deve agir.', 'Nao publica nem altera skills.'],
634
- success: 'Router imprime recomendacao, modo de ativacao e gates aplicaveis.',
635
- command: nodeExecPath,
636
- args: [join(rootDir, 'bin', 'skill-master-activation.mjs'), '--route-prompt-interactive'],
637
- },
638
- {
639
- key: 'successNotifications',
640
- aliases: ['notifications', 'notificacoes', 'success-notifications'],
641
- label: 'Notificacoes de skills aprendidas',
642
- description: 'Mostra drafts pendentes, aprovacoes e skills externas para estudar.',
643
- details: ['Use para decidir o que aprovar, estudar ou rejeitar.', 'Nao ativa skill automaticamente.'],
644
- success: 'Lista de pendencias e proximas acoes exibida.',
645
- command: nodeExecPath,
646
- args: [join(rootDir, 'bin', 'skill-master-success-skills.mjs'), '--notify'],
647
- },
648
- {
649
- key: 'studySkills',
650
- aliases: ['study-skills', 'skills-estudo', 'estudar'],
651
- label: 'Skills para estudar',
652
- description: 'Lista skills externas e links de criadores ainda nao aprovados.',
653
- details: ['Material de estudo, nao ativacao operacional.', 'Bom para revisao humana antes de virar skill local/global.'],
654
- success: 'Links e motivos de estudo exibidos.',
655
- command: nodeExecPath,
656
- args: [join(rootDir, 'bin', 'skill-master-success-skills.mjs'), '--study'],
657
- },
658
- {
659
- key: 'approvalPackage',
660
- aliases: ['approval-package', 'aprovar', 'pacote-aprovacao'],
661
- label: 'Gerar pacote de aprovacao humana',
662
- description: 'Cria um relatorio local para revisar e aprovar skills aprendidas.',
663
- details: ['Exige decisao humana antes de ativacao.', 'Inclui riscos, evidencias e recomendacao.'],
664
- success: 'Pacote local de aprovacao gerado.',
665
- command: nodeExecPath,
666
- args: [join(rootDir, 'bin', 'skill-master-success-skills.mjs'), '--approval-package'],
667
- },
668
- {
669
- key: 'markLearnedStudy',
670
- aliases: ['mark-study', 'manter-estudo'],
671
- label: 'Manter skill candidata para estudo',
672
- description: 'Seleciona uma candidata e registra decisao de manter para estudo.',
673
- details: ['Preserva historico sem ativar a skill.', 'Use quando a fonte ainda precisa revisao.'],
674
- success: 'Decisao registrada no historico local.',
675
- command: nodeExecPath,
676
- args: [join(rootDir, 'bin', 'skill-master-success-skills.mjs'), '--mark-study-interactive'],
677
- },
678
- {
679
- key: 'rejectLearnedSkill',
680
- aliases: ['reject-skill', 'rejeitar-skill'],
681
- label: 'Rejeitar skill candidata',
682
- description: 'Seleciona uma candidata, registra rejeicao e preserva historico.',
683
- details: ['Nao apaga evidencias.', 'Use quando risco, fonte ou utilidade nao passam no gate.'],
684
- success: 'Rejeicao registrada com motivo.',
685
- command: nodeExecPath,
686
- args: [join(rootDir, 'bin', 'skill-master-success-skills.mjs'), '--reject-interactive'],
687
- confirmMessage: 'Abrir fluxo para rejeitar uma skill candidata?',
688
- },
689
- {
690
- key: 'activateLearnedLocal',
691
- aliases: ['activate-learned-local', 'ativar-local', 'skill-local'],
692
- label: 'Ativar skill aprendida no workspace',
693
- description: 'Instala uma skill aprendida em .codex/skills do projeto atual.',
694
- details: ['Escopo limitado ao workspace.', 'Mais conservador que ativar globalmente.'],
695
- success: 'Skill copiada para .codex/skills do projeto.',
696
- command: nodeExecPath,
697
- args: [join(rootDir, 'bin', 'skill-master-success-skills.mjs'), '--activate-interactive', '--target', 'local'],
698
- confirmMessage: 'Selecionar e ativar uma skill aprendida no workspace atual?',
699
- },
700
- {
701
- key: 'activateLearnedGlobal',
702
- aliases: ['activate-learned-global', 'ativar-global', 'skill-global'],
703
- label: 'Ativar skill aprendida global',
704
- description: 'Instala uma skill aprendida em CODEX_HOME/skills ou ~/.codex/skills.',
705
- details: ['Afeta todos os projetos deste usuario.', 'Use apenas depois de aprovacao humana.'],
706
- success: 'Skill instalada no diretorio global de skills.',
707
- command: nodeExecPath,
708
- args: [join(rootDir, 'bin', 'skill-master-success-skills.mjs'), '--activate-interactive', '--target', 'global'],
709
- confirmMessage: 'Selecionar e ativar uma skill aprendida como skill global deste usuario?',
710
- },
711
- {
712
- key: 'notionSummary',
713
- aliases: ['notion-summary', 'resumo-notion'],
714
- label: 'Abrir resumo para Notion',
715
- description: 'Mostra um resumo copiavel do estado de ativacao para registrar no ledger Notion.',
716
- details: ['Nao escreve no Notion sozinho.', 'Gera texto de continuidade para documentacao.'],
717
- success: 'Resumo pronto para registro exibido.',
718
- command: nodeExecPath,
719
- args: [join(rootDir, 'bin', 'skill-master-activation.mjs'), '--notion-summary'],
720
- },
721
- ];
722
- }
723
-
724
- export function buildMenuChoices(commands) {
725
- return commands.map((command) => ({
726
- title: command.label,
727
- description: [command.description, command.success ? `Resultado: ${command.success}` : null].filter(Boolean).join(' '),
728
- value: command.key,
729
- disabled: command.disabledReason ?? false,
730
- })).concat({
731
- title: 'Sair',
732
- description: 'Fecha o menu operacional.',
733
- value: '__exit__',
734
- });
735
- }
736
-
737
- export function resolveActionKey(input, commands) {
738
- const normalized = (input ?? '').trim().toLowerCase();
739
- if (!normalized) {
740
- return null;
741
- }
742
-
743
- return commands.find((command) =>
744
- command.key.toLowerCase() === normalized || command.aliases.includes(normalized),
745
- )?.key ?? null;
746
- }
747
-
748
- export function isInteractiveTerminal() {
749
- return Boolean(process.stdin.isTTY && process.stdout.isTTY);
750
- }
751
-
752
- export function formatStatusReport(status) {
753
- const readiness = status.globalReadiness;
754
- const bundledCatalog = status.bundledCatalog ?? { total: 0, categories: [] };
755
- const lines = [
756
- 'Skill Master MCP - status local',
757
- `Diretorio: ${status.rootDir}`,
758
- `Pacote: ${status.packageName}`,
759
- `Semver local: ${status.semver}`,
760
- `Manifesto stable: ${status.manifestVersion}`,
761
- `Manifesto semver: ${status.manifestSemver}`,
762
- `Drafts de skills aprendidas: ${status.pendingSuccessDrafts}`,
763
- `Skills externas para estudar: ${status.studyCandidates}`,
764
- `Global readiness: ${readiness.ready ? 'pronto' : readiness.mode}`,
765
- `Global skills instaladas: ${readiness.installed.length}/${readiness.required}`,
766
- `Bundle de skills embutidas: ${bundledCatalog.total}`,
767
- `Dominios do bundle: ${formatBundledCategoryLine(bundledCatalog)}`,
768
- `Codex MCP robusto: ${readiness.codex.globalCommand ? 'sim' : readiness.codex.kind}`,
769
- `Claude MCP robusto: ${readiness.claude.globalCommand ? 'sim' : readiness.claude.present ? readiness.claude.kind : 'ausente'}`,
770
- `Gemini MCP robusto: ${readiness.gemini.globalCommand ? 'sim' : readiness.gemini.present ? readiness.gemini.kind : 'ausente'}`,
771
- `Antigravity MCP robusto: ${readiness.antigravity.globalCommand ? 'sim' : readiness.antigravity.present ? readiness.antigravity.kind : 'ausente'}`,
772
- ];
773
-
774
- if (status.versionText) {
775
- lines.push('', 'VERSION.md:');
776
- lines.push(...status.versionText.split('\n').slice(0, 6));
777
- }
778
-
779
- return lines.join('\n');
780
- }
781
-
782
- function formatGlobalAlert(status, { useColor = false } = {}) {
783
- const readiness = status.globalReadiness;
784
- if (readiness.ready) {
785
- return renderPanelLines(
786
- [
787
- colorize('GLOBAL READY', ANSI.bold, useColor),
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.',
790
- 'A instalacao global e as skills embutidas ja estao prontas para uso.',
791
- ],
792
- { color: ANSI.green, useColor },
793
- );
794
- }
795
-
796
- return [
797
- renderPanelLines(
798
- [
799
- colorize('ALERTA GLOBAL', ANSI.bold, useColor),
800
- 'Este computador ainda nao esta pronto para uso robusto do Skill Master.',
801
- 'Execute agora: skill-master-menu --run bootstrap-global --yes',
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',
804
- ],
805
- { color: ANSI.red, useColor },
806
- ),
807
- renderPanelLines(
808
- [
809
- colorize('AVISO SUTIL', ANSI.dim, useColor),
810
- 'Sem instalacao global, o MCP continua util localmente, mas nao fica integrado como parte do sistema em todos os clientes.',
811
- `Skills globais detectadas: ${readiness.installed.length}/${readiness.required}`,
812
- ],
813
- { color: ANSI.yellow, useColor },
814
- ),
815
- ].join('\n\n');
816
- }
817
-
818
- function renderPanelLines(lines, { color = ANSI.cyan, useColor = false } = {}) {
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(''));
978
- }
979
-
980
- export function formatMenuBanner(status, { useColor = false } = {}) {
981
- const bundledCatalog = status.bundledCatalog ?? { total: 0, categories: [] };
982
- const lines = [
983
- colorize('Skill Master MCP', ANSI.bold, useColor),
984
- 'Menu operacional para manutencao local',
985
- `Versao local ${status.semver} | canal ${status.manifestVersion}`,
986
- `Pendencias: ${status.pendingSuccessDrafts} drafts | estudo: ${status.studyCandidates} links`,
987
- `Global: ${status.globalReadiness.ready ? 'pronto' : 'requer instalacao global'}`,
988
- `Bundle: ${bundledCatalog.total} skills | ${formatBundledCategoryLine(bundledCatalog)}`,
989
- colorize('Setas + Enter para navegar', ANSI.dim, useColor),
990
- ];
991
- return [
992
- renderPanelLines(lines, { color: ANSI.cyan, useColor }),
993
- formatGlobalAlert(status, { useColor }),
994
- ].join('\n\n');
995
- }
996
-
997
- function dnaPanelLines(tick, width, height, status, selected, { useColor = false, compact = false } = {}) {
998
- const lines = [];
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);
1012
-
1013
- lines.push(fitText(colorize(`DNA CYBER HELIX / ${actionArea}`, ANSI.bold, useColor), width));
1014
-
1015
- const xA = (y) => Math.round(center + Math.sin(y * 0.19 + phase) * amplitude);
1016
- const xB = (y) => Math.round(center - Math.sin(y * 0.19 + phase) * amplitude);
1017
- const frontA = (y) => Math.cos(y * 0.19 + phase) >= 0;
1018
- const scanY = Math.floor(scanPhase * Math.max(1, pixelHeight - 1));
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);
1024
- }
1025
- }
1026
-
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);
1053
- }
1054
-
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
- }
1066
- }
1067
- }
1068
-
1069
- for (const line of renderBrailleCanvas(canvas, { useColor })) {
1070
- lines.push(line);
1071
- }
1072
-
1073
- lines.push(fitText(colorize(`frames ${tick % frameCount}/${frameCount} global ${status.globalReadiness.installed.length}/${status.globalReadiness.required}`, ANSI.amber, useColor), width));
1074
-
1075
- return lines.slice(0, height).map((line) => fitText(line, width));
1076
- }
1077
-
1078
- function renderMeterBar(value, total, width, { useColor = false, color = ANSI.teal } = {}) {
1079
- const safeTotal = Math.max(1, total);
1080
- const safeValue = Math.max(0, Math.min(value, safeTotal));
1081
- const fill = Math.round((safeValue / safeTotal) * width);
1082
- const filled = '█'.repeat(fill);
1083
- const empty = '░'.repeat(Math.max(0, width - fill));
1084
- return `${colorize(filled, color, useColor)}${colorize(empty, ANSI.gray, useColor)}`;
1085
- }
1086
-
1087
- function renderSparkline(series, { useColor = false, color = ANSI.teal } = {}) {
1088
- const blocks = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'];
1089
- return series.map((value) => colorize(blocks[Math.max(0, Math.min(blocks.length - 1, value))], color, useColor)).join('');
1090
- }
1091
-
1092
- function buildTelemetrySeries(seed, length, tick) {
1093
- return Array.from({ length }, (_, index) => {
1094
- const raw = Math.sin((index + 1 + seed) * 0.72 + tick * 0.18) + Math.cos((index + seed) * 0.33 + tick * 0.11);
1095
- const normalized = Math.max(0, Math.min(1, (raw + 2) / 4));
1096
- return Math.round(normalized * 7);
1097
- });
1098
- }
1099
-
1100
- function buildOverviewSignalLine(status, width, tick, { useColor = false, compact = false } = {}) {
1101
- const signalWidth = Math.max(6, Math.min(compact ? 8 : 12, Math.floor(width / (compact ? 10 : 8))));
1102
- const healthWidth = Math.max(6, Math.min(compact ? 8 : 12, Math.floor(width / (compact ? 10 : 8))));
1103
- const signal = renderSparkline(
1104
- buildTelemetrySeries(status.globalReadiness.installed.length + status.pendingSuccessDrafts, signalWidth, tick),
1105
- { useColor, color: ANSI.teal },
1106
- );
1107
- const health = renderSparkline(
1108
- buildTelemetrySeries(status.studyCandidates + (status.globalReadiness.ready ? 7 : 3), healthWidth, tick + 3),
1109
- { useColor, color: ANSI.amber },
1110
- );
1111
- return compact
1112
- ? `signal ${signal} health ${health}`
1113
- : `signal ${signal} health ${health}`;
1114
- }
1115
-
1116
- function telemetryPanelLines(status, selected, width, height, { useColor = false, tick = 0, compact = false } = {}) {
1117
- const commandArea = resolveCommandArea(selected);
1118
- const bundledCatalog = status.bundledCatalog ?? { total: 0, categories: [] };
1119
- const categoryLead = bundledCatalog.categories
1120
- ?.filter((category) => category.count > 0)
1121
- .sort((left, right) => right.count - left.count)[0];
1122
- const categoryName = categoryLead?.label ?? 'none';
1123
- const categoryCount = categoryLead?.count ?? 0;
1124
- const meterWidth = Math.max(8, Math.min(16, width - 14));
1125
- const sparkWidth = Math.max(8, Math.min(compact ? 8 : 12, width - 18));
1126
- const skillsSpark = renderSparkline(buildTelemetrySeries(status.globalReadiness.installed.length + categoryCount, sparkWidth, tick), { useColor, color: ANSI.amber });
1127
- const draftsSpark = renderSparkline(buildTelemetrySeries(status.pendingSuccessDrafts + 2, sparkWidth, tick + 2), { useColor, color: ANSI.teal });
1128
- const studySpark = renderSparkline(buildTelemetrySeries(status.studyCandidates + 4, sparkWidth, tick + 4), { useColor, color: ANSI.blue });
1129
- const lines = [
1130
- fitText(colorize('SYSTEM WINDOWS', ANSI.bold, useColor), width),
1131
- fitText(colorize(`selected area ${commandArea}`, ANSI.gray, useColor), width),
1132
- fitText(colorize(`dominant skill ${categoryName} ${categoryCount}`, ANSI.gray, useColor), width),
1133
- fitText(`bundle ${String(bundledCatalog.total).padStart(2, '0')} ${renderMeterBar(bundledCatalog.total, Math.max(36, bundledCatalog.total), meterWidth, { useColor, color: ANSI.white })}`, width),
1134
- fitText(`global ${status.globalReadiness.installed.length}/${status.globalReadiness.required} ${renderMeterBar(status.globalReadiness.installed.length, status.globalReadiness.required, meterWidth, { useColor, color: ANSI.amber })}`, width),
1135
- fitText(`skills ${skillsSpark}`, width),
1136
- fitText(`drafts ${String(status.pendingSuccessDrafts).padStart(2, '0')} ${renderMeterBar(status.pendingSuccessDrafts, Math.max(6, status.pendingSuccessDrafts || 1), meterWidth, { useColor, color: ANSI.teal })}`, width),
1137
- fitText(`queue ${draftsSpark}`, width),
1138
- fitText(`study ${String(status.studyCandidates).padStart(2, '0')} ${renderMeterBar(status.studyCandidates, Math.max(8, status.studyCandidates || 1), meterWidth, { useColor, color: ANSI.blue })}`, width),
1139
- fitText(`links ${studySpark}`, width),
1140
- fitText(colorize(compact ? 'codex / claude / gemini' : 'codex / claude / gemini / antigravity', ANSI.dim, useColor), width),
1141
- ];
1142
-
1143
- return lines.slice(0, height).map((line) => fitText(line, width));
1144
- }
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
-
1163
- function renderBox(lines, width, title, { useColor = false, color = ANSI.cyan, style = 'normal' } = {}) {
1164
- const innerWidth = Math.max(10, width - 4);
1165
- const titleText = title ? ` ${title.toUpperCase()} ` : '';
1166
- const topLeft = style === 'focus' ? '╭' : style === 'hud' ? '┏' : '┌';
1167
- const topRight = style === 'focus' ? '╮' : style === 'hud' ? '┓' : '┐';
1168
- const bottomLeft = style === 'focus' ? '╰' : style === 'hud' ? '┗' : '└';
1169
- const bottomRight = style === 'focus' ? '╯' : style === 'hud' ? '┛' : '┘';
1170
- const topFill = style === 'focus' ? '═' : '─';
1171
- const bottomTail = style === 'focus' ? '══' : '┄┄';
1172
- const top = `${topLeft}${titleText}${topFill.repeat(Math.max(0, width - 2 - titleText.length))}${topRight}`;
1173
- const bottom = `${bottomLeft}${topFill.repeat(Math.max(0, width - 4))}${bottomTail}${bottomRight}`;
1174
- const body = lines.map((line, index) => {
1175
- const left = index === 0
1176
- ? (style === 'focus' ? '╞' : '├')
1177
- : index === lines.length - 1
1178
- ? (style === 'focus' ? '╘' : '╰')
1179
- : '│';
1180
- const right = index === 0
1181
- ? (style === 'focus' ? '╡' : '┤')
1182
- : index === lines.length - 1
1183
- ? (style === 'focus' ? '╛' : '╯')
1184
- : '│';
1185
- return `${colorize(left, color, useColor)} ${fitText(line, innerWidth)} ${colorize(right, color, useColor)}`;
1186
- });
1187
- return [
1188
- colorize(top, color, useColor),
1189
- ...body,
1190
- colorize(bottom, color, useColor),
1191
- ];
1192
- }
1193
-
1194
- function joinHorizontalBoxes(boxes, gutter = 2) {
1195
- const height = Math.max(...boxes.map((box) => box.lines.length));
1196
- const gap = ' '.repeat(gutter);
1197
- const rows = [];
1198
-
1199
- for (let index = 0; index < height; index += 1) {
1200
- rows.push(boxes.map((box) => box.lines[index] ?? ' '.repeat(box.width)).join(gap));
1201
- }
1202
-
1203
- return rows;
1204
- }
1205
-
1206
- export function formatCyberMenuFrame(status, commands, selectedIndex, tick = 0, {
1207
- columns = 120,
1208
- rows = 32,
1209
- useColor = false,
1210
- } = {}) {
1211
- const width = Math.max(72, Math.min(columns, 150));
1212
- const height = Math.max(22, Math.min(rows - 1, 42));
1213
- const compactMode = width < 104 || height < 27;
1214
- const rightWidth = compactMode ? (width >= 92 ? 28 : 26) : width >= 132 ? 44 : width >= 108 ? 38 : 30;
1215
- const gutter = 2;
1216
- const leftWidth = width - rightWidth - gutter;
1217
- const selected = commands[selectedIndex] ?? commands[0];
1218
- const actionArea = resolveCommandArea(selected);
1219
- const bundledCatalog = status.bundledCatalog ?? { total: 0, categories: [] };
1220
- const summaryOuterHeight = compactMode ? 7 : 8;
1221
- const bodyOuterHeight = Math.max(12, height - summaryOuterHeight - 1);
1222
- const detailOuterHeight = compactMode
1223
- ? Math.min(8, Math.max(7, Math.floor(bodyOuterHeight * 0.46)))
1224
- : Math.min(10, Math.max(8, Math.floor(bodyOuterHeight * 0.42)));
1225
- const actionOuterHeight = compactMode
1226
- ? Math.max(8, bodyOuterHeight - detailOuterHeight)
1227
- : Math.max(8, bodyOuterHeight - detailOuterHeight);
1228
- const rightBottomOuterHeight = compactMode
1229
- ? Math.min(8, Math.max(7, Math.floor(bodyOuterHeight * 0.4)))
1230
- : Math.min(10, Math.max(8, Math.floor(bodyOuterHeight * 0.34)));
1231
- const rightTopOuterHeight = Math.max(8, bodyOuterHeight - rightBottomOuterHeight);
1232
- const summaryInnerWidth = width - 4;
1233
- const actionInnerWidth = leftWidth - 4;
1234
- const detailInnerWidth = leftWidth - 4;
1235
- const rightInnerWidth = rightWidth - 4;
1236
- const summaryInnerHeight = summaryOuterHeight - 2;
1237
- const actionInnerHeight = actionOuterHeight - 2;
1238
- const detailInnerHeight = detailOuterHeight - 2;
1239
- const rightTopInnerHeight = rightTopOuterHeight - 2;
1240
- const rightBottomInnerHeight = rightBottomOuterHeight - 2;
1241
- const visibleActionRows = Math.max(compactMode ? 2 : 3, actionInnerHeight - (compactMode ? 2 : 3));
1242
- const scrollStart = Math.min(
1243
- Math.max(0, selectedIndex - Math.floor(visibleActionRows / 2)),
1244
- Math.max(0, commands.length - visibleActionRows),
1245
- );
1246
- const visibleCommands = commands.slice(scrollStart, scrollStart + visibleActionRows);
1247
-
1248
- const summaryLines = compactMode
1249
- ? [
1250
- colorize('SKILL MASTER MENU', ANSI.bold, useColor),
1251
- `version ${status.semver} | channel ${status.manifestVersion} | readiness ${status.globalReadiness.ready ? 'ready' : status.globalReadiness.mode}`,
1252
- colorize(`GLOBAL SKILLS ${status.globalReadiness.installed.length}/${status.globalReadiness.required}`, ANSI.amber, useColor),
1253
- `bundle ${bundledCatalog.total} | drafts ${status.pendingSuccessDrafts} | study ${status.studyCandidates}`,
1254
- buildOverviewSignalLine(status, summaryInnerWidth, tick, { useColor, compact: true }),
1255
- colorize('compact cyberpunk hud / operator safe', ANSI.dim, useColor),
1256
- ]
1257
- : [
1258
- colorize('SKILL MASTER MENU', ANSI.bold, useColor),
1259
- `version ${status.semver} | channel ${status.manifestVersion} | readiness ${status.globalReadiness.ready ? 'ready' : status.globalReadiness.mode}`,
1260
- colorize(`GLOBAL SKILLS ${status.globalReadiness.installed.length}/${status.globalReadiness.required}`, ANSI.amber, useColor),
1261
- `bundle ${bundledCatalog.total} | drafts ${status.pendingSuccessDrafts} | study ${status.studyCandidates}`,
1262
- buildOverviewSignalLine(status, summaryInnerWidth, tick, { useColor, compact: false }),
1263
- formatBundledCategoryLine(bundledCatalog),
1264
- colorize('workflow windows / cyberpunk terminal / operator safe', ANSI.dim, useColor),
1265
- ];
1266
-
1267
- const actionLines = [
1268
- colorize('ACTION GRID', ANSI.gray, useColor),
1269
- colorize('up/down move enter run q exit', ANSI.dim, useColor),
1270
- ...visibleCommands.map((command, offset) => {
1271
- const index = scrollStart + offset;
1272
- const marker = index === selectedIndex ? '>' : '.';
1273
- const disabled = command.disabledReason ? ' [off]' : '';
1274
- const label = `${marker} ${command.label}${disabled}`;
1275
- return index === selectedIndex
1276
- ? colorize(fitText(label, actionInnerWidth), ANSI.teal, useColor)
1277
- : fitText(label, actionInnerWidth);
1278
- }),
1279
- ];
1280
-
1281
- while (actionLines.length < actionInnerHeight) {
1282
- actionLines.push(fitText('', actionInnerWidth));
1283
- }
1284
-
1285
- const descriptionLines = [
1286
- colorize('DETAIL FOCUS', ANSI.amber, useColor),
1287
- colorize(selected?.label ?? 'Nenhuma acao', ANSI.bold, useColor),
1288
- fitText(colorize(`area ${actionArea} | action ${Math.min(selectedIndex + 1, commands.length)}/${commands.length}`, ANSI.gray, useColor), detailInnerWidth),
1289
- fitText(colorize(patternFill('-=', 32), ANSI.amber, useColor), detailInnerWidth),
1290
- ...splitText(selected?.description, detailInnerWidth, 1),
1291
- ...splitText(selected?.success ? `resultado: ${selected.success}` : '', detailInnerWidth, 1),
1
+ import { spawn } from 'node:child_process';
2
+ import { existsSync, readFileSync, readdirSync } from 'node:fs';
3
+ import os from 'node:os';
4
+ import { dirname, join } from 'node:path';
5
+ import process from 'node:process';
6
+ import {
7
+ assessCodexConfigContent,
8
+ assessMcpServerConfig,
9
+ defaultClientConfigPaths,
10
+ } from './client-config.mjs';
11
+ import { buildOperationResultCopy } from './operation-result.mjs';
12
+
13
+ const ANSI = {
14
+ reset: '\x1b[0m',
15
+ bold: '\x1b[1m',
16
+ dim: '\x1b[2m',
17
+ cyan: '\x1b[36m',
18
+ green: '\x1b[32m',
19
+ yellow: '\x1b[33m',
20
+ red: '\x1b[31m',
21
+ white: '\x1b[37m',
22
+ gray: '\x1b[90m',
23
+ teal: '\x1b[38;5;80m',
24
+ amber: '\x1b[38;5;179m',
25
+ blue: '\x1b[38;5;75m',
26
+ rose: '\x1b[38;5;211m',
27
+ violet: '\x1b[38;5;141m',
28
+ };
29
+
30
+ const REQUIRED_GLOBAL_SKILLS = [
31
+ 'catholic-moral-discernment',
32
+ 'ai-ethics-human-dignity',
33
+ 'professional-boundary-triage',
34
+ 'broad-domain-router',
35
+ 'language-quality-pt-en-fr',
36
+ 'math-science-reasoning',
37
+ 'philosophy-sociology-discernment',
38
+ 'engineering-systems-master',
39
+ 'source-authority-reviewer',
40
+ 'release-ethics-gate',
41
+ 'mcp-client-readiness',
42
+ 'terminal-menu-operations',
43
+ 'terminal-pixel-art-tui',
44
+ 'senior-master-code-optimizer',
45
+ 'typescript-senior-master-engineering',
46
+ 'python-senior-master-engineering',
47
+ 'javascript-senior-master-engineering',
48
+ 'go-senior-master-engineering',
49
+ 'sql-senior-master-engineering',
50
+ 'json-senior-master-engineering',
51
+ 'ruby-senior-master-engineering',
52
+ 'react-senior-master-engineering',
53
+ 'html-senior-master-engineering',
54
+ 'css-senior-master-engineering',
55
+ 'csharp-senior-master-engineering',
56
+ ];
57
+
58
+ const BUNDLED_SKILL_DOMAIN_GROUPS = [
59
+ {
60
+ key: 'moral',
61
+ label: 'moral-core',
62
+ skills: [
63
+ 'ai-ethics-human-dignity',
64
+ 'broad-domain-router',
65
+ 'catholic-moral-discernment',
66
+ 'engineering-systems-master',
67
+ 'language-quality-pt-en-fr',
68
+ 'math-science-reasoning',
69
+ 'philosophy-sociology-discernment',
70
+ 'professional-boundary-triage',
71
+ 'release-ethics-gate',
72
+ 'source-authority-reviewer',
73
+ ],
74
+ },
75
+ {
76
+ key: 'frontend',
77
+ label: 'frontend-ui',
78
+ skills: [
79
+ 'figma',
80
+ 'figma-code-connect-components',
81
+ 'figma-create-design-system-rules',
82
+ 'figma-generate-design',
83
+ 'figma-generate-library',
84
+ 'figma-implement-design',
85
+ 'figma-use',
86
+ 'frontend-dev-guidelines',
87
+ 'frontend-design',
88
+ 'frontend-ui-ux-systems',
89
+ 'html-senior-master-engineering',
90
+ 'nextjs',
91
+ 'playwright',
92
+ 'react-senior-master-engineering',
93
+ 'screenshot',
94
+ 'senior-fullstack',
95
+ 'shadcn',
96
+ 'css-senior-master-engineering',
97
+ 'ui-component-primitives',
98
+ 'webapp-testing',
99
+ 'web-mobile-design-systems',
100
+ 'winui-app',
101
+ ],
102
+ },
103
+ {
104
+ key: 'backend',
105
+ label: 'backend-data',
106
+ skills: [
107
+ 'csharp-senior-master-engineering',
108
+ 'go-engineering',
109
+ 'go-senior-master-engineering',
110
+ 'java-engineering',
111
+ 'javascript-engineering',
112
+ 'javascript-senior-master-engineering',
113
+ 'json-contract-design',
114
+ 'json-senior-master-engineering',
115
+ 'mcp-builder',
116
+ 'polyglot-backend-engineering',
117
+ 'python-engineering',
118
+ 'python-senior-master-engineering',
119
+ 'ruby-engineering',
120
+ 'ruby-senior-master-engineering',
121
+ 'senior-master-code-optimizer',
122
+ 'sql-postgresql-engineering',
123
+ 'sql-senior-master-engineering',
124
+ 'typescript-expert',
125
+ 'typescript-senior-master-engineering',
126
+ ],
127
+ },
128
+ {
129
+ key: 'ops',
130
+ label: 'ops-clients',
131
+ skills: [
132
+ 'cli-creator',
133
+ 'developer-workstation-ops',
134
+ 'git-version-control-ops',
135
+ 'github',
136
+ 'mcp-client-readiness',
137
+ 'multi-client-mcp-ops',
138
+ 'openai-docs',
139
+ 'skill-master-orchestrator',
140
+ 'terminal-shell-ops',
141
+ 'terminal-menu-operations',
142
+ 'terminal-pixel-art-tui',
143
+ 'windows-linux-platform-ops',
144
+ ],
145
+ },
146
+ {
147
+ key: 'knowledge',
148
+ label: 'knowledge',
149
+ skills: [
150
+ 'language-quality-pt-en-fr-it-ru',
151
+ 'math-physics-reasoning',
152
+ ],
153
+ },
154
+ ];
155
+
156
+ const COMMAND_AREA_LABELS = {
157
+ status: 'diagnostico',
158
+ doctor: 'diagnostico',
159
+ check: 'desenvolvimento',
160
+ build: 'desenvolvimento',
161
+ publicNpm: 'distribuicao',
162
+ updateGlobal: 'distribuicao',
163
+ privateRegistry: 'registry',
164
+ activationStatus: 'ativacao',
165
+ activationBalanced: 'ativacao',
166
+ activationAlwaysOn: 'ativacao',
167
+ installGlobalSkills: 'skills-globais',
168
+ installProjectSkills: 'skills-projeto',
169
+ bootstrapGlobal: 'bootstrap-global',
170
+ registerClients: 'clientes-mcp',
171
+ promptRecommendation: 'roteamento',
172
+ successNotifications: 'aprendizado',
173
+ studySkills: 'aprendizado',
174
+ approvalPackage: 'aprovacao',
175
+ markLearnedStudy: 'aprovacao',
176
+ rejectLearnedSkill: 'aprovacao',
177
+ activateLearnedLocal: 'ativacao-skill',
178
+ activateLearnedGlobal: 'ativacao-skill',
179
+ notionSummary: 'documentacao',
180
+ };
181
+
182
+ const CRITICAL_SYSTEM_SETUP_ACTIONS = new Set([
183
+ 'bootstrapGlobal',
184
+ ]);
185
+
186
+ const SUPPORT_SYSTEM_SETUP_ACTIONS = new Set([
187
+ 'installGlobalSkills',
188
+ 'registerClients',
189
+ ]);
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
+
229
+ function colorize(text, color, enabled) {
230
+ return enabled ? `${color}${text}${ANSI.reset}` : text;
231
+ }
232
+
233
+ function getActionAttentionState(command, status) {
234
+ if (command?.disabledReason) return 'disabled';
235
+ if (!status?.globalReadiness?.ready) {
236
+ if (CRITICAL_SYSTEM_SETUP_ACTIONS.has(command?.key)) return 'critical';
237
+ if (SUPPORT_SYSTEM_SETUP_ACTIONS.has(command?.key)) return 'priority';
238
+ }
239
+ return 'normal';
240
+ }
241
+
242
+ function getActionAttentionBadge(command, status) {
243
+ const state = getActionAttentionState(command, status);
244
+ if (state === 'critical') return '[SETUP AGORA] ';
245
+ if (state === 'priority') return '[SISTEMA] ';
246
+ return '';
247
+ }
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
+
258
+ function getActionLineColor(command, selected, status) {
259
+ const state = getActionAttentionState(command, status);
260
+ if (selected && state === 'critical') return ANSI.rose;
261
+ if (selected && state === 'priority') return ANSI.amber;
262
+ if (selected) return ANSI.teal;
263
+ if (state === 'critical') return ANSI.rose;
264
+ if (state === 'priority') return ANSI.amber;
265
+ return null;
266
+ }
267
+
268
+ function buildSetupAlertLine(status, compact) {
269
+ if (status?.globalReadiness?.ready) return null;
270
+ return compact
271
+ ? 'SETUP GLOBAL PENDENTE -> use [SETUP AGORA] / [SISTEMA]'
272
+ : 'SETUP GLOBAL PENDENTE -> priorize [SETUP AGORA] e [SISTEMA] para autenticar o MCP no computador';
273
+ }
274
+
275
+ function stripAnsi(text) {
276
+ return String(text).replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, '');
277
+ }
278
+
279
+ function visibleLength(text) {
280
+ return stripAnsi(text).length;
281
+ }
282
+
283
+ function fitText(text, width) {
284
+ const value = String(text ?? '');
285
+ if (visibleLength(value) <= width) {
286
+ return `${value}${' '.repeat(width - visibleLength(value))}`;
287
+ }
288
+
289
+ return `${value.slice(0, Math.max(0, width - 1))}~`;
290
+ }
291
+
292
+ function truncateVisibleText(text, width) {
293
+ if (width <= 0) return '';
294
+ const value = String(text ?? '');
295
+ let output = '';
296
+ let visible = 0;
297
+ let hasOpenAnsi = false;
298
+
299
+ for (let index = 0; index < value.length && visible < width;) {
300
+ if (value[index] === '\x1b' && value[index + 1] === '[') {
301
+ const end = value.indexOf('m', index);
302
+ if (end === -1) break;
303
+ const sequence = value.slice(index, end + 1);
304
+ output += sequence;
305
+ hasOpenAnsi = sequence !== ANSI.reset;
306
+ index = end + 1;
307
+ continue;
308
+ }
309
+
310
+ const [char] = Array.from(value.slice(index));
311
+ output += char;
312
+ visible += 1;
313
+ index += char.length;
314
+ }
315
+
316
+ return hasOpenAnsi ? `${output}${ANSI.reset}` : output;
317
+ }
318
+
319
+ function splitText(text, width, maxLines = 3) {
320
+ const words = String(text ?? '').split(/\s+/).filter(Boolean);
321
+ const lines = [];
322
+ let current = '';
323
+
324
+ for (const word of words) {
325
+ const next = current ? `${current} ${word}` : word;
326
+ if (next.length <= width) {
327
+ current = next;
328
+ continue;
329
+ }
330
+ if (current) lines.push(current);
331
+ current = word.length > width ? word.slice(0, width - 1) + '~' : word;
332
+ if (lines.length >= maxLines) break;
333
+ }
334
+ if (current && lines.length < maxLines) lines.push(current);
335
+ return lines.length ? lines : [''];
336
+ }
337
+
338
+ function globalSkillsRoot() {
339
+ return join(process.env.CODEX_HOME ?? join(os.homedir(), '.codex'), 'skills');
340
+ }
341
+
342
+ function readClientConfigState(filePath) {
343
+ if (!existsSync(filePath)) {
344
+ return { present: false, kind: 'missing', globalCommand: false };
345
+ }
346
+
347
+ const content = readFileSync(filePath, 'utf8');
348
+ const assessment = assessCodexConfigContent(content);
349
+
350
+ return {
351
+ present: true,
352
+ kind: assessment.mode,
353
+ globalCommand: assessment.robust,
354
+ command: assessment.command,
355
+ };
356
+ }
357
+
358
+ function readJsonState(filePath) {
359
+ if (!existsSync(filePath)) {
360
+ return { present: false, kind: 'missing', globalCommand: false };
361
+ }
362
+
363
+ try {
364
+ const parsed = JSON.parse(readFileSync(filePath, 'utf8'));
365
+ const server = parsed?.mcpServers?.skill_master;
366
+ const assessment = assessMcpServerConfig(server);
367
+ return {
368
+ present: true,
369
+ kind: assessment.mode,
370
+ globalCommand: assessment.robust,
371
+ command: assessment.command,
372
+ };
373
+ } catch {
374
+ return { present: true, kind: 'invalid-json', globalCommand: false };
375
+ }
376
+ }
377
+
378
+ function inspectGlobalReadiness() {
379
+ const root = globalSkillsRoot();
380
+ const installed = REQUIRED_GLOBAL_SKILLS.filter((name) => existsSync(join(root, name, 'SKILL.md')));
381
+ const missing = REQUIRED_GLOBAL_SKILLS.filter((name) => !installed.includes(name));
382
+ const clientPaths = defaultClientConfigPaths();
383
+ const codex = readClientConfigState(clientPaths.codex);
384
+ const claude = readJsonState(clientPaths.claude);
385
+ const gemini = readJsonState(clientPaths.gemini);
386
+ const antigravity = readJsonState(clientPaths.antigravity);
387
+ const ready = missing.length === 0
388
+ && codex.globalCommand
389
+ && claude.globalCommand
390
+ && gemini.globalCommand
391
+ && antigravity.globalCommand;
392
+ const mode = ready
393
+ ? 'ready'
394
+ : codex.present || claude.present || gemini.present || antigravity.present
395
+ ? 'partial'
396
+ : 'missing';
397
+
398
+ return {
399
+ root,
400
+ required: REQUIRED_GLOBAL_SKILLS.length,
401
+ installed,
402
+ missing,
403
+ ready,
404
+ mode,
405
+ codex,
406
+ claude,
407
+ gemini,
408
+ antigravity,
409
+ };
410
+ }
411
+
412
+ export function readJson(rootDir, relativePath) {
413
+ const target = join(rootDir, relativePath);
414
+ if (!existsSync(target)) {
415
+ return null;
416
+ }
417
+
418
+ return JSON.parse(readFileSync(target, 'utf8'));
419
+ }
420
+
421
+ export function readText(rootDir, relativePath) {
422
+ const target = join(rootDir, relativePath);
423
+ if (!existsSync(target)) {
424
+ return null;
425
+ }
426
+
427
+ return readFileSync(target, 'utf8').trim();
428
+ }
429
+
430
+ export function getMenuStatus(rootDir) {
431
+ const packageJson = readJson(rootDir, 'package.json');
432
+ const stableManifest = readJson(rootDir, 'manifests/channels/stable.json');
433
+ const versionText = readText(rootDir, 'VERSION.md');
434
+ const successLearningDir = process.env.SKILL_MASTER_SUCCESS_LEARNING_DIR
435
+ ?? join(process.env.SKILL_MASTER_HOME ?? join(process.env.HOME ?? process.env.USERPROFILE ?? '', '.skill-master'), 'data', 'success-learning');
436
+ const studyCandidates = readJson(rootDir, 'network/unapproved-skill-candidates.json');
437
+ const globalReadiness = inspectGlobalReadiness();
438
+ const bundledCatalog = inspectBundledSkillCatalog(rootDir);
439
+
440
+ return {
441
+ packageName: packageJson?.name ?? 'nao encontrado',
442
+ semver: packageJson?.version ?? 'nao encontrado',
443
+ manifestVersion: stableManifest?.version ?? 'nao encontrado',
444
+ manifestSemver: stableManifest?.semver ?? 'nao encontrado',
445
+ versionText,
446
+ rootDir,
447
+ pendingSuccessDrafts: countManifestFiles(successLearningDir),
448
+ studyCandidates: Array.isArray(studyCandidates?.candidates) ? studyCandidates.candidates.length : 0,
449
+ globalReadiness,
450
+ bundledCatalog,
451
+ };
452
+ }
453
+
454
+ function countManifestFiles(directory) {
455
+ if (!directory || !existsSync(directory)) {
456
+ return 0;
457
+ }
458
+
459
+ let count = 0;
460
+ const visit = (current) => {
461
+ for (const entry of readdirSync(current, { withFileTypes: true })) {
462
+ const fullPath = join(current, entry.name);
463
+ if (entry.isDirectory()) visit(fullPath);
464
+ else if (entry.isFile() && entry.name === 'manifest.json') count += 1;
465
+ }
466
+ };
467
+
468
+ visit(directory);
469
+ return count;
470
+ }
471
+
472
+ function listBundledSkillNames(rootDir) {
473
+ const source = join(rootDir, 'docs', 'skill-candidates');
474
+ if (!existsSync(source)) {
475
+ return [];
476
+ }
477
+
478
+ const entries = readdirSync(source, { withFileTypes: true });
479
+ const directSkills = entries
480
+ .filter((entry) => entry.isDirectory() && existsSync(join(source, entry.name, 'SKILL.md')))
481
+ .map((entry) => entry.name);
482
+
483
+ if (directSkills.length) {
484
+ return Array.from(new Set(directSkills)).sort();
485
+ }
486
+
487
+ return Array.from(new Set(
488
+ entries
489
+ .filter((entry) => entry.isDirectory())
490
+ .flatMap((entry) => {
491
+ const versionPath = join(source, entry.name);
492
+ return readdirSync(versionPath, { withFileTypes: true })
493
+ .filter((skillEntry) => skillEntry.isDirectory() && existsSync(join(versionPath, skillEntry.name, 'SKILL.md')))
494
+ .map((skillEntry) => skillEntry.name);
495
+ }),
496
+ )).sort();
497
+ }
498
+
499
+ function inspectBundledSkillCatalog(rootDir) {
500
+ const names = listBundledSkillNames(rootDir);
501
+ const categories = BUNDLED_SKILL_DOMAIN_GROUPS.map((group) => ({
502
+ key: group.key,
503
+ label: group.label,
504
+ count: group.skills.filter((skill) => names.includes(skill)).length,
505
+ }));
506
+
507
+ return {
508
+ total: names.length,
509
+ names,
510
+ categories,
511
+ };
512
+ }
513
+
514
+ function formatBundledCategoryLine(catalog) {
515
+ if (!catalog?.categories?.length) {
516
+ return 'bundled skills: 0';
517
+ }
518
+
519
+ return catalog.categories
520
+ .filter((category) => category.count > 0)
521
+ .map((category) => `${category.label} ${category.count}`)
522
+ .join(' | ');
523
+ }
524
+
525
+ function resolveCommandArea(action) {
526
+ if (!action) {
527
+ return 'geral';
528
+ }
529
+
530
+ return COMMAND_AREA_LABELS[action.key] ?? 'geral';
531
+ }
532
+
533
+ function formatActionCommand(action) {
534
+ if (!action?.command) {
535
+ return '';
536
+ }
537
+
538
+ return [action.command, ...(action.args ?? [])].join(' ');
539
+ }
540
+
541
+ function resolveNpmCommand(nodeExecPath) {
542
+ if (process.platform !== 'win32') {
543
+ return { command: 'npm', argsPrefix: [], shell: false };
544
+ }
545
+
546
+ const candidates = [
547
+ process.env.npm_execpath,
548
+ join(dirname(nodeExecPath), 'node_modules', 'npm', 'bin', 'npm-cli.js'),
549
+ ].filter(Boolean);
550
+ const npmCli = candidates.find((candidate) => existsSync(candidate));
551
+
552
+ if (npmCli) {
553
+ return { command: nodeExecPath, argsPrefix: [npmCli], shell: false };
554
+ }
555
+
556
+ return { command: 'npm.cmd', argsPrefix: [], shell: true };
557
+ }
558
+
559
+ export function buildMenuCommands({ rootDir, currentFile, nodeExecPath = process.execPath, invocationCwd = process.cwd() }) {
560
+ const isDevCheckout = existsSync(join(rootDir, 'tsconfig.json')) && existsSync(join(rootDir, 'src'));
561
+ const repoOnlyReason = 'Disponivel apenas no clone de desenvolvimento; no pacote npm use Doctor ou Status.';
562
+ const npmCommand = resolveNpmCommand(nodeExecPath);
563
+
564
+ return [
565
+ {
566
+ key: 'status',
567
+ aliases: ['status'],
568
+ label: 'Status local',
569
+ description: 'Mostra versao local, manifesto e informacoes do pacote.',
570
+ details: ['Nao altera arquivos.', 'Use para confirmar se o menu esta lendo a instalacao correta.'],
571
+ success: 'Relatorio impresso com versao, canal, skills globais e clientes configurados.',
572
+ command: nodeExecPath,
573
+ args: [currentFile, '--status'],
574
+ },
575
+ {
576
+ key: 'doctor',
577
+ aliases: ['doctor', 'diagnostico', 'verificar-menu', 'validar-menu'],
578
+ label: 'Doctor do menu e clientes MCP',
579
+ description: 'Valida pacote, binarios, skills globais e registros em Codex, Claude Desktop, Claude Code, Gemini e Antigravity.',
580
+ details: ['Seguro para outros notebooks.', 'Nao publica versao e nao mexe em configuracoes sem uma acao separada.'],
581
+ success: 'Mostra um relatorio GO/NO-GO com proximos comandos recomendados.',
582
+ command: nodeExecPath,
583
+ args: [join(rootDir, 'bin', 'skill-master-doctor.mjs')],
584
+ },
585
+ {
586
+ key: 'check',
587
+ aliases: ['check', 'gate'],
588
+ label: 'Rodar gate completo',
589
+ description: 'Executa build, testes e validacao de manifestos.',
590
+ details: ['Exige clone do repositorio com src, testes e tsconfig.', 'Nao e uma acao indicada para instalacao global via npm.'],
591
+ success: 'Build, testes e manifestos passam no checkout de desenvolvimento.',
592
+ disabledReason: isDevCheckout ? null : repoOnlyReason,
593
+ command: npmCommand.command,
594
+ args: [...npmCommand.argsPrefix, 'run', 'check'],
595
+ shell: npmCommand.shell,
596
+ },
597
+ {
598
+ key: 'build',
599
+ aliases: ['build'],
600
+ label: 'Rodar build',
601
+ description: 'Compila o projeto localmente.',
602
+ details: ['Exige clone do repositorio com tsconfig.', 'Pacotes npm publicados ja usam dist precompilado.'],
603
+ success: 'dist atualizado no checkout de desenvolvimento.',
604
+ disabledReason: isDevCheckout ? null : repoOnlyReason,
605
+ command: npmCommand.command,
606
+ args: [...npmCommand.argsPrefix, 'run', 'build'],
607
+ shell: npmCommand.shell,
608
+ },
609
+ {
610
+ key: 'publicNpm',
611
+ aliases: ['public-npm', 'npm', 'registry'],
612
+ label: 'Validar pacote no npm publico',
613
+ description: 'Consulta a versao publicada em registry.npmjs.org.',
614
+ details: ['Usa somente leitura no registry publico.', 'Bom para confirmar se outro notebook consegue instalar por npm.'],
615
+ success: 'Retorna a versao publicada atualmente no npmjs.',
616
+ command: npmCommand.command,
617
+ args: [
618
+ ...npmCommand.argsPrefix,
619
+ 'view',
620
+ '@fprad0/skill-master-mcp',
621
+ 'version',
622
+ '--registry=https://registry.npmjs.org',
623
+ ],
624
+ shell: npmCommand.shell,
625
+ },
626
+ {
627
+ key: 'updateGlobal',
628
+ aliases: ['update', 'update-global'],
629
+ label: 'Atualizar pacote global via npm',
630
+ description: 'Atualiza a instalacao global e exige reinicio do cliente MCP.',
631
+ details: [
632
+ 'A atualizacao deve ser feita fora do processo MCP stdio.',
633
+ 'Se o menu estiver usando o pacote global no Windows, o fluxo faz handoff seguro em vez de atualizar a pasta em uso.',
634
+ 'Use antes de abrir Codex, Claude, Gemini ou Antigravity.',
635
+ ],
636
+ success: 'Pacote global atualizado para a versao latest do npm publico.',
637
+ command: nodeExecPath,
638
+ args: [join(rootDir, 'bin', 'skill-master-update.mjs')],
639
+ confirmMessage: 'Atualizar o pacote global agora?',
640
+ },
641
+ {
642
+ key: 'privateRegistry',
643
+ aliases: ['private-registry', 'private', 'github-packages'],
644
+ label: 'Configurar registry privado GitHub Packages',
645
+ description: 'Prepara o .npmrc e valida o acesso ao pacote privado.',
646
+ details: ['Uso avancado para ambientes privados.', 'Nao e necessario para o pacote publico do npmjs.'],
647
+ success: '.npmrc configurado e validacao npm view executada.',
648
+ command: nodeExecPath,
649
+ args: ['scripts/configure-private-registry.mjs', '--validate'],
650
+ confirmMessage: 'Rodar a configuracao de registry privado agora?',
651
+ },
652
+ {
653
+ key: 'activationStatus',
654
+ aliases: ['activation-status', 'modo', 'modo-ativacao'],
655
+ label: 'Modo de ativacao atual',
656
+ description: 'Mostra o modo manual, balanced ou always-on-assisted configurado localmente.',
657
+ details: ['Nao altera configuracao.', 'Ajuda a entender quando o Skill Master deve sugerir skills.'],
658
+ success: 'Modo atual e caminho da configuracao impressos no terminal.',
659
+ command: nodeExecPath,
660
+ args: [join(rootDir, 'bin', 'skill-master-activation.mjs'), '--status'],
661
+ },
662
+ {
663
+ key: 'activationBalanced',
664
+ aliases: ['set-balanced', 'balanced'],
665
+ label: 'Usar modo balanced',
666
+ description: 'Define balanced como modo padrao de ativacao do Skill Master.',
667
+ details: ['Recomendado para uso diario.', 'Ativa o MCP quando ha ganho claro para o prompt.'],
668
+ success: 'Modo balanced salvo localmente.',
669
+ command: nodeExecPath,
670
+ args: [join(rootDir, 'bin', 'skill-master-activation.mjs'), '--set-mode', 'balanced'],
671
+ },
672
+ {
673
+ key: 'activationAlwaysOn',
674
+ aliases: ['set-always-on', 'always-on-assisted'],
675
+ label: 'Usar modo always-on-assisted',
676
+ description: 'Define avaliacao assistida quase sempre ativa, mantendo no-op quando nao houver ganho.',
677
+ details: ['Mais agressivo que balanced.', 'Indicado para usuarios que querem o Skill Master avaliando quase todos os prompts.'],
678
+ success: 'Modo always-on-assisted salvo localmente.',
679
+ command: nodeExecPath,
680
+ args: [join(rootDir, 'bin', 'skill-master-activation.mjs'), '--set-mode', 'always-on-assisted'],
681
+ confirmMessage: 'Alterar o modo local para always-on-assisted?',
682
+ },
683
+ {
684
+ key: 'installGlobalSkills',
685
+ aliases: ['install-global-skills', 'skills-globais', 'global-skills'],
686
+ label: 'Instalar skills globais do Skill Master',
687
+ description: 'Copia as skills amplas e morais embutidas para CODEX_HOME/skills ou ~/.codex/skills.',
688
+ details: ['Necessario para Codex descobrir as skills globais.', 'Pode ser reexecutado com seguranca pelo bootstrap.'],
689
+ success: 'Skills globais copiadas para a pasta global do usuario.',
690
+ command: nodeExecPath,
691
+ args: [join(rootDir, 'bin', 'skill-master-install-global-skills.mjs')],
692
+ confirmMessage: 'Instalar as skills globais embutidas neste usuario?',
693
+ },
694
+ {
695
+ key: 'installProjectSkills',
696
+ aliases: ['install-project-skills', 'skills-projeto', 'project-skills', 'agents-skills'],
697
+ label: 'Instalar skills no projeto atual',
698
+ description: 'Copia o bundle para .agents/skills, .codex/skills, .claude/skills e .gemini/skills do projeto onde o menu foi aberto.',
699
+ details: ['Registra .skill-master/catalog.json e metadados em package.json quando existir.', `Projeto alvo: ${invocationCwd}`],
700
+ success: 'Skills de projeto copiadas e catalogo local atualizado.',
701
+ command: nodeExecPath,
702
+ args: [join(rootDir, 'bin', 'skill-master-install-project-skills.mjs'), '--project-root', invocationCwd, '--sync-package-json'],
703
+ cwd: invocationCwd,
704
+ confirmMessage: 'Instalar e catalogar as skills do Skill Master no projeto atual?',
705
+ },
706
+ {
707
+ key: 'bootstrapGlobal',
708
+ aliases: ['bootstrap-global', 'global-bootstrap', 'ativar-global-completo'],
709
+ label: 'Ativar MCP global neste computador',
710
+ description: 'Instala skills globais e registra Codex, Claude, Gemini e Antigravity no mesmo passo.',
711
+ details: ['Acao principal para notebooks novos.', 'Depois reinicie os clientes para eles recarregarem MCP e skills.'],
712
+ success: 'Skills instaladas e configuracoes MCP aplicadas nos clientes encontrados.',
713
+ command: nodeExecPath,
714
+ args: [join(rootDir, 'bin', 'skill-master-bootstrap-global.mjs')],
715
+ confirmMessage: 'Executar o bootstrap global do Skill Master neste computador?',
716
+ },
717
+ {
718
+ key: 'registerClients',
719
+ aliases: ['register-clients', 'registrar-clientes', 'codex-claude-gemini', 'antigravity'],
720
+ label: 'Registrar clientes MCP',
721
+ description: 'Gera/aplica configuracoes MCP para Codex, Claude Desktop, Claude Code, Gemini e Antigravity reconhecerem skill_master.',
722
+ details: ['Usa Node absoluto e o entrypoint absoluto do pacote.', 'Claude Code e registrado via claude mcp add quando o CLI esta disponivel.'],
723
+ success: 'Arquivos de configuracao mesclados com o servidor skill_master.',
724
+ command: nodeExecPath,
725
+ args: [join(rootDir, 'bin', 'skill-master-register-clients.mjs'), '--apply-all'],
726
+ confirmMessage: 'Aplicar registro do skill_master em Codex, Claude, Gemini e Antigravity?',
727
+ },
728
+ {
729
+ key: 'promptRecommendation',
730
+ aliases: ['recommend-prompt', 'recomendar-prompt', 'prompt-router'],
731
+ label: 'Ver recomendacao para um prompt',
732
+ description: 'Abre um fluxo interativo para avaliar um prompt pelo router local.',
733
+ details: ['Ajuda a decidir se skill-master, skill_master ou skill-master-mcp deve agir.', 'Nao publica nem altera skills.'],
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".',
739
+ command: nodeExecPath,
740
+ args: [join(rootDir, 'bin', 'skill-master-activation.mjs'), '--route-prompt-interactive'],
741
+ cwd: invocationCwd,
742
+ },
743
+ {
744
+ key: 'successNotifications',
745
+ aliases: ['notifications', 'notificacoes', 'success-notifications'],
746
+ label: 'Notificacoes de skills aprendidas',
747
+ description: 'Mostra drafts pendentes, aprovacoes e skills externas para estudar.',
748
+ details: ['Use para decidir o que aprovar, estudar ou rejeitar.', 'Nao ativa skill automaticamente.'],
749
+ success: 'Lista de pendencias e proximas acoes exibida.',
750
+ command: nodeExecPath,
751
+ args: [join(rootDir, 'bin', 'skill-master-success-skills.mjs'), '--notify'],
752
+ },
753
+ {
754
+ key: 'studySkills',
755
+ aliases: ['study-skills', 'skills-estudo', 'estudar'],
756
+ label: 'Skills para estudar',
757
+ description: 'Lista skills externas e links de criadores ainda nao aprovados.',
758
+ details: ['Material de estudo, nao ativacao operacional.', 'Bom para revisao humana antes de virar skill local/global.'],
759
+ success: 'Links e motivos de estudo exibidos.',
760
+ command: nodeExecPath,
761
+ args: [join(rootDir, 'bin', 'skill-master-success-skills.mjs'), '--study'],
762
+ },
763
+ {
764
+ key: 'approvalPackage',
765
+ aliases: ['approval-package', 'aprovar', 'pacote-aprovacao'],
766
+ label: 'Gerar pacote de aprovacao humana',
767
+ description: 'Cria um relatorio local para revisar e aprovar skills aprendidas.',
768
+ details: ['Exige decisao humana antes de ativacao.', 'Inclui riscos, evidencias e recomendacao.'],
769
+ success: 'Pacote local de aprovacao gerado.',
770
+ effect: 'writes-local',
771
+ risk: 'medium',
772
+ command: nodeExecPath,
773
+ args: [join(rootDir, 'bin', 'skill-master-success-skills.mjs'), '--approval-package'],
774
+ },
775
+ {
776
+ key: 'markLearnedStudy',
777
+ aliases: ['mark-study', 'manter-estudo'],
778
+ label: 'Manter skill candidata para estudo',
779
+ description: 'Seleciona uma candidata e registra decisao de manter para estudo.',
780
+ details: ['Preserva historico sem ativar a skill.', 'Use quando a fonte ainda precisa revisao.'],
781
+ success: 'Decisao registrada no historico local.',
782
+ command: nodeExecPath,
783
+ args: [join(rootDir, 'bin', 'skill-master-success-skills.mjs'), '--mark-study-interactive'],
784
+ },
785
+ {
786
+ key: 'rejectLearnedSkill',
787
+ aliases: ['reject-skill', 'rejeitar-skill'],
788
+ label: 'Rejeitar skill candidata',
789
+ description: 'Seleciona uma candidata, registra rejeicao e preserva historico.',
790
+ details: ['Nao apaga evidencias.', 'Use quando risco, fonte ou utilidade nao passam no gate.'],
791
+ success: 'Rejeicao registrada com motivo.',
792
+ command: nodeExecPath,
793
+ args: [join(rootDir, 'bin', 'skill-master-success-skills.mjs'), '--reject-interactive'],
794
+ confirmMessage: 'Abrir fluxo para rejeitar uma skill candidata?',
795
+ },
796
+ {
797
+ key: 'activateLearnedLocal',
798
+ aliases: ['activate-learned-local', 'ativar-local', 'skill-local'],
799
+ label: 'Ativar skill aprendida no workspace',
800
+ description: 'Instala uma skill aprendida em .codex/skills do projeto atual.',
801
+ details: ['Escopo limitado ao workspace.', 'Mais conservador que ativar globalmente.'],
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.',
807
+ command: nodeExecPath,
808
+ args: [join(rootDir, 'bin', 'skill-master-success-skills.mjs'), '--activate-interactive', '--target', 'local'],
809
+ cwd: invocationCwd,
810
+ confirmMessage: 'Selecionar e ativar uma skill aprendida no workspace atual?',
811
+ },
812
+ {
813
+ key: 'activateLearnedGlobal',
814
+ aliases: ['activate-learned-global', 'ativar-global', 'skill-global'],
815
+ label: 'Ativar skill aprendida global',
816
+ description: 'Instala uma skill aprendida em CODEX_HOME/skills ou ~/.codex/skills.',
817
+ details: ['Afeta todos os projetos deste usuario.', 'Use apenas depois de aprovacao humana.'],
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.',
823
+ command: nodeExecPath,
824
+ args: [join(rootDir, 'bin', 'skill-master-success-skills.mjs'), '--activate-interactive', '--target', 'global'],
825
+ cwd: invocationCwd,
826
+ confirmMessage: 'Selecionar e ativar uma skill aprendida como skill global deste usuario?',
827
+ },
828
+ {
829
+ key: 'notionSummary',
830
+ aliases: ['notion-summary', 'resumo-notion'],
831
+ label: 'Abrir resumo para Notion',
832
+ description: 'Mostra um resumo copiavel do estado de ativacao para registrar no ledger Notion.',
833
+ details: ['Nao escreve no Notion sozinho.', 'Gera texto de continuidade para documentacao.'],
834
+ success: 'Resumo pronto para registro exibido.',
835
+ command: nodeExecPath,
836
+ args: [join(rootDir, 'bin', 'skill-master-activation.mjs'), '--notion-summary'],
837
+ },
838
+ ];
839
+ }
840
+
841
+ export function buildMenuChoices(commands) {
842
+ return commands.map((command) => ({
843
+ title: command.label,
844
+ description: [command.description, command.success ? `Resultado: ${command.success}` : null].filter(Boolean).join(' '),
845
+ value: command.key,
846
+ disabled: command.disabledReason ?? false,
847
+ })).concat({
848
+ title: 'Sair',
849
+ description: 'Fecha o menu operacional.',
850
+ value: '__exit__',
851
+ });
852
+ }
853
+
854
+ export function resolveInitialSelectedIndex(status, commands) {
855
+ if (!status?.globalReadiness?.ready) {
856
+ const bootstrapIndex = commands.findIndex((command) => command.key === 'bootstrapGlobal');
857
+ if (bootstrapIndex >= 0) return bootstrapIndex;
858
+ }
859
+ return 0;
860
+ }
861
+
862
+ export function resolveActionKey(input, commands) {
863
+ const normalized = (input ?? '').trim().toLowerCase();
864
+ if (!normalized) {
865
+ return null;
866
+ }
867
+
868
+ return commands.find((command) =>
869
+ command.key.toLowerCase() === normalized || command.aliases.includes(normalized),
870
+ )?.key ?? null;
871
+ }
872
+
873
+ export function isInteractiveTerminal() {
874
+ return Boolean(process.stdin.isTTY && process.stdout.isTTY);
875
+ }
876
+
877
+ export function formatStatusReport(status) {
878
+ const readiness = status.globalReadiness;
879
+ const bundledCatalog = status.bundledCatalog ?? { total: 0, categories: [] };
880
+ const lines = [
881
+ 'Skill Master MCP - status local',
882
+ `Diretorio: ${status.rootDir}`,
883
+ `Pacote: ${status.packageName}`,
884
+ `Semver local: ${status.semver}`,
885
+ `Manifesto stable: ${status.manifestVersion}`,
886
+ `Manifesto semver: ${status.manifestSemver}`,
887
+ `Drafts de skills aprendidas: ${status.pendingSuccessDrafts}`,
888
+ `Skills externas para estudar: ${status.studyCandidates}`,
889
+ `Global readiness: ${readiness.ready ? 'pronto' : readiness.mode}`,
890
+ `Global skills instaladas: ${readiness.installed.length}/${readiness.required}`,
891
+ `Bundle de skills embutidas: ${bundledCatalog.total}`,
892
+ `Dominios do bundle: ${formatBundledCategoryLine(bundledCatalog)}`,
893
+ `Codex MCP robusto: ${readiness.codex.globalCommand ? 'sim' : readiness.codex.kind}`,
894
+ `Claude MCP robusto: ${readiness.claude.globalCommand ? 'sim' : readiness.claude.present ? readiness.claude.kind : 'ausente'}`,
895
+ `Gemini MCP robusto: ${readiness.gemini.globalCommand ? 'sim' : readiness.gemini.present ? readiness.gemini.kind : 'ausente'}`,
896
+ `Antigravity MCP robusto: ${readiness.antigravity.globalCommand ? 'sim' : readiness.antigravity.present ? readiness.antigravity.kind : 'ausente'}`,
897
+ ];
898
+
899
+ if (status.versionText) {
900
+ lines.push('', 'VERSION.md:');
901
+ lines.push(...status.versionText.split('\n').slice(0, 6));
902
+ }
903
+
904
+ return lines.join('\n');
905
+ }
906
+
907
+ function formatGlobalAlert(status, { useColor = false } = {}) {
908
+ const readiness = status.globalReadiness;
909
+ if (readiness.ready) {
910
+ return renderPanelLines(
911
+ [
912
+ colorize('GLOBAL READY', ANSI.bold, useColor),
913
+ 'O MCP esta registrado com Node absoluto neste computador.',
914
+ 'Codex, Claude Desktop, Claude Code, Gemini e Antigravity podem iniciar o servidor sem depender do PATH.',
915
+ 'A instalacao global e as skills embutidas ja estao prontas para uso.',
916
+ ],
917
+ { color: ANSI.green, useColor },
918
+ );
919
+ }
920
+
921
+ return [
922
+ renderPanelLines(
923
+ [
924
+ colorize('ALERTA GLOBAL', ANSI.bold, useColor),
925
+ 'Este computador ainda nao esta pronto para uso robusto do Skill Master.',
926
+ 'Execute agora: skill-master-menu --run bootstrap-global --yes',
927
+ 'Isso instala as skills globais e registra Codex, Claude Desktop, Claude Code, Gemini e Antigravity com Node absoluto.',
928
+ 'Ou migre somente os clientes: skill-master-register-clients --apply-all --force',
929
+ ],
930
+ { color: ANSI.red, useColor },
931
+ ),
932
+ renderPanelLines(
933
+ [
934
+ colorize('AVISO SUTIL', ANSI.dim, useColor),
935
+ 'Sem instalacao global, o MCP continua util localmente, mas nao fica integrado como parte do sistema em todos os clientes.',
936
+ `Skills globais detectadas: ${readiness.installed.length}/${readiness.required}`,
937
+ ],
938
+ { color: ANSI.yellow, useColor },
939
+ ),
940
+ ].join('\n\n');
941
+ }
942
+
943
+ function renderPanelLines(lines, { color = ANSI.cyan, useColor = false } = {}) {
944
+ const width = Math.max(...lines.map((line) => visibleLength(line)), 24) + 4;
945
+ return renderCyberFrame(
946
+ lines.map((line) => fitText(line, width - 4)),
947
+ width,
948
+ 'skill-master',
949
+ { color, useColor, style: 'data' },
950
+ ).join('\n');
951
+ }
952
+
953
+ function patternFill(pattern, width) {
954
+ if (width <= 0) return '';
955
+ return Array.from({ length: Math.ceil(width / pattern.length) }, () => pattern).join('').slice(0, width);
956
+ }
957
+
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
+
966
+ if (style === 'focus') {
967
+ return { topLeft: '╭╼', topRight: '╾╮', bottomLeft: '╰╼', bottomRight: '╾╯', fill: '═', left: '▌ ', right: ' ▐', accent: '▰', micro: '▱' };
968
+ }
969
+ if (style === 'hud') {
970
+ return { topLeft: '┏╾', topRight: '╼┓', bottomLeft: '┗╾', bottomRight: '╼┛', fill: '─', left: '▌ ', right: ' ▐', accent: '▰', micro: '▱' };
971
+ }
972
+ if (style === 'running') {
973
+ return { topLeft: '╔╼', topRight: '╾╗', bottomLeft: '╚╼', bottomRight: '╾╝', fill: '═', left: '▌▌', right: '▐▐', accent: '▰', micro: '▱' };
974
+ }
975
+ if (style === 'thin') {
976
+ return { topLeft: '┌╴', topRight: '╶┐', bottomLeft: '└╴', bottomRight: '╶┘', fill: '─', left: '│ ', right: ' │', accent: '▱', micro: '╍' };
977
+ }
978
+ return { topLeft: '╭╴', topRight: '╶╮', bottomLeft: '╰╴', bottomRight: '╶╯', fill: '─', left: '│ ', right: ' │', accent: '▰', micro: '▱' };
979
+ }
980
+
981
+ function fillWithPattern(text, width, fill) {
982
+ const length = visibleLength(text);
983
+ if (length >= width) return fitText(text, width);
984
+ return `${text}${patternFill(fill, width - length)}`;
985
+ }
986
+
987
+ function renderCyberRail(width, title, tokens, { bottom = false, tick = 0 } = {}) {
988
+ const left = bottom ? tokens.bottomLeft : tokens.topLeft;
989
+ const right = bottom ? tokens.bottomRight : tokens.topRight;
990
+ const innerWidth = Math.max(0, width - visibleLength(left) - visibleLength(right));
991
+ const phaseMicro = tick % 2 === 0 ? tokens.micro : tokens.fill;
992
+ const titleText = !bottom && title ? ` ${String(title).toUpperCase()} ` : '';
993
+ const titleWidth = titleText ? Math.min(visibleLength(titleText), Math.max(0, innerWidth - 8)) : 0;
994
+ const safeTitle = titleText ? fitText(titleText, titleWidth).trimEnd() : '';
995
+
996
+ if (bottom) {
997
+ const lead = patternFill(tokens.fill, Math.max(2, Math.floor(innerWidth * 0.18)));
998
+ const plate = innerWidth >= 34 ? `${tokens.accent}${tokens.accent}${tokens.accent}` : tokens.accent;
999
+ const vent = innerWidth >= 44 ? patternFill(`${phaseMicro}${tokens.fill}`, 8) : '';
1000
+ return `${left}${fillWithPattern(`${lead}${plate}${tokens.fill}${vent}`, innerWidth, tokens.fill)}${right}`;
1001
+ }
1002
+
1003
+ const leadWidth = Math.max(2, Math.min(10, Math.floor(innerWidth * 0.16)));
1004
+ const lead = patternFill(tokens.fill, leadWidth);
1005
+ const chip = innerWidth >= 36 ? `${tokens.accent}${phaseMicro}${phaseMicro}` : tokens.accent;
1006
+ const tab = innerWidth >= 48 ? `${tokens.fill}${tokens.fill}${tokens.accent}${tokens.fill}` : tokens.fill;
1007
+ return `${left}${fillWithPattern(`${lead}${safeTitle}${chip}${tab}`, innerWidth, tokens.fill)}${right}`;
1008
+ }
1009
+
1010
+ function renderCyberFrame(lines, width, title, { useColor = false, color = ANSI.cyan, style = 'data', tick = 0, glyphSet = resolveGlyphSet() } = {}) {
1011
+ const safeWidth = Math.max(16, width);
1012
+ const innerWidth = safeWidth - 4;
1013
+ const tokens = frameTokens(style, glyphSet);
1014
+ const top = renderCyberRail(safeWidth, title, tokens, { tick });
1015
+ const bottom = renderCyberRail(safeWidth, title, tokens, { bottom: true, tick });
1016
+ const body = lines.map((line, index) => {
1017
+ const marker = style === 'running' && index % 3 === Math.abs(tick % 3)
1018
+ ? tokens.accent
1019
+ : index === 0 || index === lines.length - 1
1020
+ ? tokens.micro
1021
+ : ' ';
1022
+ const content = fitText(`${marker}${truncateVisibleText(line, Math.max(0, innerWidth - 1))}`, innerWidth);
1023
+ return `${colorize(tokens.left, color, useColor)}${content}${colorize(tokens.right, color, useColor)}`;
1024
+ });
1025
+
1026
+ return [
1027
+ colorize(top, color, useColor),
1028
+ ...body,
1029
+ colorize(bottom, color, useColor),
1030
+ ];
1031
+ }
1032
+
1033
+ function renderCircuitRail(width, tick, { useColor = false, color = ANSI.teal } = {}) {
1034
+ const patterns = ['<==/==>--', '<=/===/>-', '<<==--==>', '<==\\==>--'];
1035
+ return colorize(patternFill(patterns[Math.abs(tick) % patterns.length], width), color, useColor);
1036
+ }
1037
+
1038
+ function renderScanGrid(width, height, tick, { useColor = false } = {}) {
1039
+ const lines = [];
1040
+ const scanCol = width > 0 ? tick % width : 0;
1041
+ const scanRow = height > 0 ? Math.floor(tick / 2) % height : 0;
1042
+
1043
+ for (let row = 0; row < height; row += 1) {
1044
+ const cells = Array.from({ length: width }, (_, col) => {
1045
+ if (row === scanRow && col % 3 === 0) return colorize('-', ANSI.white, useColor);
1046
+ if (col === scanCol && row % 2 === 0) return colorize('|', ANSI.teal, useColor);
1047
+ if ((row + col + tick) % 17 === 0) return colorize('+', ANSI.amber, useColor);
1048
+ if (row % 4 === 0 && col % 8 === 0) return colorize('.', ANSI.gray, useColor);
1049
+ return ' ';
1050
+ });
1051
+ lines.push(cells.join(''));
1052
+ }
1053
+
1054
+ return lines;
1055
+ }
1056
+
1057
+ const BRAILLE_DOTS = [
1058
+ [0x01, 0x02, 0x04, 0x40],
1059
+ [0x08, 0x10, 0x20, 0x80],
1060
+ ];
1061
+
1062
+ function createBrailleCanvas(width, height) {
1063
+ return {
1064
+ width,
1065
+ height,
1066
+ dots: Array.from({ length: height }, () => Array.from({ length: width }, () => 0)),
1067
+ colors: Array.from({ length: height }, () => Array.from({ length: width }, () => 'gray')),
1068
+ priorities: Array.from({ length: height }, () => Array.from({ length: width }, () => -1)),
1069
+ };
1070
+ }
1071
+
1072
+ function plotBraille(canvas, x, y, color = 'gray', priority = 0) {
1073
+ if (x < 0 || y < 0) return;
1074
+ const cellX = Math.floor(x / 2);
1075
+ const cellY = Math.floor(y / 4);
1076
+ if (cellX < 0 || cellY < 0 || cellX >= canvas.width || cellY >= canvas.height) return;
1077
+ canvas.dots[cellY][cellX] |= BRAILLE_DOTS[x % 2][y % 4];
1078
+ if (priority >= canvas.priorities[cellY][cellX]) {
1079
+ canvas.colors[cellY][cellX] = color;
1080
+ canvas.priorities[cellY][cellX] = priority;
1081
+ }
1082
+ }
1083
+
1084
+ function plotBrailleThick(canvas, x, y, radius = 0, color = 'gray', priority = 0) {
1085
+ for (let dy = -radius; dy <= radius; dy += 1) {
1086
+ for (let dx = -radius; dx <= radius; dx += 1) {
1087
+ if (Math.abs(dx) + Math.abs(dy) <= radius + 1) {
1088
+ plotBraille(canvas, x + dx, y + dy, color, priority);
1089
+ }
1090
+ }
1091
+ }
1092
+ }
1093
+
1094
+ function drawBrailleLine(canvas, x0, y0, x1, y1, thickness = 0, color = 'gray', priority = 0) {
1095
+ const steps = Math.max(Math.abs(x1 - x0), Math.abs(y1 - y0), 1);
1096
+ for (let step = 0; step <= steps; step += 1) {
1097
+ const ratio = step / steps;
1098
+ const x = Math.round(x0 + (x1 - x0) * ratio);
1099
+ const y = Math.round(y0 + (y1 - y0) * ratio);
1100
+ plotBrailleThick(canvas, x, y, thickness, color, priority);
1101
+ }
1102
+ }
1103
+
1104
+ function renderBrailleCanvas(canvas, { useColor = false, glyphSet = resolveGlyphSet() } = {}) {
1105
+ return canvas.dots.map((row, rowIndex) => row.map((value, colIndex) => {
1106
+ if (value === 0) return ' ';
1107
+ if (glyphSet.ascii) return glyphSet.brailleFilled;
1108
+ const color = ANSI[canvas.colors[rowIndex][colIndex]] ?? ANSI.teal;
1109
+ return colorize(String.fromCodePoint(0x2800 + value), color, useColor);
1110
+ }).join(''));
1111
+ }
1112
+
1113
+ function renderStatusCapsule(label, value, width, { useColor = false, color = ANSI.teal } = {}) {
1114
+ return fitText(`${colorize(`[${label}]`, color, useColor)} ${value}`, width);
1115
+ }
1116
+
1117
+ function summarizeClientReadiness(globalReadiness) {
1118
+ const states = [
1119
+ ['cx', globalReadiness?.codex?.globalCommand],
1120
+ ['cl', globalReadiness?.claude?.globalCommand],
1121
+ ['gm', globalReadiness?.gemini?.globalCommand],
1122
+ ['ag', globalReadiness?.antigravity?.globalCommand],
1123
+ ];
1124
+
1125
+ return states
1126
+ .map(([label, ready]) => `${label}${ready ? '+' : '-'}`)
1127
+ .join(' ');
1128
+ }
1129
+
1130
+ export function formatMenuBanner(status, { useColor = false } = {}) {
1131
+ const bundledCatalog = status.bundledCatalog ?? { total: 0, categories: [] };
1132
+ const lines = [
1133
+ colorize('Skill Master MCP', ANSI.bold, useColor),
1134
+ 'Menu operacional para manutencao local',
1135
+ `Versao local ${status.semver} | canal ${status.manifestVersion}`,
1136
+ `Pendencias: ${status.pendingSuccessDrafts} drafts | estudo: ${status.studyCandidates} links`,
1137
+ `Global: ${status.globalReadiness.ready ? 'pronto' : 'requer instalacao global'}`,
1138
+ `Bundle: ${bundledCatalog.total} skills | ${formatBundledCategoryLine(bundledCatalog)}`,
1139
+ colorize('Setas + Enter para navegar', ANSI.dim, useColor),
1140
+ ];
1141
+ return [
1142
+ renderPanelLines(lines, { color: ANSI.cyan, useColor }),
1143
+ formatGlobalAlert(status, { useColor }),
1144
+ ].join('\n\n');
1145
+ }
1146
+
1147
+ function dnaPanelLines(tick, width, height, status, selected, { useColor = false, compact = false, glyphSet = resolveGlyphSet() } = {}) {
1148
+ const lines = [];
1149
+ const canvasWidth = Math.max(18, width);
1150
+ const canvasHeight = Math.max(compact ? 8 : 10, height - 4);
1151
+ const pixelWidth = canvasWidth * 2;
1152
+ const pixelHeight = canvasHeight * 4;
1153
+ const center = Math.floor(pixelWidth / 2);
1154
+ const amplitude = Math.max(7, Math.min(Math.floor(pixelWidth * 0.2), compact ? 10 : 15));
1155
+ const frameCount = 48;
1156
+ const frameRatio = (tick % frameCount) / frameCount;
1157
+ const phase = frameRatio * Math.PI * 2;
1158
+ const scanPhase = (1 - Math.cos(frameRatio * Math.PI * 2)) / 2;
1159
+ const pairEvery = compact ? 11 : 9;
1160
+ const actionArea = resolveCommandArea(selected);
1161
+ const canvas = createBrailleCanvas(canvasWidth, canvasHeight);
1162
+
1163
+ lines.push(fitText(colorize(`DNA CYBER HELIX / ${actionArea}`, ANSI.bold, useColor), width));
1164
+ lines.push(fitText(colorize(compact ? 'fine strand / focus' : 'luminous fine helix / low flicker', ANSI.dim, useColor), width));
1165
+
1166
+ const twist = compact ? 0.19 : 0.168;
1167
+ const xA = (y) => Math.round(center + Math.sin(y * twist + phase) * amplitude);
1168
+ const xB = (y) => Math.round(center - Math.sin(y * twist + phase) * amplitude);
1169
+ const frontA = (y) => Math.cos(y * twist + phase) >= 0;
1170
+ const scanY = Math.floor(scanPhase * Math.max(1, pixelHeight - 1));
1171
+ const pulse = 0.72 + ((Math.sin(phase * 2) + 1) / 2) * 0.18;
1172
+
1173
+ for (let y = 0; y < pixelHeight; y += 12) {
1174
+ plotBrailleThick(canvas, center, y, 0, 'gray', 0);
1175
+ }
1176
+
1177
+ for (let y = 0; y < pixelHeight; y += 1) {
1178
+ if (y % 10 === 0) {
1179
+ for (let x = 2; x < pixelWidth - 2; x += 10) {
1180
+ plotBraille(canvas, x, y, 'gray', 0);
1181
+ }
1182
+ }
1183
+
1184
+ const a = xA(y);
1185
+ const b = xB(y);
1186
+ const previousA = xA(Math.max(0, y - 1));
1187
+ const previousB = xB(Math.max(0, y - 1));
1188
+ const active = Math.abs(y - scanY) <= 2;
1189
+ const nearA = frontA(y);
1190
+ const wave = y * twist + phase;
1191
+ const depthA = (Math.cos(wave) + 1) / 2;
1192
+ const depthB = 1 - depthA;
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)));
1195
+ const strandAColor = active ? 'white' : nearA ? 'rose' : 'violet';
1196
+ const strandBColor = active ? 'white' : nearA ? 'teal' : 'blue';
1197
+ const strandAPriority = active ? 7 : nearA ? 5 : 3;
1198
+ const strandBPriority = active ? 7 : nearA ? 3 : 5;
1199
+
1200
+ if (y > 0) {
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
+ }
1205
+ drawBrailleLine(canvas, previousA, y - 1, a, y, strandAWidth, strandAColor, strandAPriority);
1206
+ drawBrailleLine(canvas, previousB, y - 1, b, y, strandBWidth, strandBColor, strandBPriority);
1207
+ } else {
1208
+ plotBrailleThick(canvas, a, y, strandAWidth, strandAColor, strandAPriority);
1209
+ plotBrailleThick(canvas, b, y, strandBWidth, strandBColor, strandBPriority);
1210
+ }
1211
+
1212
+ if ((y + tick) % pairEvery === 0 || active) {
1213
+ const left = Math.min(a, b);
1214
+ const right = Math.max(a, b);
1215
+ const rungColor = active ? 'white' : nearA ? 'teal' : 'rose';
1216
+ const rungPriority = active ? 6 : 4;
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);
1220
+ }
1221
+ plotBrailleThick(canvas, Math.round((left + right) / 2), y, active ? 1 : 0, active ? 'white' : 'amber', rungPriority + 1);
1222
+ plotBrailleThick(canvas, a, y, 0, strandAColor, strandAPriority + 1);
1223
+ plotBrailleThick(canvas, b, y, 0, strandBColor, strandBPriority + 1);
1224
+ }
1225
+ }
1226
+
1227
+ for (const line of renderBrailleCanvas(canvas, { useColor, glyphSet })) {
1228
+ lines.push(line);
1229
+ }
1230
+
1231
+ lines.push(renderStatusCapsule('HELIX', `frames ${tick % frameCount}/${frameCount} | global ${status.globalReadiness.installed.length}/${status.globalReadiness.required}`, width, { useColor, color: ANSI.amber }));
1232
+
1233
+ return lines.slice(0, height).map((line) => fitText(line, width));
1234
+ }
1235
+
1236
+ function renderMeterBar(value, total, width, { useColor = false, color = ANSI.teal, glyphSet = resolveGlyphSet() } = {}) {
1237
+ const safeTotal = Math.max(1, total);
1238
+ const safeValue = Math.max(0, Math.min(value, safeTotal));
1239
+ const fill = Math.round((safeValue / safeTotal) * width);
1240
+ const filled = glyphSet.meterFilled.repeat(fill);
1241
+ const empty = glyphSet.meterEmpty.repeat(Math.max(0, width - fill));
1242
+ return `${colorize(filled, color, useColor)}${colorize(empty, ANSI.gray, useColor)}`;
1243
+ }
1244
+
1245
+ function renderSparkline(series, { useColor = false, color = ANSI.teal, glyphSet = resolveGlyphSet() } = {}) {
1246
+ const blocks = glyphSet.sparkline;
1247
+ return series.map((value) => colorize(blocks[Math.max(0, Math.min(blocks.length - 1, value))], color, useColor)).join('');
1248
+ }
1249
+
1250
+ function buildTelemetrySeries(seed, length, tick) {
1251
+ return Array.from({ length }, (_, index) => {
1252
+ const raw = Math.sin((index + 1 + seed) * 0.72 + tick * 0.18) + Math.cos((index + seed) * 0.33 + tick * 0.11);
1253
+ const normalized = Math.max(0, Math.min(1, (raw + 2) / 4));
1254
+ return Math.round(normalized * 7);
1255
+ });
1256
+ }
1257
+
1258
+ function buildOverviewSignalLine(status, width, tick, { useColor = false, compact = false, glyphSet = resolveGlyphSet() } = {}) {
1259
+ const signalWidth = Math.max(6, Math.min(compact ? 8 : 12, Math.floor(width / (compact ? 10 : 8))));
1260
+ const healthWidth = Math.max(6, Math.min(compact ? 8 : 12, Math.floor(width / (compact ? 10 : 8))));
1261
+ const signal = renderSparkline(
1262
+ buildTelemetrySeries(status.globalReadiness.installed.length + status.pendingSuccessDrafts, signalWidth, tick),
1263
+ { useColor, color: ANSI.teal, glyphSet },
1264
+ );
1265
+ const health = renderSparkline(
1266
+ buildTelemetrySeries(status.studyCandidates + (status.globalReadiness.ready ? 7 : 3), healthWidth, tick + 3),
1267
+ { useColor, color: ANSI.amber, glyphSet },
1268
+ );
1269
+ return compact
1270
+ ? `signal ${signal} health ${health}`
1271
+ : `signal ${signal} health ${health}`;
1272
+ }
1273
+
1274
+ function telemetryPanelLines(status, selected, width, height, { useColor = false, tick = 0, compact = false, glyphSet = resolveGlyphSet() } = {}) {
1275
+ const commandArea = resolveCommandArea(selected);
1276
+ const bundledCatalog = status.bundledCatalog ?? { total: 0, categories: [] };
1277
+ const categoryLead = bundledCatalog.categories
1278
+ ?.filter((category) => category.count > 0)
1279
+ .sort((left, right) => right.count - left.count)[0];
1280
+ const categoryName = categoryLead?.label ?? 'none';
1281
+ const categoryCount = categoryLead?.count ?? 0;
1282
+ const meterWidth = Math.max(8, Math.min(16, width - 14));
1283
+ const sparkWidth = Math.max(8, Math.min(compact ? 8 : 12, width - 18));
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 });
1287
+ const lines = [
1288
+ fitText(colorize('SYSTEM WINDOWS', ANSI.bold, useColor), width),
1289
+ renderStatusCapsule('AREA', commandArea, width, { useColor, color: ANSI.blue }),
1290
+ renderStatusCapsule('LEAD', `${categoryName} ${categoryCount}`, width, { useColor, color: ANSI.gray }),
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),
1293
+ renderStatusCapsule('LINK', summarizeClientReadiness(status.globalReadiness), width, { useColor, color: ANSI.teal }),
1294
+ fitText(`skills ${skillsSpark}`, 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),
1296
+ fitText(`queue ${draftsSpark}`, 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),
1298
+ fitText(`links ${studySpark}`, width),
1299
+ fitText(colorize(compact ? 'codex / claude / gemini' : 'codex / claude / gemini / antigravity', ANSI.dim, useColor), width),
1300
+ ];
1301
+
1302
+ return lines.slice(0, height).map((line) => fitText(line, width));
1303
+ }
1304
+
1305
+ function cyberCorePanelLines(status, selected, width, height, { useColor = false, tick = 0, compact = false } = {}) {
1306
+ const actionArea = resolveCommandArea(selected);
1307
+ const gridHeight = Math.max(compact ? 4 : 6, height - (compact ? 5 : 6));
1308
+ const gridWidth = Math.max(12, width);
1309
+ const lines = [
1310
+ fitText(colorize('SKILL_MASTER CORE', ANSI.bold, useColor), width),
1311
+ fitText(colorize(compact ? 'cyber lattice / low flicker' : 'cyberpunk pixel art workflow engine', ANSI.dim, useColor), width),
1312
+ fitText(colorize(`area ${actionArea} | tick ${String(tick).padStart(3, '0')}`, ANSI.gray, useColor), width),
1313
+ fitText(renderCircuitRail(width, tick, { useColor, color: ANSI.teal }), width),
1314
+ ...renderScanGrid(gridWidth, gridHeight, tick, { useColor }).map((line) => fitText(line, width)),
1315
+ fitText(renderCircuitRail(width, tick + 2, { useColor, color: ANSI.amber }), width),
1316
+ fitText(colorize(`global ${status.globalReadiness.installed.length}/${status.globalReadiness.required} | bundle ${status.bundledCatalog?.total ?? 0}`, ANSI.gray, useColor), width),
1317
+ ];
1318
+
1319
+ return lines.slice(0, height).map((line) => fitText(line, width));
1320
+ }
1321
+
1322
+ function renderBox(lines, width, title, { useColor = false, color = ANSI.cyan, style = 'normal' } = {}) {
1323
+ const innerWidth = Math.max(10, width - 4);
1324
+ const titleText = title ? ` ${title.toUpperCase()} ` : '';
1325
+ const topLeft = style === 'focus' ? '╭' : style === 'hud' ? '┏' : '┌';
1326
+ const topRight = style === 'focus' ? '╮' : style === 'hud' ? '┓' : '┐';
1327
+ const bottomLeft = style === 'focus' ? '╰' : style === 'hud' ? '┗' : '└';
1328
+ const bottomRight = style === 'focus' ? '╯' : style === 'hud' ? '┛' : '┘';
1329
+ const topFill = style === 'focus' ? '═' : '─';
1330
+ const bottomTail = style === 'focus' ? '══' : '┄┄';
1331
+ const top = `${topLeft}${titleText}${topFill.repeat(Math.max(0, width - 2 - titleText.length))}${topRight}`;
1332
+ const bottom = `${bottomLeft}${topFill.repeat(Math.max(0, width - 4))}${bottomTail}${bottomRight}`;
1333
+ const body = lines.map((line, index) => {
1334
+ const left = index === 0
1335
+ ? (style === 'focus' ? '╞' : '├')
1336
+ : index === lines.length - 1
1337
+ ? (style === 'focus' ? '╘' : '╰')
1338
+ : '│';
1339
+ const right = index === 0
1340
+ ? (style === 'focus' ? '╡' : '┤')
1341
+ : index === lines.length - 1
1342
+ ? (style === 'focus' ? '╛' : '╯')
1343
+ : '│';
1344
+ return `${colorize(left, color, useColor)} ${fitText(line, innerWidth)} ${colorize(right, color, useColor)}`;
1345
+ });
1346
+ return [
1347
+ colorize(top, color, useColor),
1348
+ ...body,
1349
+ colorize(bottom, color, useColor),
1350
+ ];
1351
+ }
1352
+
1353
+ function joinHorizontalBoxes(boxes, gutter = 2) {
1354
+ const height = Math.max(...boxes.map((box) => box.lines.length));
1355
+ const gap = ' '.repeat(gutter);
1356
+ const rows = [];
1357
+
1358
+ for (let index = 0; index < height; index += 1) {
1359
+ rows.push(boxes.map((box) => box.lines[index] ?? ' '.repeat(box.width)).join(gap));
1360
+ }
1361
+
1362
+ return rows;
1363
+ }
1364
+
1365
+ export function formatCyberMenuFrame(status, commands, selectedIndex, tick = 0, {
1366
+ columns = 120,
1367
+ rows = 32,
1368
+ useColor = false,
1369
+ ascii = false,
1370
+ } = {}) {
1371
+ const glyphSet = resolveGlyphSet({ ascii });
1372
+ const width = Math.max(72, Math.min(columns, 150));
1373
+ const height = Math.max(22, Math.min(rows - 1, 42));
1374
+ const compactMode = width < 104 || height < 27;
1375
+ const rightWidth = compactMode ? (width >= 92 ? 28 : 26) : width >= 132 ? 44 : width >= 118 ? 40 : width >= 108 ? 36 : 34;
1376
+ const gutter = 2;
1377
+ const leftWidth = width - rightWidth - gutter;
1378
+ const selected = commands[selectedIndex] ?? commands[0];
1379
+ const actionArea = resolveCommandArea(selected);
1380
+ const bundledCatalog = status.bundledCatalog ?? { total: 0, categories: [] };
1381
+ const summaryOuterHeight = compactMode ? 7 : 9;
1382
+ const bodyOuterHeight = Math.max(12, height - summaryOuterHeight - 1);
1383
+ const detailOuterHeight = compactMode
1384
+ ? Math.min(8, Math.max(7, Math.floor(bodyOuterHeight * 0.46)))
1385
+ : Math.min(10, Math.max(8, Math.floor(bodyOuterHeight * 0.42)));
1386
+ const actionOuterHeight = compactMode
1387
+ ? Math.max(8, bodyOuterHeight - detailOuterHeight)
1388
+ : Math.max(8, bodyOuterHeight - detailOuterHeight);
1389
+ const rightBottomOuterHeight = compactMode
1390
+ ? Math.min(8, Math.max(7, Math.floor(bodyOuterHeight * 0.4)))
1391
+ : Math.min(10, Math.max(9, Math.floor(bodyOuterHeight * 0.36)));
1392
+ const rightTopOuterHeight = Math.max(8, bodyOuterHeight - rightBottomOuterHeight);
1393
+ const summaryInnerWidth = width - 4;
1394
+ const actionInnerWidth = leftWidth - 4;
1395
+ const detailInnerWidth = leftWidth - 4;
1396
+ const rightInnerWidth = rightWidth - 4;
1397
+ const summaryInnerHeight = summaryOuterHeight - 2;
1398
+ const actionInnerHeight = actionOuterHeight - 2;
1399
+ const detailInnerHeight = detailOuterHeight - 2;
1400
+ const rightTopInnerHeight = rightTopOuterHeight - 2;
1401
+ const rightBottomInnerHeight = rightBottomOuterHeight - 2;
1402
+ const visibleActionRows = Math.max(compactMode ? 2 : 3, actionInnerHeight - (compactMode ? 2 : 3));
1403
+ const scrollStart = Math.min(
1404
+ Math.max(0, selectedIndex - Math.floor(visibleActionRows / 2)),
1405
+ Math.max(0, commands.length - visibleActionRows),
1406
+ );
1407
+ const visibleCommands = commands.slice(scrollStart, scrollStart + visibleActionRows);
1408
+
1409
+ const summaryLines = compactMode
1410
+ ? [
1411
+ colorize('SKILL MASTER MENU', ANSI.bold, useColor),
1412
+ `version ${status.semver} | channel ${status.manifestVersion} | readiness ${status.globalReadiness.ready ? 'ready' : status.globalReadiness.mode}`,
1413
+ colorize(`GLOBAL SKILLS ${status.globalReadiness.installed.length}/${status.globalReadiness.required}`, ANSI.amber, useColor),
1414
+ `bundle ${bundledCatalog.total} | drafts ${status.pendingSuccessDrafts} | study ${status.studyCandidates}`,
1415
+ buildOverviewSignalLine(status, summaryInnerWidth, tick, { useColor, compact: true, glyphSet }),
1416
+ colorize(buildSetupAlertLine(status, true) ?? 'compact cyberpunk hud / operator safe', status.globalReadiness.ready ? ANSI.dim : ANSI.rose, useColor),
1417
+ ]
1418
+ : [
1419
+ colorize('SKILL MASTER MENU', ANSI.bold, useColor),
1420
+ `version ${status.semver} | channel ${status.manifestVersion} | readiness ${status.globalReadiness.ready ? 'ready' : status.globalReadiness.mode}`,
1421
+ colorize(`GLOBAL SKILLS ${status.globalReadiness.installed.length}/${status.globalReadiness.required}`, ANSI.amber, useColor),
1422
+ `bundle ${bundledCatalog.total} | drafts ${status.pendingSuccessDrafts} | study ${status.studyCandidates}`,
1423
+ renderStatusCapsule('FOCUS', `${actionArea} / ${selected?.key ?? 'status'}`, summaryInnerWidth, { useColor, color: ANSI.teal }),
1424
+ buildOverviewSignalLine(status, summaryInnerWidth, tick, { useColor, compact: false, glyphSet }),
1425
+ colorize(buildSetupAlertLine(status, false) ?? formatBundledCategoryLine(bundledCatalog), status.globalReadiness.ready ? ANSI.white : ANSI.rose, useColor),
1426
+ colorize(buildSetupAlertLine(status, false) ?? `clients ${summarizeClientReadiness(status.globalReadiness)}`, status.globalReadiness.ready ? ANSI.dim : ANSI.rose, useColor),
1427
+ ];
1428
+
1429
+ const actionLines = [
1430
+ colorize('ACTION MATRIX', ANSI.gray, useColor),
1431
+ colorize('up/down move enter run q exit', ANSI.dim, useColor),
1432
+ fitText(colorize(`window ${Math.min(selectedIndex + 1, commands.length)}/${commands.length} | visible ${visibleCommands.length}`, ANSI.gray, useColor), actionInnerWidth),
1433
+ ...visibleCommands.map((command, offset) => {
1434
+ const index = scrollStart + offset;
1435
+ const marker = index === selectedIndex ? '>' : glyphSet.ascii ? '-' : '·';
1436
+ const disabled = command.disabledReason ? ' [off]' : '';
1437
+ const flair = index === selectedIndex && !compactMode ? glyphSet.ascii ? ' >' : ' ◢' : '';
1438
+ const badge = getActionAttentionBadge(command, status);
1439
+ const label = `${marker} ${badge}${command.label}${disabled}${flair}`;
1440
+ const color = getActionLineColor(command, index === selectedIndex, status);
1441
+ const fitted = fitText(label, actionInnerWidth);
1442
+ return color ? colorize(fitted, color, useColor) : fitted;
1443
+ }),
1444
+ ];
1445
+
1446
+ while (actionLines.length < actionInnerHeight) {
1447
+ actionLines.push(fitText('', actionInnerWidth));
1448
+ }
1449
+
1450
+ const descriptionLines = [
1451
+ colorize('DETAIL FOCUS', ANSI.amber, useColor),
1452
+ colorize(selected?.label ?? 'Nenhuma acao', ANSI.bold, useColor),
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)] : []),
1455
+ fitText(colorize(patternFill('=-', 32), ANSI.amber, useColor), detailInnerWidth),
1456
+ ...(() => {
1457
+ const attention = getActionAttentionState(selected, status);
1458
+ if (attention === 'critical') {
1459
+ return splitText('priority: setup inicial fortemente recomendado. Esta acao autentica o MCP no sistema e prepara os clientes deste computador.', detailInnerWidth, 2);
1460
+ }
1461
+ if (attention === 'priority') {
1462
+ return splitText('priority: suporte ao setup global. Use junto do bootstrap para deixar Codex, Claude, Gemini e skills prontos neste computador.', detailInnerWidth, 2);
1463
+ }
1464
+ return [];
1465
+ })(),
1466
+ ...splitText(selected?.description ? `desc: ${selected.description}` : '', detailInnerWidth, 1),
1467
+ ...((selected?.details ?? []).slice(0, 1).flatMap((detail) => splitText(`detail: ${detail}`, detailInnerWidth, 1))),
1468
+ ...(selected?.key === 'updateGlobal'
1469
+ ? splitText('recommended: se houver handoff ou aviso, feche o menu, rode o comando exibido e reabra os clientes MCP depois.', detailInnerWidth, 3)
1470
+ : []),
1471
+ ...(selected?.automationHint ? splitText(`automation: ${selected.automationHint}`, detailInnerWidth, 2) : []),
1292
1472
  ...splitText(`exec: ${formatActionCommand(selected)}`, detailInnerWidth, 1),
1293
- ...(selected?.confirmMessage ? splitText(`confirmacao: ${selected.confirmMessage}`, detailInnerWidth, 2) : []),
1294
- ...(selected?.disabledReason ? splitText(`Indisponivel: ${selected.disabledReason}`, detailInnerWidth, 2) : []),
1295
- ];
1296
-
1297
- while (descriptionLines.length < detailInnerHeight) {
1298
- descriptionLines.push(fitText('', detailInnerWidth));
1299
- }
1300
-
1301
- const rightTopLines = dnaPanelLines(tick, rightInnerWidth, rightTopInnerHeight, status, selected, { useColor, compact: compactMode });
1302
- const rightBottomLines = telemetryPanelLines(status, selected, rightInnerWidth, rightBottomInnerHeight, { useColor, tick, compact: compactMode });
1303
- const summaryBox = renderCyberFrame(
1304
- summaryLines.slice(0, summaryInnerHeight).map((line) => fitText(line, summaryInnerWidth)),
1305
- width,
1306
- 'overview',
1307
- { useColor, color: ANSI.white, style: 'hud', tick },
1308
- );
1309
- const actionBox = renderCyberFrame(
1310
- actionLines.slice(0, actionInnerHeight).map((line) => fitText(line, actionInnerWidth)),
1311
- leftWidth,
1312
- 'actions',
1313
- { useColor, color: ANSI.gray, style: 'thin', tick },
1314
- );
1315
- const detailBox = renderCyberFrame(
1316
- descriptionLines.slice(0, detailInnerHeight).map((line) => fitText(line, detailInnerWidth)),
1317
- leftWidth,
1318
- 'details',
1319
- { useColor, color: ANSI.amber, style: 'focus', tick },
1320
- );
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 });
1323
- const bodyRows = joinHorizontalBoxes([
1324
- { lines: [...actionBox, ...detailBox], width: leftWidth },
1325
- { lines: [...dnaBox, ...telemetryBox], width: rightWidth },
1326
- ], gutter);
1327
-
1328
- return [...summaryBox, '', ...bodyRows].join('\n');
1329
- }
1330
-
1331
- export function formatRunningActionFrame(status, action, tick = 0, {
1332
- columns = 120,
1333
- rows = 32,
1334
- useColor = false,
1335
- } = {}) {
1336
- const width = Math.max(72, Math.min(columns, 150));
1337
- const height = Math.max(18, Math.min(rows - 1, 30));
1338
- const innerWidth = width - 4;
1339
- const commandArea = resolveCommandArea(action);
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));
1342
- const lines = [
1343
- colorize('SKILL_MASTER WORKFLOW ONLINE', ANSI.bold, useColor),
1344
- fitText(colorize(`area ${commandArea} | launching ${action?.label ?? 'acao'}`, ANSI.amber, useColor), innerWidth),
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 }),
1348
- fitText('', innerWidth),
1349
- ...splitText(action?.description ?? '', innerWidth, 2),
1350
- ...splitText(action?.success ? `resultado esperado: ${action.success}` : '', innerWidth, 2),
1351
- ...splitText(`exec: ${formatActionCommand(action)}`, innerWidth, 2),
1352
- fitText('', innerWidth),
1353
- fitText(colorize('handoff: HUD -> stream real do comando', ANSI.dim, useColor), innerWidth),
1354
- ];
1355
-
1356
- while (lines.length < Math.max(8, height - 2)) {
1357
- lines.push(fitText('', innerWidth));
1358
- }
1359
-
1360
- return renderCyberFrame(
1361
- lines.slice(0, Math.max(8, height - 2)).map((line) => fitText(line, innerWidth)),
1362
- width,
1363
- 'running',
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 },
1432
- ).join('\n');
1433
- }
1434
-
1435
- export function formatActionHeader(action, { useColor = false } = {}) {
1436
- const lines = [
1437
- colorize(action.label, ANSI.bold, useColor),
1438
- action.description,
1439
- ...(action.details ?? []),
1440
- action.success ? `Resultado esperado: ${action.success}` : null,
1441
- action.disabledReason ? `Indisponivel: ${action.disabledReason}` : null,
1442
- ].filter(Boolean);
1443
-
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');
1451
- }
1452
-
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 } = {}) {
1470
- if (code === 0) {
1471
- return colorize('Comando concluido com sucesso.', ANSI.green, useColor);
1472
- }
1473
-
1474
- return colorize(`Comando terminou com codigo ${code}.`, ANSI.red, useColor);
1475
- }
1476
-
1477
- export function formatHelp(commands) {
1478
- const actionList = commands.map((command) => {
1479
- const aliases = command.aliases.length ? ` aliases: ${command.aliases.join(', ')}` : '';
1480
- const disabled = command.disabledReason ? ` indisponivel: ${command.disabledReason}` : '';
1481
- return ` - ${command.key}: ${command.label}\n ${command.description}${aliases}${disabled}`;
1482
- }).join('\n');
1483
-
1484
- return `Skill Master Menu
1485
-
1486
- Uso:
1487
- skill-master-menu
1473
+ ...splitText(selected?.success ? `result: ${selected.success}` : '', detailInnerWidth, 1),
1474
+ ...((selected?.details ?? []).slice(1).flatMap((detail) => splitText(`detail: ${detail}`, detailInnerWidth, 2))),
1475
+ ...(selected?.confirmMessage ? splitText(`confirmacao: ${selected.confirmMessage}`, detailInnerWidth, 2) : []),
1476
+ ...(selected?.disabledReason ? splitText(`Indisponivel: ${selected.disabledReason}`, detailInnerWidth, 2) : []),
1477
+ ];
1478
+
1479
+ while (descriptionLines.length < detailInnerHeight) {
1480
+ descriptionLines.push(fitText('', detailInnerWidth));
1481
+ }
1482
+
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 });
1485
+ const summaryBox = renderCyberFrame(
1486
+ summaryLines.slice(0, summaryInnerHeight).map((line) => fitText(line, summaryInnerWidth)),
1487
+ width,
1488
+ 'overview',
1489
+ { useColor, color: ANSI.white, style: 'hud', tick, glyphSet },
1490
+ );
1491
+ const actionBox = renderCyberFrame(
1492
+ actionLines.slice(0, actionInnerHeight).map((line) => fitText(line, actionInnerWidth)),
1493
+ leftWidth,
1494
+ 'actions',
1495
+ { useColor, color: ANSI.gray, style: 'thin', tick, glyphSet },
1496
+ );
1497
+ const detailBox = renderCyberFrame(
1498
+ descriptionLines.slice(0, detailInnerHeight).map((line) => fitText(line, detailInnerWidth)),
1499
+ leftWidth,
1500
+ 'details',
1501
+ { useColor, color: ANSI.amber, style: 'focus', tick, glyphSet },
1502
+ );
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 });
1505
+ const bodyRows = joinHorizontalBoxes([
1506
+ { lines: [...actionBox, ...detailBox], width: leftWidth },
1507
+ { lines: [...dnaBox, ...telemetryBox], width: rightWidth },
1508
+ ], gutter);
1509
+
1510
+ return [...summaryBox, '', ...bodyRows].join('\n');
1511
+ }
1512
+
1513
+ export function formatRunningActionFrame(status, action, tick = 0, {
1514
+ columns = 120,
1515
+ rows = 32,
1516
+ useColor = false,
1517
+ ascii = false,
1518
+ } = {}) {
1519
+ const glyphSet = resolveGlyphSet({ ascii });
1520
+ const width = Math.max(72, Math.min(columns, 150));
1521
+ const height = Math.max(18, Math.min(rows - 1, 30));
1522
+ const innerWidth = width - 4;
1523
+ const commandArea = resolveCommandArea(action);
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 });
1525
+ const gridHeight = Math.max(4, Math.min(8, height - 14));
1526
+ const lines = [
1527
+ colorize('SKILL_MASTER WORKFLOW ONLINE', ANSI.bold, useColor),
1528
+ fitText(colorize(`area ${commandArea} | launching ${action?.label ?? 'acao'}`, ANSI.amber, useColor), innerWidth),
1529
+ fitText(colorize(`pulse ${pulse}`, ANSI.gray, useColor), innerWidth),
1530
+ fitText(renderCircuitRail(innerWidth, tick, { useColor, color: ANSI.teal }), innerWidth),
1531
+ ...renderScanGrid(innerWidth, gridHeight, tick, { useColor }),
1532
+ fitText('', innerWidth),
1533
+ ...splitText(action?.description ?? '', innerWidth, 2),
1534
+ ...splitText(action?.success ? `resultado esperado: ${action.success}` : '', innerWidth, 2),
1535
+ ...splitText(`exec: ${formatActionCommand(action)}`, innerWidth, 2),
1536
+ fitText('', innerWidth),
1537
+ fitText(colorize('handoff: HUD -> stream real do comando', ANSI.dim, useColor), innerWidth),
1538
+ ];
1539
+
1540
+ while (lines.length < Math.max(8, height - 2)) {
1541
+ lines.push(fitText('', innerWidth));
1542
+ }
1543
+
1544
+ return renderCyberFrame(
1545
+ lines.slice(0, Math.max(8, height - 2)).map((line) => fitText(line, innerWidth)),
1546
+ width,
1547
+ 'running',
1548
+ { useColor, color: ANSI.teal, style: 'running', tick, glyphSet },
1549
+ ).join('\n');
1550
+ }
1551
+
1552
+ export function formatSkillMasterIntroFrame(status, tick = 0, {
1553
+ columns = 120,
1554
+ rows = 32,
1555
+ useColor = false,
1556
+ ascii = false,
1557
+ message = 'skill_master assumindo o workflow',
1558
+ } = {}) {
1559
+ const glyphSet = resolveGlyphSet({ ascii });
1560
+ const width = Math.max(72, Math.min(columns, 150));
1561
+ const height = Math.max(18, Math.min(rows - 1, 34));
1562
+ const innerWidth = width - 4;
1563
+ const gridHeight = Math.max(6, Math.min(12, height - 12));
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 });
1565
+ const lines = [
1566
+ colorize('REBORN / SKILL_MASTER', ANSI.bold, useColor),
1567
+ fitText(colorize(message, ANSI.amber, useColor), innerWidth),
1568
+ fitText(colorize(`package ${status.packageName} | version ${status.semver} | channel ${status.manifestVersion}`, ANSI.gray, useColor), innerWidth),
1569
+ fitText(colorize(`signal ${signal}`, ANSI.teal, useColor), innerWidth),
1570
+ fitText(renderCircuitRail(innerWidth, tick, { useColor, color: ANSI.white }), innerWidth),
1571
+ ...renderScanGrid(innerWidth, gridHeight, tick, { useColor }),
1572
+ fitText(renderCircuitRail(innerWidth, tick + 3, { useColor, color: ANSI.amber }), innerWidth),
1573
+ fitText(colorize(`global ${status.globalReadiness.installed.length}/${status.globalReadiness.required} | bundle ${status.bundledCatalog?.total ?? 0} | study ${status.studyCandidates}`, ANSI.gray, useColor), innerWidth),
1574
+ fitText(colorize('modo visual seguro: terminal humano ativo / stdio MCP preservado', ANSI.dim, useColor), innerWidth),
1575
+ ];
1576
+
1577
+ while (lines.length < Math.max(8, height - 2)) {
1578
+ lines.push(fitText('', innerWidth));
1579
+ }
1580
+
1581
+ return renderCyberFrame(
1582
+ lines.slice(0, Math.max(8, height - 2)).map((line) => fitText(line, innerWidth)),
1583
+ width,
1584
+ 'boot-sequence',
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 },
1636
+ ).join('\n');
1637
+ }
1638
+
1639
+ export function formatCyberConfirmFrame(status, action, tick = 0, {
1640
+ columns = 120,
1641
+ rows = 32,
1642
+ useColor = false,
1643
+ ascii = false,
1644
+ } = {}) {
1645
+ const glyphSet = resolveGlyphSet({ ascii });
1646
+ const width = Math.max(72, Math.min(columns, 140));
1647
+ const height = Math.max(16, Math.min(rows - 1, 28));
1648
+ const innerWidth = width - 4;
1649
+ const commandArea = resolveCommandArea(action);
1650
+ const lines = [
1651
+ colorize('CONFIRMATION GATE', ANSI.bold, useColor),
1652
+ fitText(colorize(action?.confirmMessage ?? 'Confirmar acao?', ANSI.amber, useColor), innerWidth),
1653
+ fitText(`area ${commandArea} | action ${action?.key ?? 'unknown'}`, innerWidth),
1654
+ fitText(renderCircuitRail(innerWidth, tick, { useColor, color: ANSI.amber }), innerWidth),
1655
+ ...splitText(action?.description ?? '', innerWidth, 2),
1656
+ ...splitText(action?.success ? `resultado esperado: ${action.success}` : '', innerWidth, 2),
1657
+ fitText('', innerWidth),
1658
+ fitText(colorize('Y/Enter confirma N/Esc cancela', ANSI.teal, useColor), innerWidth),
1659
+ ];
1660
+
1661
+ while (lines.length < Math.max(8, height - 2)) {
1662
+ lines.push(fitText('', innerWidth));
1663
+ }
1664
+
1665
+ return renderCyberFrame(
1666
+ lines.slice(0, Math.max(8, height - 2)).map((line) => fitText(line, innerWidth)),
1667
+ width,
1668
+ 'subroutine',
1669
+ { useColor, color: ANSI.amber, style: 'focus', tick, glyphSet },
1670
+ ).join('\n');
1671
+ }
1672
+
1673
+ export function formatActionHeader(action, { useColor = false } = {}) {
1674
+ const lines = [
1675
+ colorize(action.label, ANSI.bold, useColor),
1676
+ formatActionBadges(action),
1677
+ action.description,
1678
+ ...(action.details ?? []),
1679
+ action.automationHint ? `Automacao: ${action.automationHint}` : null,
1680
+ action.success ? `Resultado esperado: ${action.success}` : null,
1681
+ action.disabledReason ? `Indisponivel: ${action.disabledReason}` : null,
1682
+ ].filter(Boolean);
1683
+
1684
+ const width = Math.max(72, Math.min(120, Math.max(...lines.map((line) => visibleLength(line))) + 8));
1685
+ return renderCyberFrame(
1686
+ lines.map((line) => fitText(line, width - 4)),
1687
+ width,
1688
+ 'skill-master action',
1689
+ { color: ANSI.green, useColor, style: 'hud' },
1690
+ ).join('\n');
1691
+ }
1692
+
1693
+ export function formatResultMessage(code, { useColor = false } = {}) {
1694
+ const result = buildOperationResultCopy(code);
1695
+ const tone = result.kind === 'ok' ? 'ok' : result.kind === 'warning' ? 'warning' : 'error';
1696
+ const color = tone === 'ok' ? ANSI.green : tone === 'warning' ? ANSI.amber : ANSI.red;
1697
+ const lines = [
1698
+ colorize(result.heading, color, useColor),
1699
+ result.summary,
1700
+ result.next,
1701
+ tone === 'warning' ? 'consulte o proximo passo recomendado no log acima' : null,
1702
+ ];
1703
+
1704
+ return renderCyberFrame(
1705
+ lines.filter(Boolean).map((line) => fitText(line, 68)),
1706
+ 72,
1707
+ result.frameTitle,
1708
+ { color, useColor, style: tone === 'ok' ? 'hud' : 'focus' },
1709
+ ).join('\n');
1710
+ }
1711
+
1712
+ export function formatPlainResultMessage(code, { useColor = false } = {}) {
1713
+ const result = buildOperationResultCopy(code);
1714
+ const color = result.kind === 'ok' ? ANSI.green : result.kind === 'warning' ? ANSI.amber : ANSI.red;
1715
+ return colorize(result.summary, color, useColor);
1716
+ }
1717
+
1718
+ export function formatHelp(commands) {
1719
+ const actionList = commands.map((command) => {
1720
+ const aliases = command.aliases.length ? ` aliases: ${command.aliases.join(', ')}` : '';
1721
+ const disabled = command.disabledReason ? ` indisponivel: ${command.disabledReason}` : '';
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}`;
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');
1734
+
1735
+ return `Skill Master Menu
1736
+
1737
+ Uso humano:
1738
+ skill-master-menu
1739
+ skill-master-menu --help
1740
+
1741
+ Automacao sem TTY:
1488
1742
  skill-master-menu --status
1489
1743
  skill-master-menu --run <acao>
1490
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
1491
1750
  skill-master-menu --help
1492
1751
 
1493
- Acoes disponiveis:
1494
- ${actionList}
1495
-
1496
- O comando de menu e voltado para operacao humana. O binario MCP stdio continua sendo:
1497
- skill-master-mcp
1498
- `;
1499
- }
1500
-
1501
- export function runCommand(action, { cwd, env = process.env } = {}) {
1502
- return new Promise((resolve, reject) => {
1503
- let child;
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}
1504
1760
 
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
- }
1761
+ Acoes globais ou de risco alto:
1762
+ ${globalActions}
1516
1763
 
1517
- child.on('error', reject);
1518
- child.on('close', (code) => {
1519
- resolve(code ?? 1);
1520
- });
1521
- });
1522
- }
1764
+ Acoes disponiveis:
1765
+ ${actionList}
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
+
1774
+ O comando de menu e voltado para operacao humana. O binario MCP stdio continua sendo:
1775
+ skill-master-mcp
1776
+ `;
1777
+ }
1778
+
1779
+ export function runCommand(action, { cwd, env = process.env } = {}) {
1780
+ return new Promise((resolve, reject) => {
1781
+ let child;
1782
+
1783
+ try {
1784
+ child = spawn(action.command, action.args, {
1785
+ cwd,
1786
+ env,
1787
+ shell: Boolean(action.shell),
1788
+ stdio: 'inherit',
1789
+ });
1790
+ } catch (error) {
1791
+ reject(error);
1792
+ return;
1793
+ }
1794
+
1795
+ child.on('error', reject);
1796
+ child.on('close', (code) => {
1797
+ resolve(code ?? 1);
1798
+ });
1799
+ });
1800
+ }