@fprad0/skill-master-mcp 0.0.10 → 0.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/CHANGELOG.md +12 -1
  2. package/README.md +59 -10
  3. package/VERSION.md +3 -3
  4. package/bin/lib/client-config.mjs +293 -0
  5. package/bin/lib/menu-core.mjs +509 -131
  6. package/bin/lib/skill-installation.mjs +215 -0
  7. package/bin/skill-master-bootstrap-global.mjs +2 -1
  8. package/bin/skill-master-doctor.mjs +92 -32
  9. package/bin/skill-master-install-global-skills.mjs +4 -42
  10. package/bin/skill-master-install-project-skills.mjs +97 -0
  11. package/bin/skill-master-menu.mjs +91 -6
  12. package/bin/skill-master-register-clients.mjs +91 -115
  13. package/docs/operations/GUIA_MULTI_COMPUTADOR.md +262 -0
  14. package/docs/operations/GUIA_NPM_PUBLICO.md +147 -0
  15. package/docs/operations/MENU_VISUAL_EVIDENCE_2026-06-28.md +66 -0
  16. package/docs/operations/assets/menu-frame-compact.html +76 -0
  17. package/docs/operations/assets/menu-frame-compact.png +0 -0
  18. package/docs/operations/assets/menu-frame-large.html +84 -0
  19. package/docs/operations/assets/menu-frame-large.png +0 -0
  20. package/docs/operations/assets/menu-frame-running.html +80 -0
  21. package/docs/operations/assets/menu-frame-running.png +0 -0
  22. package/docs/operations/cross-platform-auth-transfer/ANALISE_COMPATIBILIDADE_MCP_2026-06-28.md +140 -0
  23. package/docs/operations/cross-platform-auth-transfer/README_TRANSFERENCIA.md +85 -0
  24. package/docs/operations/reborn-menu-cyberpunk-transfer/ANALISE_MENU_REBORN_CYBERPUNK_2026-06-28.md +174 -0
  25. package/docs/operations/reborn-menu-cyberpunk-transfer/HANDOFF_IMPLEMENTACAO_REBORN_CYBERPUNK_2026-06-28.md +119 -0
  26. package/docs/operations/reborn-menu-cyberpunk-transfer/ORDEM_DE_EXECUCAO_MENU_REBORN_CYBERPUNK.md +134 -0
  27. package/docs/operations/reborn-menu-cyberpunk-transfer/README_TRANSFERENCIA.md +84 -0
  28. package/docs/operations/reborn-menu-cyberpunk-transfer/README_TRANSFERENCIA_REBORN_PACKAGE.md +56 -0
  29. package/docs/operations/reborn-menu-cyberpunk-transfer/references/cyan-hud-frame-sheet.jpg +0 -0
  30. package/docs/operations/reborn-menu-cyberpunk-transfer/references/cyberpunk-pattern-sheet.jpg +0 -0
  31. package/docs/operations/reborn-menu-cyberpunk-transfer/references/fluid-workflow-windows.gif +0 -0
  32. package/docs/prompt-tasks/PROMPT_TASK_001_BOOTSTRAP_SKILL_MASTER_MCP.md +6 -0
  33. package/docs/prompt-tasks/PROMPT_TASK_002_AUTO_UPDATE_LAUNCHER.md +6 -0
  34. package/docs/prompt-tasks/PROMPT_TASK_003_REMOTE_MANIFEST_AND_RELEASES.md +6 -0
  35. package/docs/prompt-tasks/PROMPT_TASK_004_MULTI_USER_DISTRIBUTION.md +6 -0
  36. package/docs/prompt-tasks/PROMPT_TASK_005_SECURITY_AND_QUALITY_GATE.md +6 -0
  37. package/docs/prompt-tasks/PROMPT_TASK_006_MASTER_ACIONAMENTO_APRENDIZADO.md +83 -0
  38. package/docs/prompt-tasks/PROMPT_TASK_007_PERSONA_ORQUESTRADORA.md +88 -0
  39. package/docs/prompt-tasks/PROMPT_TASK_008_PROMPT_ROUTER_MODOS_ATIVACAO.md +156 -0
  40. package/docs/prompt-tasks/PROMPT_TASK_009_PIPELINE_APRENDIZADO_SUCESSO.md +105 -0
  41. package/docs/prompt-tasks/PROMPT_TASK_010_EVALS_GOVERNANCA_ATIVACAO.md +119 -0
  42. package/docs/prompt-tasks/PROMPT_TASK_011_MENU_NOTIFICACOES_NOTION.md +120 -0
  43. package/docs/prompt-tasks/PROMPT_TASK_012_MENU_CYBERPUNK_PIXEL_FRAME.md +123 -0
  44. package/docs/prompt-tasks/PROMPT_TASK_013_MENU_FLUID_DNA_ANIMATION.md +114 -0
  45. package/docs/prompt-tasks/PROMPT_TASK_014_MENU_FUNCTIONAL_PARITY_QA.md +157 -0
  46. package/docs/prompt-tasks/PROMPT_TASK_015_TRANSFER_RELEASE_HANDOFF.md +127 -0
  47. package/docs/prompt-tasks/PROMPT_TASK_016_CROSS_PLATFORM_MCP_AUTH_REGISTRATION.md +107 -0
  48. package/docs/prompt-tasks/PROMPT_TASK_018_NPM_PUBLISH_2FA_SETUP.md +80 -0
  49. package/docs/prompt-tasks/PROMPT_TASK_MASTER_EXECUTOR.md +6 -0
  50. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/SKILL.md +399 -0
  51. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/common-patterns.md +331 -0
  52. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/complete-examples.md +872 -0
  53. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/component-patterns.md +502 -0
  54. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/data-fetching.md +767 -0
  55. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/file-organization.md +502 -0
  56. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/loading-and-error-states.md +501 -0
  57. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/performance.md +406 -0
  58. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/routing-guide.md +364 -0
  59. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/styling-guide.md +428 -0
  60. package/docs/skill-candidates/v0.0.11/frontend-dev-guidelines/resources/typescript-standards.md +418 -0
  61. package/docs/skill-candidates/v0.0.11/git-version-control-ops/SKILL.md +34 -0
  62. package/docs/skill-candidates/v0.0.11/go-engineering/SKILL.md +34 -0
  63. package/docs/skill-candidates/v0.0.11/java-engineering/SKILL.md +34 -0
  64. package/docs/skill-candidates/v0.0.11/javascript-engineering/SKILL.md +34 -0
  65. package/docs/skill-candidates/v0.0.11/json-contract-design/SKILL.md +34 -0
  66. package/docs/skill-candidates/v0.0.11/multi-client-mcp-ops/SKILL.md +36 -0
  67. package/docs/skill-candidates/v0.0.11/nextjs/SKILL.md +745 -0
  68. package/docs/skill-candidates/v0.0.11/nextjs/agents/openai.yaml +3 -0
  69. package/docs/skill-candidates/v0.0.11/nextjs/references/app-router-files.md +94 -0
  70. package/docs/skill-candidates/v0.0.11/python-engineering/SKILL.md +34 -0
  71. package/docs/skill-candidates/v0.0.11/ruby-engineering/SKILL.md +34 -0
  72. package/docs/skill-candidates/v0.0.11/senior-fullstack/SKILL.md +209 -0
  73. package/docs/skill-candidates/v0.0.11/senior-fullstack/references/architecture_patterns.md +103 -0
  74. package/docs/skill-candidates/v0.0.11/senior-fullstack/references/development_workflows.md +103 -0
  75. package/docs/skill-candidates/v0.0.11/senior-fullstack/references/tech_stack_guide.md +103 -0
  76. package/docs/skill-candidates/v0.0.11/senior-fullstack/scripts/code_quality_analyzer.py +114 -0
  77. package/docs/skill-candidates/v0.0.11/senior-fullstack/scripts/fullstack_scaffolder.py +114 -0
  78. package/docs/skill-candidates/v0.0.11/senior-fullstack/scripts/project_scaffolder.py +114 -0
  79. package/docs/skill-candidates/v0.0.11/shadcn/SKILL.md +573 -0
  80. package/docs/skill-candidates/v0.0.11/shadcn/agents/openai.yaml +3 -0
  81. package/docs/skill-candidates/v0.0.11/sql-postgresql-engineering/SKILL.md +34 -0
  82. package/docs/skill-candidates/v0.0.11/terminal-shell-ops/SKILL.md +34 -0
  83. package/docs/skill-candidates/v0.0.11/typescript-expert/SKILL.md +429 -0
  84. package/docs/skill-candidates/v0.0.11/typescript-expert/references/tsconfig-strict.json +92 -0
  85. package/docs/skill-candidates/v0.0.11/typescript-expert/references/typescript-cheatsheet.md +383 -0
  86. package/docs/skill-candidates/v0.0.11/typescript-expert/references/utility-types.ts +335 -0
  87. package/docs/skill-candidates/v0.0.11/typescript-expert/scripts/ts_diagnostic.py +203 -0
  88. package/docs/skill-candidates/v0.0.11/ui-component-primitives/SKILL.md +34 -0
  89. package/docs/skill-candidates/v0.0.11/web-mobile-design-systems/SKILL.md +34 -0
  90. package/docs/skill-candidates/v0.0.11/windows-linux-platform-ops/SKILL.md +34 -0
  91. package/docs/skill-candidates/v0.0.12/csharp-senior-master-engineering/SKILL.md +32 -0
  92. package/docs/skill-candidates/v0.0.12/css-senior-master-engineering/SKILL.md +32 -0
  93. package/docs/skill-candidates/v0.0.12/go-senior-master-engineering/SKILL.md +32 -0
  94. package/docs/skill-candidates/v0.0.12/html-senior-master-engineering/SKILL.md +32 -0
  95. package/docs/skill-candidates/v0.0.12/javascript-senior-master-engineering/SKILL.md +32 -0
  96. package/docs/skill-candidates/v0.0.12/json-senior-master-engineering/SKILL.md +32 -0
  97. package/docs/skill-candidates/v0.0.12/python-senior-master-engineering/SKILL.md +32 -0
  98. package/docs/skill-candidates/v0.0.12/react-senior-master-engineering/SKILL.md +32 -0
  99. package/docs/skill-candidates/v0.0.12/ruby-senior-master-engineering/SKILL.md +32 -0
  100. package/docs/skill-candidates/v0.0.12/senior-master-code-optimizer/SKILL.md +48 -0
  101. package/docs/skill-candidates/v0.0.12/sql-senior-master-engineering/SKILL.md +31 -0
  102. package/docs/skill-candidates/v0.0.12/typescript-senior-master-engineering/SKILL.md +35 -0
  103. package/examples/client-configs/claude-code.commands.md +11 -7
  104. package/manifests/channels/beta.json +7 -7
  105. package/manifests/channels/stable.json +8 -8
  106. package/package.json +14 -2
  107. package/scripts/render-menu-evidence.mjs +130 -0
  108. package/scripts/verify-menu-actions.mjs +117 -0
@@ -7,10 +7,12 @@ import { fileURLToPath } from 'node:url';
7
7
  import {
8
8
  buildMenuCommands,
9
9
  formatActionHeader,
10
+ formatCyberConfirmFrame,
10
11
  formatCyberMenuFrame,
11
12
  formatHelp,
12
13
  formatRunningActionFrame,
13
14
  formatResultMessage,
15
+ formatSkillMasterIntroFrame,
14
16
  formatStatusReport,
15
17
  getMenuStatus,
16
18
  isInteractiveTerminal,
@@ -20,7 +22,8 @@ import {
20
22
 
21
23
  const currentFile = fileURLToPath(import.meta.url);
22
24
  const rootDir = dirname(dirname(currentFile));
23
- const commands = buildMenuCommands({ rootDir, currentFile });
25
+ const invocationCwd = process.cwd();
26
+ const commands = buildMenuCommands({ rootDir, currentFile, invocationCwd });
24
27
  readline.emitKeypressEvents(process.stdin);
25
28
  const CONTROL = {
26
29
  alternateScreenIn: '\x1b[?1049h',
@@ -100,7 +103,7 @@ async function runSelectedAction(action, { yes = false, useColor = false } = {})
100
103
  console.log('');
101
104
  console.log(formatActionHeader(action, { useColor }));
102
105
  console.log('');
103
- const code = await runCommand(action, { cwd: rootDir });
106
+ const code = await runCommand(action, { cwd: action.cwd ?? rootDir });
104
107
  console.log('');
105
108
  console.log(formatResultMessage(code, { useColor }));
106
109
  return { cancelled: false, code };
@@ -183,6 +186,13 @@ function writeStableFrame(frame, { clear = false } = {}) {
183
186
  ].join(''));
184
187
  }
185
188
 
189
+ function visualHudAllowed() {
190
+ return isInteractiveTerminal()
191
+ && process.env.SKILL_MASTER_NO_VISUAL !== '1'
192
+ && process.env.CI !== 'true'
193
+ && process.env.TERM !== 'dumb';
194
+ }
195
+
186
196
  function renderCyberMenu(selectedIndex, tick, { clear = false } = {}) {
187
197
  const status = getMenuStatus(rootDir);
188
198
  const frame = formatCyberMenuFrame(status, commands, selectedIndex, tick, {
@@ -203,8 +213,65 @@ function renderRunningAction(action, tick) {
203
213
  writeStableFrame(frame, { clear: true });
204
214
  }
205
215
 
216
+ function renderIntro(tick, message) {
217
+ const status = getMenuStatus(rootDir);
218
+ const frame = formatSkillMasterIntroFrame(status, tick, {
219
+ columns: process.stdout.columns ?? 120,
220
+ rows: process.stdout.rows ?? 32,
221
+ useColor: true,
222
+ message,
223
+ });
224
+ writeStableFrame(frame, { clear: tick === 0 });
225
+ }
226
+
227
+ function renderConfirmAction(action, tick) {
228
+ const status = getMenuStatus(rootDir);
229
+ const frame = formatCyberConfirmFrame(status, action, tick, {
230
+ columns: process.stdout.columns ?? 120,
231
+ rows: process.stdout.rows ?? 32,
232
+ useColor: true,
233
+ });
234
+ writeStableFrame(frame, { clear: tick === 0 });
235
+ }
236
+
206
237
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
207
238
 
239
+ async function animateIntro(message = 'skill_master assumindo o workflow', frames = 6) {
240
+ for (let frame = 0; frame < frames; frame += 1) {
241
+ renderIntro(frame, message);
242
+ await sleep(70);
243
+ }
244
+ }
245
+
246
+ async function animateRunningAction(action, startTick) {
247
+ for (let frame = 0; frame < 5; frame += 1) {
248
+ renderRunningAction(action, startTick + frame);
249
+ await sleep(90);
250
+ }
251
+ }
252
+
253
+ async function confirmVisualAction(action, startTick) {
254
+ let tick = startTick;
255
+ const reader = createKeyReader();
256
+
257
+ try {
258
+ while (true) {
259
+ renderConfirmAction(action, tick);
260
+ const key = await reader.read(180);
261
+ if (!key) {
262
+ tick += 1;
263
+ continue;
264
+ }
265
+
266
+ if (key.ctrl && key.name === 'c') return { confirmed: false, code: 130 };
267
+ if (key.name === 'return' || key.name === 'enter' || key.name === 'y') return { confirmed: true, code: 0 };
268
+ if (key.name === 'escape' || key.name === 'q' || key.name === 'n') return { confirmed: false, code: 0 };
269
+ }
270
+ } finally {
271
+ reader.close();
272
+ }
273
+ }
274
+
208
275
  async function waitForAnyKey(message = 'Pressione qualquer tecla para voltar ao menu...') {
209
276
  process.stdout.write(`\n${message}`);
210
277
  setRawInput(true);
@@ -225,6 +292,8 @@ async function runVisualMenu() {
225
292
  restoreMenuInput();
226
293
 
227
294
  try {
295
+ await animateIntro('skill_master online / carregando menu operacional', 7);
296
+
228
297
  while (true) {
229
298
  const columns = process.stdout.columns ?? 120;
230
299
  const rows = process.stdout.rows ?? 32;
@@ -233,7 +302,7 @@ async function runVisualMenu() {
233
302
  previousRows = rows;
234
303
  renderCyberMenu(selectedIndex, tick, { clear: needsClear || resized });
235
304
  needsClear = false;
236
- const key = await keyReader.read(240);
305
+ const key = await keyReader.read(160);
237
306
 
238
307
  if (!key) {
239
308
  tick += 1;
@@ -253,12 +322,20 @@ async function runVisualMenu() {
253
322
  if (key.name === 'return' || key.name === 'enter') {
254
323
  const action = commands[selectedIndex];
255
324
  keyReader.close();
256
- renderRunningAction(action, tick);
257
- await sleep(180);
325
+ if (action.confirmMessage) {
326
+ const confirmation = await confirmVisualAction(action, tick);
327
+ if (confirmation.code === 130) return 130;
328
+ if (!confirmation.confirmed) {
329
+ keyReader = createKeyReader();
330
+ needsClear = true;
331
+ continue;
332
+ }
333
+ }
334
+ await animateRunningAction(action, tick);
258
335
  setRawInput(false);
259
336
  leaveVisualScreen();
260
337
  try {
261
- const result = await runSelectedAction(action, { useColor: true });
338
+ const result = await runSelectedAction(action, { yes: Boolean(action.confirmMessage), useColor: true });
262
339
  lastCode = result.code;
263
340
  } catch (error) {
264
341
  const message = error instanceof Error ? error.message : String(error);
@@ -300,6 +377,14 @@ async function main() {
300
377
  }
301
378
 
302
379
  const action = commands.find((entry) => entry.key === actionKey);
380
+ if (visualHudAllowed()) {
381
+ enterVisualScreen();
382
+ try {
383
+ await animateIntro(`skill_master executando ${action.label}`, 6);
384
+ } finally {
385
+ leaveVisualScreen();
386
+ }
387
+ }
303
388
  const result = await runSelectedAction(action, { yes: args.yes, useColor: isInteractiveTerminal() });
304
389
  return result.code;
305
390
  }
@@ -1,7 +1,20 @@
1
1
  #!/usr/bin/env node
2
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { spawnSync } from 'node:child_process';
3
+ import { mkdirSync, writeFileSync } from 'node:fs';
3
4
  import os from 'node:os';
4
- import path from 'node:path';
5
+ import path, { dirname } from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+ import {
8
+ appendCodexServer,
9
+ buildClaudeCodeAddArgs,
10
+ buildCodexBlock,
11
+ buildJsonMcpSnippet,
12
+ buildSkillMasterServerConfig,
13
+ defaultClientConfigPaths,
14
+ formatShellCommand,
15
+ mergeMcpServer,
16
+ writeJson,
17
+ } from './lib/client-config.mjs';
5
18
 
6
19
  const args = process.argv.slice(2);
7
20
  const has = (flag) => args.includes(flag);
@@ -10,6 +23,9 @@ const readValue = (flag, fallback) => {
10
23
  return index >= 0 ? args[index + 1] : fallback;
11
24
  };
12
25
 
26
+ const here = dirname(fileURLToPath(import.meta.url));
27
+ const rootDir = dirname(here);
28
+
13
29
  if (has('--help')) {
14
30
  console.log(`Skill Master client registration
15
31
 
@@ -17,136 +33,87 @@ Uso:
17
33
  skill-master-register-clients --write-snippets
18
34
  skill-master-register-clients --apply-codex
19
35
  skill-master-register-clients --apply-claude
36
+ skill-master-register-clients --apply-claude-code
20
37
  skill-master-register-clients --apply-gemini
21
38
  skill-master-register-clients --apply-antigravity
22
39
  skill-master-register-clients --apply-all
23
40
  skill-master-register-clients --apply-codex --force
41
+ skill-master-register-clients --apply-claude-code --claude-code-scope user
42
+
43
+ Registra o servidor MCP skill_master como stdio usando Node absoluto e o
44
+ entrypoint absoluto do pacote. Isso evita falhas de PATH em Windows, Linux,
45
+ macOS e apps desktop que nao herdam o terminal.
24
46
 
25
- Registra o servidor MCP skill_master como stdio usando o binario global:
26
- skill-master-mcp
47
+ Para Claude Code, o registrador usa o CLI oficial: claude mcp add.
27
48
  `);
28
49
  process.exit(0);
29
50
  }
30
51
 
31
- const home = os.homedir();
32
- const snippetsDir = readValue('--snippets-dir', path.join(home, '.skill-master', 'client-configs'));
33
- const codexConfig = readValue('--codex-config', path.join(process.env.CODEX_HOME ?? path.join(home, '.codex'), 'config.toml'));
34
- const defaultClaudeConfig = () => {
35
- if (process.platform === 'win32') {
36
- return path.join(process.env.APPDATA ?? path.join(home, 'AppData', 'Roaming'), 'Claude', 'claude_desktop_config.json');
37
- }
38
- if (process.platform === 'darwin') {
39
- return path.join(home, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
40
- }
41
- return path.join(home, '.config', 'Claude', 'claude_desktop_config.json');
42
- };
43
- const claudeConfig = readValue('--claude-config', defaultClaudeConfig());
44
- const geminiConfig = readValue('--gemini-config', path.join(home, '.gemini', 'settings.json'));
45
- const antigravityConfig = readValue('--antigravity-config', path.join(home, '.gemini', 'config', 'mcp_config.json'));
52
+ const clientPaths = defaultClientConfigPaths();
53
+ const snippetsDir = readValue('--snippets-dir', path.join(process.env.SKILL_MASTER_HOME ?? path.join(os.homedir(), '.skill-master'), 'client-configs'));
54
+ const codexConfig = readValue('--codex-config', clientPaths.codex);
55
+ const claudeConfig = readValue('--claude-config', clientPaths.claude);
56
+ const geminiConfig = readValue('--gemini-config', clientPaths.gemini);
57
+ const antigravityConfig = readValue('--antigravity-config', clientPaths.antigravity);
58
+ const claudeCodeScope = readValue('--claude-code-scope', 'user');
59
+ const claudeCodeCommand = readValue('--claude-code-command', 'claude');
46
60
 
47
61
  const applyAll = has('--apply-all');
48
62
  const applyCodex = applyAll || has('--apply-codex');
49
63
  const applyClaude = applyAll || has('--apply-claude');
64
+ const applyClaudeCode = applyAll || has('--apply-claude-code');
50
65
  const applyGemini = applyAll || has('--apply-gemini');
51
66
  const applyAntigravity = applyAll || has('--apply-antigravity');
52
- const writeSnippets = applyAll || has('--write-snippets') || !(applyCodex || applyClaude || applyGemini || applyAntigravity);
67
+ const writeSnippets = applyAll || has('--write-snippets') || !(applyCodex || applyClaude || applyClaudeCode || applyGemini || applyAntigravity);
53
68
  const force = has('--force');
69
+ const serverConfig = buildSkillMasterServerConfig({ rootDir });
70
+ const codexBlock = buildCodexBlock(serverConfig);
71
+ const jsonSnippet = buildJsonMcpSnippet(serverConfig);
72
+ const claudeCodeArgs = buildClaudeCodeAddArgs(serverConfig, { scope: claudeCodeScope });
73
+
74
+ function commandExists(command) {
75
+ const lookup = process.platform === 'win32' ? 'where' : 'command';
76
+ const lookupArgs = process.platform === 'win32' ? [command] : ['-v', command];
77
+ const result = spawnSync(lookup, lookupArgs, { shell: process.platform !== 'win32', stdio: 'ignore' });
78
+ return result.status === 0;
79
+ }
54
80
 
55
- const mcpServer = {
56
- command: 'skill-master-mcp',
57
- args: [],
58
- env: {
59
- SKILL_MASTER_UPDATE_CHANNEL: 'stable',
60
- },
61
- };
62
-
63
- const codexBlock = `
64
- [mcp_servers.skill_master]
65
- command = "skill-master-mcp"
66
- startup_timeout_sec = 120
67
-
68
- [mcp_servers.skill_master.env]
69
- SKILL_MASTER_UPDATE_CHANNEL = "stable"
70
- `;
71
-
72
- const claudeSnippet = {
73
- mcpServers: {
74
- skill_master: mcpServer,
75
- },
76
- };
77
-
78
- const geminiSnippet = {
79
- mcpServers: {
80
- skill_master: mcpServer,
81
- },
82
- };
83
-
84
- const antigravitySnippet = {
85
- mcpServers: {
86
- skill_master: mcpServer,
87
- },
88
- };
89
-
90
- const ensureParent = (filePath) => mkdirSync(path.dirname(filePath), { recursive: true });
91
-
92
- const readJsonOrEmpty = (filePath) => {
93
- if (!existsSync(filePath)) return {};
94
- const raw = readFileSync(filePath, 'utf8').trim();
95
- return raw ? JSON.parse(raw) : {};
96
- };
97
-
98
- const writeJson = (filePath, value) => {
99
- ensureParent(filePath);
100
- writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
101
- };
81
+ function runClaudeCodeRegistration() {
82
+ if (!commandExists(claudeCodeCommand)) {
83
+ return {
84
+ ok: false,
85
+ skipped: true,
86
+ message: `Claude Code CLI '${claudeCodeCommand}' not found; run manually: ${formatShellCommand(claudeCodeCommand, claudeCodeArgs)}`,
87
+ };
88
+ }
102
89
 
103
- const mergeMcpServer = (filePath) => {
104
- const current = readJsonOrEmpty(filePath);
105
- const next = {
106
- ...current,
107
- mcpServers: {
108
- ...(current.mcpServers ?? {}),
109
- skill_master: mcpServer,
110
- },
90
+ const result = spawnSync(claudeCodeCommand, claudeCodeArgs, {
91
+ encoding: 'utf8',
92
+ stdio: ['ignore', 'pipe', 'pipe'],
93
+ });
94
+ const output = `${result.stdout ?? ''}${result.stderr ?? ''}`.trim();
95
+
96
+ return {
97
+ ok: result.status === 0,
98
+ skipped: false,
99
+ message: result.status === 0
100
+ ? `Claude Code registered via ${formatShellCommand(claudeCodeCommand, claudeCodeArgs)}`
101
+ : `Claude Code registration failed (${result.status ?? 'unknown'}): ${output || 'no output'}`,
111
102
  };
112
- writeJson(filePath, next);
113
- };
103
+ }
114
104
 
115
105
  const writeSnippetsFiles = () => {
116
106
  mkdirSync(snippetsDir, { recursive: true });
117
107
  writeFileSync(path.join(snippetsDir, 'codex.config.toml'), codexBlock.trimStart(), 'utf8');
118
- writeJson(path.join(snippetsDir, 'claude_desktop_config.skill_master.json'), claudeSnippet);
119
- writeJson(path.join(snippetsDir, 'gemini.settings.skill_master.json'), geminiSnippet);
120
- writeJson(path.join(snippetsDir, 'antigravity.mcp_config.skill_master.json'), antigravitySnippet);
121
- };
122
-
123
- const removeCodexBlock = (content) => {
124
- const lines = content.split('\n');
125
- const kept = [];
126
- let skipping = false;
127
- for (const line of lines) {
128
- const trimmed = line.trim();
129
- if (trimmed === '[mcp_servers.skill_master]') {
130
- skipping = true;
131
- continue;
132
- }
133
- if (skipping && trimmed.startsWith('[') && !trimmed.startsWith('[mcp_servers.skill_master')) {
134
- skipping = false;
135
- }
136
- if (!skipping) kept.push(line);
137
- }
138
- return kept.join('\n').trimEnd();
139
- };
140
-
141
- const appendCodex = () => {
142
- ensureParent(codexConfig);
143
- const current = existsSync(codexConfig) ? readFileSync(codexConfig, 'utf8') : '';
144
- if (current.includes('[mcp_servers.skill_master]') && !force) {
145
- return false;
146
- }
147
- const base = force ? removeCodexBlock(current) : current.trimEnd();
148
- writeFileSync(codexConfig, `${base}\n${codexBlock}`, 'utf8');
149
- return true;
108
+ writeJson(path.join(snippetsDir, 'claude_desktop_config.skill_master.json'), jsonSnippet);
109
+ writeFileSync(
110
+ path.join(snippetsDir, 'claude-code.commands.md'),
111
+ `# Claude Code - registrar skill_master\n\n\`\`\`bash\n${formatShellCommand(claudeCodeCommand, claudeCodeArgs)}\n\`\`\`\n\nDepois reinicie a sessao do Claude Code e valide com:\n\n\`\`\`bash\nclaude mcp list\n\`\`\`\n`,
112
+ 'utf8',
113
+ );
114
+ writeJson(path.join(snippetsDir, 'claude-code.project.mcp.json'), jsonSnippet);
115
+ writeJson(path.join(snippetsDir, 'gemini.settings.skill_master.json'), jsonSnippet);
116
+ writeJson(path.join(snippetsDir, 'antigravity.mcp_config.skill_master.json'), jsonSnippet);
150
117
  };
151
118
 
152
119
  const actions = [];
@@ -155,23 +122,32 @@ if (writeSnippets) {
155
122
  actions.push(`snippets written to ${snippetsDir}`);
156
123
  }
157
124
  if (applyCodex) {
158
- actions.push(appendCodex() ? `Codex registered at ${codexConfig}` : `Codex already had skill_master at ${codexConfig}`);
125
+ actions.push(appendCodexServer(codexConfig, serverConfig, { force }) ? `Codex registered at ${codexConfig}` : `Codex already had skill_master at ${codexConfig}`);
159
126
  }
160
127
  if (applyClaude) {
161
- mergeMcpServer(claudeConfig);
162
- actions.push(`Claude config merged at ${claudeConfig}`);
128
+ mergeMcpServer(claudeConfig, serverConfig, { recoverInvalidJson: force });
129
+ actions.push(`Claude Desktop config merged at ${claudeConfig}`);
130
+ }
131
+ if (applyClaudeCode) {
132
+ const result = runClaudeCodeRegistration();
133
+ actions.push(result.message);
134
+ if (!result.ok && !result.skipped) {
135
+ process.exitCode = 1;
136
+ }
163
137
  }
164
138
  if (applyGemini) {
165
- mergeMcpServer(geminiConfig);
139
+ mergeMcpServer(geminiConfig, serverConfig, { recoverInvalidJson: force });
166
140
  actions.push(`Gemini config merged at ${geminiConfig}`);
167
141
  }
168
142
  if (applyAntigravity) {
169
- mergeMcpServer(antigravityConfig);
143
+ mergeMcpServer(antigravityConfig, serverConfig, { recoverInvalidJson: force });
170
144
  actions.push(`Antigravity config merged at ${antigravityConfig}`);
171
145
  }
172
146
 
173
147
  console.log('[skill_master] Client registration');
148
+ console.log(`- Command: ${serverConfig.command}`);
149
+ console.log(`- Args: ${serverConfig.args.join(' ')}`);
174
150
  for (const action of actions) {
175
151
  console.log(`- ${action}`);
176
152
  }
177
- console.log('- Restart Codex, Claude, Gemini or Antigravity after registration.');
153
+ console.log('- Restart Codex, Claude Desktop, Claude Code, Gemini or Antigravity after registration.');
@@ -0,0 +1,262 @@
1
+ # Guia Multi-Computador - Skill Master MCP
2
+
3
+ ## 1. Cenario
4
+
5
+ Voce quer usar o mesmo MCP `skill_master` em varios computadores, mantendo todos atualizados e escolhendo o canal de distribuicao mais adequado para cada caso.
6
+
7
+ ## 2. Modelos recomendados
8
+
9
+ Hoje existem dois caminhos validos.
10
+
11
+ ### Modelo A - npm publico
12
+
13
+ Melhor para:
14
+
15
+ - notebooks novos
16
+ - instalacao rapida
17
+ - ambientes onde o usuario nao deve clonar o repositorio
18
+ - Claude, Codex, Gemini e clientes MCP que aceitam `npx`
19
+
20
+ Base:
21
+
22
+ ```bash
23
+ npx -y @fprad0/skill-master-mcp@latest
24
+ ```
25
+
26
+ ### Modelo B - clone + launcher com auto-update
27
+
28
+ Melhor para:
29
+
30
+ - desenvolvimento do MCP
31
+ - testes de `stable` e `beta`
32
+ - controle maior sobre manifests e atualizacao por `git pull --ff-only`
33
+ - ambiente com repo privado
34
+
35
+ Cada computador tera:
36
+
37
+ - clone local do repositorio
38
+ - `git`
39
+ - `node` 18+
40
+ - launcher configurado no cliente MCP
41
+ - configuracao local em `%USERPROFILE%/.skill-master/config.json` no Windows ou `~/.skill-master/config.json` no Linux/macOS
42
+
43
+ Quando o menu detectar que o computador ainda nao esta globalmente pronto, ele vai mostrar o caminho de bootstrap e o atalho unico:
44
+
45
+ ```bash
46
+ skill-master-bootstrap-global
47
+ ```
48
+
49
+ Esse atalho instala as skills globais embutidas e registra Codex, Claude, Gemini e Antigravity para reconhecer `skill_master` como parte do sistema no cliente local.
50
+
51
+ A partir da correcao multi-OS, o registro recomendado nao depende mais de `PATH`. O registrador grava o Node.js absoluto como `command` e o `bin/skill-master.mjs` absoluto como `args`, o que evita falhas comuns em Windows, Linux, macOS e apps desktop que nao herdam o ambiente do terminal.
52
+
53
+ A partir da preparacao `0.0.11`, use o doctor para validar notebooks novos:
54
+
55
+ ```bash
56
+ skill-master-menu --run doctor
57
+ skill-master-menu --run bootstrap-global --yes
58
+ ```
59
+
60
+ O bootstrap registra Codex, Claude Desktop, Claude Code, Gemini CLI e Antigravity quando os clientes locais estiverem disponiveis. Depois, reinicie os clientes.
61
+
62
+ ## 3. Instalacao em um novo computador
63
+
64
+ ### 3.1 Opcao rapida - npm publico
65
+
66
+ #### Windows
67
+
68
+ ```powershell
69
+ npm install -g @fprad0/skill-master-mcp
70
+ ```
71
+
72
+ ou:
73
+
74
+ ```powershell
75
+ npx -y @fprad0/skill-master-mcp@latest
76
+ ```
77
+
78
+ #### Linux / macOS
79
+
80
+ ```bash
81
+ npm install -g @fprad0/skill-master-mcp
82
+ ```
83
+
84
+ ou:
85
+
86
+ ```bash
87
+ npx -y @fprad0/skill-master-mcp@latest
88
+ ```
89
+
90
+ ### 3.2 Opcao controlada - clone local
91
+
92
+ Pre-requisitos:
93
+
94
+ - Git instalado
95
+ - Node.js 18+
96
+ - acesso ao GitHub
97
+ - token read-only se o repositorio for privado
98
+
99
+ Clone:
100
+
101
+ ```powershell
102
+ cd C:\Users\CDT\Documents
103
+ git clone https://github.com/FPrad0/skill-master-mcp.git
104
+ cd skill-master-mcp
105
+ npm ci
106
+ npm run build
107
+ ```
108
+
109
+ ### 3.3 Configuracao local
110
+
111
+ Criar:
112
+
113
+ ```text
114
+ C:\Users\<USER>\.skill-master\config.json
115
+ ```
116
+
117
+ Modelo:
118
+
119
+ ```json
120
+ {
121
+ "channel": "stable",
122
+ "autoUpdate": true,
123
+ "updateMode": "on-start",
124
+ "manifestUrl": "https://raw.githubusercontent.com/FPrad0/skill-master-mcp/main/manifests/channels/stable.json",
125
+ "localSkillRoots": [
126
+ "%USERPROFILE%/.codex/skills",
127
+ "%USERPROFILE%/.agents/skills"
128
+ ],
129
+ "allowWebSources": false
130
+ }
131
+ ```
132
+
133
+ ## 4. Configuracao do cliente MCP
134
+
135
+ No modelo npm global, prefira sempre o registro gerado por `skill-master-register-clients`, porque ele usa caminhos absolutos.
136
+
137
+ No modelo clone + launcher, o launcher ainda e valido para desenvolvimento, mas o doctor vai classificar esse modo como `launcher`, nao como o modo robusto de pacote global.
138
+
139
+ Exemplo robusto para Codex:
140
+
141
+ ```toml
142
+ [mcp_servers.skill_master]
143
+ command = "C:\\Program Files\\nodejs\\node.exe"
144
+ args = ["C:\\Users\\CDT\\AppData\\Roaming\\npm\\node_modules\\@fprad0\\skill-master-mcp\\bin\\skill-master.mjs"]
145
+ startup_timeout_sec = 120
146
+
147
+ [mcp_servers.skill_master.env]
148
+ SKILL_MASTER_UPDATE_CHANNEL = "stable"
149
+ ```
150
+
151
+ Para migrar clientes antigos que ainda usam `skill-master-mcp` via PATH, `npx` ou launcher local:
152
+
153
+ ```bash
154
+ skill-master-register-clients --apply-all --force
155
+ skill-master-menu --run doctor
156
+ ```
157
+
158
+ Para corrigir apenas o Claude Code depois de `npm install -g @fprad0/skill-master-mcp`, use:
159
+
160
+ ```bash
161
+ skill-master-register-clients --apply-claude-code --claude-code-scope user
162
+ claude mcp list
163
+ ```
164
+
165
+ No modelo clone + launcher, o cliente deve chamar o launcher, nao o `dist/index.js` diretamente.
166
+
167
+ Exemplo conceitual:
168
+
169
+ ```json
170
+ {
171
+ "mcpServers": {
172
+ "skill_master": {
173
+ "command": "powershell",
174
+ "args": [
175
+ "-ExecutionPolicy",
176
+ "Bypass",
177
+ "-File",
178
+ "C:/Users/CDT/Documents/skill-master-mcp/scripts/skill-master-launcher.ps1"
179
+ ]
180
+ }
181
+ }
182
+ }
183
+ ```
184
+
185
+ ## 5. Como as atualizacoes chegam automaticamente
186
+
187
+ ### No modelo npm publico
188
+
189
+ 1. voce publica uma nova versao no npm publico
190
+ 2. a maquina cliente usa `@latest`
191
+ 3. no proximo `npx` ou reinstalacao, a nova versao e resolvida
192
+
193
+ ### No modelo clone + launcher
194
+
195
+ 1. voce faz push no GitHub
196
+ 2. voce atualiza o manifesto `stable.json`
197
+ 3. o usuario abre o MCP
198
+ 4. o launcher verifica o manifesto
199
+ 5. se houver versao nova e o clone local estiver limpo, o launcher atualiza
200
+ 6. o MCP inicia ja atualizado
201
+
202
+ ## 6. Como publicar uma atualizacao
203
+
204
+ ### Caminho publico
205
+
206
+ 1. subir a nova versao para `main`
207
+ 2. publicar pelo workflow `Publish Skill Master to npmjs` somente com autorizacao explicita
208
+ 3. validar com `npm view` e `npx`
209
+
210
+ ### Caminho clone + launcher
211
+
212
+ 1. subir a nova versao para `main`
213
+ 2. atualizar `manifests/channels/stable.json`
214
+ 3. criar release/tag quando fizer sentido
215
+ 4. deixar o launcher puxar com `git pull --ff-only`
216
+
217
+ ## 7. Estrategia para varios usuarios
218
+
219
+ ### Poucos usuarios tecnicos
220
+
221
+ Use clone privado + launcher. E a rota com mais controle.
222
+
223
+ ### Varios usuarios sem perfil tecnico
224
+
225
+ Use npm publico.
226
+
227
+ Se for necessario restringir distribuicao, evolua para:
228
+
229
+ - pacote npm privado
230
+ - instalador/bundle
231
+ - MCP remoto
232
+
233
+ ### Equipe ou empresa
234
+
235
+ Evoluir para:
236
+
237
+ - GitHub Packages com permissoes
238
+ - workflow de release
239
+ - canais `stable` e `beta`
240
+ - logs e telemetria minima opcional
241
+
242
+ ## 8. Manutencao
243
+
244
+ Recomendacoes:
245
+
246
+ - manter changelog curto
247
+ - nunca quebrar configuracao sem migracao
248
+ - testar update em uma maquina secundaria antes de mover `stable`
249
+ - manter `beta` para validacao
250
+ - documentar breaking changes
251
+
252
+ ## 9. Quando nao atualizar automaticamente
253
+
254
+ No modelo clone + launcher, o updater deve recusar atualizacao automatica quando:
255
+
256
+ - houver arquivos locais modificados
257
+ - o GitHub estiver inacessivel
258
+ - o manifesto estiver invalido
259
+ - houver mudanca de versao de Node nao atendida
260
+ - `npm ci` ou build falhar
261
+
262
+ Nesses casos, ele deve iniciar a versao local e mostrar o status pela ferramenta `skill_master_sync_status`.