wizz-method 1.18.2 → 1.19.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 (50) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/skills-registry.yaml +2 -11
  4. package/src/modules/lowticket/README.md +2 -2
  5. package/src/modules/lowticket/module.yaml +1 -1
  6. package/src/modules/lowticket/skills/lowticket-funil/SKILL.md +3 -3
  7. package/src/modules/lowticket/skills/lowticket-funil/references/copy-das-paginas.md +5 -3
  8. package/src/modules/lowticket/skills/lowticket-funil/references/mapa-do-funil.md +2 -0
  9. package/src/modules/lowticket/skills/lowticket-metodologia/SKILL.md +3 -3
  10. package/src/modules/lowticket/skills/lowticket-metodologia/knowledge/03-campanha-bidcap.md +18 -0
  11. package/src/modules/lowticket/skills/lowticket-metodologia/knowledge/04a-criativos-ia.md +47 -1
  12. package/src/modules/lowticket/skills/lowticket-metodologia/knowledge/04b-criativos-formatos.md +33 -2
  13. package/src/modules/lowticket/skills/lowticket-metodologia/knowledge/05-oferta.md +7 -4
  14. package/src/modules/lowticket/skills/lowticket-metodologia/knowledge/06-pagina-vendas.md +10 -8
  15. package/src/modules/lowticket/skills/lowticket-metodologia/knowledge/06a-instagram.md +9 -0
  16. package/src/modules/lowticket/skills/lowticket-metodologia/knowledge/06b-player-vsl.md +79 -0
  17. package/src/modules/lowticket/skills/lowticket-metodologia/knowledge/07-funil-upsell.md +61 -7
  18. package/src/modules/lowticket/skills/lowticket-metodologia/knowledge/07a-copy-humanizacao.md +73 -0
  19. package/src/modules/lowticket/skills/lowticket-metodologia/knowledge/09-especialista-coproducao.md +1 -1
  20. package/src/modules/lowticket/skills/lowticket-metodologia/knowledge/11-checklist-roi.md +3 -3
  21. package/src/modules/lowticket/skills/lowticket-metodologia/knowledge/12-mineracao-ofertas.md +64 -5
  22. package/src/modules/lowticket/skills/lowticket-metodologia/knowledge/12a-mineracao-veredito.md +48 -2
  23. package/src/modules/lowticket/skills/lowticket-metodologia/knowledge/12b-fabrica-de-ofertas.md +41 -94
  24. package/src/modules/lowticket/skills/lowticket-metodologia/knowledge/12c-anatomia-low-ticket.md +111 -0
  25. package/src/modules/lowticket/skills/lowticket-metodologia/knowledge/15-como-estender.md +5 -3
  26. package/src/modules/lowticket/skills/lowticket-metodologia/knowledge/17-rastreamento.md +127 -0
  27. package/src/modules/lowticket/skills/lowticket-metodologia/knowledge/18-copy-desejo-oferta.md +245 -0
  28. package/src/modules/lowticket/skills/lowticket-metodologia/knowledge/INDEX.md +14 -7
  29. package/src/modules/lowticket/skills/lowticket-metodologia/knowledge/guardrails.md +59 -5
  30. package/src/modules/lowticket/skills/lowticket-metodologia/knowledge/pontos-em-aberto.md +72 -11
  31. package/src/modules/lowticket/skills/lowticket-minerador/SKILL.md +6 -6
  32. package/src/modules/lowticket/skills/lowticket-minerador/references/rubrica.md +27 -2
  33. package/src/modules/lowticket/skills/lowticket-pagina/SKILL.md +5 -3
  34. package/src/modules/lowticket/skills/lowticket-pagina/references/metas-e-medicao.md +1 -0
  35. package/src/modules/lowticket/skills/lowticket-pagina/references/padroes-de-codigo.md +4 -0
  36. package/src/modules/lowticket/skills/lowticket-trafego/SKILL.md +2 -0
  37. package/src/modules/lowticket/skills/lowticket-trafego/references/reguas.md +4 -3
  38. package/src/modules/wizz/README.md +1 -1
  39. package/src/modules/wizz/_shared/model-ladder.md +2 -0
  40. package/src/modules/wizz/agents/wizz-ads/SKILL.md +3 -6
  41. package/src/modules/wizz/agents/wizz-ads/customize.toml +1 -8
  42. package/src/modules/wizz/agents/wizz-maestro/SKILL.md +1 -1
  43. package/src/modules/wizz/module.yaml +1 -1
  44. package/src/skills-lib/ad-creative/SKILL.md +1 -1
  45. package/src/skills-lib/paid-ads/SKILL.md +1 -1
  46. package/src/skills-lib/wizz-router/references/routing-table-flat.md +2 -5
  47. package/tools/installer/core/installer.js +20 -3
  48. package/tools/installer/modules/env-vars.js +41 -0
  49. package/tools/installer/modules/mcp-config.js +98 -14
  50. package/tools/installer/ui.js +6 -1
@@ -59,8 +59,7 @@ Use esta tabela pra mapear skills/CLIs/MCPs direto **fora de projeto Wizz** (sem
59
59
  | Intenção / palavras-chave | Skills a oferecer | Prioridade |
60
60
  |---|---|---|
61
61
  | Ideias de marketing, estratégia de marketing, posicionamento | `marketing-ideas` + `marketing-psychology` + `product-marketing-context` | 1 |
62
- | Paid ads, anúncios, Google Ads, Meta Ads, TikTok Ads, mídia paga | `paid-ads` + `ad-creative` + `analytics-tracking` + **MCP meta-ads** | 1 |
63
- | Gestão de campanha Meta/Facebook/Instagram via API real | **MCP meta-ads** (`mcp-meta-ads`) direto | 1 |
62
+ | Paid ads, anúncios, Google Ads, Meta Ads, TikTok Ads, mídia paga | `paid-ads` + `ad-creative` + `analytics-tracking` | 1 |
64
63
  | Pixel/UTM/atribuição erradas, "a venda não bate com o gerenciador", "onde está furando o funil", tráfego orgânico inflado, conferir tracking antes de subir campanha | `tracking-audit` (+ `analytics-tracking` se for SaaS/produto) | 1 |
65
64
  | Lançamento de feature, lançamento de produto, go-to-market | `launch-strategy` + `social-content` + `email-sequence` | 1 |
66
65
  | Site pronto pra subir, pré-lançamento de SITE, "revisa antes do deploy", checklist de go-live, ou item pontual pré-lançamento (og:image/prévia de link, FAQ com schema, robots.txt, LGPD, alt text) | `site-launch-kit` | 1 |
@@ -97,6 +96,4 @@ Quando nenhuma skill/MCP instalado cobrir o pedido, **classifique o que falta**
97
96
 
98
97
  **Falta um MCP:** informe → `claude mcp list` → consulte `skills-registry.yaml` (`mcps:`/`mcp_utility:`, com `server` pronto) → proponha `claude mcp add <id> [-e VAR=$VAR] -- <command> [args]`. Secrets sempre via env/placeholder, nunca token real.
99
98
 
100
- MCPs comuns: context7 (docs de libs), supabase (Postgres), meta-ads (Meta), exa (pesquisa). Browser/E2E é sempre via CLI `agent-browser`, nunca via MCP Playwright.
101
-
102
- Para paid ads Meta, o MCP `mcp-meta-ads` dá acesso real à API Meta Marketing (campanhas, ad sets, ads, métricas, criativos). Combine com `paid-ads` + `ad-creative` + `analytics-tracking`. O `META_ACCESS_TOKEN` vem de env local: nunca exponha em logs ou código commitado.
99
+ MCPs comuns: context7 (docs de libs), supabase (Postgres), exa (pesquisa). Browser/E2E é sempre via CLI `agent-browser`, nunca via MCP Playwright.
@@ -4,7 +4,7 @@ const { Manifest } = require('./manifest');
4
4
  const { OfficialModules } = require('../modules/official-modules');
5
5
  const { installSkillsLib } = require('../modules/skills-lib');
6
6
  const { writeMcpConfig, renderAddCommand, prepareMcps, partitionAlreadyConfigured } = require('../modules/mcp-config');
7
- const { promptMissingEnvVars } = require('../modules/env-vars');
7
+ const { promptMissingEnvVars, mcpsWithMissingRequiredEnv } = require('../modules/env-vars');
8
8
  const { installClis, renderInstallCommand } = require('../modules/cli-config');
9
9
  const { writeDepsCache, readPreviousMcpPins } = require('../modules/deps-cache');
10
10
  const { IdeManager } = require('../ide/manager');
@@ -423,7 +423,7 @@ class Installer {
423
423
  // is still cheap for an already-installed binary: prepareMcp is
424
424
  // detect-first, so only a genuinely missing binary pays for the
425
425
  // network-heavy install/post_install steps.
426
- const { ready: mcpsReady, failed: mcpsFailed } = await prepareMcps({ mcps: toWrite });
426
+ let { ready: mcpsReady, failed: mcpsFailed } = await prepareMcps({ mcps: toWrite });
427
427
  for (const f of mcpsFailed) {
428
428
  await prompts.log.warn(
429
429
  `MCP ${f.id} não pôde ser preparado (${f.error}). Config NÃO escrita para evitar ENOENT — instale manualmente e adicione depois com \`claude mcp add\`.`,
@@ -451,10 +451,27 @@ class Installer {
451
451
  mcps: mcpsReady,
452
452
  });
453
453
  const envInteractive = !config.skipPrompts && !!process.stdin.isTTY;
454
- await promptMissingEnvVars(mcpsForEnvVars, {
454
+ const envResult = await promptMissingEnvVars(mcpsForEnvVars, {
455
455
  projectDir: paths.projectRoot,
456
456
  interactive: envInteractive,
457
457
  });
458
+
459
+ // A required (no-default) var that stayed unresolved means
460
+ // the server would be written with a permanently-broken
461
+ // literal `${VAR}` — never write it (E1: never fail the
462
+ // install, just drop that server and tell the user how to
463
+ // retry).
464
+ const missingEnv = mcpsWithMissingRequiredEnv(mcpsReady, envResult.skipped);
465
+ if (missingEnv.length > 0) {
466
+ const missingIds = new Set(missingEnv.map((m) => m.id));
467
+ mcpsReady = mcpsReady.filter((m) => !missingIds.has(m.id));
468
+ for (const m of missingEnv) {
469
+ await prompts.log.warn(
470
+ `MCP ${m.id} não gravado: faltou ${m.missing.join(', ')}. Rode o installer de novo quando tiver a chave, ou adicione com \`claude mcp add\`.`,
471
+ );
472
+ }
473
+ addResult('MCP servers', 'warn', `não gravados por falta de env var: ${missingEnv.map((m) => m.id).join(', ')}`);
474
+ }
458
475
  } catch (error) {
459
476
  await prompts.log.warn(`Falha ao configurar env vars dos MCPs: ${error.message}`);
460
477
  addResult('MCP env vars', 'warn', error.message);
@@ -589,9 +589,50 @@ async function promptMissingEnvVars(mcps, opts = {}) {
589
589
  };
590
590
  }
591
591
 
592
+ /**
593
+ * Find which of `mcps` have at least one REQUIRED (`hasDefault: false`) env
594
+ * placeholder that stayed unresolved, per the `skipped` list returned by
595
+ * `promptMissingEnvVars`/`resolveEnvVars`. A var WITH a default
596
+ * (`hasDefault: true`) is fine even when skipped — the Claude Code runtime
597
+ * resolves `${VAR:-default}` itself — so only `hasDefault: false` entries
598
+ * count here.
599
+ *
600
+ * Writing a server whose required placeholder never resolved to anywhere
601
+ * (not `process.env`, not `.env`, not the global store, not typed
602
+ * interactively) would embed a literal, permanently-broken `${VAR}` into
603
+ * `.mcp.json`: the server sits in the config but can never actually start.
604
+ * Callers use this to drop such a server from the write instead. Pure: no
605
+ * I/O.
606
+ *
607
+ * @param {Array<{id: string}>} mcps - Resolved MCP entries to check
608
+ * @param {Array<{name: string, mcpIds: string[], hasDefault: boolean}>} [skipped]
609
+ * - The `skipped` array from `promptMissingEnvVars`/`resolveEnvVars`
610
+ * @returns {Array<{id: string, missing: string[]}>} One entry per affected
611
+ * MCP id, with the names of its unresolved required vars (in encounter order)
612
+ */
613
+ function mcpsWithMissingRequiredEnv(mcps, skipped) {
614
+ const missingByMcpId = new Map();
615
+ for (const entry of skipped || []) {
616
+ if (!entry || entry.hasDefault) continue;
617
+ for (const id of entry.mcpIds || []) {
618
+ if (!missingByMcpId.has(id)) missingByMcpId.set(id, []);
619
+ missingByMcpId.get(id).push(entry.name);
620
+ }
621
+ }
622
+
623
+ const result = [];
624
+ for (const mcp of mcps || []) {
625
+ if (mcp && mcp.id && missingByMcpId.has(mcp.id)) {
626
+ result.push({ id: mcp.id, missing: missingByMcpId.get(mcp.id) });
627
+ }
628
+ }
629
+ return result;
630
+ }
631
+
592
632
  module.exports = {
593
633
  extractEnvPlaceholders,
594
634
  resolveEnvVars,
635
+ mcpsWithMissingRequiredEnv,
595
636
  persistEnvValues,
596
637
  persistProjectEnv,
597
638
  persistGlobalEnv,
@@ -326,25 +326,101 @@ async function partitionAlreadyConfigured({ projectDir, mcps }) {
326
326
  return { toPrepare, alreadyConfigured };
327
327
  }
328
328
 
329
+ /**
330
+ * Resolve the set of MCP ids covered by an installed Claude Code plugin
331
+ * (`~/.claude/plugins/installed_plugins.json`). A plugin key in that file's
332
+ * `plugins` map is `<name>@<marketplace>` (e.g.
333
+ * `supabase@claude-plugins-official`); the plugin covers catalog MCP id
334
+ * `<name>`. If the plugin ships its own `.mcp.json`
335
+ * (`<installPath>/.mcp.json`, `installPath` from the plugin's install
336
+ * record) with an `mcpServers` block, every key in that block is ALSO
337
+ * covered (e.g. the `vercel` plugin ships a server named `vercel`).
338
+ *
339
+ * Read failures at the top level (missing/malformed
340
+ * `installed_plugins.json`) fall back to "nothing covered", same spirit as
341
+ * the `~/.claude.json` handling in `partitionGloballyConfigured`. A failure
342
+ * reading ONE plugin's own `.mcp.json` (missing `installPath`, missing
343
+ * file, malformed JSON) only skips that plugin's extra ids — the plugin's
344
+ * own name stays covered, since that didn't require reading anything past
345
+ * the already-parsed top-level file.
346
+ *
347
+ * @param {string} [installedPluginsPath] - Override of
348
+ * `~/.claude/plugins/installed_plugins.json` (tests)
349
+ * @returns {Promise<Set<string>>}
350
+ */
351
+ async function resolvePluginCoveredIds(installedPluginsPath) {
352
+ const file = installedPluginsPath || path.join(os.homedir(), '.claude', 'plugins', 'installed_plugins.json');
353
+ const ids = new Set();
354
+
355
+ if (!(await fs.pathExists(file))) return ids;
356
+
357
+ let data;
358
+ try {
359
+ data = JSON.parse(await fs.readFile(file, 'utf8'));
360
+ } catch {
361
+ return ids;
362
+ }
363
+
364
+ const plugins = data && typeof data === 'object' && data.plugins && typeof data.plugins === 'object' ? data.plugins : null;
365
+ if (!plugins) return ids;
366
+
367
+ for (const [key, entries] of Object.entries(plugins)) {
368
+ const name = String(key).split('@')[0];
369
+ if (name) ids.add(name);
370
+
371
+ if (!Array.isArray(entries)) continue;
372
+ for (const entry of entries) {
373
+ const installPath = entry && entry.installPath;
374
+ if (!installPath || typeof installPath !== 'string') continue;
375
+
376
+ const mcpJsonPath = path.join(installPath, '.mcp.json');
377
+ try {
378
+ if (!(await fs.pathExists(mcpJsonPath))) continue;
379
+ const mcpConfig = JSON.parse(await fs.readFile(mcpJsonPath, 'utf8'));
380
+ const servers =
381
+ mcpConfig && typeof mcpConfig === 'object' && mcpConfig.mcpServers && !Array.isArray(mcpConfig.mcpServers)
382
+ ? mcpConfig.mcpServers
383
+ : null;
384
+ if (servers) {
385
+ for (const serverKey of Object.keys(servers)) ids.add(serverKey);
386
+ }
387
+ } catch {
388
+ // Malformed/unreadable plugin .mcp.json: skip its extra ids, the
389
+ // plugin's own name (added above) stays covered.
390
+ }
391
+ }
392
+ }
393
+
394
+ return ids;
395
+ }
396
+
329
397
  /**
330
398
  * Split resolved MCP entries into those still worth offering/installing in
331
- * the project and those the user already configured GLOBALLY (user scope, the
332
- * `mcpServers` key of `~/.claude.json`). A server configured there is live in
333
- * every project already usually with the real key embedded — so writing the
334
- * registry's `${VAR}`-placeholder copy into the project `.mcp.json` would at
335
- * best duplicate it and at worst shadow a working global config with a broken
336
- * placeholder one, then prompt the user for a key they already provided.
399
+ * the project and those the user already has covered elsewhere: configured
400
+ * GLOBALLY (user scope, the `mcpServers` key of `~/.claude.json`), or
401
+ * covered by an installed Claude Code PLUGIN
402
+ * (`~/.claude/plugins/installed_plugins.json`, see
403
+ * `resolvePluginCoveredIds`). Either way, the server is already live
404
+ * usually with the real key embedded so writing the registry's
405
+ * `${VAR}`-placeholder copy into the project `.mcp.json` would at best
406
+ * duplicate it and at worst shadow a working config with a broken
407
+ * placeholder one, then prompt the user for a key they already have.
337
408
  *
338
- * Read failures (missing/malformed `~/.claude.json`) fall back to "nothing is
339
- * global", the safe default: worst case the user sees the old behavior.
409
+ * Read failures (missing/malformed `~/.claude.json` or
410
+ * `installed_plugins.json`) fall back to "nothing is global"/"nothing is
411
+ * plugin-covered", the safe default: worst case the user sees the old
412
+ * behavior. An id present in BOTH sources is reported once, in
413
+ * `globallyConfigured` only (never duplicated into `pluginCovered`).
340
414
  *
341
415
  * @param {Object} args
342
416
  * @param {Array<{id: string}>} args.mcps - Resolved MCP entries to partition
343
417
  * @param {string} [args.claudeJsonPath] - Override of `~/.claude.json` (tests)
344
- * @returns {Promise<{toInstall: Array<Object>, globallyConfigured: string[]}>}
418
+ * @param {string} [args.installedPluginsPath] - Override of
419
+ * `~/.claude/plugins/installed_plugins.json` (tests)
420
+ * @returns {Promise<{toInstall: Array<Object>, globallyConfigured: string[], pluginCovered: string[]}>}
345
421
  */
346
- async function partitionGloballyConfigured({ mcps, claudeJsonPath }) {
347
- if (!mcps || mcps.length === 0) return { toInstall: [], globallyConfigured: [] };
422
+ async function partitionGloballyConfigured({ mcps, claudeJsonPath, installedPluginsPath }) {
423
+ if (!mcps || mcps.length === 0) return { toInstall: [], globallyConfigured: [], pluginCovered: [] };
348
424
 
349
425
  const file = claudeJsonPath || path.join(os.homedir(), '.claude.json');
350
426
  let globalIds = new Set();
@@ -359,13 +435,21 @@ async function partitionGloballyConfigured({ mcps, claudeJsonPath }) {
359
435
  }
360
436
  }
361
437
 
438
+ const pluginIds = await resolvePluginCoveredIds(installedPluginsPath);
439
+
362
440
  const toInstall = [];
363
441
  const globallyConfigured = [];
442
+ const pluginCovered = [];
364
443
  for (const mcp of mcps) {
365
- if (mcp && mcp.id && globalIds.has(mcp.id)) globallyConfigured.push(mcp.id);
366
- else toInstall.push(mcp);
444
+ if (mcp && mcp.id && globalIds.has(mcp.id)) {
445
+ globallyConfigured.push(mcp.id);
446
+ } else if (mcp && mcp.id && pluginIds.has(mcp.id)) {
447
+ pluginCovered.push(mcp.id);
448
+ } else {
449
+ toInstall.push(mcp);
450
+ }
367
451
  }
368
- return { toInstall, globallyConfigured };
452
+ return { toInstall, globallyConfigured, pluginCovered };
369
453
  }
370
454
 
371
455
  /**
@@ -629,9 +629,14 @@ class UI {
629
629
  // duplicate config and re-prompt for a key that already works. Filter it
630
630
  // out of every path below (multiselect, --mcps, --yes) with an info line
631
631
  // so nothing disappears silently.
632
- const { toInstall, globallyConfigured } = await partitionGloballyConfigured({ mcps: resolved });
632
+ const { toInstall, globallyConfigured, pluginCovered } = await partitionGloballyConfigured({ mcps: resolved });
633
633
  if (globallyConfigured.length > 0) {
634
634
  await prompts.log.info(`MCPs já configurados no seu Claude global (~/.claude.json), pulados: ${globallyConfigured.join(', ')}`);
635
+ }
636
+ if (pluginCovered.length > 0) {
637
+ await prompts.log.info(`MCPs já cobertos por plugin do Claude Code (user scope), pulados: ${pluginCovered.join(', ')}`);
638
+ }
639
+ if (globallyConfigured.length > 0 || pluginCovered.length > 0) {
635
640
  resolved = toInstall;
636
641
  if (resolved.length === 0) return { toWrite: [], toRecommend: [] };
637
642
  }