wizz-method 2.11.2 → 2.13.0

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 (83) hide show
  1. package/README.md +2 -1
  2. package/package.json +1 -1
  3. package/skills-registry.yaml +38 -29
  4. package/src/bmm-skills/module.yaml +1 -1
  5. package/src/modules/lowticket/skills/wizz-lowticket-metodologia/knowledge/04-criativos.md +1 -1
  6. package/src/modules/lowticket/skills/wizz-lowticket-metodologia/knowledge/11-checklist-roi.md +1 -1
  7. package/src/modules/lowticket/skills/wizz-lowticket-metodologia/knowledge/12-mineracao-ofertas.md +1 -1
  8. package/src/modules/lowticket/skills/wizz-lowticket-metodologia/knowledge/pontos-em-aberto.md +2 -2
  9. package/src/modules/wizz/_shared/video-pipeline.md +12 -4
  10. package/src/modules/wizz/agents/wizz-designer/SKILL.md +1 -1
  11. package/src/modules/wizz/agents/wizz-maestro/SKILL.md +1 -1
  12. package/src/modules/wizz/agents/wizz-social/SKILL.md +1 -0
  13. package/src/skills-lib/wizz-remotion-best-practices/rules/sfx.md +3 -1
  14. package/src/skills-lib/wizz-router/references/routing-table-flat.md +3 -2
  15. package/src/skills-lib/wizz-video-effects/SKILL.md +85 -0
  16. package/src/skills-lib/wizz-video-effects/assets/effects.config.template.json +178 -0
  17. package/src/skills-lib/wizz-video-effects/assets/remotion/EffectsLayer.tsx +87 -0
  18. package/src/skills-lib/wizz-video-effects/assets/remotion/README.md +37 -0
  19. package/src/skills-lib/wizz-video-effects/assets/remotion/useEffects.ts +120 -0
  20. package/src/skills-lib/wizz-video-effects/assets/sfx/CREDITS.md +56 -0
  21. package/src/skills-lib/wizz-video-effects/assets/sfx/cash.mp3 +0 -0
  22. package/src/skills-lib/wizz-video-effects/assets/sfx/chime.mp3 +0 -0
  23. package/src/skills-lib/wizz-video-effects/assets/sfx/click-soft.mp3 +0 -0
  24. package/src/skills-lib/wizz-video-effects/assets/sfx/click.mp3 +0 -0
  25. package/src/skills-lib/wizz-video-effects/assets/sfx/coin.mp3 +0 -0
  26. package/src/skills-lib/wizz-video-effects/assets/sfx/error.mp3 +0 -0
  27. package/src/skills-lib/wizz-video-effects/assets/sfx/impact-bass-1.mp3 +0 -0
  28. package/src/skills-lib/wizz-video-effects/assets/sfx/impact-bass-2.mp3 +0 -0
  29. package/src/skills-lib/wizz-video-effects/assets/sfx/key-press.mp3 +0 -0
  30. package/src/skills-lib/wizz-video-effects/assets/sfx/notif.mp3 +0 -0
  31. package/src/skills-lib/wizz-video-effects/assets/sfx/notification.mp3 +0 -0
  32. package/src/skills-lib/wizz-video-effects/assets/sfx/paste.mp3 +0 -0
  33. package/src/skills-lib/wizz-video-effects/assets/sfx/ping.mp3 +0 -0
  34. package/src/skills-lib/wizz-video-effects/assets/sfx/pop.mp3 +0 -0
  35. package/src/skills-lib/wizz-video-effects/assets/sfx/riser-metal.mp3 +0 -0
  36. package/src/skills-lib/wizz-video-effects/assets/sfx/riser.mp3 +0 -0
  37. package/src/skills-lib/wizz-video-effects/assets/sfx/send.mp3 +0 -0
  38. package/src/skills-lib/wizz-video-effects/assets/sfx/sparkle.mp3 +0 -0
  39. package/src/skills-lib/wizz-video-effects/assets/sfx/stamp.mp3 +0 -0
  40. package/src/skills-lib/wizz-video-effects/assets/sfx/success.mp3 +0 -0
  41. package/src/skills-lib/wizz-video-effects/assets/sfx/type.mp3 +0 -0
  42. package/src/skills-lib/wizz-video-effects/assets/sfx/typing.mp3 +0 -0
  43. package/src/skills-lib/wizz-video-effects/assets/sfx/whoosh-cinematic.mp3 +0 -0
  44. package/src/skills-lib/wizz-video-effects/assets/sfx/whoosh-short.mp3 +0 -0
  45. package/src/skills-lib/wizz-video-effects/assets/sfx/whoosh.mp3 +0 -0
  46. package/src/skills-lib/wizz-video-effects/references/audio.md +87 -0
  47. package/src/skills-lib/wizz-video-effects/references/data-contract.md +145 -0
  48. package/src/skills-lib/wizz-video-effects/references/do-not-use.md +54 -0
  49. package/src/skills-lib/wizz-video-effects/references/editing-patterns.md +58 -0
  50. package/src/skills-lib/wizz-video-effects/references/gates.md +37 -0
  51. package/src/skills-lib/wizz-video-effects/references/sfx-licensing.md +40 -0
  52. package/src/skills-lib/wizz-video-effects/references/when-to-use.md +129 -0
  53. package/src/skills-lib/wizz-video-effects/scripts/budget.py +211 -0
  54. package/src/skills-lib/wizz-video-effects/scripts/check_excess.py +218 -0
  55. package/src/skills-lib/wizz-video-effects/scripts/check_loudness.py +175 -0
  56. package/src/skills-lib/wizz-video-effects/scripts/check_rhythm.py +137 -0
  57. package/src/skills-lib/wizz-video-effects/scripts/check_safe_zone.py +221 -0
  58. package/src/skills-lib/wizz-video-effects/scripts/config.py +583 -0
  59. package/src/skills-lib/wizz-video-effects/scripts/fill_breaths.py +355 -0
  60. package/src/skills-lib/wizz-video-effects/scripts/measure_density.py +122 -0
  61. package/src/skills-lib/wizz-video-effects/scripts/normalize_audio.py +189 -0
  62. package/src/skills-lib/wizz-video-effects/scripts/sound.py +342 -0
  63. package/src/skills-lib/wizz-video-effects/scripts/test_budget.py +131 -0
  64. package/src/skills-lib/wizz-video-effects/scripts/test_check_loudness.py +156 -0
  65. package/src/skills-lib/wizz-video-effects/scripts/test_checks.py +190 -0
  66. package/src/skills-lib/wizz-video-effects/scripts/test_config.py +135 -0
  67. package/src/skills-lib/wizz-video-effects/scripts/test_fill_breaths.py +179 -0
  68. package/src/skills-lib/wizz-video-effects/scripts/test_sound.py +173 -0
  69. package/src/skills-lib/wizz-video-effects/scripts/timeline.py +85 -0
  70. package/tools/installer/banner.js +80 -29
  71. package/tools/installer/commands/catalog.js +87 -0
  72. package/tools/installer/commands/install.js +9 -0
  73. package/tools/installer/guided/detect-tools.js +53 -0
  74. package/tools/installer/guided/flow.js +203 -0
  75. package/tools/installer/guided/labels.js +185 -0
  76. package/tools/installer/guided/messages.js +132 -0
  77. package/tools/installer/guided/outro.js +32 -0
  78. package/tools/installer/guided/profiles.js +95 -0
  79. package/tools/installer/guided/steps.js +154 -0
  80. package/tools/installer/install-messages.yaml +3 -3
  81. package/tools/installer/modules/external-manager.js +4 -0
  82. package/tools/installer/ui.js +57 -22
  83. package/wizz-modules.yaml +1 -0
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Human-facing labels for the guided installer: one line per area and per
3
+ * CLI, in English (default) and Portuguese.
4
+ *
5
+ * Lives here, not in skills-registry.yaml, on purpose: agents read the
6
+ * registry at runtime and every extra field there costs tokens on every
7
+ * routed request. The registry stays the routing contract; this file is only
8
+ * what a person reads while installing. test/test-guided-installer.js fails
9
+ * when an area or CLI in the registry has no entry here, so the two cannot
10
+ * drift apart silently.
11
+ */
12
+
13
+ const AREA_LABELS = {
14
+ designer: {
15
+ en: { name: 'Design', help: 'UI, landing pages, design systems, motion, video' },
16
+ pt: { name: 'Design', help: 'interface, landing page, design system, motion, vídeo' },
17
+ },
18
+ copy: {
19
+ en: { name: 'Copy', help: 'sales copy, headlines, emails, sequences' },
20
+ pt: { name: 'Copy', help: 'texto de venda, headline, e-mail, sequência' },
21
+ },
22
+ seo: {
23
+ en: { name: 'SEO', help: 'audits, AI search, schema, programmatic pages' },
24
+ pt: { name: 'SEO', help: 'auditoria, busca por IA, schema, páginas em escala' },
25
+ },
26
+ growth: {
27
+ en: { name: 'Growth', help: 'conversion, launch, pricing, retention, offers' },
28
+ pt: { name: 'Growth', help: 'conversão, lançamento, preço, retenção, oferta' },
29
+ },
30
+ social: {
31
+ en: { name: 'Social', help: 'posts, Reels scripts, content calendar, publishing' },
32
+ pt: { name: 'Social', help: 'post, roteiro de Reels, calendário, publicação' },
33
+ },
34
+ ads: {
35
+ en: { name: 'Ads', help: 'paid campaigns, ad creatives, conversion tracking' },
36
+ pt: { name: 'Anúncios', help: 'campanha paga, criativo, tracking de conversão' },
37
+ },
38
+ architect: {
39
+ en: { name: 'Architecture', help: 'databases, security, infra, technical decisions' },
40
+ pt: { name: 'Arquitetura', help: 'banco, segurança, infra, decisão técnica' },
41
+ },
42
+ qa: {
43
+ en: { name: 'QA', help: 'tests, browser checks, security audit, launch readiness' },
44
+ pt: { name: 'QA', help: 'teste, verificação no navegador, auditoria, pré-lançamento' },
45
+ },
46
+ analyst: {
47
+ en: { name: 'Analysis', help: 'research, project kickoff, implementation plan' },
48
+ pt: { name: 'Análise', help: 'pesquisa, kickoff de projeto, plano de implementação' },
49
+ },
50
+ dev: {
51
+ en: { name: 'Development', help: 'build features, fix bugs, code review, sprints' },
52
+ pt: { name: 'Desenvolvimento', help: 'feature, bug, review de código, sprint' },
53
+ },
54
+ pm: {
55
+ en: { name: 'Product', help: 'briefs, PRDs, requirements, scope' },
56
+ pt: { name: 'Produto', help: 'brief, PRD, requisitos, escopo' },
57
+ },
58
+ 'tech-writer': {
59
+ en: { name: 'Docs', help: 'technical documentation and diagrams' },
60
+ pt: { name: 'Documentação', help: 'docs técnicas e diagramas' },
61
+ },
62
+ 'ux-designer': {
63
+ en: { name: 'UX', help: 'user flows, UX specs, user stories' },
64
+ pt: { name: 'UX', help: 'fluxos, especificação de UX, user stories' },
65
+ },
66
+ memoria: {
67
+ en: { name: 'Memory', help: 'saves decisions and project state between sessions' },
68
+ pt: { name: 'Memória', help: 'guarda decisões e estado do projeto entre sessões' },
69
+ },
70
+ };
71
+
72
+ // Requirement tokens a CLI can carry. Anything not listed means the tool is
73
+ // free and light. `platform` gates come from the registry itself.
74
+ const NEED_LABELS = {
75
+ 'api-key': { en: 'API key', pt: 'chave de API' },
76
+ account: { en: 'account', pt: 'conta' },
77
+ paid: { en: 'paid', pt: 'pago' },
78
+ 'free-plan': { en: 'free plan', pt: 'plano grátis' },
79
+ docker: { en: 'Docker', pt: 'Docker' },
80
+ python: { en: 'Python', pt: 'Python' },
81
+ ffmpeg: { en: 'ffmpeg', pt: 'ffmpeg' },
82
+ heavy: { en: 'heavy download', pt: 'download pesado' },
83
+ };
84
+
85
+ const CLI_LABELS = {
86
+ hyperframes: {
87
+ needs: ['ffmpeg'],
88
+ en: 'design video scenes in HTML',
89
+ pt: 'desenhar cenas de vídeo em HTML',
90
+ },
91
+ 'claude-video': {
92
+ needs: ['ffmpeg'],
93
+ en: 'watch and summarize an existing video',
94
+ pt: 'assistir e resumir um vídeo existente',
95
+ },
96
+ 'get-brolls': {
97
+ needs: ['ffmpeg', 'python'],
98
+ en: 'find real B-roll footage for a video',
99
+ pt: 'achar imagens de apoio reais para um vídeo',
100
+ },
101
+ buttercut: {
102
+ needs: ['heavy'],
103
+ en: 'cut and rough-edit recorded video',
104
+ pt: 'cortar e pré-editar vídeo gravado',
105
+ },
106
+ voicebox: {
107
+ needs: ['heavy'],
108
+ en: 'local voice-over and voice cloning',
109
+ pt: 'narração e clonagem de voz local',
110
+ },
111
+ 'ctc-align': {
112
+ needs: ['python'],
113
+ en: 'word-level timing to sync captions',
114
+ pt: 'timing palavra a palavra para legenda',
115
+ },
116
+ 'apple-design': {
117
+ needs: [],
118
+ en: 'review native iOS/macOS UI against Apple HIG',
119
+ pt: 'revisar UI nativa iOS/macOS pelo guia da Apple',
120
+ },
121
+ distribb: {
122
+ needs: ['account', 'paid'],
123
+ en: 'publish SEO articles and build backlinks',
124
+ pt: 'publicar artigos de SEO e ganhar backlinks',
125
+ },
126
+ buffer: {
127
+ needs: ['account', 'free-plan'],
128
+ en: 'publish and schedule social posts',
129
+ pt: 'publicar e agendar posts nas redes',
130
+ },
131
+ 'sentry-cli': {
132
+ needs: ['account'],
133
+ en: 'read errors from a project that already uses Sentry',
134
+ pt: 'ler erros de um projeto que já usa Sentry',
135
+ },
136
+ archify: {
137
+ needs: [],
138
+ en: 'draw validated architecture diagrams',
139
+ pt: 'desenhar diagramas de arquitetura validados',
140
+ },
141
+ 'agent-browser': {
142
+ needs: [],
143
+ en: 'drive a real browser for checks and screenshots',
144
+ pt: 'usar um navegador real para testar e tirar print',
145
+ },
146
+ strix: {
147
+ needs: ['docker', 'api-key'],
148
+ en: 'automated pentest, only on systems you own',
149
+ pt: 'pentest automático, só em sistema seu',
150
+ },
151
+ rtk: {
152
+ needs: [],
153
+ en: 'cuts 60-90% of tokens on dev commands',
154
+ pt: 'corta 60-90% dos tokens em comandos de dev',
155
+ },
156
+ };
157
+
158
+ /**
159
+ * Requirement badges for a CLI, in the chosen language. Adds the platform
160
+ * gate (Apple Silicon, macOS) from the registry entry.
161
+ * @param {{id: string, platform?: string|string[]}} cli - Resolved CLI entry
162
+ * @param {'en'|'pt'} lang - Display language
163
+ * @returns {string[]} Short badge texts; empty means free and light
164
+ */
165
+ function cliBadges(cli, lang) {
166
+ const info = CLI_LABELS[cli.id];
167
+ const badges = ((info && info.needs) || []).map((need) => NEED_LABELS[need][lang]);
168
+ const platforms = [cli.platform || []].flat();
169
+ if (platforms.includes('darwin-arm64')) badges.push('Apple Silicon');
170
+ else if (platforms.length === 1 && platforms[0] === 'darwin') badges.push('macOS');
171
+ return badges;
172
+ }
173
+
174
+ /**
175
+ * One-line description for a CLI, falling back to the registry id.
176
+ * @param {string} id - CLI id
177
+ * @param {'en'|'pt'} lang - Display language
178
+ * @returns {string}
179
+ */
180
+ function cliDescription(id, lang) {
181
+ const info = CLI_LABELS[id];
182
+ return info ? info[lang] : '';
183
+ }
184
+
185
+ module.exports = { AREA_LABELS, CLI_LABELS, NEED_LABELS, cliBadges, cliDescription };
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Guided installer text, English (default) and Portuguese. Every string the
3
+ * guided path prints comes from here, so adding a language is one more
4
+ * block and nothing else.
5
+ */
6
+
7
+ const MESSAGES = {
8
+ en: {
9
+ tagline: 'Your AI team: one agent for each area.',
10
+ whatHappens: 'What happens next',
11
+ duration: '~2 min',
12
+ stepNames: ['Where to install', "What you'll use it for", 'Which AI tool you use', 'Your name'],
13
+ languageHint: 'Language: English. Portuguese is available on the next screen.',
14
+ languageQuestion: 'Language · Idioma',
15
+ step: 'Step {n} of {total}',
16
+ dirQuestion: 'Where should Wizz Method be installed?',
17
+ dirCreate: 'This folder does not exist yet. Create {dir}?',
18
+ dirRetry: 'Okay, pick another folder.',
19
+ existingFound: 'Wizz Method is already installed here. Choose what to do.',
20
+ profileQuestion: 'What will you use it for?',
21
+ toolQuestion: 'Which AI tool do you use?',
22
+ toolDetected: 'found on this machine',
23
+ toolOther: 'Other tools…',
24
+ toolOtherHelp: '45 more: Windsurf, Cline, Gemini CLI, Kiro…',
25
+ toolOtherQuestion: 'Pick your tools (type to filter)',
26
+ toolNone: 'Pick at least one tool so the agents have somewhere to live.',
27
+ nameQuestion: 'What should the agents call you?',
28
+ modulesQuestion: 'Which modules?',
29
+ fromBmad: 'from BMAD',
30
+ areasQuestion: 'Which areas?',
31
+ mcpsQuestion: 'Which MCP servers? (they give the agents live access to a service)',
32
+ reviewTitle: "You're getting",
33
+ reviewAgents: '{n} agents',
34
+ reviewSkills: '{n} skills',
35
+ reviewSkillsHint: 'list them anytime: wizz catalog',
36
+ reviewMemory: 'Memory',
37
+ reviewMemoryHint: 'decisions saved per project',
38
+ reviewTools: 'AI tool',
39
+ reviewLowticket: 'Low Ticket playbook',
40
+ optionalTitle: 'Optional tools (tick to install now, or install later)',
41
+ free: 'free',
42
+ alreadyInstalled: 'already installed: {list}',
43
+ reviewAction: 'Ready?',
44
+ actionInstall: 'Install',
45
+ actionProfile: 'Change profile',
46
+ actionCustomize: 'Customize',
47
+ nextSteps: 'Next steps',
48
+ tryAsking: 'Try asking your agent:',
49
+ skippedTools: 'Tools left for later (install commands are listed above):',
50
+ docs: 'Docs',
51
+ help: 'Help',
52
+ catalogLegend: '✓ installed ⌘ command-line tool ◇ MCP server',
53
+ },
54
+ pt: {
55
+ tagline: 'Seu time de IA: um agente para cada área.',
56
+ whatHappens: 'O que vai acontecer',
57
+ duration: '~2 min',
58
+ stepNames: ['Onde instalar', 'Pra que você vai usar', 'Qual IA você usa', 'Seu nome'],
59
+ languageHint: 'Idioma: inglês. Português está disponível na próxima tela.',
60
+ languageQuestion: 'Language · Idioma',
61
+ step: 'Passo {n} de {total}',
62
+ dirQuestion: 'Onde instalar o Wizz Method?',
63
+ dirCreate: 'Essa pasta ainda não existe. Criar {dir}?',
64
+ dirRetry: 'Tudo bem, escolha outra pasta.',
65
+ existingFound: 'O Wizz Method já está instalado aqui. Escolha o que fazer.',
66
+ profileQuestion: 'Pra que você vai usar?',
67
+ toolQuestion: 'Qual IA você usa?',
68
+ toolDetected: 'encontrada nesta máquina',
69
+ toolOther: 'Outras ferramentas…',
70
+ toolOtherHelp: 'mais 45: Windsurf, Cline, Gemini CLI, Kiro…',
71
+ toolOtherQuestion: 'Escolha suas ferramentas (digite pra filtrar)',
72
+ toolNone: 'Escolha pelo menos uma ferramenta, é nela que os agentes vão morar.',
73
+ nameQuestion: 'Como os agentes devem te chamar?',
74
+ modulesQuestion: 'Quais módulos?',
75
+ fromBmad: 'do BMAD',
76
+ areasQuestion: 'Quais áreas?',
77
+ mcpsQuestion: 'Quais servidores MCP? (dão aos agentes acesso direto a um serviço)',
78
+ reviewTitle: 'O que vai entrar',
79
+ reviewAgents: '{n} agentes',
80
+ reviewSkills: '{n} skills',
81
+ reviewSkillsHint: 'veja a lista quando quiser: wizz catalog',
82
+ reviewMemory: 'Memória',
83
+ reviewMemoryHint: 'decisões salvas por projeto',
84
+ reviewTools: 'IA',
85
+ reviewLowticket: 'Playbook Low Ticket',
86
+ optionalTitle: 'Ferramentas opcionais (marque pra instalar agora, ou instale depois)',
87
+ free: 'grátis',
88
+ alreadyInstalled: 'já instaladas: {list}',
89
+ reviewAction: 'Tudo certo?',
90
+ actionInstall: 'Instalar',
91
+ actionProfile: 'Trocar perfil',
92
+ actionCustomize: 'Personalizar',
93
+ nextSteps: 'Próximos passos',
94
+ tryAsking: 'Experimente pedir ao seu agente:',
95
+ skippedTools: 'Ferramentas que ficaram pra depois (os comandos estão logo acima):',
96
+ docs: 'Docs',
97
+ help: 'Ajuda',
98
+ catalogLegend: '✓ instalada ⌘ ferramenta de terminal ◇ servidor MCP',
99
+ },
100
+ };
101
+
102
+ const LANGUAGES = [
103
+ { value: 'en', label: 'English', communication: 'English' },
104
+ { value: 'pt', label: 'Português', communication: 'Português (Brasil)' },
105
+ ];
106
+
107
+ /**
108
+ * Normalize a --lang value to a supported code.
109
+ * @param {string|undefined} raw - e.g. "pt", "pt-BR", "en"
110
+ * @returns {'en'|'pt'|null} null when absent or unknown
111
+ */
112
+ function normalizeLang(raw) {
113
+ if (!raw) return null;
114
+ const code = String(raw).toLowerCase().slice(0, 2);
115
+ return MESSAGES[code] ? code : null;
116
+ }
117
+
118
+ /**
119
+ * Translate a key, filling {placeholders}.
120
+ * @param {'en'|'pt'} lang - Display language
121
+ * @param {string} key - Message key
122
+ * @param {Object} [vars] - Placeholder values
123
+ * @returns {string}
124
+ */
125
+ function t(lang, key, vars = {}) {
126
+ const table = MESSAGES[lang] || MESSAGES.en;
127
+ const value = key in table ? table[key] : MESSAGES.en[key];
128
+ if (typeof value !== 'string') return value;
129
+ return value.replaceAll(/\{(\w+)\}/g, (match, name) => (name in vars ? String(vars[name]) : match));
130
+ }
131
+
132
+ module.exports = { MESSAGES, LANGUAGES, normalizeLang, t };
@@ -0,0 +1,32 @@
1
+ const prompts = require('../prompts');
2
+ const { paint, BRAND_ORANGE } = require('../banner');
3
+ const { t } = require('./messages');
4
+ const { getProfile } = require('./profiles');
5
+
6
+ /**
7
+ * Closing screen of the guided install: three things to ask the agent
8
+ * right now (picked by profile) and the names of the tools the user left
9
+ * for later.
10
+ * @param {Object} config - The config returned by runGuidedInstall
11
+ */
12
+ async function showGuidedOutro(config) {
13
+ const { lang, profile } = config.guided;
14
+ const color = await prompts.getColor();
15
+ const colorOn = color.isColorSupported !== false;
16
+ const lines = [color.bold(t(lang, 'tryAsking'))];
17
+ for (const ask of getProfile(profile).tryAsking[lang]) {
18
+ lines.push(` ${paint(BRAND_ORANGE, '›', colorOn)} ${ask}`);
19
+ }
20
+
21
+ // Only the names here: the full install commands were printed above by the
22
+ // installer, and repeating long clone commands buries the next steps.
23
+ const skipped = ((config.cliPlan && config.cliPlan.toRecommend) || []).map((cli) => cli.id);
24
+ if (skipped.length > 0) {
25
+ lines.push('', color.bold(t(lang, 'skippedTools')), ` ${color.dim(skipped.join(' · '))}`);
26
+ }
27
+
28
+ lines.push('', color.dim(`${t(lang, 'docs')} method.wizzcomms.com`), color.dim(`${t(lang, 'help')} wizz doctor · wizz catalog`));
29
+ await prompts.note(lines.join('\n'), t(lang, 'nextSteps'));
30
+ }
31
+
32
+ module.exports = { showGuidedOutro };
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Install profiles for the guided installer. A profile answers "what will
3
+ * you use it for?" and decides modules and skill areas in one choice, so the
4
+ * normal path never shows the module or area lists.
5
+ *
6
+ * `areas: null` means every area (the installSkillsLib sentinel is `[]`).
7
+ */
8
+
9
+ const BASE_MODULES = ['wizz', 'bmm'];
10
+
11
+ const PROFILES = [
12
+ {
13
+ id: 'everything',
14
+ modules: BASE_MODULES,
15
+ areas: null,
16
+ en: { name: 'Everything', help: 'product, code, design, content and marketing' },
17
+ pt: { name: 'Completo', help: 'produto, código, design, conteúdo e marketing' },
18
+ tryAsking: {
19
+ en: ['plan a new feature from the brief to the first story', 'write a LinkedIn post about our launch', 'audit the SEO of my site'],
20
+ pt: [
21
+ 'planeja uma feature nova, do brief à primeira story',
22
+ 'escreve um post pro LinkedIn sobre o lançamento',
23
+ 'faz uma auditoria de SEO do meu site',
24
+ ],
25
+ },
26
+ },
27
+ {
28
+ id: 'marketing',
29
+ modules: BASE_MODULES,
30
+ areas: ['designer', 'copy', 'seo', 'growth', 'social', 'ads', 'analyst', 'memoria'],
31
+ en: { name: 'Content & marketing', help: 'copy, social, video, ads and SEO' },
32
+ pt: { name: 'Conteúdo e marketing', help: 'copy, social, vídeo, anúncios e SEO' },
33
+ tryAsking: {
34
+ en: ['write a LinkedIn post about our launch', "plan next week's content calendar", 'write 3 headline variations for a Meta ad'],
35
+ pt: [
36
+ 'escreve um post pro LinkedIn sobre o lançamento',
37
+ 'monta o calendário de conteúdo da semana',
38
+ 'cria 3 variações de headline pra um anúncio na Meta',
39
+ ],
40
+ },
41
+ },
42
+ {
43
+ id: 'product',
44
+ modules: BASE_MODULES,
45
+ areas: ['designer', 'architect', 'qa', 'analyst', 'dev', 'pm', 'tech-writer', 'ux-designer', 'memoria'],
46
+ en: { name: 'Product & code', help: 'planning, architecture, development and tests' },
47
+ pt: { name: 'Produto e código', help: 'planejamento, arquitetura, desenvolvimento e testes' },
48
+ tryAsking: {
49
+ en: ['write the PRD for a new feature', 'fix this bug and add a test for it', 'review the code in my last commit'],
50
+ pt: ['escreve o PRD de uma feature nova', 'corrige esse bug e cria um teste pra ele', 'revisa o código do meu último commit'],
51
+ },
52
+ },
53
+ {
54
+ id: 'custom',
55
+ modules: null,
56
+ areas: null,
57
+ en: { name: 'Custom', help: 'pick modules, areas and tools one by one' },
58
+ pt: { name: 'Personalizar', help: 'escolher módulos, áreas e ferramentas um a um' },
59
+ tryAsking: {
60
+ en: ['what can you do in this project?', 'start a new project with me'],
61
+ pt: ['o que você sabe fazer neste projeto?', 'começa um projeto novo comigo'],
62
+ },
63
+ },
64
+ ];
65
+
66
+ /**
67
+ * @param {string} id - Profile id
68
+ * @returns {Object|undefined}
69
+ */
70
+ function getProfile(id) {
71
+ return PROFILES.find((profile) => profile.id === id);
72
+ }
73
+
74
+ /**
75
+ * What a set of areas brings in, read from the registry so the review screen
76
+ * never shows a number that drifted from the real catalog.
77
+ * @param {Object|null} registry - Parsed skills-registry.yaml
78
+ * @param {string[]|null} areas - Chosen areas; null or [] means all
79
+ * @returns {{areas: string[], agents: number, skills: number}}
80
+ */
81
+ function summarizeAreas(registry, areas) {
82
+ const all = (registry && registry.areas) || {};
83
+ const chosen = !areas || areas.length === 0 ? Object.keys(all) : areas.filter((key) => all[key]);
84
+ const skillIds = new Set();
85
+ const agents = new Set();
86
+ for (const key of chosen) {
87
+ const area = all[key];
88
+ if (area.agent) agents.add(area.agent);
89
+ for (const skill of area.skills || []) skillIds.add(skill.id);
90
+ }
91
+ for (const skill of (registry && registry.utility) || []) skillIds.add(skill.id);
92
+ return { areas: chosen, agents: agents.size, skills: skillIds.size };
93
+ }
94
+
95
+ module.exports = { PROFILES, getProfile, summarizeAreas };
@@ -0,0 +1,154 @@
1
+ /**
2
+ * One function per guided screen. Each returns plain data; the order and the
3
+ * loop (change profile, customize) live in flow.js.
4
+ */
5
+ const os = require('node:os');
6
+ const fs = require('../fs-native');
7
+ const prompts = require('../prompts');
8
+ const { paint, BRAND_ORANGE } = require('../banner');
9
+ const { t, LANGUAGES } = require('./messages');
10
+ const { PROFILES } = require('./profiles');
11
+ const { AREA_LABELS, cliBadges, cliDescription } = require('./labels');
12
+ const { detectAiTools } = require('./detect-tools');
13
+
14
+ const TOTAL_STEPS = 4;
15
+ const OTHER_TOOLS = '__other__';
16
+
17
+ async function stepHeader(lang, n) {
18
+ const color = await prompts.getColor();
19
+ const colorOn = color.isColorSupported !== false;
20
+ const track = Array.from({ length: TOTAL_STEPS }, (_, i) => (i < n ? '●' : '○')).join('───');
21
+ await prompts.log.message(`${paint(BRAND_ORANGE, track, colorOn)} ${color.dim(t(lang, 'step', { n, total: TOTAL_STEPS }))}`);
22
+ }
23
+
24
+ async function askLanguage() {
25
+ return prompts.select({
26
+ message: t('en', 'languageQuestion'),
27
+ choices: LANGUAGES.map((l) => ({ name: l.label, value: l.value })),
28
+ default: 'en',
29
+ });
30
+ }
31
+
32
+ async function askDirectory(ui, lang) {
33
+ for (;;) {
34
+ const raw = await prompts.directory({
35
+ message: t(lang, 'dirQuestion'),
36
+ default: process.cwd(),
37
+ placeholder: process.cwd(),
38
+ validate: (input) => ui.validateDirectorySync(input),
39
+ });
40
+ const directory = raw && raw.trim() ? ui.expandUserPath(raw) : process.cwd();
41
+ if (await fs.pathExists(directory)) return directory;
42
+ const create = await prompts.confirm({ message: t(lang, 'dirCreate', { dir: directory }), default: true });
43
+ if (create) return directory;
44
+ await prompts.log.warn(t(lang, 'dirRetry'));
45
+ }
46
+ }
47
+
48
+ async function askProfile(lang, current) {
49
+ return prompts.select({
50
+ message: t(lang, 'profileQuestion'),
51
+ choices: PROFILES.map((p) => ({ name: p[lang].name, value: p.id, hint: p[lang].help })),
52
+ default: current || 'everything',
53
+ });
54
+ }
55
+
56
+ async function askAreas(lang, registry) {
57
+ const keys = Object.keys((registry && registry.areas) || {});
58
+ const selected = await prompts.multiselect({
59
+ message: t(lang, 'areasQuestion'),
60
+ options: keys.map((key) => {
61
+ const label = AREA_LABELS[key] ? AREA_LABELS[key][lang] : { name: key, help: '' };
62
+ return { label: label.name, value: key, hint: label.help };
63
+ }),
64
+ initialValues: keys,
65
+ required: true,
66
+ });
67
+ return selected.length === keys.length ? [] : selected;
68
+ }
69
+
70
+ async function askModules(lang, available) {
71
+ const selected = await prompts.multiselect({
72
+ message: t(lang, 'modulesQuestion'),
73
+ options: available.map((mod) => ({
74
+ label: mod.fromBmad ? `${mod.name} · ${t(lang, 'fromBmad')}` : mod.name,
75
+ value: mod.code,
76
+ hint: mod.description,
77
+ })),
78
+ initialValues: available.filter((mod) => mod.defaultSelected).map((mod) => mod.code),
79
+ required: true,
80
+ });
81
+ return selected;
82
+ }
83
+
84
+ async function askTools(lang, ideManager) {
85
+ const preferred = ideManager.getPreferredIdes();
86
+ const others = ideManager.getOtherIdes();
87
+ const detected = new Set(detectAiTools());
88
+ const initial = preferred.filter((ide) => detected.has(ide.value)).map((ide) => ide.value);
89
+ const picked = await prompts.multiselect({
90
+ message: t(lang, 'toolQuestion'),
91
+ options: [
92
+ ...preferred.map((ide) => ({
93
+ label: ide.name,
94
+ value: ide.value,
95
+ hint: detected.has(ide.value) ? t(lang, 'toolDetected') : undefined,
96
+ })),
97
+ { label: t(lang, 'toolOther'), value: OTHER_TOOLS, hint: t(lang, 'toolOtherHelp') },
98
+ ],
99
+ initialValues: initial.length > 0 ? initial : [preferred[0].value],
100
+ required: true,
101
+ });
102
+ const chosen = picked.filter((id) => id !== OTHER_TOOLS);
103
+ if (!picked.includes(OTHER_TOOLS)) return chosen;
104
+ const extra = await prompts.autocompleteMultiselect({
105
+ message: t(lang, 'toolOtherQuestion'),
106
+ options: others.map((ide) => ({ label: ide.name, value: ide.value })),
107
+ required: chosen.length === 0,
108
+ maxItems: 8,
109
+ });
110
+ return [...chosen, ...(extra || [])];
111
+ }
112
+
113
+ function defaultUserName() {
114
+ let name;
115
+ try {
116
+ name = os.userInfo().username;
117
+ } catch {
118
+ name = process.env.USER || process.env.USERNAME || 'User';
119
+ }
120
+ return name.charAt(0).toUpperCase() + name.slice(1);
121
+ }
122
+
123
+ async function askName(lang) {
124
+ const fallback = defaultUserName();
125
+ const value = await prompts.text({ message: t(lang, 'nameQuestion'), default: fallback, placeholder: fallback });
126
+ return (value && String(value).trim()) || fallback;
127
+ }
128
+
129
+ /**
130
+ * Label and hint for a CLI in the optional-tools list: plain description,
131
+ * then requirement badges ("free" when there are none).
132
+ */
133
+ function cliOption(cli, lang) {
134
+ const badges = cliBadges(cli, lang);
135
+ const needs = badges.length > 0 ? badges.join(' · ') : t(lang, 'free');
136
+ const description = cliDescription(cli.id, lang);
137
+ // Description in the label, not the hint: clack only shows a hint for the
138
+ // focused row, and the point here is comparing tools at a glance.
139
+ const label = description ? `${cli.id.padEnd(14)} ${description} · ${needs}` : `${cli.id.padEnd(14)} ${needs}`;
140
+ return { label, value: cli.id };
141
+ }
142
+
143
+ module.exports = {
144
+ stepHeader,
145
+ askLanguage,
146
+ askDirectory,
147
+ askProfile,
148
+ askAreas,
149
+ askModules,
150
+ askTools,
151
+ askName,
152
+ cliOption,
153
+ defaultUserName,
154
+ };
@@ -6,11 +6,11 @@
6
6
  startMessage: |
7
7
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
8
8
 
9
- Método de agência orientado por IA, em PT-BR. Agentes por área, skills curadas e memória de projeto.
10
- Escolha os módulos durante o setup para montar a instalação do seu jeito.
9
+ Full install: choices come from your flags or from the complete question list.
10
+ For the short guided install, run `npx wizz-method install` with no flags.
11
11
 
12
12
  📖 Docs: https://method.wizzcomms.com/
13
- ✉️ Suporte: support@wizzcomms.com
13
+ ✉️ Support: support@wizzcomms.com
14
14
 
15
15
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
16
16
 
@@ -145,6 +145,10 @@ class ExternalModuleManager {
145
145
  marketplacePlugin: mod.marketplace_plugin === true || mod['marketplace-plugin'] === true || mod.marketplacePlugin === true,
146
146
  postInstallMessage: mod.post_install_message || mod['post-install-message'] || mod.postInstallMessage || null,
147
147
  builtIn: mod.built_in === true,
148
+ // Só aparece no modo avançado do instalador guiado (--advanced ou a
149
+ // lista completa de módulos); experimental ou de nicho demais pra
150
+ // quem instala pela primeira vez.
151
+ advancedOnly: mod.advanced_only === true,
148
152
  isExternal: mod.built_in !== true,
149
153
  // Códigos anteriores sob os quais este módulo foi registrado (ex.: `bmad-loop`
150
154
  // era `bauto`). Deixa um módulo renomeado continuar resolvendo instalações