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.
- package/package.json +3 -2
- package/skills-registry.yaml +35 -104
- package/src/bmm-skills/3-solutioning/wizz-architecture/scripts/__pycache__/lint_spine.cpython-313.pyc +0 -0
- package/src/bmm-skills/3-solutioning/wizz-architecture/scripts/tests/__pycache__/test_lint_spine.cpython-313-pytest-9.1.1.pyc +0 -0
- package/src/bmm-skills/4-implementation/wizz-code-review/SKILL.md +3 -5
- package/src/bmm-skills/4-implementation/wizz-code-review/customize.toml +71 -0
- package/src/bmm-skills/4-implementation/wizz-code-review/references/claims-check.md +14 -0
- package/src/bmm-skills/4-implementation/wizz-code-review/references/deletion-check.md +14 -0
- package/src/bmm-skills/4-implementation/wizz-code-review/review-prompts/edge-case-hunter.md +110 -0
- package/src/bmm-skills/4-implementation/wizz-code-review/review-prompts/verification-gap.md +113 -0
- package/src/bmm-skills/4-implementation/wizz-code-review/steps/step-01-gather-context.md +32 -23
- package/src/bmm-skills/4-implementation/wizz-code-review/steps/step-02-review.md +10 -13
- package/src/bmm-skills/4-implementation/wizz-code-review/steps/step-03-triage.md +16 -22
- package/src/bmm-skills/4-implementation/wizz-code-review/steps/step-04-present.md +4 -2
- package/src/bmm-skills/4-implementation/wizz-quick-dev/customize.toml +76 -0
- package/src/bmm-skills/4-implementation/wizz-quick-dev/references/claims-check.md +14 -0
- package/src/bmm-skills/4-implementation/wizz-quick-dev/references/deletion-check.md +14 -0
- package/src/bmm-skills/4-implementation/wizz-quick-dev/review-prompts/edge-case-hunter.md +110 -0
- package/src/bmm-skills/4-implementation/wizz-quick-dev/review-prompts/verification-gap.md +113 -0
- package/src/bmm-skills/4-implementation/wizz-quick-dev/step-04-review.md +38 -16
- package/src/bmm-skills/4-implementation/wizz-retrospective/SKILL.md +56 -1488
- package/src/bmm-skills/4-implementation/wizz-retrospective/customize.toml +3 -5
- package/src/bmm-skills/4-implementation/wizz-retrospective/references/acceptance-verdict.md +55 -0
- package/src/bmm-skills/4-implementation/wizz-retrospective/references/aggregate-views.md +17 -0
- package/src/bmm-skills/4-implementation/wizz-retrospective/references/evidence-gathering.md +30 -0
- package/src/bmm-skills/4-implementation/wizz-retrospective/references/retro-document.md +84 -0
- package/src/bmm-skills/4-implementation/wizz-retrospective/references/team-discussion.md +22 -0
- package/src/bmm-skills/4-implementation/wizz-retrospective/scripts/__pycache__/sprint_status.cpython-313.pyc +0 -0
- package/src/bmm-skills/4-implementation/wizz-retrospective/scripts/git_evidence.py +304 -0
- package/src/bmm-skills/4-implementation/wizz-retrospective/scripts/sprint_status.py +746 -0
- package/src/bmm-skills/4-implementation/wizz-retrospective/scripts/tests/__pycache__/test_git_evidence.cpython-313-pytest-9.1.1.pyc +0 -0
- package/src/bmm-skills/4-implementation/wizz-retrospective/scripts/tests/__pycache__/test_sprint_status.cpython-313-pytest-9.1.1.pyc +0 -0
- package/src/bmm-skills/4-implementation/wizz-retrospective/scripts/tests/fixtures/sprint-status-template.yaml +71 -0
- package/src/bmm-skills/4-implementation/wizz-retrospective/scripts/tests/test_git_evidence.py +750 -0
- package/src/bmm-skills/4-implementation/wizz-retrospective/scripts/tests/test_sprint_status.py +1579 -0
- package/src/bmm-skills/4-implementation/wizz-sprint-planning/SKILL.md +39 -296
- package/src/bmm-skills/4-implementation/wizz-sprint-planning/customize.toml +1 -3
- package/src/bmm-skills/4-implementation/wizz-sprint-planning/references/fix-sprint-status.md +30 -0
- package/src/bmm-skills/4-implementation/wizz-sprint-planning/references/generate-tracking.md +25 -0
- package/src/bmm-skills/4-implementation/wizz-sprint-planning/references/readiness-gate.md +20 -0
- package/src/bmm-skills/4-implementation/wizz-sprint-planning/references/status-view.md +14 -0
- package/src/bmm-skills/4-implementation/wizz-sprint-planning/references/validate.md +10 -0
- package/src/bmm-skills/4-implementation/wizz-sprint-planning/scripts/__pycache__/sprint_plan.cpython-313.pyc +0 -0
- package/src/bmm-skills/4-implementation/wizz-sprint-planning/scripts/sprint_plan.py +697 -0
- package/src/bmm-skills/4-implementation/wizz-sprint-planning/scripts/tests/__pycache__/test_sprint_plan.cpython-313-pytest-9.1.1.pyc +0 -0
- package/src/bmm-skills/4-implementation/wizz-sprint-planning/scripts/tests/test_sprint_plan.py +524 -0
- package/src/bmm-skills/4-implementation/wizz-sprint-planning/sprint-status-template.yaml +9 -7
- package/src/bmm-skills/module-help.csv +3 -3
- package/src/core-skills/module-help.csv +1 -0
- package/src/core-skills/wizz-advanced-elicitation/SKILL.md +36 -114
- package/src/core-skills/wizz-advanced-elicitation/{methods.csv → assets/methods.csv} +29 -27
- package/src/core-skills/wizz-advanced-elicitation/customize.toml +54 -0
- package/src/core-skills/wizz-advanced-elicitation/scripts/__pycache__/pick_methods.cpython-313.pyc +0 -0
- package/src/core-skills/wizz-advanced-elicitation/scripts/pick_methods.py +233 -0
- package/src/core-skills/wizz-advanced-elicitation/scripts/tests/__pycache__/test_pick_methods.cpython-313-pytest-9.1.1.pyc +0 -0
- package/src/core-skills/wizz-advanced-elicitation/scripts/tests/test_pick_methods.py +228 -0
- package/src/core-skills/wizz-brainstorming/SKILL.md +2 -2
- package/src/core-skills/wizz-brainstorming/assets/brain-selector.html +2 -0
- package/src/core-skills/wizz-brainstorming/references/converge.md +1 -1
- package/src/core-skills/wizz-brainstorming/references/finalize.md +1 -1
- package/src/core-skills/wizz-brainstorming/references/headless.md +1 -1
- package/src/core-skills/wizz-brainstorming/references/mode-autonomous.md +1 -1
- package/src/core-skills/wizz-brainstorming/scripts/__pycache__/brain.cpython-313.pyc +0 -0
- package/src/core-skills/wizz-brainstorming/scripts/brain.py +36 -6
- package/src/core-skills/wizz-brainstorming/scripts/tests/__pycache__/test_brain.cpython-313-pytest-9.1.1.pyc +0 -0
- package/src/core-skills/wizz-brainstorming/scripts/tests/__pycache__/test_brain.cpython-314.pyc +0 -0
- package/src/core-skills/wizz-brainstorming/scripts/tests/test_brain.py +24 -2
- package/src/core-skills/wizz-customize/scripts/__pycache__/list_customizable_skills.cpython-313.pyc +0 -0
- package/src/core-skills/wizz-customize/scripts/tests/__pycache__/test_list_customizable_skills.cpython-313-pytest-9.1.1.pyc +0 -0
- package/src/core-skills/wizz-forge-idea/SKILL.md +107 -0
- package/src/core-skills/wizz-forge-idea/customize.toml +41 -0
- package/src/core-skills/wizz-forge-idea/scripts/__pycache__/resolve_personas.cpython-313.pyc +0 -0
- package/src/core-skills/wizz-forge-idea/scripts/__pycache__/resolve_personas.cpython-314.pyc +0 -0
- package/src/core-skills/wizz-forge-idea/scripts/resolve_personas.py +275 -0
- package/src/core-skills/wizz-forge-idea/scripts/tests/__pycache__/test_resolve_personas.cpython-313-pytest-9.1.1.pyc +0 -0
- package/src/core-skills/wizz-forge-idea/scripts/tests/__pycache__/test_resolve_personas.cpython-314.pyc +0 -0
- package/src/core-skills/wizz-forge-idea/scripts/tests/test_resolve_personas.py +138 -0
- package/src/core-skills/wizz-party-mode/SKILL.md +37 -54
- package/src/core-skills/wizz-party-mode/customize.toml +61 -2
- package/src/core-skills/wizz-party-mode/references/create-party.md +8 -3
- package/src/core-skills/wizz-party-mode/references/mode-agent-team.md +3 -1
- package/src/core-skills/wizz-party-mode/references/mode-subagent.md +16 -4
- package/src/core-skills/wizz-party-mode/references/party-memory.md +51 -0
- package/src/core-skills/wizz-party-mode/scripts/__pycache__/resolve_party.cpython-313.pyc +0 -0
- package/src/core-skills/wizz-party-mode/scripts/__pycache__/resolve_party.cpython-314.pyc +0 -0
- package/src/core-skills/wizz-party-mode/scripts/resolve_party.py +22 -7
- package/src/core-skills/wizz-party-mode/scripts/tests/__pycache__/test_resolve_party.cpython-313-pytest-9.1.1.pyc +0 -0
- package/src/core-skills/wizz-party-mode/scripts/tests/__pycache__/test_resolve_party.cpython-314.pyc +0 -0
- package/src/core-skills/wizz-party-mode/scripts/tests/{test-resolve_party.py → test_resolve_party.py} +8 -0
- package/src/core-skills/wizz-review-edge-case-hunter/SKILL.md +17 -3
- package/src/modules/wizz/agents/wizz-growth/customize.toml +10 -0
- package/src/scripts/__pycache__/memlog.cpython-313.pyc +0 -0
- package/src/scripts/tests/__pycache__/test_memlog.cpython-313-pytest-9.1.1.pyc +0 -0
- package/src/scripts/tests/__pycache__/test_resolve_customization.cpython-313-pytest-9.1.1.pyc +0 -0
- package/src/skills-lib/pesquisa-de-publico-do-piva/README.md +83 -0
- package/src/skills-lib/pesquisa-de-publico-do-piva/SKILL.md +65 -0
- package/src/skills-lib/pesquisa-de-publico-do-piva/assets/template.html +414 -0
- package/src/skills-lib/pesquisa-de-publico-do-piva/prompts.md +131 -0
- package/src/skills-lib/wizz-offer-forge/README.md +13 -0
- package/src/skills-lib/wizz-offer-forge/SKILL.md +74 -0
- package/src/skills-lib/wizz-offer-forge/references/empilhamento-valor.md +25 -0
- package/src/skills-lib/wizz-offer-forge/references/garantia-reversa.md +22 -0
- package/src/skills-lib/wizz-offer-forge/references/mecanismo-unico.md +28 -0
- package/src/skills-lib/wizz-offer-forge/references/ponte-trafego.md +21 -0
- package/src/skills-lib/wizz-router/references/routing-table-flat.md +2 -0
- package/tools/installer/core/installer.js +57 -0
- package/tools/installer/ide/_config-driven.js +3 -3
- package/tools/installer/ide/platform-codes.yaml +28 -0
- package/tools/installer/modules/external-manager.js +140 -2
- package/tools/installer/modules/official-modules.js +68 -21
- package/tools/installer/prompts.js +45 -103
- package/tools/installer/ui.js +64 -7
- package/wizz-modules.yaml +65 -11
- package/src/bmm-skills/4-implementation/wizz-sprint-planning/checklist.md +0 -34
- package/src/core-skills/wizz-brainstorming/scripts/memlog.py +0 -202
- 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
|
-
|
|
595
|
-
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
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
|
-
|
|
610
|
-
|
|
611
|
-
const
|
|
612
|
-
|
|
613
|
-
|
|
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
|
|
646
|
-
*
|
|
647
|
-
*
|
|
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
|
|
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
|
-
* @
|
|
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
|
-
|
|
666
|
-
|
|
634
|
+
const prompt = new core.TextPrompt({
|
|
635
|
+
...(options.input ? { input: options.input } : {}),
|
|
636
|
+
...(options.output ? { output: options.output } : {}),
|
|
667
637
|
initialValue: options.default,
|
|
668
|
-
|
|
669
|
-
|
|
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
|
-
|
|
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
|
/**
|
package/tools/installer/ui.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
45
|
-
url: https://github.com/bmad-code-org/
|
|
46
|
-
module-definition: skills/module.yaml
|
|
47
|
-
code:
|
|
48
|
-
|
|
49
|
-
|
|
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:
|
|
52
|
-
|
|
53
|
-
|
|
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/
|
|
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/
|
|
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())
|