wizz-method 1.15.0 → 1.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/package.json +3 -2
  2. package/skills-registry.yaml +35 -104
  3. package/src/bmm-skills/3-solutioning/wizz-architecture/scripts/__pycache__/lint_spine.cpython-313.pyc +0 -0
  4. package/src/bmm-skills/3-solutioning/wizz-architecture/scripts/tests/__pycache__/test_lint_spine.cpython-313-pytest-9.1.1.pyc +0 -0
  5. package/src/bmm-skills/4-implementation/wizz-code-review/SKILL.md +3 -5
  6. package/src/bmm-skills/4-implementation/wizz-code-review/customize.toml +71 -0
  7. package/src/bmm-skills/4-implementation/wizz-code-review/references/claims-check.md +14 -0
  8. package/src/bmm-skills/4-implementation/wizz-code-review/references/deletion-check.md +14 -0
  9. package/src/bmm-skills/4-implementation/wizz-code-review/review-prompts/edge-case-hunter.md +110 -0
  10. package/src/bmm-skills/4-implementation/wizz-code-review/review-prompts/verification-gap.md +113 -0
  11. package/src/bmm-skills/4-implementation/wizz-code-review/steps/step-01-gather-context.md +32 -23
  12. package/src/bmm-skills/4-implementation/wizz-code-review/steps/step-02-review.md +10 -13
  13. package/src/bmm-skills/4-implementation/wizz-code-review/steps/step-03-triage.md +16 -22
  14. package/src/bmm-skills/4-implementation/wizz-code-review/steps/step-04-present.md +4 -2
  15. package/src/bmm-skills/4-implementation/wizz-quick-dev/customize.toml +76 -0
  16. package/src/bmm-skills/4-implementation/wizz-quick-dev/references/claims-check.md +14 -0
  17. package/src/bmm-skills/4-implementation/wizz-quick-dev/references/deletion-check.md +14 -0
  18. package/src/bmm-skills/4-implementation/wizz-quick-dev/review-prompts/edge-case-hunter.md +110 -0
  19. package/src/bmm-skills/4-implementation/wizz-quick-dev/review-prompts/verification-gap.md +113 -0
  20. package/src/bmm-skills/4-implementation/wizz-quick-dev/step-04-review.md +38 -16
  21. package/src/bmm-skills/4-implementation/wizz-retrospective/SKILL.md +56 -1488
  22. package/src/bmm-skills/4-implementation/wizz-retrospective/customize.toml +3 -5
  23. package/src/bmm-skills/4-implementation/wizz-retrospective/references/acceptance-verdict.md +55 -0
  24. package/src/bmm-skills/4-implementation/wizz-retrospective/references/aggregate-views.md +17 -0
  25. package/src/bmm-skills/4-implementation/wizz-retrospective/references/evidence-gathering.md +30 -0
  26. package/src/bmm-skills/4-implementation/wizz-retrospective/references/retro-document.md +84 -0
  27. package/src/bmm-skills/4-implementation/wizz-retrospective/references/team-discussion.md +22 -0
  28. package/src/bmm-skills/4-implementation/wizz-retrospective/scripts/__pycache__/sprint_status.cpython-313.pyc +0 -0
  29. package/src/bmm-skills/4-implementation/wizz-retrospective/scripts/git_evidence.py +304 -0
  30. package/src/bmm-skills/4-implementation/wizz-retrospective/scripts/sprint_status.py +746 -0
  31. package/src/bmm-skills/4-implementation/wizz-retrospective/scripts/tests/__pycache__/test_git_evidence.cpython-313-pytest-9.1.1.pyc +0 -0
  32. package/src/bmm-skills/4-implementation/wizz-retrospective/scripts/tests/__pycache__/test_sprint_status.cpython-313-pytest-9.1.1.pyc +0 -0
  33. package/src/bmm-skills/4-implementation/wizz-retrospective/scripts/tests/fixtures/sprint-status-template.yaml +71 -0
  34. package/src/bmm-skills/4-implementation/wizz-retrospective/scripts/tests/test_git_evidence.py +750 -0
  35. package/src/bmm-skills/4-implementation/wizz-retrospective/scripts/tests/test_sprint_status.py +1579 -0
  36. package/src/bmm-skills/4-implementation/wizz-sprint-planning/SKILL.md +39 -296
  37. package/src/bmm-skills/4-implementation/wizz-sprint-planning/customize.toml +1 -3
  38. package/src/bmm-skills/4-implementation/wizz-sprint-planning/references/fix-sprint-status.md +30 -0
  39. package/src/bmm-skills/4-implementation/wizz-sprint-planning/references/generate-tracking.md +25 -0
  40. package/src/bmm-skills/4-implementation/wizz-sprint-planning/references/readiness-gate.md +20 -0
  41. package/src/bmm-skills/4-implementation/wizz-sprint-planning/references/status-view.md +14 -0
  42. package/src/bmm-skills/4-implementation/wizz-sprint-planning/references/validate.md +10 -0
  43. package/src/bmm-skills/4-implementation/wizz-sprint-planning/scripts/__pycache__/sprint_plan.cpython-313.pyc +0 -0
  44. package/src/bmm-skills/4-implementation/wizz-sprint-planning/scripts/sprint_plan.py +697 -0
  45. package/src/bmm-skills/4-implementation/wizz-sprint-planning/scripts/tests/__pycache__/test_sprint_plan.cpython-313-pytest-9.1.1.pyc +0 -0
  46. package/src/bmm-skills/4-implementation/wizz-sprint-planning/scripts/tests/test_sprint_plan.py +524 -0
  47. package/src/bmm-skills/4-implementation/wizz-sprint-planning/sprint-status-template.yaml +9 -7
  48. package/src/bmm-skills/module-help.csv +3 -3
  49. package/src/core-skills/module-help.csv +1 -0
  50. package/src/core-skills/wizz-advanced-elicitation/SKILL.md +36 -114
  51. package/src/core-skills/wizz-advanced-elicitation/{methods.csv → assets/methods.csv} +29 -27
  52. package/src/core-skills/wizz-advanced-elicitation/customize.toml +54 -0
  53. package/src/core-skills/wizz-advanced-elicitation/scripts/__pycache__/pick_methods.cpython-313.pyc +0 -0
  54. package/src/core-skills/wizz-advanced-elicitation/scripts/pick_methods.py +233 -0
  55. package/src/core-skills/wizz-advanced-elicitation/scripts/tests/__pycache__/test_pick_methods.cpython-313-pytest-9.1.1.pyc +0 -0
  56. package/src/core-skills/wizz-advanced-elicitation/scripts/tests/test_pick_methods.py +228 -0
  57. package/src/core-skills/wizz-brainstorming/SKILL.md +2 -2
  58. package/src/core-skills/wizz-brainstorming/assets/brain-selector.html +2 -0
  59. package/src/core-skills/wizz-brainstorming/references/converge.md +1 -1
  60. package/src/core-skills/wizz-brainstorming/references/finalize.md +1 -1
  61. package/src/core-skills/wizz-brainstorming/references/headless.md +1 -1
  62. package/src/core-skills/wizz-brainstorming/references/mode-autonomous.md +1 -1
  63. package/src/core-skills/wizz-brainstorming/scripts/__pycache__/brain.cpython-313.pyc +0 -0
  64. package/src/core-skills/wizz-brainstorming/scripts/brain.py +36 -6
  65. package/src/core-skills/wizz-brainstorming/scripts/tests/__pycache__/test_brain.cpython-313-pytest-9.1.1.pyc +0 -0
  66. package/src/core-skills/wizz-brainstorming/scripts/tests/__pycache__/test_brain.cpython-314.pyc +0 -0
  67. package/src/core-skills/wizz-brainstorming/scripts/tests/test_brain.py +24 -2
  68. package/src/core-skills/wizz-customize/scripts/__pycache__/list_customizable_skills.cpython-313.pyc +0 -0
  69. package/src/core-skills/wizz-customize/scripts/tests/__pycache__/test_list_customizable_skills.cpython-313-pytest-9.1.1.pyc +0 -0
  70. package/src/core-skills/wizz-forge-idea/SKILL.md +107 -0
  71. package/src/core-skills/wizz-forge-idea/customize.toml +41 -0
  72. package/src/core-skills/wizz-forge-idea/scripts/__pycache__/resolve_personas.cpython-313.pyc +0 -0
  73. package/src/core-skills/wizz-forge-idea/scripts/__pycache__/resolve_personas.cpython-314.pyc +0 -0
  74. package/src/core-skills/wizz-forge-idea/scripts/resolve_personas.py +275 -0
  75. package/src/core-skills/wizz-forge-idea/scripts/tests/__pycache__/test_resolve_personas.cpython-313-pytest-9.1.1.pyc +0 -0
  76. package/src/core-skills/wizz-forge-idea/scripts/tests/__pycache__/test_resolve_personas.cpython-314.pyc +0 -0
  77. package/src/core-skills/wizz-forge-idea/scripts/tests/test_resolve_personas.py +138 -0
  78. package/src/core-skills/wizz-party-mode/SKILL.md +37 -54
  79. package/src/core-skills/wizz-party-mode/customize.toml +61 -2
  80. package/src/core-skills/wizz-party-mode/references/create-party.md +8 -3
  81. package/src/core-skills/wizz-party-mode/references/mode-agent-team.md +3 -1
  82. package/src/core-skills/wizz-party-mode/references/mode-subagent.md +16 -4
  83. package/src/core-skills/wizz-party-mode/references/party-memory.md +51 -0
  84. package/src/core-skills/wizz-party-mode/scripts/__pycache__/resolve_party.cpython-313.pyc +0 -0
  85. package/src/core-skills/wizz-party-mode/scripts/__pycache__/resolve_party.cpython-314.pyc +0 -0
  86. package/src/core-skills/wizz-party-mode/scripts/resolve_party.py +22 -7
  87. package/src/core-skills/wizz-party-mode/scripts/tests/__pycache__/test_resolve_party.cpython-313-pytest-9.1.1.pyc +0 -0
  88. package/src/core-skills/wizz-party-mode/scripts/tests/__pycache__/test_resolve_party.cpython-314.pyc +0 -0
  89. package/src/core-skills/wizz-party-mode/scripts/tests/{test-resolve_party.py → test_resolve_party.py} +8 -0
  90. package/src/core-skills/wizz-review-edge-case-hunter/SKILL.md +17 -3
  91. package/src/modules/wizz/agents/wizz-growth/customize.toml +10 -0
  92. package/src/scripts/__pycache__/memlog.cpython-313.pyc +0 -0
  93. package/src/scripts/tests/__pycache__/test_memlog.cpython-313-pytest-9.1.1.pyc +0 -0
  94. package/src/scripts/tests/__pycache__/test_resolve_customization.cpython-313-pytest-9.1.1.pyc +0 -0
  95. package/src/skills-lib/pesquisa-de-publico-do-piva/README.md +83 -0
  96. package/src/skills-lib/pesquisa-de-publico-do-piva/SKILL.md +65 -0
  97. package/src/skills-lib/pesquisa-de-publico-do-piva/assets/template.html +414 -0
  98. package/src/skills-lib/pesquisa-de-publico-do-piva/prompts.md +131 -0
  99. package/src/skills-lib/wizz-offer-forge/README.md +13 -0
  100. package/src/skills-lib/wizz-offer-forge/SKILL.md +74 -0
  101. package/src/skills-lib/wizz-offer-forge/references/empilhamento-valor.md +25 -0
  102. package/src/skills-lib/wizz-offer-forge/references/garantia-reversa.md +22 -0
  103. package/src/skills-lib/wizz-offer-forge/references/mecanismo-unico.md +28 -0
  104. package/src/skills-lib/wizz-offer-forge/references/ponte-trafego.md +21 -0
  105. package/src/skills-lib/wizz-router/references/routing-table-flat.md +2 -0
  106. package/tools/installer/core/installer.js +57 -0
  107. package/tools/installer/ide/_config-driven.js +3 -3
  108. package/tools/installer/ide/platform-codes.yaml +28 -0
  109. package/tools/installer/modules/external-manager.js +140 -2
  110. package/tools/installer/modules/official-modules.js +68 -21
  111. package/tools/installer/prompts.js +45 -103
  112. package/tools/installer/ui.js +64 -7
  113. package/wizz-modules.yaml +65 -11
  114. package/src/bmm-skills/4-implementation/wizz-sprint-planning/checklist.md +0 -34
  115. package/src/core-skills/wizz-brainstorming/scripts/memlog.py +0 -202
  116. package/src/core-skills/wizz-brainstorming/scripts/tests/test_memlog.py +0 -265
@@ -10,7 +10,6 @@
10
10
  let _clack = null;
11
11
  let _clackCore = null;
12
12
  let _picocolors = null;
13
- const fs = require('node:fs');
14
13
  const os = require('node:os');
15
14
  const path = require('node:path');
16
15
 
@@ -578,10 +577,6 @@ async function autocomplete(options) {
578
577
  return result;
579
578
  }
580
579
 
581
- function hasPathSeparator(value) {
582
- return value.endsWith('/') || value.endsWith('\\');
583
- }
584
-
585
580
  function expandHome(input) {
586
581
  if (!input) return input;
587
582
  if (input === '~') return os.homedir();
@@ -591,91 +586,60 @@ function expandHome(input) {
591
586
  return input;
592
587
  }
593
588
 
594
- function toDirectoryOption(value, label = value, synthetic = false) {
595
- return { value, label, synthetic };
596
- }
597
-
598
- function isExistingDirectory(value) {
599
- try {
600
- return fs.existsSync(value) && fs.statSync(value).isDirectory();
601
- } catch {
602
- return false;
603
- }
604
- }
605
-
606
- function listDirectoryOptions(input, options) {
589
+ /**
590
+ * Resolve o texto do prompt de diretório para um caminho absoluto.
591
+ * @param {string} input - Texto submetido
592
+ * @param {Object} [options] - { cwd, default }
593
+ * @returns {string} Caminho absoluto
594
+ */
595
+ function resolveDirectoryInput(input, options = {}) {
607
596
  const cwd = options.cwd || process.cwd();
608
- const rawInput = input.trim();
609
- const expandedInput = expandHome(rawInput);
610
- const trailingSep = hasPathSeparator(rawInput) || hasPathSeparator(expandedInput);
611
- const resolvedInput = expandedInput ? path.resolve(cwd, expandedInput) : cwd;
612
- const browseDir = expandedInput && !trailingSep && !isExistingDirectory(resolvedInput) ? path.dirname(resolvedInput) : resolvedInput;
613
- const prefix = expandedInput && browseDir !== resolvedInput ? path.basename(resolvedInput).toLowerCase() : '';
614
- const results = [];
615
-
616
- if (!trailingSep && isExistingDirectory(resolvedInput)) {
617
- results.push(toDirectoryOption(resolvedInput, `. (use this directory)`));
618
- }
619
-
620
- if (isExistingDirectory(browseDir)) {
621
- try {
622
- for (const entry of fs.readdirSync(browseDir, { withFileTypes: true })) {
623
- if (!entry.isDirectory()) continue;
624
- if (prefix && !entry.name.toLowerCase().startsWith(prefix)) continue;
625
- const fullPath = path.join(browseDir, entry.name);
626
- if (!results.some((option) => option.value === fullPath)) {
627
- results.push(toDirectoryOption(fullPath));
628
- }
629
- }
630
- } catch {
631
- // Skip unreadable directories; validation still reports path issues.
632
- }
633
- }
634
-
635
- const validation = options.validate?.(rawInput);
636
- const hasMatchingOption = results.some((option) => option.value === resolvedInput);
637
- if (expandedInput && !validation && !hasMatchingOption) {
638
- results.unshift(toDirectoryOption(resolvedInput, `Create/use: ${resolvedInput}`, true));
639
- }
640
-
641
- return results;
597
+ const rawInput = typeof input === 'string' ? input.trim() : '';
598
+ // Linha vazia significa "usar o default", resolvido do mesmo jeito que texto
599
+ // digitado, para um default `~/…` ou relativo ainda devolver caminho absoluto.
600
+ const effective = rawInput || (options.default || '').trim();
601
+ if (!effective) return cwd;
602
+ return path.resolve(cwd, expandHome(effective));
642
603
  }
643
604
 
644
605
  /**
645
- * Directory prompt with autocomplete candidates and create-directory support.
646
- * Uses @clack/core directly so typed paths that do not exist yet can still be
647
- * submitted when validation allows creating them.
606
+ * Directory prompt.
607
+ *
608
+ * Entrada de texto simples: o que está na linha é o que é submetido, resolvido
609
+ * para caminho absoluto.
610
+ *
611
+ * A linha vem pré-preenchida com o default (o diretório atual, de onde os
612
+ * installs costumam ser rodados) como texto editável de verdade, e não como
613
+ * placeholder, para poder ser encurtada ou estendida em vez de redigitada.
614
+ * Limpar a linha e apertar Enter ainda aceita o default.
615
+ *
616
+ * Substitui uma AutocompletePrompt cuja lista de candidatos era invisível: o
617
+ * Enter devolvia a opção em foco, não o texto digitado, então dava para instalar
618
+ * num caminho que o usuário nunca escreveu. Com TextPrompt o defeito some por
619
+ * construção — não existe seleção escondida que o Enter possa submeter.
620
+ *
648
621
  * @param {Object} options - Prompt options
649
622
  * @param {string} options.message - Prompt message
650
- * @param {string} [options.default] - Default directory
651
- * @param {string} [options.placeholder] - Placeholder text
623
+ * @param {string} [options.default] - Default directory, pré-preenchido na linha
624
+ * @param {string} [options.placeholder] - Placeholder mostrado só se a linha for limpa
652
625
  * @param {Function} [options.validate] - Sync validation function
653
- * @returns {Promise<string>} Selected or typed directory path
626
+ * @param {Object} [options.input] - Input stream (default process.stdin; testes injetam)
627
+ * @param {Object} [options.output] - Output stream (default process.stdout; testes injetam)
628
+ * @returns {Promise<string>} Caminho absoluto do diretório
654
629
  */
655
630
  async function directory(options) {
656
631
  const core = await getClackCore();
657
632
  const color = await getPicocolors();
658
- const tabCompletion = {
659
- prefix: '',
660
- index: -1,
661
- options: [],
662
- lastValue: '',
663
- };
664
633
 
665
- let prompt;
666
- prompt = new core.AutocompletePrompt({
634
+ const prompt = new core.TextPrompt({
635
+ ...(options.input ? { input: options.input } : {}),
636
+ ...(options.output ? { output: options.output } : {}),
667
637
  initialValue: options.default,
668
- options: () => listDirectoryOptions(prompt?.userInput || '', options),
669
- filter: () => true,
670
- validate: (value) => options.validate?.(value ?? prompt.userInput),
638
+ defaultValue: options.default,
639
+ validate: options.validate,
671
640
  render() {
672
- const title = `${color.gray('◆')} ${options.message}`;
673
641
  const bar = color.gray('│');
674
- const barEnd = color.gray('└');
675
642
  const userInput = this.userInput;
676
- const placeholder = options.placeholder || options.default;
677
- const inputDisplay = userInput ? this.userInputWithCursor : `${color.inverse(color.hidden('_'))}${color.dim(placeholder || '')}`;
678
- const errorLine = this.state === 'error' ? [`${color.yellow('│')} ${color.yellow(this.error)}`] : [];
679
643
 
680
644
  switch (this.state) {
681
645
  case 'submit': {
@@ -685,42 +649,20 @@ async function directory(options) {
685
649
  return `${color.gray('◇')} ${options.message}\n${bar} ${color.strikethrough(color.dim(userInput || ''))}`;
686
650
  }
687
651
  default: {
688
- return [title, `${bar} ${inputDisplay}`, ...errorLine, barEnd].join('\n');
652
+ const placeholder = options.placeholder || options.default;
653
+ const inputDisplay = userInput ? this.userInputWithCursor : `${color.inverse(color.hidden('_'))}${color.dim(placeholder || '')}`;
654
+ const lines = [`${color.gray('◆')} ${options.message}`, `${bar} ${inputDisplay}`];
655
+ if (this.state === 'error') lines.push(`${color.yellow('│')} ${color.yellow(this.error)}`);
656
+ lines.push(color.gray('└'));
657
+ return lines.join('\n');
689
658
  }
690
659
  }
691
660
  },
692
661
  });
693
662
 
694
- const hasSetUserInput = typeof prompt._setUserInput === 'function';
695
- const hasClearUserInput = typeof prompt._clearUserInput === 'function';
696
-
697
- prompt.on('key', (_, key) => {
698
- if (key?.name !== 'tab') return;
699
- if (!hasSetUserInput) return; // @clack/core API surface changed — skip Tab silently.
700
- const currentInput = prompt.userInput;
701
- const isContinuingCycle = tabCompletion.lastValue && currentInput === tabCompletion.lastValue;
702
- const completionOptions = isContinuingCycle ? tabCompletion.options : prompt.filteredOptions.filter((option) => !option.synthetic);
703
- if (completionOptions.length === 0) return;
704
-
705
- if (isContinuingCycle) {
706
- tabCompletion.index = (tabCompletion.index + 1) % completionOptions.length;
707
- } else {
708
- tabCompletion.prefix = currentInput;
709
- tabCompletion.options = completionOptions;
710
- tabCompletion.index = 0;
711
- }
712
-
713
- const focusedOption = completionOptions[tabCompletion.index];
714
- if (!focusedOption) return;
715
- const completedValue = focusedOption.value;
716
- tabCompletion.lastValue = completedValue;
717
- if (hasClearUserInput) prompt._clearUserInput();
718
- prompt._setUserInput(completedValue, true);
719
- });
720
-
721
663
  const result = await prompt.prompt();
722
664
  await handleCancel(result);
723
- return result;
665
+ return resolveDirectoryInput(result, options);
724
666
  }
725
667
 
726
668
  /**
@@ -114,6 +114,45 @@ async function getModuleVersion(moduleCode, { repoUrl = null, registryDefault =
114
114
  * UI utilities for the installer
115
115
  */
116
116
  class UI {
117
+ /**
118
+ * Avisa uma vez para cada módulo selecionado que o registry marca como
119
+ * depreciado.
120
+ *
121
+ * Um módulo depreciado nunca é removido da seleção — uma instalação
122
+ * existente continua funcionando e continua sendo atualizada quando pedido.
123
+ * O aviso é a única mudança de comportamento, e é ele que informa quem usa
124
+ * CLI (`--modules`, `--yes`) o que o picker interativo mostra como dica.
125
+ *
126
+ * @param {Array<string>} selectedModules - Códigos prestes a ser instalados
127
+ * @returns {Promise<Array<string>>} Os códigos depreciados que geraram aviso
128
+ */
129
+ async _warnDeprecatedModules(selectedModules = []) {
130
+ const externalManager = new ExternalModuleManager();
131
+ let registryModules;
132
+ try {
133
+ registryModules = await externalManager.listAvailable();
134
+ } catch {
135
+ return []; // Registry ilegível — nunca travar uma instalação por causa de um aviso.
136
+ }
137
+
138
+ const deprecatedByCode = new Map();
139
+ for (const mod of registryModules) {
140
+ if (!mod.deprecated) continue;
141
+ deprecatedByCode.set(mod.code, mod);
142
+ for (const alias of mod.aliases) deprecatedByCode.set(alias, mod);
143
+ }
144
+
145
+ const warned = [];
146
+ for (const code of selectedModules) {
147
+ const mod = deprecatedByCode.get(code);
148
+ if (!mod || warned.includes(mod.code)) continue;
149
+ warned.push(mod.code);
150
+ const detail = mod.deprecationMessage || 'It is no longer receiving updates.';
151
+ await prompts.log.warn(`${mod.name} (${mod.code}) is deprecated. ${detail}`);
152
+ }
153
+ return warned;
154
+ }
155
+
117
156
  async _retainUnavailableInstalledModules(selectedModules, installedModuleIds, wizzDir, options = {}) {
118
157
  const { OfficialModules } = require('./modules/official-modules');
119
158
  const officialCodes = new Set(['core']);
@@ -274,6 +313,10 @@ class UI {
274
313
 
275
314
  // Handle quick update separately
276
315
  if (actionType === 'quick-update') {
316
+ // O Quick Update nunca mostra o picker de módulos, então este é o único
317
+ // ponto em que uma instalação existente de módulo depreciado ouve o aviso.
318
+ await this._warnDeprecatedModules(existingInstall.moduleIds || []);
319
+
277
320
  return {
278
321
  actionType: 'quick-update',
279
322
  directory: confirmedDirectory,
@@ -338,6 +381,11 @@ class UI {
338
381
  );
339
382
  }
340
383
 
384
+ // Mostra os avisos de depreciação para o que acabou selecionado. O picker
385
+ // interativo só dá a dica na lista de opções, e os caminhos --modules /
386
+ // --yes nunca veem essa lista.
387
+ await this._warnDeprecatedModules(selectedModules);
388
+
341
389
  // For existing installs, resolve per-module update decisions BEFORE
342
390
  // we clone anything. Reads the existing manifest's recorded channel
343
391
  // per module and prompts the user on available upgrades (patch/minor
@@ -423,6 +471,8 @@ class UI {
423
471
  selectedModules.unshift('core');
424
472
  }
425
473
 
474
+ await this._warnDeprecatedModules(selectedModules);
475
+
426
476
  // Interactive channel gate: "Ready to install (all stable)? [Y/n]"
427
477
  // Only shown for fresh installs with no channel flags and an external module
428
478
  // selected. Skipped for prerelease launches because channelOptions.global
@@ -1236,25 +1286,32 @@ class UI {
1236
1286
  }
1237
1287
  }
1238
1288
 
1239
- // Add external registry modules (skip built-in duplicates)
1240
- const externalRegistryModules = registryModules.filter((mod) => !mod.builtIn && !builtInCodes.has(mod.code));
1289
+ // Add external registry modules (skip built-in duplicates and deprecated
1290
+ // modules that are not already installed — um módulo depreciado continua
1291
+ // visível só para quem já o tem instalado poder seguir gerenciando).
1292
+ const externalRegistryModules = registryModules.filter(
1293
+ (mod) => !mod.builtIn && !builtInCodes.has(mod.code) && (!mod.deprecated || installedModuleIds.has(mod.code)),
1294
+ );
1241
1295
  let externalRegistryEntries = [];
1242
1296
  if (externalRegistryModules.length > 0) {
1243
1297
  const spinner = await prompts.spinner();
1244
1298
  spinner.start('Checking latest module versions...');
1245
1299
 
1246
1300
  externalRegistryEntries = await Promise.all(
1247
- externalRegistryModules.map(async (mod) => ({
1248
- code: mod.code,
1249
- entry: await buildModuleEntry(
1301
+ externalRegistryModules.map(async (mod) => {
1302
+ const entry = await buildModuleEntry(
1250
1303
  mod.code,
1251
1304
  mod.name,
1252
1305
  mod.description,
1253
1306
  mod.defaultSelected,
1254
1307
  mod.url || null,
1255
1308
  mod.defaultChannel || null,
1256
- ),
1257
- })),
1309
+ );
1310
+ if (mod.deprecated && mod.deprecationMessage) {
1311
+ entry.hint = entry.hint ? `${entry.hint} — ${mod.deprecationMessage}` : mod.deprecationMessage;
1312
+ }
1313
+ return { code: mod.code, entry };
1314
+ }),
1258
1315
  );
1259
1316
 
1260
1317
  spinner.stop('Checked latest module versions.');
package/wizz-modules.yaml CHANGED
@@ -17,6 +17,29 @@
17
17
  # repo changes owner, disappears, or ships something unexpected.
18
18
  # type: experimental — same delegated trust, plus unstable by upstream's
19
19
  # own definition.
20
+ #
21
+ # deprecated (optional, default false) — when true, the module is hidden from
22
+ # the installer picker UNLESS it is already installed (so existing users can
23
+ # still see/manage it, but new users are not offered it).
24
+ # deprecation-message (optional) — surfaced in the picker hint for a deprecated
25
+ # module that is still installed; use it to point users to the replacement.
26
+ #
27
+ # marketplace-plugin (optional, default false) — when true, the module's
28
+ # installable skills are resolved from its .claude-plugin/marketplace.json via
29
+ # the plugin resolver (instead of copying the single module-definition dir).
30
+ # Use this for modules whose module.yaml does not sit alongside the skill
31
+ # folders. module-definition should still point at the real module.yaml so
32
+ # config/version lookups work.
33
+ #
34
+ # post-install-message (optional) — an "action needed" notice shown to the user
35
+ # after install completes, once for each install run that includes the module.
36
+ # Interactive installs require the user to acknowledge it (press Enter);
37
+ # non-interactive (--yes) installs print it and continue. Use for required
38
+ # follow-up steps (e.g. "run the X setup skill").
39
+ #
40
+ # aliases (optional) — prior `code` values this module was registered under.
41
+ # An existing install recorded under an alias resolves to this entry instead of
42
+ # being orphaned as "no source available" when the code changes.
20
43
 
21
44
  modules:
22
45
  bmad-method-test-architecture-enterprise:
@@ -41,19 +64,33 @@ modules:
41
64
  npmPackage: bmad-builder
42
65
  default_channel: stable
43
66
 
44
- wizz-automator:
45
- url: https://github.com/bmad-code-org/wizz-automator
46
- module-definition: skills/module.yaml
47
- code: automator
48
- name: "Wizz Automator Epic Builder Experimental"
49
- description: "EXPERIMENTAL: only supports claude and codex currently"
67
+ bmad-loop:
68
+ url: https://github.com/bmad-code-org/bmad-loop
69
+ module-definition: src/bmad_loop/data/skills/bmad-loop-setup/assets/module.yaml
70
+ code: bmad-loop
71
+ # bauto é o código pré-rename (o módulo era bmad-auto antes do repo e do
72
+ # conteúdo serem renomeados para bmad-loop); mantém instalações em bauto
73
+ # migrando para a frente em vez de virarem órfãs.
74
+ aliases: [bauto]
75
+ name: "Wizz Loop"
76
+ description: "Builds, verifies, and retros a whole epic unattended"
50
77
  defaultSelected: false
51
- type: experimental
52
- npmPackage: bmad-story-automator
53
- default_channel: next
78
+ type: upstream-org
79
+ default_channel: stable
80
+ # As skills vivem fora de um diretório único de module.yaml; resolve pelo
81
+ # .claude-plugin/marketplace.json via plugin resolver (ver external-manager).
82
+ marketplace-plugin: true
83
+ post-install-message: |
84
+ Wizz Loop installed. To finish setup, run the bmad-loop-setup skill
85
+ from your agent:
86
+
87
+ > use the bmad-loop-setup skill
88
+
89
+ It installs the bmad-loop orchestrator tool and wires up the per-project
90
+ hooks and policy. The automation skills don't run until setup completes.
54
91
 
55
92
  bmad-creative-intelligence-suite:
56
- url: https://github.com/bmad-code-org/wizz-module-creative-intelligence-suite
93
+ url: https://github.com/bmad-code-org/bmad-module-creative-intelligence-suite
57
94
  module-definition: src/module.yaml
58
95
  code: cis
59
96
  name: "Wizz Creative Intelligence Suite"
@@ -64,7 +101,7 @@ modules:
64
101
  default_channel: stable
65
102
 
66
103
  bmad-game-dev-studio:
67
- url: https://github.com/bmad-code-org/wizz-module-game-dev-studio.git
104
+ url: https://github.com/bmad-code-org/bmad-module-game-dev-studio.git
68
105
  module-definition: src/module.yaml
69
106
  code: gds
70
107
  name: "Wizz Game Dev Studio"
@@ -74,6 +111,21 @@ modules:
74
111
  npmPackage: bmad-game-dev-studio
75
112
  default_channel: stable
76
113
 
114
+ # --- Deprecated: hidden from the picker unless already installed ---
115
+
116
+ wizz-automator:
117
+ url: https://github.com/bmad-code-org/bmad-automator
118
+ module-definition: skills/module.yaml
119
+ code: automator
120
+ name: "Wizz Automator Epic Builder Experimental"
121
+ description: "EXPERIMENTAL: only supports claude and codex currently"
122
+ defaultSelected: false
123
+ type: experimental
124
+ npmPackage: bmad-story-automator
125
+ default_channel: next
126
+ deprecated: true
127
+ deprecation-message: "Wizz Automator has been deprecated and is replaced by Wizz Loop (bmad-loop). Install Wizz Loop instead."
128
+
77
129
  bmad-method-wds-expansion:
78
130
  url: https://github.com/bmad-code-org/bmad-method-wds-expansion
79
131
  module-definition: src/module.yaml
@@ -85,3 +137,5 @@ modules:
85
137
  type: upstream-org
86
138
  npmPackage: bmad-wds
87
139
  default_channel: stable
140
+ deprecated: true
141
+ deprecation-message: "Whiteport Design Studio is no longer receiving updates. Its capabilities are being folded into the bmm module as enhanced UX features. Your existing install is kept as-is."
@@ -1,34 +0,0 @@
1
- # Sprint Planning Validation Checklist
2
-
3
- ## Core Validation
4
-
5
- ### Complete Coverage Check
6
-
7
- - [ ] Every epic found in epic\*.md files appears in sprint-status.yaml
8
- - [ ] Every story found in epic\*.md files appears in sprint-status.yaml
9
- - [ ] Every epic has a corresponding retrospective entry
10
- - [ ] No development_status items in sprint-status.yaml that don't exist in epic files
11
- - [ ] action_items section (if it existed) carried over unchanged
12
-
13
- ### Parsing Verification
14
-
15
- Compare epic files against generated sprint-status.yaml:
16
-
17
- ```
18
- Epic Files Contains: Sprint Status Contains:
19
- ✓ Epic 1 ✓ epic-1: [status]
20
- ✓ Story 1.1: User Auth ✓ 1-1-user-auth: [status]
21
- ✓ Story 1.2: Account Mgmt ✓ 1-2-account-mgmt: [status]
22
- ✓ Story 1.3: Plant Naming ✓ 1-3-plant-naming: [status]
23
- ✓ epic-1-retrospective: [status]
24
- ✓ Epic 2 ✓ epic-2: [status]
25
- ✓ Story 2.1: Personality Model ✓ 2-1-personality-model: [status]
26
- ✓ Story 2.2: Chat Interface ✓ 2-2-chat-interface: [status]
27
- ✓ epic-2-retrospective: [status]
28
- ```
29
-
30
- ### Final Check
31
-
32
- - [ ] Total count of epics matches
33
- - [ ] Total count of stories matches
34
- - [ ] All items are in the expected order (epic, stories, retrospective)
@@ -1,202 +0,0 @@
1
- #!/usr/bin/env python3
2
- # /// script
3
- # requires-python = ">=3.10"
4
- # ///
5
- """memlog — an append-only memory log: LLM-optimal working memory for a skill.
6
-
7
- A memlog is the dense, chronological record of everything that mattered in a piece of
8
- work — every item the user generated or accepted — kept minimal like human memory: only
9
- what's important, never bloated. It persists ACROSS sessions, so a fresh session can
10
- load it and continue. It is NOT a deliverable; downstream artifacts (a brief, a PRD, a
11
- deck, a report) are *derived* from it on demand. The host skill supplies the vocabulary
12
- by how it calls `append` — the tool stays neutral.
13
-
14
- It is a FLAT log: there are no sections or grouping. Every entry is one line, recorded
15
- at the END in the order it happened. The chronology itself is the structure — an event
16
- like "started technique X" is just another entry, same as an idea or an insight.
17
-
18
- Two invariants make it trustworthy:
19
-
20
- 1. Append-only, chronological. Entries land at the end, in the order they happen.
21
- Nothing is ever inserted backward, reordered, or grouped.
22
- 2. Write-only / blind. Every command is an atomic, context-free write and echoes the
23
- new state as JSON, so the caller never re-reads the file mid-session. The one time
24
- the file is read is on resume — and the caller reads it itself, not via this script.
25
-
26
- The file shape (.memlog.md):
27
-
28
- ---
29
- topic: Onboarding flow for a budgeting app
30
- goal: lift week-1 retention
31
- status: active
32
- updated: 2026-05-30T14:22
33
- ---
34
-
35
- - (note) user picked techniques: SCAMPER, then Six Thinking Hats
36
- - (technique) started SCAMPER
37
- - (idea) skip the signup wall: let people try with sample data first
38
- - (idea) auto-import one bank account so the first screen shows real numbers
39
- - (question) is open-banking consent too heavy for step one?
40
- - (technique) started Six Thinking Hats
41
- - (idea) black-hat: imported transactions look scary before they're categorized
42
- - (insight) the "scary numbers" risk and the "real numbers" idea are one lever: show real data, pre-categorized
43
- - (direction) user wants to optimize for the anxious first-timer, not the power user
44
- - (decision) lead with one pre-categorized account; defer multi-account import
45
-
46
- Each entry may carry an optional `--type` — what KIND it is (idea, insight, question,
47
- decision, technique, …) — and an optional `--by` naming who it came from (e.g. `user`,
48
- `coach`), for sessions where authorship matters. Both render into one short inline tag:
49
- `(idea)`, `(idea by user)`, `(by coach)`. Omit them for a plain note. The host skill
50
- names the vocabulary; the script does not.
51
-
52
- Commands:
53
- init --workspace DIR [--field k=v ...] create the memlog (errors if it exists)
54
- append --workspace DIR --text STR [--type T] [--by W] append one entry at the end
55
- set --workspace DIR --key K --value V set/replace a frontmatter field
56
-
57
- The workspace is the run folder; the memlog is always {workspace}/.memlog.md.
58
- """
59
- import argparse
60
- import json
61
- import os
62
- import sys
63
- from datetime import datetime
64
- from pathlib import Path
65
-
66
- MEMLOG = ".memlog.md"
67
-
68
-
69
- def now() -> str:
70
- return datetime.now().strftime("%Y-%m-%dT%H:%M")
71
-
72
-
73
- def memlog_path(workspace: str) -> Path:
74
- return Path(workspace) / MEMLOG
75
-
76
-
77
- def split(text: str) -> tuple[dict, str]:
78
- """Return (frontmatter dict in source order, body str). Frontmatter is plain key: value.
79
-
80
- The closing fence is the first line that is *exactly* `---`, so a `---` inside a
81
- field value (topic/goal are free user text) never truncates the frontmatter.
82
- """
83
- lines = text.splitlines()
84
- if not lines or lines[0] != "---":
85
- raise ValueError(".memlog.md has no frontmatter")
86
- end = next((i for i in range(1, len(lines)) if lines[i] == "---"), None)
87
- if end is None:
88
- raise ValueError(".memlog.md frontmatter is not terminated")
89
- meta: dict[str, str] = {}
90
- for line in lines[1:end]:
91
- if ":" in line:
92
- k, v = line.split(":", 1)
93
- meta[k.strip()] = v.strip()
94
- return meta, "\n".join(lines[end + 1:]).lstrip("\n")
95
-
96
-
97
- def render(meta: dict, body: str) -> str:
98
- # Neutralize newlines in values so a multi-line field can't break the fence on re-read.
99
- fm = "\n".join(f"{k}: {' '.join(str(v).splitlines())}" for k, v in meta.items())
100
- return "---\n" + fm + "\n---\n\n" + body.rstrip("\n") + "\n"
101
-
102
-
103
- def touch(meta: dict) -> None:
104
- """Stamp `updated` and keep it last so the field order stays predictable."""
105
- meta.pop("updated", None)
106
- meta["updated"] = now()
107
-
108
-
109
- def write_atomic(path: Path, text: str) -> None:
110
- tmp = path.with_suffix(path.suffix + ".tmp")
111
- tmp.write_text(text, encoding="utf-8")
112
- os.replace(tmp, path)
113
-
114
-
115
- def entry_count(body: str) -> int:
116
- return sum(1 for ln in body.splitlines() if ln.startswith("- "))
117
-
118
-
119
- def ack(path: Path, meta: dict, body: str) -> None:
120
- """Echo new state so the caller never re-reads the file to know where it stands."""
121
- print(json.dumps({
122
- "ok": True,
123
- "memlog": str(path),
124
- "status": meta.get("status", ""),
125
- "entries": entry_count(body),
126
- }))
127
-
128
-
129
- def cmd_init(args) -> int:
130
- path = memlog_path(args.workspace)
131
- if path.exists():
132
- print(f"error: {path} already exists; use append/set to update it", file=sys.stderr)
133
- return 2
134
- path.parent.mkdir(parents=True, exist_ok=True)
135
- meta: dict[str, str] = {}
136
- for pair in args.field or []:
137
- if "=" not in pair:
138
- print(f"error: --field expects key=value, got {pair!r}", file=sys.stderr)
139
- return 2
140
- k, v = pair.split("=", 1)
141
- meta[k.strip()] = v.strip()
142
- meta.setdefault("status", "active")
143
- touch(meta)
144
- write_atomic(path, render(meta, ""))
145
- ack(path, meta, "")
146
- return 0
147
-
148
-
149
- def cmd_append(args) -> int:
150
- path = memlog_path(args.workspace)
151
- meta, body = split(path.read_text(encoding="utf-8"))
152
- text = " ".join(args.text.split()) # collapse newlines/runs → one-line entry, no prose bloat
153
- label = args.type or ""
154
- if args.by:
155
- label = f"{label} by {args.by}".strip() # attribution: "(idea by user)" / "(by coach)"
156
- tag = f"({label}) " if label else ""
157
- entry = f"- {tag}{text}"
158
- body = (body.rstrip("\n") + "\n" + entry) if body.strip() else entry # always at the end
159
- touch(meta)
160
- write_atomic(path, render(meta, body))
161
- ack(path, meta, body)
162
- return 0
163
-
164
-
165
- def cmd_set(args) -> int:
166
- path = memlog_path(args.workspace)
167
- meta, body = split(path.read_text(encoding="utf-8"))
168
- meta[args.key] = args.value
169
- touch(meta)
170
- write_atomic(path, render(meta, body))
171
- ack(path, meta, body)
172
- return 0
173
-
174
-
175
- def main(argv: list[str] | None = None) -> int:
176
- p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
177
- sub = p.add_subparsers(dest="cmd", required=True)
178
-
179
- pi = sub.add_parser("init", help="create the memlog")
180
- pi.add_argument("--workspace", required=True)
181
- pi.add_argument("--field", action="append", metavar="KEY=VALUE", help="frontmatter field (repeatable)")
182
- pi.set_defaults(func=cmd_init)
183
-
184
- pa = sub.add_parser("append", help="append one entry at the end")
185
- pa.add_argument("--workspace", required=True)
186
- pa.add_argument("--text", required=True)
187
- pa.add_argument("--type", help="entry kind, rendered as an inline tag")
188
- pa.add_argument("--by", help="who the entry came from (e.g. user, coach); rendered into the tag")
189
- pa.set_defaults(func=cmd_append)
190
-
191
- pset = sub.add_parser("set", help="set a frontmatter field")
192
- pset.add_argument("--workspace", required=True)
193
- pset.add_argument("--key", required=True)
194
- pset.add_argument("--value", required=True)
195
- pset.set_defaults(func=cmd_set)
196
-
197
- args = p.parse_args(argv)
198
- return args.func(args)
199
-
200
-
201
- if __name__ == "__main__":
202
- sys.exit(main())