edusquads-cli 0.2.1 → 0.2.3
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/.claude/skills/edusquads/SKILL.md +33 -17
- package/README.md +18 -1
- package/_edusquads/memoria/USUARIO-ATIVO.md +20 -31
- package/base/comandos/edusquads-comandos.md +14 -4
- package/base/protocolo-memoria-usuario.md +37 -35
- package/base/scripts/__pycache__/edusquads_onboarding_memoria.cpython-312.pyc +0 -0
- package/base/scripts/edusquads_onboarding_memoria.py +176 -0
- package/bin/edusquads.js +77 -15
- package/carrosseis.md +27 -9
- package/package.json +1 -1
- package/squads/02-pesquisa/skills/refinar-icp.md +60 -0
|
@@ -34,10 +34,11 @@ Operar squads multiagente no Claude Code com comportamento semelhante ao OpenSqu
|
|
|
34
34
|
## Processo
|
|
35
35
|
1. ler contexto obrigatório e memória ativa
|
|
36
36
|
2. validar campos críticos de memória; coletar lacunas antes de planejar
|
|
37
|
-
3.
|
|
38
|
-
4.
|
|
39
|
-
5.
|
|
40
|
-
6.
|
|
37
|
+
3. refinar público com skill de ICP quando o público estiver genérico
|
|
38
|
+
4. diagnosticar intenção e mapear squads/agentes/skills adequados
|
|
39
|
+
5. propor ou executar plano em etapas com checkpoints
|
|
40
|
+
6. registrar decisões, evidências e status no run correspondente
|
|
41
|
+
7. encerrar com próximos passos objetivos
|
|
41
42
|
|
|
42
43
|
## Saída
|
|
43
44
|
- plano de execução multi-squad (quando aplicável)
|
|
@@ -87,21 +88,34 @@ Operar squads multiagente no Claude Code com comportamento semelhante ao OpenSqu
|
|
|
87
88
|
- Toda saída deve conter critério de qualidade.
|
|
88
89
|
|
|
89
90
|
## Memória do usuário (obrigatório)
|
|
90
|
-
###
|
|
91
|
-
- `_edusquads/memoria/USUARIO-ATIVO.md`
|
|
91
|
+
### Arquivos
|
|
92
|
+
- ativo: `_edusquads/memoria/USUARIO-ATIVO.md`
|
|
93
|
+
- modelo: `_edusquads/memoria/USUARIO-MODELO.md`
|
|
94
|
+
- perfis: `_edusquads/memoria/usuarios/<slug>.md`
|
|
92
95
|
|
|
93
96
|
### Campos críticos
|
|
94
|
-
-
|
|
95
|
-
-
|
|
96
|
-
-
|
|
97
|
-
-
|
|
98
|
-
-
|
|
97
|
+
- nome preferido
|
|
98
|
+
- o que a pessoa faz
|
|
99
|
+
- onde vende
|
|
100
|
+
- meta principal (30-90 dias)
|
|
101
|
+
- formato favorito
|
|
102
|
+
- público inicial (rascunho)
|
|
99
103
|
|
|
100
104
|
### Fluxo de memória
|
|
101
105
|
1. Ler memória ativa.
|
|
102
|
-
2. Se campo crítico faltar,
|
|
103
|
-
3.
|
|
104
|
-
4.
|
|
106
|
+
2. Se campo crítico faltar, iniciar onboarding enxuto.
|
|
107
|
+
3. Salvar briefing no perfil individual (`usuarios/<slug>.md`).
|
|
108
|
+
4. Atualizar `USUARIO-ATIVO.md` com o briefing coletado.
|
|
109
|
+
5. Refinar público com skill ICP (`squads/02-pesquisa/skills/refinar-icp.md`).
|
|
110
|
+
6. Só então propor plano de squads.
|
|
111
|
+
|
|
112
|
+
### Roteiro de briefing obrigatório
|
|
113
|
+
1. quem é a pessoa e o que ela faz
|
|
114
|
+
2. o que vende e onde vende
|
|
115
|
+
3. meta principal (30-90 dias)
|
|
116
|
+
4. formato favorito de conteúdo
|
|
117
|
+
5. público inicial em linguagem da pessoa
|
|
118
|
+
6. acionar skill de ICP para refinar público (`squads/02-pesquisa/skills/refinar-icp.md`)
|
|
105
119
|
|
|
106
120
|
## Modo de comando `/edusquads`
|
|
107
121
|
Interpretar intenção do usuário por subcomando semântico.
|
|
@@ -110,9 +124,11 @@ Interpretar intenção do usuário por subcomando semântico.
|
|
|
110
124
|
- Diagnóstico rápido e sugestão do próximo passo.
|
|
111
125
|
|
|
112
126
|
### `/edusquads init`
|
|
113
|
-
-
|
|
114
|
-
- Coleta
|
|
115
|
-
|
|
127
|
+
- Inicia onboarding do usuário.
|
|
128
|
+
- Coleta briefing enxuto e salva perfil + usuário ativo.
|
|
129
|
+
|
|
130
|
+
### `/edusquads onboarding`
|
|
131
|
+
- Força nova coleta de briefing para troca/atualização de usuário.
|
|
116
132
|
|
|
117
133
|
### `/edusquads status`
|
|
118
134
|
- Resume estado atual: memória, squads sugeridos, artefatos-chave.
|
package/README.md
CHANGED
|
@@ -29,8 +29,22 @@ npx edusquads-cli install --ide claude,codex,opencode,kilocode,antigravity
|
|
|
29
29
|
npx edusquads-cli install --ide all
|
|
30
30
|
npx edusquads-cli install --force
|
|
31
31
|
npx edusquads-cli install --dry-run
|
|
32
|
+
npx edusquads-cli install --skip-onboarding
|
|
32
33
|
```
|
|
33
34
|
|
|
35
|
+
Após instalar, o CLI pergunta no terminal se a pessoa quer preencher o briefing na hora.
|
|
36
|
+
Se aceitar, ele executa onboarding enxuto e grava a memória automaticamente.
|
|
37
|
+
|
|
38
|
+
Briefing enxuto coleta apenas:
|
|
39
|
+
- o que a pessoa faz
|
|
40
|
+
- o que vende e onde vende
|
|
41
|
+
- meta principal (30-90 dias)
|
|
42
|
+
- formato favorito
|
|
43
|
+
- público inicial (rascunho)
|
|
44
|
+
|
|
45
|
+
Depois disso, o sistema refina o público com a skill de ICP.
|
|
46
|
+
|
|
47
|
+
|
|
34
48
|
Depois da instalação, abra o projeto no Claude Code e use:
|
|
35
49
|
|
|
36
50
|
- `/edusquads`
|
|
@@ -43,7 +57,8 @@ Como o Claude Code suporta skills em `.claude/skills/<nome>/SKILL.md`, a skill `
|
|
|
43
57
|
- `especialistas/`: biblioteca de fundamentos por especialista
|
|
44
58
|
- `squads/`: definição dos squads e agentes oficiais
|
|
45
59
|
- `pesquisa/web/`: sínteses com fontes públicas
|
|
46
|
-
- `_edusquads/memoria/`: memória operacional do usuário
|
|
60
|
+
- `_edusquads/memoria/`: memória operacional do usuário (ativo + perfis em `usuarios/`)
|
|
61
|
+
- `squads/02-pesquisa/skills/refinar-icp.md`: skill para refinar público a partir do briefing
|
|
47
62
|
- `_edusquads/runs/`: histórico de execuções do `/edusquads`
|
|
48
63
|
- `_edusquads/browser_profile/`: sessão local de navegação (Playwright)
|
|
49
64
|
- `_edusquads/evidencias/`: evidências de investigação web
|
|
@@ -54,6 +69,8 @@ Sistema operacional implementado com foco em:
|
|
|
54
69
|
- comando `/edusquads`
|
|
55
70
|
- paridade funcional base inspirada no OpenSquad
|
|
56
71
|
- memória persistida do usuário (`_edusquads/memoria/USUARIO-ATIVO.md`)
|
|
72
|
+
- onboarding interativo enxuto de briefing (`base/scripts/edusquads_onboarding_memoria.py`)
|
|
73
|
+
- refino de público via skill ICP (`squads/02-pesquisa/skills/refinar-icp.md`)
|
|
57
74
|
- histórico de runs (`_edusquads/runs/`)
|
|
58
75
|
- protocolo de investigação web com Playwright (`base/protocolo-playwright-edusquads.md`)
|
|
59
76
|
- playbooks por plataforma (`base/playbooks/investigacao/`)
|
|
@@ -1,42 +1,31 @@
|
|
|
1
1
|
# Memória do Usuário Ativo
|
|
2
2
|
|
|
3
3
|
## Identidade
|
|
4
|
-
- Nome preferido:
|
|
5
|
-
-
|
|
6
|
-
- Papel do usuário: arquiteto/produto do framework
|
|
4
|
+
- Nome preferido:
|
|
5
|
+
- O que a pessoa faz:
|
|
7
6
|
|
|
8
|
-
##
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
- Oferta principal: sistema `/edusquads` com squads interligados e memória operacional
|
|
12
|
-
- Maturidade da operação (inicial/intermediária/avançada): intermediária (arquitetura já estruturada)
|
|
7
|
+
## Negócio e Canal
|
|
8
|
+
- O que vende:
|
|
9
|
+
- Onde vende:
|
|
13
10
|
|
|
14
|
-
##
|
|
15
|
-
-
|
|
16
|
-
- Objetivo secundário: consolidar integração entre squads e catálogo operacional
|
|
17
|
-
- KPI principal: capacidade de iniciar objetivo e receber plano/execução multi-squad com memória aplicada
|
|
11
|
+
## Meta principal
|
|
12
|
+
- Meta (30-90 dias):
|
|
18
13
|
|
|
19
|
-
##
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
-
|
|
26
|
-
- Equipe disponível: não informado
|
|
27
|
-
- Orçamento aproximado: não informado
|
|
28
|
-
- Limites operacionais: manter padrão de fundamento por especialista e comando único `/edusquads`
|
|
14
|
+
## Público
|
|
15
|
+
- Público inicial (rascunho):
|
|
16
|
+
- ICP prioritário (refinado):
|
|
17
|
+
- Variações de ICP:
|
|
18
|
+
-
|
|
19
|
+
-
|
|
20
|
+
-
|
|
29
21
|
|
|
30
|
-
##
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
- Dados disponíveis: documentação interna do projeto + pesquisas web registradas
|
|
22
|
+
## Preferências
|
|
23
|
+
- Formato favorito:
|
|
24
|
+
- Estilo de comunicação preferido:
|
|
34
25
|
|
|
35
26
|
## Histórico de decisões
|
|
36
|
-
- [
|
|
37
|
-
- [x] estrutura oficial: 16 squads e 160 agentes
|
|
38
|
-
- [x] memória ativa do usuário em `_edusquads/memoria/USUARIO-ATIVO.md`
|
|
27
|
+
- [ ]
|
|
39
28
|
|
|
40
29
|
## Última atualização
|
|
41
|
-
- Data:
|
|
42
|
-
- Origem da atualização:
|
|
30
|
+
- Data:
|
|
31
|
+
- Origem da atualização:
|
|
@@ -6,11 +6,16 @@
|
|
|
6
6
|
|
|
7
7
|
## Subcomandos semânticos (via args)
|
|
8
8
|
- `/edusquads init`
|
|
9
|
-
-
|
|
9
|
+
- inicia onboarding e coleta briefing enxuto da pessoa usuária
|
|
10
|
+
- salva perfil em `_edusquads/memoria/usuarios/` e atualiza `USUARIO-ATIVO.md`
|
|
11
|
+
- `/edusquads onboarding`
|
|
12
|
+
- força nova coleta de briefing para troca/atualização de usuário
|
|
10
13
|
- `/edusquads status`
|
|
11
14
|
- resume estado dos squads, memória e artefatos
|
|
12
15
|
- `/edusquads memoria`
|
|
13
16
|
- mostra e atualiza memória ativa
|
|
17
|
+
- `/edusquads icp`
|
|
18
|
+
- refina público-alvo com a skill de ICP (`squads/02-pesquisa/skills/refinar-icp.md`)
|
|
14
19
|
- `/edusquads squads`
|
|
15
20
|
- lista os 16 squads e indica quais usar no objetivo atual
|
|
16
21
|
- `/edusquads plano <objetivo>`
|
|
@@ -28,13 +33,19 @@
|
|
|
28
33
|
- inicia fluxo de autenticação manual para sessão Playwright
|
|
29
34
|
- `/edusquads revogar-sessoes`
|
|
30
35
|
- remove sessão local persistida em `_edusquads/browser_profile/`
|
|
36
|
+
- `/edusquads estruturar-coleta <run_id>`
|
|
37
|
+
- converte coleta bruta em sinais estruturados por plataforma
|
|
38
|
+
- usa `python base/scripts/edusquads_estruturar_coleta.py`
|
|
31
39
|
- `/edusquads concluir-investigacao <run_id>`
|
|
32
40
|
- preenche síntese/extrações automaticamente e fecha run
|
|
33
41
|
- usa `python base/scripts/edusquads_concluir_investigacao.py`
|
|
34
42
|
|
|
35
43
|
## Regras
|
|
36
44
|
- todos os subcomandos usam `_edusquads/memoria/USUARIO-ATIVO.md`
|
|
37
|
-
-
|
|
45
|
+
- onboarding é obrigatório quando memória crítica estiver vazia
|
|
46
|
+
- onboarding pode ser executado por `base/scripts/edusquads_onboarding_memoria.py`
|
|
47
|
+
- após onboarding, refinar público com `squads/02-pesquisa/skills/refinar-icp.md`
|
|
48
|
+
- onboarding deve salvar perfil em `_edusquads/memoria/usuarios/` e atualizar `USUARIO-ATIVO.md`
|
|
38
49
|
- cada execução relevante deve gerar/atualizar um run em `_edusquads/runs/`
|
|
39
50
|
- toda investigação web deve seguir `base/protocolo-playwright-edusquads.md`
|
|
40
51
|
- toda investigação web deve aplicar o playbook da plataforma em `base/playbooks/investigacao/`
|
|
@@ -42,5 +53,4 @@
|
|
|
42
53
|
- para estruturar coleta, usar `base/scripts/edusquads_estruturar_coleta.py`
|
|
43
54
|
- para fechar investigação, usar `base/scripts/edusquads_concluir_investigacao.py`
|
|
44
55
|
- nunca persistir sessão sem consentimento explícito
|
|
45
|
-
- decisões relevantes devem ser registradas na
|
|
46
|
-
ões relevantes devem ser registradas na seção de histórico da memória e no run ativo
|
|
56
|
+
- decisões relevantes devem ser registradas na memória e no run ativo
|
|
@@ -1,50 +1,52 @@
|
|
|
1
1
|
# Protocolo de Memória do Usuário (EduSquads)
|
|
2
2
|
|
|
3
3
|
## Objetivo
|
|
4
|
-
Manter uma memória operacional
|
|
4
|
+
Manter uma memória operacional da pessoa usuária para personalizar a atuação dos squads no comando `/edusquads`.
|
|
5
5
|
|
|
6
|
-
##
|
|
7
|
-
- `_edusquads/memoria/USUARIO-ATIVO.md`
|
|
6
|
+
## Arquivos de memória
|
|
7
|
+
- ativo: `_edusquads/memoria/USUARIO-ATIVO.md`
|
|
8
|
+
- modelo: `_edusquads/memoria/USUARIO-MODELO.md`
|
|
9
|
+
- perfis por pessoa: `_edusquads/memoria/usuarios/<slug>.md`
|
|
8
10
|
|
|
9
11
|
## Quando coletar memória
|
|
10
12
|
1. Primeira execução do `/edusquads` no projeto
|
|
11
|
-
2. Quando
|
|
12
|
-
3. Quando
|
|
13
|
-
|
|
14
|
-
## Campos mínimos
|
|
15
|
-
- nome preferido
|
|
16
|
-
- projeto/negócio principal
|
|
17
|
-
- nicho
|
|
18
|
-
- público-alvo
|
|
19
|
-
- objetivos atuais
|
|
20
|
-
- canais prioritários
|
|
21
|
-
- stack/ferramentas
|
|
22
|
-
- restrições (tempo, orçamento, equipe)
|
|
23
|
-
- estilo de comunicação preferido
|
|
24
|
-
- decisões já tomadas
|
|
13
|
+
2. Quando houver troca de usuário
|
|
14
|
+
3. Quando a pessoa pedir atualização de contexto
|
|
25
15
|
|
|
26
16
|
## Princípios
|
|
27
|
-
- coletar
|
|
28
|
-
-
|
|
29
|
-
-
|
|
17
|
+
- coletar somente dados úteis para execução
|
|
18
|
+
- manter briefing enxuto
|
|
19
|
+
- não pedir KPI técnico no onboarding inicial
|
|
30
20
|
- nunca registrar segredos/token/senhas na memória
|
|
31
21
|
|
|
32
|
-
## Formato de atualização
|
|
33
|
-
Sempre atualizar por bloco:
|
|
34
|
-
1. `## Estado atual`
|
|
35
|
-
2. `## Objetivos em andamento`
|
|
36
|
-
3. `## Preferências`
|
|
37
|
-
4. `## Restrições`
|
|
38
|
-
5. `## Histórico de decisões`
|
|
39
|
-
|
|
40
22
|
## Regras de uso no comando
|
|
41
23
|
- antes de planejar squads, ler `USUARIO-ATIVO.md`
|
|
42
|
-
- se campos críticos estiverem vazios,
|
|
43
|
-
-
|
|
24
|
+
- se campos críticos estiverem vazios, executar onboarding enxuto
|
|
25
|
+
- salvar briefing no perfil individual e atualizar o usuário ativo
|
|
26
|
+
- após onboarding, refinar público com skill ICP
|
|
44
27
|
|
|
45
28
|
## Campos críticos (não seguir sem isso)
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
29
|
+
- nome preferido
|
|
30
|
+
- o que a pessoa faz
|
|
31
|
+
- onde vende
|
|
32
|
+
- meta principal (30-90 dias)
|
|
33
|
+
- formato favorito
|
|
34
|
+
- público inicial (rascunho)
|
|
35
|
+
|
|
36
|
+
## Roteiro obrigatório de briefing (onboarding enxuto)
|
|
37
|
+
1. Quem é a pessoa e o que ela faz
|
|
38
|
+
2. O que vende e onde vende
|
|
39
|
+
3. Meta principal para os próximos 30-90 dias
|
|
40
|
+
4. Formato favorito de conteúdo
|
|
41
|
+
5. Público inicial em linguagem da pessoa
|
|
42
|
+
6. Refinar esse público com a skill de ICP (`squads/02-pesquisa/skills/refinar-icp.md`)
|
|
43
|
+
|
|
44
|
+
## Formato de atualização
|
|
45
|
+
Sempre atualizar por bloco:
|
|
46
|
+
1. `## Identidade`
|
|
47
|
+
2. `## Negócio e Canal`
|
|
48
|
+
3. `## Meta principal`
|
|
49
|
+
4. `## Público`
|
|
50
|
+
5. `## Preferências`
|
|
51
|
+
6. `## Histórico de decisões`
|
|
52
|
+
7. `## Última atualização`
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Onboarding enxuto de memória do EduSquads.
|
|
4
|
+
|
|
5
|
+
Coleta briefing essencial e salva em:
|
|
6
|
+
- _edusquads/memoria/USUARIO-ATIVO.md
|
|
7
|
+
- _edusquads/memoria/usuarios/<slug>.md
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import argparse
|
|
13
|
+
import datetime as dt
|
|
14
|
+
import re
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
RESET = "\033[0m"
|
|
18
|
+
BOLD = "\033[1m"
|
|
19
|
+
DIM = "\033[2m"
|
|
20
|
+
CYAN = "\033[36m"
|
|
21
|
+
GREEN = "\033[32m"
|
|
22
|
+
YELLOW = "\033[33m"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def ui_title(text: str) -> None:
|
|
26
|
+
print(f"\n{BOLD}{CYAN}╭──────────────────────────────────────────────╮{RESET}")
|
|
27
|
+
print(f"{BOLD}{CYAN}│{RESET} {BOLD}{text:<44}{RESET} {BOLD}{CYAN}│{RESET}")
|
|
28
|
+
print(f"{BOLD}{CYAN}╰──────────────────────────────────────────────╯{RESET}\n")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def ui_step(number: int, title: str) -> None:
|
|
32
|
+
print(f"{BOLD}{CYAN}[{number}/6]{RESET} {BOLD}{title}{RESET}")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def ask(prompt: str, required: bool = True, hint: str | None = None) -> str:
|
|
36
|
+
if hint:
|
|
37
|
+
print(f"{DIM}{hint}{RESET}")
|
|
38
|
+
while True:
|
|
39
|
+
value = input(f"{prompt}: ").strip()
|
|
40
|
+
if value or not required:
|
|
41
|
+
return value
|
|
42
|
+
print(f"{YELLOW}Campo obrigatório. Responda para continuar.{RESET}")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def slugify(text: str) -> str:
|
|
46
|
+
t = text.strip().lower()
|
|
47
|
+
t = re.sub(r"[^a-z0-9\s-]", "", t)
|
|
48
|
+
t = re.sub(r"\s+", "-", t)
|
|
49
|
+
t = re.sub(r"-+", "-", t)
|
|
50
|
+
return t or "usuario"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def refine_icp(data: dict) -> tuple[str, list[str]]:
|
|
54
|
+
publico_base = data["publico_inicial"]
|
|
55
|
+
canal = data["onde_vende"]
|
|
56
|
+
meta = data["meta_90_dias"]
|
|
57
|
+
formato = data["formato_favorito"]
|
|
58
|
+
|
|
59
|
+
icp_prioritario = (
|
|
60
|
+
f"Profissionais que já reconhecem o problema que {data['o_que_faz']} resolve, "
|
|
61
|
+
f"estão buscando avanço prático nos próximos 90 dias e consomem {formato} "
|
|
62
|
+
f"antes de decidir compra em {canal}."
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
variacoes = [
|
|
66
|
+
f"Conservador: {publico_base} com urgência imediata para atingir '{meta}'.",
|
|
67
|
+
f"Principal: {icp_prioritario}",
|
|
68
|
+
f"Expansão: público adjacente a {publico_base}, com dor semelhante e menor consciência do problema.",
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
return icp_prioritario, variacoes
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def render_memory(data: dict, source: str) -> str:
|
|
75
|
+
today = dt.date.today().isoformat()
|
|
76
|
+
icp_prioritario, variacoes = refine_icp(data)
|
|
77
|
+
|
|
78
|
+
return f"""# Memória do Usuário Ativo
|
|
79
|
+
|
|
80
|
+
## Identidade
|
|
81
|
+
- Nome preferido: {data['nome']}
|
|
82
|
+
- O que a pessoa faz: {data['o_que_faz']}
|
|
83
|
+
|
|
84
|
+
## Negócio e Canal
|
|
85
|
+
- O que vende: {data['o_que_vende']}
|
|
86
|
+
- Onde vende: {data['onde_vende']}
|
|
87
|
+
|
|
88
|
+
## Meta principal
|
|
89
|
+
- Meta (30-90 dias): {data['meta_90_dias']}
|
|
90
|
+
|
|
91
|
+
## Público
|
|
92
|
+
- Público inicial (rascunho): {data['publico_inicial']}
|
|
93
|
+
- ICP prioritário (refinado): {icp_prioritario}
|
|
94
|
+
- Variações de ICP:
|
|
95
|
+
- {variacoes[0]}
|
|
96
|
+
- {variacoes[1]}
|
|
97
|
+
- {variacoes[2]}
|
|
98
|
+
|
|
99
|
+
## Preferências
|
|
100
|
+
- Formato favorito: {data['formato_favorito']}
|
|
101
|
+
- Estilo de comunicação preferido: {data['estilo']}
|
|
102
|
+
|
|
103
|
+
## Histórico de decisões
|
|
104
|
+
- [x] briefing inicial coletado no onboarding enxuto
|
|
105
|
+
- [x] público refinado via skill de ICP (`squads/02-pesquisa/skills/refinar-icp.md`)
|
|
106
|
+
|
|
107
|
+
## Última atualização
|
|
108
|
+
- Data: {today}
|
|
109
|
+
- Origem da atualização: {source}
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def main() -> int:
|
|
114
|
+
parser = argparse.ArgumentParser(description="Onboarding enxuto de memória EduSquads")
|
|
115
|
+
parser.add_argument("--target", default=".", help="Diretório do projeto")
|
|
116
|
+
args = parser.parse_args()
|
|
117
|
+
|
|
118
|
+
root = Path(args.target).resolve()
|
|
119
|
+
mem_dir = root / "_edusquads" / "memoria"
|
|
120
|
+
users_dir = mem_dir / "usuarios"
|
|
121
|
+
mem_dir.mkdir(parents=True, exist_ok=True)
|
|
122
|
+
users_dir.mkdir(parents=True, exist_ok=True)
|
|
123
|
+
|
|
124
|
+
ui_title("EduSquads · Onboarding de Briefing")
|
|
125
|
+
print("Briefing enxuto: poucas perguntas, resposta objetiva.\n")
|
|
126
|
+
|
|
127
|
+
ui_step(1, "Identidade")
|
|
128
|
+
nome = ask("Nome preferido")
|
|
129
|
+
o_que_faz = ask("O que você faz", hint="Ex.: consultoria em IA, mentoria de vendas, agência de tráfego")
|
|
130
|
+
|
|
131
|
+
ui_step(2, "Oferta")
|
|
132
|
+
o_que_vende = ask("O que você vende", hint="Produto, serviço, programa, assinatura etc.")
|
|
133
|
+
onde_vende = ask("Onde você vende", hint="Ex.: Instagram DM, WhatsApp, landing page, LinkedIn, YouTube")
|
|
134
|
+
|
|
135
|
+
ui_step(3, "Meta")
|
|
136
|
+
meta_90_dias = ask("Meta principal (30-90 dias)", hint="Troque KPI técnico por resultado que você quer alcançar")
|
|
137
|
+
|
|
138
|
+
ui_step(4, "Público inicial")
|
|
139
|
+
publico_inicial = ask("Quem você quer atingir hoje", hint="Pode ser rascunho. A skill ICP vai refinar")
|
|
140
|
+
|
|
141
|
+
ui_step(5, "Preferências")
|
|
142
|
+
formato_favorito = ask("Formato favorito de conteúdo", hint="Ex.: carrossel, reels, live, aula, newsletter")
|
|
143
|
+
estilo = ask("Estilo de comunicação", required=False) or "direto e prático"
|
|
144
|
+
|
|
145
|
+
ui_step(6, "Confirmação")
|
|
146
|
+
print(f"{DIM}Vamos salvar e gerar ICP refinado automaticamente.{RESET}")
|
|
147
|
+
|
|
148
|
+
data = {
|
|
149
|
+
"nome": nome,
|
|
150
|
+
"o_que_faz": o_que_faz,
|
|
151
|
+
"o_que_vende": o_que_vende,
|
|
152
|
+
"onde_vende": onde_vende,
|
|
153
|
+
"meta_90_dias": meta_90_dias,
|
|
154
|
+
"publico_inicial": publico_inicial,
|
|
155
|
+
"formato_favorito": formato_favorito,
|
|
156
|
+
"estilo": estilo,
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
content = render_memory(data, source="onboarding-enxuto-interativo")
|
|
160
|
+
|
|
161
|
+
active_path = mem_dir / "USUARIO-ATIVO.md"
|
|
162
|
+
active_path.write_text(content, encoding="utf-8")
|
|
163
|
+
|
|
164
|
+
slug = slugify(nome)
|
|
165
|
+
profile_path = users_dir / f"{slug}.md"
|
|
166
|
+
profile_path.write_text(content, encoding="utf-8")
|
|
167
|
+
|
|
168
|
+
print(f"\n{GREEN}✅ Briefing salvo com sucesso{RESET}")
|
|
169
|
+
print(f"- ativo: {active_path}")
|
|
170
|
+
print(f"- perfil: {profile_path}")
|
|
171
|
+
print(f"- icp skill: squads/02-pesquisa/skills/refinar-icp.md\n")
|
|
172
|
+
return 0
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
if __name__ == "__main__":
|
|
176
|
+
raise SystemExit(main())
|
package/bin/edusquads.js
CHANGED
|
@@ -3,11 +3,35 @@
|
|
|
3
3
|
const fs = require("fs");
|
|
4
4
|
const path = require("path");
|
|
5
5
|
const readline = require("readline");
|
|
6
|
+
const { spawnSync } = require("child_process");
|
|
7
|
+
|
|
8
|
+
const UI = {
|
|
9
|
+
reset: "\x1b[0m",
|
|
10
|
+
bold: "\x1b[1m",
|
|
11
|
+
dim: "\x1b[2m",
|
|
12
|
+
cyan: "\x1b[36m",
|
|
13
|
+
green: "\x1b[32m",
|
|
14
|
+
yellow: "\x1b[33m",
|
|
15
|
+
red: "\x1b[31m",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
function style(text, ...codes) {
|
|
19
|
+
return `${codes.join("")}${text}${UI.reset}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function panel(title) {
|
|
23
|
+
const w = 52;
|
|
24
|
+
const t = ` ${title} `;
|
|
25
|
+
const line = "─".repeat(Math.max(0, w - t.length));
|
|
26
|
+
console.log(`\n${style("╭" + t + line + "╮", UI.cyan, UI.bold)}`);
|
|
27
|
+
console.log(`${style("╰" + "─".repeat(w) + "╯", UI.cyan, UI.bold)}`);
|
|
28
|
+
}
|
|
6
29
|
|
|
7
30
|
const args = process.argv.slice(2);
|
|
8
31
|
const command = args.find((a) => !a.startsWith("-")) || "install";
|
|
9
32
|
const force = args.includes("--force");
|
|
10
33
|
const dryRun = args.includes("--dry-run");
|
|
34
|
+
const skipOnboarding = args.includes("--skip-onboarding");
|
|
11
35
|
const targetArgIndex = args.findIndex((a) => a === "--target");
|
|
12
36
|
const targetDir =
|
|
13
37
|
targetArgIndex >= 0 && args[targetArgIndex + 1]
|
|
@@ -34,6 +58,8 @@ const SHARED_INSTALL_ITEMS = [
|
|
|
34
58
|
{ src: "pesquisa", dest: "pesquisa" },
|
|
35
59
|
{ src: "squads", dest: "squads" },
|
|
36
60
|
{ src: "_edusquads/memoria/USUARIO-ATIVO.md", dest: "_edusquads/memoria/USUARIO-ATIVO.md" },
|
|
61
|
+
{ src: "_edusquads/memoria/USUARIO-MODELO.md", dest: "_edusquads/memoria/USUARIO-MODELO.md" },
|
|
62
|
+
{ src: "_edusquads/memoria/usuarios/README.md", dest: "_edusquads/memoria/usuarios/README.md" },
|
|
37
63
|
{ src: "_edusquads/runs/RUN-MODELO.md", dest: "_edusquads/runs/RUN-MODELO.md" },
|
|
38
64
|
{ src: "_edusquads/runs/RUNS-INDEX.md", dest: "_edusquads/runs/RUNS-INDEX.md" },
|
|
39
65
|
{ src: "_edusquads/evidencias/EVIDENCIA-MODELO.md", dest: "_edusquads/evidencias/EVIDENCIA-MODELO.md" },
|
|
@@ -56,6 +82,7 @@ Opções:
|
|
|
56
82
|
Se omitido, o CLI pergunta no terminal.
|
|
57
83
|
--force Sobrescreve arquivos existentes
|
|
58
84
|
--dry-run Simula instalação sem gravar arquivos
|
|
85
|
+
--skip-onboarding Não pergunta briefing após instalar
|
|
59
86
|
`);
|
|
60
87
|
}
|
|
61
88
|
|
|
@@ -165,6 +192,34 @@ function ask(promptText) {
|
|
|
165
192
|
});
|
|
166
193
|
}
|
|
167
194
|
|
|
195
|
+
async function askOnboarding() {
|
|
196
|
+
if (skipOnboarding || dryRun) return false;
|
|
197
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
|
|
198
|
+
const ans = (await ask(`${style("\n→", UI.cyan, UI.bold)} Deseja iniciar o briefing guiado agora? ${style("(s/N)", UI.dim)} `)).trim().toLowerCase();
|
|
199
|
+
return ["s", "sim", "y", "yes"].includes(ans);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function runOnboarding(target) {
|
|
203
|
+
const script = path.join(target, "base", "scripts", "edusquads_onboarding_memoria.py");
|
|
204
|
+
if (!fs.existsSync(script)) {
|
|
205
|
+
console.log("\n⚠️ Script de onboarding não encontrado. Pule e rode manualmente depois.");
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const pyCmd = process.platform === "win32" ? "python" : "python3";
|
|
210
|
+
const r = spawnSync(pyCmd, [script, "--target", target], {
|
|
211
|
+
stdio: "inherit",
|
|
212
|
+
cwd: target,
|
|
213
|
+
env: process.env,
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
if (r.status !== 0) {
|
|
217
|
+
console.log("\n⚠️ Não foi possível concluir o onboarding automaticamente.");
|
|
218
|
+
console.log("Rode manualmente:");
|
|
219
|
+
console.log(` ${pyCmd} base/scripts/edusquads_onboarding_memoria.py --target ${target}`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
168
223
|
async function resolveIdes() {
|
|
169
224
|
if (ideArg) return parseIdes(ideArg);
|
|
170
225
|
|
|
@@ -172,15 +227,15 @@ async function resolveIdes() {
|
|
|
172
227
|
return ["claude"];
|
|
173
228
|
}
|
|
174
229
|
|
|
175
|
-
|
|
176
|
-
console.log(" 1) claude (Claude Code)");
|
|
177
|
-
console.log(" 2) codex
|
|
178
|
-
console.log(" 3) opencode
|
|
179
|
-
console.log(" 4) kilocode
|
|
180
|
-
console.log(" 5) antigravity
|
|
181
|
-
console.log(" 6) all (todas)");
|
|
230
|
+
panel("EduSquads · Seleção de IDE");
|
|
231
|
+
console.log(`${style(" 1)", UI.cyan)} claude ${style("(Claude Code)", UI.dim)}`);
|
|
232
|
+
console.log(`${style(" 2)", UI.cyan)} codex`);
|
|
233
|
+
console.log(`${style(" 3)", UI.cyan)} opencode`);
|
|
234
|
+
console.log(`${style(" 4)", UI.cyan)} kilocode`);
|
|
235
|
+
console.log(`${style(" 5)", UI.cyan)} antigravity`);
|
|
236
|
+
console.log(`${style(" 6)", UI.cyan)} all ${style("(todas)", UI.dim)}`);
|
|
182
237
|
|
|
183
|
-
const answer = (await ask("Digite número(s) separados por vírgula (default: 1):
|
|
238
|
+
const answer = (await ask(`${style("\n→", UI.cyan, UI.bold)} Digite número(s) separados por vírgula ${style("[default: 1]", UI.dim)}: `)).trim();
|
|
184
239
|
if (!answer) return ["claude"];
|
|
185
240
|
|
|
186
241
|
const numberMap = {
|
|
@@ -240,14 +295,21 @@ async function install() {
|
|
|
240
295
|
const gitignoreChanged = patchGitignore(targetDir);
|
|
241
296
|
writeInstallMarker(targetDir, ides);
|
|
242
297
|
|
|
243
|
-
|
|
244
|
-
console.log(
|
|
245
|
-
console.log(
|
|
246
|
-
console.log(
|
|
247
|
-
console.log(
|
|
248
|
-
console.log(
|
|
298
|
+
panel("EduSquads · Instalação concluída");
|
|
299
|
+
console.log(`${style("✔", UI.green, UI.bold)} destino: ${targetDir}`);
|
|
300
|
+
console.log(`${style("✔", UI.green, UI.bold)} IDEs: ${ides.join(", ")}`);
|
|
301
|
+
console.log(`${style("•", UI.cyan)} criados: ${report.created}`);
|
|
302
|
+
console.log(`${style("•", UI.cyan)} sobrescritos: ${report.overwritten}`);
|
|
303
|
+
console.log(`${style("•", UI.cyan)} ignorados: ${report.skipped}`);
|
|
304
|
+
console.log(`${style("•", UI.cyan)} .gitignore atualizado: ${gitignoreChanged ? "sim" : "não"}`);
|
|
305
|
+
|
|
306
|
+
const doOnboarding = await askOnboarding();
|
|
307
|
+
if (doOnboarding) {
|
|
308
|
+
console.log(`\n${style("→", UI.cyan, UI.bold)} Iniciando briefing guiado...\n`);
|
|
309
|
+
runOnboarding(targetDir);
|
|
310
|
+
}
|
|
249
311
|
|
|
250
|
-
console.log("
|
|
312
|
+
console.log(`\n${style("Próximo passo", UI.bold)}:`);
|
|
251
313
|
for (const ide of ides) {
|
|
252
314
|
if (ide === "claude") console.log("- Claude Code: /edusquads");
|
|
253
315
|
else console.log(`- ${ide}: skill 'edusquads' instalada em ${IDE_SKILL_PATHS[ide]}`);
|
package/carrosseis.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
Voce e um agente especializado em criar carrosseis profissionais para o Instagram no estilo editorial/investigativo inspirado em [PERFIL_INSPIRACAO] e perfis de growth/conteudo viral. Todos os carrosseis sao de autoria do **[SEU_INSTAGRAM]** e tem como tema principal **Inteligencia Artificial**.
|
|
4
4
|
|
|
5
|
+
## Pre-check obrigatorio (antes de qualquer criacao)
|
|
6
|
+
|
|
7
|
+
Antes de iniciar, **sempre** coletar e validar as credenciais da pessoa usuaria:
|
|
8
|
+
|
|
9
|
+
- `APIFY_API_TOKEN` (para pesquisa de tendencias no Reddit)
|
|
10
|
+
- `UNSPLASH_ACCESS_KEY` (para busca de fotos)
|
|
11
|
+
|
|
12
|
+
Regras:
|
|
13
|
+
- Nao iniciar roteiro, HTML ou imagens sem essas duas chaves.
|
|
14
|
+
- Se alguma chave faltar, pausar e pedir a chave primeiro.
|
|
15
|
+
- Nunca expor a chave em resposta, log, markdown ou print.
|
|
16
|
+
- Quando o ambiente suportar coleta segura, usar fluxo seguro de segredo (ex.: coleta mascarada).
|
|
17
|
+
|
|
5
18
|
---
|
|
6
19
|
|
|
7
20
|
## Identidade Visual (Estilo Editorial/Investigativo)
|
|
@@ -243,7 +256,7 @@ Use a API do Apify para acessar o Reddit e encontrar topicos em alta sobre **Int
|
|
|
243
256
|
|
|
244
257
|
```bash
|
|
245
258
|
# Buscar posts trending no Reddit sobre IA
|
|
246
|
-
curl -X POST "https://api.apify.com/v2/acts/trudax~reddit-scraper/runs?token
|
|
259
|
+
curl -X POST "https://api.apify.com/v2/acts/trudax~reddit-scraper/runs?token=${APIFY_API_TOKEN}" \
|
|
247
260
|
-H "Content-Type: application/json" \
|
|
248
261
|
-d '{
|
|
249
262
|
"startUrls": [
|
|
@@ -266,7 +279,7 @@ Depois, busque os resultados:
|
|
|
266
279
|
|
|
267
280
|
```bash
|
|
268
281
|
# Verificar status e buscar resultados
|
|
269
|
-
curl "https://api.apify.com/v2/acts/trudax~reddit-scraper/runs/last/dataset/items?token
|
|
282
|
+
curl "https://api.apify.com/v2/acts/trudax~reddit-scraper/runs/last/dataset/items?token=${APIFY_API_TOKEN}"
|
|
270
283
|
```
|
|
271
284
|
|
|
272
285
|
**Subreddits de IA recomendados:**
|
|
@@ -288,7 +301,7 @@ Analise os posts com mais engajamento e extraia:
|
|
|
288
301
|
|
|
289
302
|
```bash
|
|
290
303
|
# Buscar imagens relevantes ao tema
|
|
291
|
-
curl -H "Authorization: Client-ID
|
|
304
|
+
curl -H "Authorization: Client-ID ${UNSPLASH_ACCESS_KEY}" \
|
|
292
305
|
"https://api.unsplash.com/search/photos?query=TEMA_AQUI&per_page=10&orientation=portrait"
|
|
293
306
|
```
|
|
294
307
|
|
|
@@ -955,6 +968,7 @@ Siga [SEU_INSTAGRAM] para mais conteudo sobre IA.
|
|
|
955
968
|
|
|
956
969
|
Quando o usuario pedir para criar um carrossel, siga este fluxo:
|
|
957
970
|
|
|
971
|
+
0. **Coletar e validar credenciais** (`APIFY_API_TOKEN` e `UNSPLASH_ACCESS_KEY`)
|
|
958
972
|
1. **Pesquise tendencias de IA** no Reddit usando Apify API
|
|
959
973
|
2. **Escolha o tema** mais relevante/viral encontrado
|
|
960
974
|
3. **Crie o roteiro** com titulos investigativos e textos para cada slide
|
|
@@ -970,19 +984,23 @@ Quando o usuario pedir para criar um carrossel, siga este fluxo:
|
|
|
970
984
|
|
|
971
985
|
### Unsplash API
|
|
972
986
|
- **Endpoint**: `https://api.unsplash.com/search/photos`
|
|
973
|
-
- **Header**: `Authorization: Client-ID
|
|
974
|
-
- **
|
|
975
|
-
- **
|
|
976
|
-
- **Secret Key**: `[SUA_UNSPLASH_SECRET_KEY]`
|
|
977
|
-
- **Redirect URI**: `urn:ietf:wg:oauth:2.0:oob`
|
|
987
|
+
- **Header**: `Authorization: Client-ID ${UNSPLASH_ACCESS_KEY}`
|
|
988
|
+
- **Variavel obrigatoria**: `UNSPLASH_ACCESS_KEY`
|
|
989
|
+
- **Opcional**: `UNSPLASH_SECRET_KEY` (nao necessaria para busca publica de fotos)
|
|
978
990
|
- **Docs**: https://unsplash.com/documentation
|
|
979
991
|
- **Campo de URL para slides**: `results[n].urls.regular` (1080px)
|
|
980
992
|
|
|
981
993
|
### Apify (Reddit Scraper)
|
|
982
|
-
- **Token**: `
|
|
994
|
+
- **Token**: `${APIFY_API_TOKEN}`
|
|
995
|
+
- **Variavel obrigatoria**: `APIFY_API_TOKEN`
|
|
983
996
|
- **Actor**: `trudax~reddit-scraper`
|
|
984
997
|
- **Endpoint base**: `https://api.apify.com/v2/acts/trudax~reddit-scraper/runs`
|
|
985
998
|
|
|
986
999
|
### Playwright MCP
|
|
987
1000
|
- Usar para renderizar HTML e capturar screenshots dos slides
|
|
988
1001
|
- Viewport: 1080x1350 (formato Instagram 4:5)
|
|
1002
|
+
|
|
1003
|
+
### Politica de seguranca
|
|
1004
|
+
- Nunca escrever chaves em markdown, codigo fonte, logs ou resposta final.
|
|
1005
|
+
- Nunca usar token fixo/hardcoded no comando.
|
|
1006
|
+
- Se faltar credencial, interromper e solicitar antes de continuar.
|
package/package.json
CHANGED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Skill: Refinar ICP
|
|
2
|
+
|
|
3
|
+
## objetivo
|
|
4
|
+
Refinar o público inicial informado pela pessoa usuária em um ICP operacional, específico e acionável para conteúdo, oferta e aquisição.
|
|
5
|
+
|
|
6
|
+
## quando usar
|
|
7
|
+
- quando o briefing tiver público genérico (ex.: "empreendedores", "empresas")
|
|
8
|
+
- quando a mensagem estiver ampla demais
|
|
9
|
+
- antes de gerar plano de conteúdo, copy ou tráfego
|
|
10
|
+
|
|
11
|
+
## quando não usar
|
|
12
|
+
- quando o usuário já trouxer ICP específico validado recentemente
|
|
13
|
+
- quando a tarefa não exigir definição de público
|
|
14
|
+
|
|
15
|
+
## entradas
|
|
16
|
+
- o que a pessoa faz
|
|
17
|
+
- o que vende
|
|
18
|
+
- onde vende
|
|
19
|
+
- meta principal (30-90 dias)
|
|
20
|
+
- formato favorito
|
|
21
|
+
- público inicial (rascunho)
|
|
22
|
+
|
|
23
|
+
## processo
|
|
24
|
+
1. Identificar quem compra hoje vs. quem a pessoa gostaria de atrair.
|
|
25
|
+
2. Mapear dor principal, contexto e nível de consciência do público.
|
|
26
|
+
3. Delimitar recorte de ICP por cenário de compra (canal + momento + problema).
|
|
27
|
+
4. Redigir ICP em linguagem objetiva (quem é, dor, objetivo, objeção, gatilho de compra).
|
|
28
|
+
5. Gerar 3 variações de ICP (foco conservador, foco principal, foco expansão).
|
|
29
|
+
6. Recomendar ICP prioritário com justificativa curta.
|
|
30
|
+
|
|
31
|
+
## saída
|
|
32
|
+
- ICP prioritário (1 parágrafo)
|
|
33
|
+
- 3 variações de ICP (bullets)
|
|
34
|
+
- sinais de validação inicial (o que observar em 2-4 semanas)
|
|
35
|
+
- ajustes de mensagem por formato favorito
|
|
36
|
+
|
|
37
|
+
## critérios
|
|
38
|
+
- específico o suficiente para segmentação prática
|
|
39
|
+
- coerente com o que a pessoa vende e onde vende
|
|
40
|
+
- linguagem simples, sem jargão desnecessário
|
|
41
|
+
- aplicável em conteúdo/copy/oferta imediatamente
|
|
42
|
+
|
|
43
|
+
## fundamento
|
|
44
|
+
Inspirado em princípios publicamente associados a April Dunford (posicionamento), Joanna Wiebe (voz do cliente) e Donald Miller (clareza de mensagem).
|
|
45
|
+
|
|
46
|
+
## heurísticas
|
|
47
|
+
- "genérico" não é ICP: sempre exigir recorte de contexto
|
|
48
|
+
- priorizar dor concreta sobre atributo demográfico isolado
|
|
49
|
+
- ICP bom facilita decisão editorial e comercial na mesma direção
|
|
50
|
+
|
|
51
|
+
## anti-padrões
|
|
52
|
+
- ICP amplo demais ("todo mundo")
|
|
53
|
+
- foco em persona estética sem dor/comportamento de compra
|
|
54
|
+
- trocar ICP a cada semana sem evidência
|
|
55
|
+
|
|
56
|
+
## fontes
|
|
57
|
+
- `especialistas/posicionamento/april-dunford.md`
|
|
58
|
+
- `especialistas/copy/joanna-wiebe.md`
|
|
59
|
+
- `especialistas/mensagem/donald-miller.md`
|
|
60
|
+
- `base/protocolo-memoria-usuario.md`
|