@fprad0/skill-master-mcp 0.0.6 → 0.0.8

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.
@@ -0,0 +1,163 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import process from 'node:process';
7
+ import { fileURLToPath } from 'node:url';
8
+ import prompts from 'prompts';
9
+ import { buildCatalog, loadConfig } from '../dist/catalog.js';
10
+ import { routeSkillMasterPrompt } from '../dist/prompt-router.js';
11
+
12
+ const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
13
+ const skillMasterHome = process.env.SKILL_MASTER_HOME ?? path.join(os.homedir(), '.skill-master');
14
+ const activationConfigPath = process.env.SKILL_MASTER_ACTIVATION_CONFIG
15
+ ?? path.join(skillMasterHome, 'activation.json');
16
+ const sourcesConfigPath = process.env.SKILL_MASTER_CONFIG ?? path.join(rootDir, 'sources.json');
17
+ const workspace = process.env.SKILL_MASTER_WORKSPACE ?? process.cwd();
18
+
19
+ const defaultConfig = {
20
+ activationMode: 'balanced',
21
+ riskTolerance: 'normal',
22
+ };
23
+
24
+ const isTty = () => Boolean(process.stdin.isTTY && process.stdout.isTTY);
25
+
26
+ const readActivationConfig = () => {
27
+ if (!existsSync(activationConfigPath)) return defaultConfig;
28
+ return { ...defaultConfig, ...JSON.parse(readFileSync(activationConfigPath, 'utf8')) };
29
+ };
30
+
31
+ const writeActivationConfig = (config) => {
32
+ mkdirSync(path.dirname(activationConfigPath), { recursive: true });
33
+ writeFileSync(activationConfigPath, JSON.stringify(config, null, 2), 'utf8');
34
+ };
35
+
36
+ const parseArgs = (argv) => {
37
+ const parsed = {
38
+ command: 'status',
39
+ mode: null,
40
+ prompt: null,
41
+ format: 'markdown',
42
+ };
43
+
44
+ for (let index = 0; index < argv.length; index += 1) {
45
+ const arg = argv[index];
46
+ if (arg === '--status') parsed.command = 'status';
47
+ else if (arg === '--set-mode') {
48
+ parsed.command = 'set-mode';
49
+ parsed.mode = argv[++index] ?? null;
50
+ } else if (arg === '--route-prompt') {
51
+ parsed.command = 'route-prompt';
52
+ parsed.prompt = argv[++index] ?? null;
53
+ } else if (arg === '--route-prompt-interactive') parsed.command = 'route-prompt-interactive';
54
+ else if (arg === '--notion-summary') parsed.command = 'notion-summary';
55
+ else if (arg === '--json') parsed.format = 'json';
56
+ else if (arg === '--help' || arg === '-h') parsed.command = 'help';
57
+ else throw new Error(`Unknown argument: ${arg}`);
58
+ }
59
+
60
+ return parsed;
61
+ };
62
+
63
+ const loadSkills = async () => {
64
+ const config = await loadConfig(sourcesConfigPath);
65
+ const catalog = await buildCatalog(config, { cwd: workspace, includeWeb: false });
66
+ return catalog.skills;
67
+ };
68
+
69
+ const routePrompt = async (prompt, { format }) => {
70
+ if (!prompt) throw new Error('Missing prompt.');
71
+ const config = readActivationConfig();
72
+ const skills = await loadSkills();
73
+ const decision = routeSkillMasterPrompt(skills, prompt, {
74
+ activationMode: config.activationMode,
75
+ riskTolerance: config.riskTolerance,
76
+ limit: 8,
77
+ sourceKinds: ['workspace', 'local', 'plugin'],
78
+ });
79
+
80
+ if (format === 'json') {
81
+ console.log(JSON.stringify(decision, null, 2));
82
+ return;
83
+ }
84
+
85
+ console.log('Skill Master - prompt router');
86
+ console.log(`- Ativar: ${decision.shouldActivate ? 'sim' : 'nao'}`);
87
+ console.log(`- Modo: ${decision.activationMode}`);
88
+ console.log(`- Execucao: ${decision.executionMode}`);
89
+ console.log(`- Persona: ${decision.personaOverlay}`);
90
+ console.log(`- Proxima acao: ${decision.nextAction}`);
91
+ if (decision.recommendedSkills.length > 0) {
92
+ console.log('- Skills:');
93
+ for (const skill of decision.recommendedSkills.slice(0, 5)) {
94
+ console.log(` - ${skill.name} (${skill.source})`);
95
+ }
96
+ }
97
+ };
98
+
99
+ const printStatus = () => {
100
+ const config = readActivationConfig();
101
+ console.log('Skill Master - modo de ativacao');
102
+ console.log(`- Config: ${activationConfigPath}`);
103
+ console.log(`- Modo: ${config.activationMode}`);
104
+ console.log(`- Tolerancia de risco: ${config.riskTolerance}`);
105
+ console.log(`- Workspace: ${workspace}`);
106
+ };
107
+
108
+ const setMode = (mode) => {
109
+ if (!['manual', 'balanced', 'always-on-assisted'].includes(mode)) {
110
+ throw new Error('Mode must be manual, balanced, or always-on-assisted.');
111
+ }
112
+ const config = { ...readActivationConfig(), activationMode: mode };
113
+ writeActivationConfig(config);
114
+ console.log(`Modo de ativacao atualizado para: ${mode}`);
115
+ };
116
+
117
+ const routePromptInteractive = async (format) => {
118
+ if (!isTty()) throw new Error('Interactive prompt routing requires a TTY.');
119
+ const response = await prompts({
120
+ type: 'text',
121
+ name: 'prompt',
122
+ message: 'Qual prompt voce quer avaliar?',
123
+ validate: (value) => value.trim().length >= 3 || 'Informe ao menos 3 caracteres.',
124
+ });
125
+ await routePrompt(response.prompt, { format });
126
+ };
127
+
128
+ const printNotionSummary = () => {
129
+ const config = readActivationConfig();
130
+ console.log('## Resumo para Notion - Skill Master Activation');
131
+ console.log('');
132
+ console.log(`- Modo atual: ${config.activationMode}`);
133
+ console.log(`- Tolerancia de risco: ${config.riskTolerance}`);
134
+ console.log(`- Workspace: ${workspace}`);
135
+ console.log('- Politica: nenhuma publicacao sem autorizacao explicita.');
136
+ console.log('- Recomendacao: registrar mudancas de modo, decisoes de skills aprendidas e releases pendentes no ledger do Skill Master.');
137
+ };
138
+
139
+ const printHelp = () => {
140
+ console.log(`Skill Master Activation
141
+
142
+ Usage:
143
+ skill-master-activation --status
144
+ skill-master-activation --set-mode manual|balanced|always-on-assisted
145
+ skill-master-activation --route-prompt "prompt" [--json]
146
+ skill-master-activation --route-prompt-interactive
147
+ skill-master-activation --notion-summary
148
+ `);
149
+ };
150
+
151
+ try {
152
+ const args = parseArgs(process.argv.slice(2));
153
+ if (args.command === 'help') printHelp();
154
+ else if (args.command === 'status') printStatus();
155
+ else if (args.command === 'set-mode') setMode(args.mode);
156
+ else if (args.command === 'route-prompt') await routePrompt(args.prompt, { format: args.format });
157
+ else if (args.command === 'route-prompt-interactive') await routePromptInteractive(args.format);
158
+ else if (args.command === 'notion-summary') printNotionSummary();
159
+ else throw new Error(`Unsupported command: ${args.command}`);
160
+ } catch (error) {
161
+ process.stderr.write(`[skill_master] ${error instanceof Error ? error.message : String(error)}\n`);
162
+ process.exitCode = 1;
163
+ }
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { runActivationEvals } from '../dist/activation-evals.js';
4
+
5
+ const report = runActivationEvals();
6
+ const asJson = process.argv.includes('--json');
7
+
8
+ if (asJson) {
9
+ console.log(JSON.stringify(report, null, 2));
10
+ } else {
11
+ console.log('Skill Master Activation Evals');
12
+ console.log(`- Generated at: ${report.generatedAt}`);
13
+ console.log(`- Total: ${report.total}`);
14
+ console.log(`- Passed: ${report.passed}`);
15
+ console.log(`- Failed: ${report.failed}`);
16
+ console.log(`- Accuracy: ${(report.accuracy * 100).toFixed(1)}%`);
17
+ console.log(`- False positives: ${report.falsePositives}`);
18
+ console.log(`- False negatives: ${report.falseNegatives}`);
19
+ console.log(`- Correct blocks: ${report.correctBlocks}`);
20
+ console.log(`- Recommendation: ${report.recommendation}`);
21
+
22
+ const failed = report.results.filter((result) => !result.passed);
23
+ if (failed.length > 0) {
24
+ console.log('');
25
+ console.log('Failed cases:');
26
+ for (const result of failed) {
27
+ console.log(`- ${result.case.id}: ${result.findings.join('; ')}`);
28
+ }
29
+ }
30
+ }
31
+
32
+ process.exitCode = report.recommendation === 'balanced-ready' ? 0 : 1;
@@ -1,179 +1,172 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { spawn } from 'node:child_process';
4
- import { existsSync, readFileSync } from 'node:fs';
5
- import { dirname, join } from 'node:path';
3
+ import prompts from 'prompts';
4
+ import { dirname } from 'node:path';
6
5
  import { fileURLToPath } from 'node:url';
7
- import { createInterface } from 'node:readline/promises';
8
- import { stdin as input, stdout as output } from 'node:process';
9
-
10
- const rootDir = dirname(dirname(fileURLToPath(import.meta.url)));
11
-
12
- const commands = {
13
- status: {
14
- label: 'Mostrar status local',
15
- command: process.execPath,
16
- args: [fileURLToPath(import.meta.url), '--status'],
17
- },
18
- check: {
19
- label: 'Rodar gate completo',
20
- command: 'npm',
21
- args: ['run', 'check'],
22
- },
23
- build: {
24
- label: 'Rodar build',
25
- command: 'npm',
26
- args: ['run', 'build'],
27
- },
28
- publicNpm: {
29
- label: 'Validar pacote no npm publico',
30
- command: 'npm',
31
- args: [
32
- 'view',
33
- '@fprad0/skill-master-mcp',
34
- 'version',
35
- '--registry=https://registry.npmjs.org',
36
- ],
37
- },
38
- updateGlobal: {
39
- label: 'Atualizar pacote global via npm',
40
- command: process.execPath,
41
- args: [join(rootDir, 'bin', 'skill-master-update.mjs')],
42
- },
43
- privateRegistry: {
44
- label: 'Configurar registry privado GitHub Packages',
45
- command: process.execPath,
46
- args: ['scripts/configure-private-registry.mjs', '--validate'],
47
- },
48
- };
49
-
50
- const menuItems = [
51
- ['1', commands.status],
52
- ['2', commands.check],
53
- ['3', commands.build],
54
- ['4', commands.publicNpm],
55
- ['5', commands.updateGlobal],
56
- ['6', commands.privateRegistry],
57
- ];
58
-
59
- function readJson(relativePath) {
60
- const target = join(rootDir, relativePath);
61
- if (!existsSync(target)) {
62
- return null;
6
+ import {
7
+ buildMenuChoices,
8
+ buildMenuCommands,
9
+ formatActionHeader,
10
+ formatHelp,
11
+ formatMenuBanner,
12
+ formatResultMessage,
13
+ formatStatusReport,
14
+ getMenuStatus,
15
+ isInteractiveTerminal,
16
+ resolveActionKey,
17
+ runCommand,
18
+ } from './lib/menu-core.mjs';
19
+
20
+ const currentFile = fileURLToPath(import.meta.url);
21
+ const rootDir = dirname(dirname(currentFile));
22
+ const commands = buildMenuCommands({ rootDir, currentFile });
23
+
24
+ function parseArgs(argv) {
25
+ const parsed = {
26
+ help: false,
27
+ status: false,
28
+ yes: false,
29
+ run: null,
30
+ };
31
+
32
+ for (let i = 0; i < argv.length; i += 1) {
33
+ const arg = argv[i];
34
+ switch (arg) {
35
+ case '--help':
36
+ case '-h':
37
+ parsed.help = true;
38
+ break;
39
+ case '--status':
40
+ parsed.status = true;
41
+ break;
42
+ case '--yes':
43
+ parsed.yes = true;
44
+ break;
45
+ case '--run':
46
+ parsed.run = argv[++i] ?? null;
47
+ break;
48
+ default:
49
+ throw new Error(`Unknown argument: ${arg}`);
50
+ }
63
51
  }
64
52
 
65
- return JSON.parse(readFileSync(target, 'utf8'));
53
+ return parsed;
66
54
  }
67
55
 
68
- function readText(relativePath) {
69
- const target = join(rootDir, relativePath);
70
- if (!existsSync(target)) {
71
- return null;
72
- }
56
+ function printHelp() {
57
+ console.log(formatHelp(commands));
58
+ }
73
59
 
74
- return readFileSync(target, 'utf8').trim();
60
+ function printStatus() {
61
+ console.log(formatStatusReport(getMenuStatus(rootDir)));
75
62
  }
76
63
 
77
- function printHelp() {
78
- console.log(`Skill Master Menu
64
+ async function runSelectedAction(action, { yes = false, useColor = false } = {}) {
65
+ if (action.confirmMessage && !yes) {
66
+ if (!isInteractiveTerminal()) {
67
+ throw new Error(`Action ${action.key} requires --yes outside an interactive terminal.`);
68
+ }
79
69
 
80
- Uso:
81
- skill-master-menu
82
- skill-master-menu --status
83
- skill-master-menu --help
70
+ const confirmation = await prompts({
71
+ type: 'confirm',
72
+ name: 'confirmed',
73
+ message: action.confirmMessage,
74
+ initial: false,
75
+ });
84
76
 
85
- Opcao 5 do menu:
86
- atualiza o pacote global com npm install -g @fprad0/skill-master-mcp@latest
77
+ if (!confirmation.confirmed) {
78
+ return { cancelled: true, code: 0 };
79
+ }
80
+ }
87
81
 
88
- O comando de menu e voltado para operacao humana. O binario MCP stdio continua sendo:
89
- skill-master-mcp
90
- `);
82
+ console.log('');
83
+ console.log(formatActionHeader(action, { useColor }));
84
+ console.log('');
85
+ const code = await runCommand(action, { cwd: rootDir });
86
+ console.log('');
87
+ console.log(formatResultMessage(code, { useColor }));
88
+ return { cancelled: false, code };
91
89
  }
92
90
 
93
- function printStatus() {
94
- const packageJson = readJson('package.json');
95
- const stableManifest = readJson('manifests/channels/stable.json');
96
- const versionText = readText('VERSION.md');
97
-
98
- console.log('Skill Master MCP - status local');
99
- console.log(`Diretorio: ${rootDir}`);
100
- console.log(`Pacote: ${packageJson?.name ?? 'nao encontrado'}`);
101
- console.log(`Semver local: ${packageJson?.version ?? 'nao encontrado'}`);
102
- console.log(`Manifesto stable: ${stableManifest?.version ?? 'nao encontrado'}`);
103
- console.log(`Manifesto semver: ${stableManifest?.semver ?? 'nao encontrado'}`);
104
-
105
- if (versionText) {
106
- const firstLines = versionText.split('\n').slice(0, 6).join('\n');
107
- console.log('\nVERSION.md:');
108
- console.log(firstLines);
109
- }
110
- }
91
+ async function runVisualMenu() {
92
+ while (true) {
93
+ console.clear();
94
+ const status = getMenuStatus(rootDir);
95
+ console.log(formatMenuBanner(status, { useColor: true }));
96
+ console.log('');
97
+
98
+ const selection = await prompts({
99
+ type: 'select',
100
+ name: 'action',
101
+ message: 'Escolha uma acao',
102
+ choices: buildMenuChoices(commands),
103
+ initial: 0,
104
+ });
111
105
 
112
- function runCommand(commandConfig) {
113
- return new Promise((resolve) => {
114
- console.log(`\n> ${commandConfig.command} ${commandConfig.args.join(' ')}\n`);
106
+ if (!selection.action || selection.action === '__exit__') {
107
+ return 0;
108
+ }
115
109
 
116
- const child = spawn(commandConfig.command, commandConfig.args, {
117
- cwd: rootDir,
118
- env: process.env,
119
- shell: process.platform === 'win32',
120
- stdio: 'inherit',
121
- });
110
+ const action = commands.find((entry) => entry.key === selection.action);
111
+ if (!action) {
112
+ return 1;
113
+ }
122
114
 
123
- child.on('close', (code) => {
124
- resolve(code ?? 1);
115
+ const result = await runSelectedAction(action, { useColor: true });
116
+ if (result.cancelled) {
117
+ continue;
118
+ }
119
+
120
+ const nextStep = await prompts({
121
+ type: 'toggle',
122
+ name: 'keepGoing',
123
+ message: 'Voltar ao menu?',
124
+ initial: true,
125
+ active: 'sim',
126
+ inactive: 'nao',
125
127
  });
126
- });
127
- }
128
128
 
129
- async function runInteractiveMenu() {
130
- const rl = createInterface({ input, output });
131
-
132
- try {
133
- while (true) {
134
- console.log('\nSkill Master Menu');
135
- for (const [key, item] of menuItems) {
136
- console.log(` ${key}. ${item.label}`);
137
- }
138
- console.log(' 0. Sair');
139
-
140
- const answer = (await rl.question('\nEscolha uma opcao: ')).trim();
141
-
142
- if (answer === '0' || answer.toLowerCase() === 'q') {
143
- return 0;
144
- }
145
-
146
- const selected = menuItems.find(([key]) => key === answer)?.[1];
147
- if (!selected) {
148
- console.log('Opcao invalida.');
149
- continue;
150
- }
151
-
152
- const code = await runCommand(selected);
153
- if (code !== 0) {
154
- console.log(`\nComando terminou com codigo ${code}.`);
155
- }
129
+ if (!nextStep.keepGoing) {
130
+ return result.code;
156
131
  }
157
- } finally {
158
- rl.close();
159
132
  }
160
133
  }
161
134
 
162
135
  async function main() {
163
- const args = process.argv.slice(2);
136
+ const args = parseArgs(process.argv.slice(2));
164
137
 
165
- if (args.includes('--help') || args.includes('-h')) {
138
+ if (args.help) {
166
139
  printHelp();
167
140
  return 0;
168
141
  }
169
142
 
170
- if (args.includes('--status')) {
143
+ if (args.status) {
171
144
  printStatus();
172
145
  return 0;
173
146
  }
174
147
 
175
- return runInteractiveMenu();
148
+ if (args.run) {
149
+ const actionKey = resolveActionKey(args.run, commands);
150
+ if (!actionKey) {
151
+ throw new Error(`Unknown action: ${args.run}`);
152
+ }
153
+
154
+ const action = commands.find((entry) => entry.key === actionKey);
155
+ const result = await runSelectedAction(action, { yes: args.yes, useColor: isInteractiveTerminal() });
156
+ return result.code;
157
+ }
158
+
159
+ if (!isInteractiveTerminal()) {
160
+ throw new Error('Interactive menu requires a TTY. Use --run <acao> or --status in automated environments.');
161
+ }
162
+
163
+ return runVisualMenu();
176
164
  }
177
165
 
178
- const exitCode = await main();
179
- process.exitCode = exitCode;
166
+ try {
167
+ process.exitCode = await main();
168
+ } catch (error) {
169
+ const message = error instanceof Error ? error.message : String(error);
170
+ process.stderr.write(`[skill_master] ${message}\n`);
171
+ process.exitCode = 1;
172
+ }