dd-harness 0.26.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/curar.js +1 -0
- package/dist/index.js +98 -2
- package/dist/skill.d.ts +71 -0
- package/dist/skill.js +143 -0
- package/dist/skills-iniciais.d.ts +20 -0
- package/dist/skills-iniciais.js +461 -0
- package/package.json +1 -1
package/dist/curar.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -21,6 +21,8 @@ import { pergunta, escolha, fechaPerguntas } from "./pergunta.js";
|
|
|
21
21
|
import { buscaPolitica } from "./politica.js";
|
|
22
22
|
import { avisoDeOrdem, blocoDeSessao, criaFase, editaFase, formataChangelog, formataRoadmap, leFases, } from "./roadmap.js";
|
|
23
23
|
import { decideDoHook, guardaCache } from "./cinto.js";
|
|
24
|
+
import { escrevePonteirosDeSkills, leSkill, leSkills, semeiaSkills } from "./skill.js";
|
|
25
|
+
import { SKILLS_INICIAIS } from "./skills-iniciais.js";
|
|
24
26
|
import { busca } from "./buscar.js";
|
|
25
27
|
import { apaga, arquiva, edita, le, promove } from "./curar.js";
|
|
26
28
|
import { criaPasta } from "./pasta.js";
|
|
@@ -75,6 +77,9 @@ const AJUDA = `dd-harness — a política e o Brain do projeto, no serviço
|
|
|
75
77
|
dd-harness cinto o interceptador pré-voo: lê a edição no stdin
|
|
76
78
|
e devolve a memória que fala daquele trecho.
|
|
77
79
|
Quem chama é o hook PreToolUse, não você
|
|
80
|
+
dd-harness skills as skills deste projeto e quando cada uma serve
|
|
81
|
+
dd-harness skill <nome> o procedimento de uma delas (o mesmo que o
|
|
82
|
+
agente recebe ao invocá-la)
|
|
78
83
|
dd-harness roadmap as fases abertas: a atual inteira, as próximas
|
|
79
84
|
por título (opcional — projeto sem fase não tem)
|
|
80
85
|
dd-harness changelog [--versao <v>]
|
|
@@ -113,6 +118,39 @@ function argumento(argv, nome) {
|
|
|
113
118
|
}
|
|
114
119
|
const API_PADRAO = "https://dd-harness.vercel.app";
|
|
115
120
|
// --- roadmap e changelog ---
|
|
121
|
+
/**
|
|
122
|
+
* `skills` e `skill <slug>` — so LEITURA.
|
|
123
|
+
*
|
|
124
|
+
* Criar e editar ficam na interface de proposito: escrever procedimento e trabalho de
|
|
125
|
+
* texto longo, e um editor de verdade e melhor que um flag de linha de comando. O que o
|
|
126
|
+
* terminal precisa e ver o que existe e conferir um procedimento sem trocar de janela.
|
|
127
|
+
*/
|
|
128
|
+
async function comandoSkills() {
|
|
129
|
+
const skills = await leSkills(process.cwd());
|
|
130
|
+
if (skills.length === 0) {
|
|
131
|
+
console.log("Nenhuma skill neste projeto.\n" +
|
|
132
|
+
"Rode `dd-harness start` para semear as iniciais, ou crie uma pela interface.");
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
for (const s of skills) {
|
|
136
|
+
console.log(`/${s.slug}${s.so_por_comando ? " (só por comando)" : ""}`);
|
|
137
|
+
console.log(` ${s.descricao}`);
|
|
138
|
+
console.log("");
|
|
139
|
+
}
|
|
140
|
+
console.log(`${skills.length} skill(s). \`dd-harness skill <nome>\` mostra o procedimento.`);
|
|
141
|
+
}
|
|
142
|
+
async function comandoSkill(argv) {
|
|
143
|
+
const slug = argv[0];
|
|
144
|
+
if (!slug || slug.startsWith("-")) {
|
|
145
|
+
throw new Error("uso: dd-harness skill <nome>");
|
|
146
|
+
}
|
|
147
|
+
const s = await leSkill(process.cwd(), slug);
|
|
148
|
+
console.log(`# ${s.slug}
|
|
149
|
+
`);
|
|
150
|
+
console.log(s.descricao);
|
|
151
|
+
console.log("");
|
|
152
|
+
console.log(s.conteudo.trim() || "_(sem procedimento escrito — a skill existe e não faz nada.)_");
|
|
153
|
+
}
|
|
116
154
|
async function comandoRoadmap() {
|
|
117
155
|
console.log(formataRoadmap(await leFases(process.cwd())));
|
|
118
156
|
}
|
|
@@ -439,6 +477,33 @@ async function comandoStart() {
|
|
|
439
477
|
acrescentado: `atualizado ${arquivo} (apontamento acrescentado ao que já existia)`,
|
|
440
478
|
}[r.estado]);
|
|
441
479
|
}
|
|
480
|
+
// 6.5. Os ponteiros das skills do projeto.
|
|
481
|
+
//
|
|
482
|
+
// O Claude Code descobre skill lendo `.claude/skills/*/SKILL.md` na ABERTURA da sessao,
|
|
483
|
+
// e servidor MCP nao fornece skill — entao o arquivo em disco e obrigatorio. O que ele
|
|
484
|
+
// carrega e so o frontmatter mais a chamada a `ler_skill`: o procedimento fica no
|
|
485
|
+
// servico, e corrigi-lo corrige em todos os repositorios de uma vez.
|
|
486
|
+
//
|
|
487
|
+
// Nunca falha o `start`: projeto sem skill e o caso comum, e nao ter skill nao impede
|
|
488
|
+
// nada do resto.
|
|
489
|
+
try {
|
|
490
|
+
// Projeto novo nasce com as skills iniciais, como nasce com politica e briefing. Num
|
|
491
|
+
// projeto que ja tem as suas, isto nao faz nada.
|
|
492
|
+
const { criadas } = await semeiaSkills(process.cwd(), SKILLS_INICIAIS);
|
|
493
|
+
if (criadas.length > 0) {
|
|
494
|
+
console.log(`criada(s) ${criadas.length} skill(s) inicial(is): ${criadas.join(", ")}`);
|
|
495
|
+
}
|
|
496
|
+
const skills = await leSkills(process.cwd());
|
|
497
|
+
if (skills.length > 0) {
|
|
498
|
+
const { escritos } = await escrevePonteirosDeSkills(process.cwd(), skills);
|
|
499
|
+
console.log(escritos > 0
|
|
500
|
+
? `escritos ${escritos} ponteiro(s) de skill em .claude/skills/`
|
|
501
|
+
: `${skills.length} skill(s) — ponteiros já estavam em dia`);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
catch {
|
|
505
|
+
// Serviço fora do ar ou projeto recém-criado: o `start` segue.
|
|
506
|
+
}
|
|
442
507
|
// 7. Onde esta o monorepo do worker nesta maquina? So pergunta uma vez, e so importa
|
|
443
508
|
// se houver memoria na fila agora — pular aqui nao trava nada, so avisa mais vezes.
|
|
444
509
|
const semWorkerConfigurado = !(await temWorkerConfigurado());
|
|
@@ -791,6 +856,25 @@ async function comandoPolitica(argv) {
|
|
|
791
856
|
if (r.memorias) {
|
|
792
857
|
await guardaCache(process.cwd(), { memorias: r.memorias });
|
|
793
858
|
}
|
|
859
|
+
// Os ponteiros das skills, pelo mesmo motivo — e por um a mais: skill criada na
|
|
860
|
+
// interface so virava arquivo no proximo `dd-harness start`, e "rode X depois" e
|
|
861
|
+
// instrucao que ninguem segue (medido na rodada 006, com o cache do cinto).
|
|
862
|
+
//
|
|
863
|
+
// Escrever AQUI acerta o tempo exato: o Claude Code descobre skill na ABERTURA da
|
|
864
|
+
// sessao, entao o ponteiro criado agora vale a partir da proxima — que e o mais cedo
|
|
865
|
+
// que a skill poderia existir para o host, faca-se o que se fizer.
|
|
866
|
+
//
|
|
867
|
+
// Custa uma requisicao a mais na abertura. Nao vai de carona no payload da politica
|
|
868
|
+
// porque skill nao entra no contexto da sessao: o que o host le e o arquivo, e o
|
|
869
|
+
// servidor so precisa ser consultado para escreve-lo.
|
|
870
|
+
try {
|
|
871
|
+
const skills = await leSkills(process.cwd());
|
|
872
|
+
if (skills.length > 0)
|
|
873
|
+
await escrevePonteirosDeSkills(process.cwd(), skills);
|
|
874
|
+
}
|
|
875
|
+
catch {
|
|
876
|
+
// Sem skills, sem rede, ou projeto novo: o hook nao pode falhar por causa disto.
|
|
877
|
+
}
|
|
794
878
|
console.log(JSON.stringify({ hookSpecificOutput: contextoDaSessao(r, worker) }));
|
|
795
879
|
return;
|
|
796
880
|
}
|
|
@@ -863,11 +947,19 @@ function contextoDaSessao(r, worker) {
|
|
|
863
947
|
// O momento certo de perguntar: quem acabou de descrever o projeto tem o contexto
|
|
864
948
|
// fresco para dizer por onde ele vai. Depois disso, ninguem mais pergunta — e uma
|
|
865
949
|
// feature que so se descobre lendo a lista de ferramentas nao se descobre.
|
|
866
|
-
"6.
|
|
950
|
+
"6. Pergunte se o trabalho deste projeto tem **fases** — algo que " +
|
|
867
951
|
"atravessa sessões, com ordem entre as partes. Se tiver, proponha as primeiras e " +
|
|
868
952
|
"crie-as com `criar_fase`; a de menor ordem vira o foco de cada sessão, e " +
|
|
869
953
|
"concluí-la a move para o changelog sozinha. Se for um projeto pequeno, diga que " +
|
|
870
|
-
"roadmap é opcional e siga sem ele — não insista
|
|
954
|
+
"roadmap é opcional e siga sem ele — não insista.\n" +
|
|
955
|
+
// O unico momento em que a stack acabou de ser descrita. Depois disto ninguem
|
|
956
|
+
// pergunta de novo, e uma skill que so se descobre lendo a lista nao se descobre —
|
|
957
|
+
// foi o mesmo gap que o roadmap teve, e que o passo 6 fechou.
|
|
958
|
+
"7. Por fim, invoque a skill `propor-ferramentas`. A stack acabou de ser " +
|
|
959
|
+
"descrita e as restrições estão frescas — é o único momento em que a pesquisa " +
|
|
960
|
+
"tem o filtro certo. Ela **propõe**: MCP, skill ou lib que combine com este " +
|
|
961
|
+
"projeto, nada é instalado sem o OK do usuário, e recusar tudo é resposta " +
|
|
962
|
+
"válida. Se a skill não existir nesta sessão, siga sem ela." +
|
|
871
963
|
fila,
|
|
872
964
|
};
|
|
873
965
|
}
|
|
@@ -1067,6 +1159,10 @@ async function principal() {
|
|
|
1067
1159
|
return comandoPolitica(resto);
|
|
1068
1160
|
case "cinto":
|
|
1069
1161
|
return comandoCinto();
|
|
1162
|
+
case "skills":
|
|
1163
|
+
return comandoSkills();
|
|
1164
|
+
case "skill":
|
|
1165
|
+
return comandoSkill(resto);
|
|
1070
1166
|
case "roadmap":
|
|
1071
1167
|
return comandoRoadmap();
|
|
1072
1168
|
case "changelog":
|
package/dist/skill.d.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skills: o procedimento mora no servico, o disco guarda so o ponteiro.
|
|
3
|
+
*
|
|
4
|
+
* O Claude Code descobre skill lendo `.claude/skills/<nome>/SKILL.md` na ABERTURA da
|
|
5
|
+
* sessao, e nenhum servidor MCP pode fornecer skill — o protocolo serve ferramentas e
|
|
6
|
+
* prompts, nao skills. Entao o arquivo em disco e inevitavel.
|
|
7
|
+
*
|
|
8
|
+
* O que e evitavel e o CONTEUDO em disco. O ponteiro carrega o frontmatter real (que e o
|
|
9
|
+
* que o host le para listar e decidir invocar) e um corpo de poucas linhas mandando
|
|
10
|
+
* chamar `ler_skill`. O procedimento fica num lugar so, e corrigi-lo corrige em todos os
|
|
11
|
+
* repositorios — que e o motivo de skills terem saido do disco.
|
|
12
|
+
*
|
|
13
|
+
* Repare que o custo de contexto NAO e o motivo: o Claude Code ja carrega so a descricao
|
|
14
|
+
* de cada skill e busca o corpo quando invocada. O ponteiro nao economiza nada ali — ele
|
|
15
|
+
* resolve divergencia, nao peso.
|
|
16
|
+
*/
|
|
17
|
+
export type Skill = {
|
|
18
|
+
slug: string;
|
|
19
|
+
descricao: string;
|
|
20
|
+
so_por_comando: boolean;
|
|
21
|
+
ferramentas: string[];
|
|
22
|
+
dica_de_argumento: string | null;
|
|
23
|
+
caminhos: string[];
|
|
24
|
+
atualizada_em: string;
|
|
25
|
+
};
|
|
26
|
+
export type SkillCompleta = Skill & {
|
|
27
|
+
conteudo: string;
|
|
28
|
+
};
|
|
29
|
+
export declare function leSkills(raiz: string): Promise<Skill[]>;
|
|
30
|
+
export declare function leSkill(raiz: string, slug: string): Promise<SkillCompleta>;
|
|
31
|
+
export declare function criaSkill(raiz: string, dados: NovaSkillInicial & Partial<Pick<Skill, "so_por_comando" | "ferramentas" | "caminhos" | "dica_de_argumento">>): Promise<{
|
|
32
|
+
skill: Skill;
|
|
33
|
+
}>;
|
|
34
|
+
/**
|
|
35
|
+
* O SKILL.md que vai para o disco: frontmatter de verdade, corpo que aponta para o MCP.
|
|
36
|
+
*
|
|
37
|
+
* `allowed-tools` sempre inclui a ferramenta que busca o procedimento — sem ela o ponteiro
|
|
38
|
+
* pediria permissao para ler a propria skill, e um "permitir?" antes de cada invocacao
|
|
39
|
+
* ensina a desligar o mecanismo.
|
|
40
|
+
*/
|
|
41
|
+
export declare function ponteiroDaSkill(skill: Skill): string;
|
|
42
|
+
/**
|
|
43
|
+
* Escreve os ponteiros de todas as skills do projeto.
|
|
44
|
+
*
|
|
45
|
+
* NAO apaga diretorio que sobrou: uma skill apagada no servico deixa o ponteiro orfao, e
|
|
46
|
+
* varrer `.claude/skills/` inteiro levaria junto as skills que a pessoa escreveu a mao —
|
|
47
|
+
* que sao legitimas e nao passam por aqui. O DELETE da API avisa o que apagar; a decisao
|
|
48
|
+
* de apagar arquivo alheio nao e nossa.
|
|
49
|
+
*/
|
|
50
|
+
export declare function escrevePonteirosDeSkills(raiz: string, skills: Skill[]): Promise<{
|
|
51
|
+
escritos: number;
|
|
52
|
+
}>;
|
|
53
|
+
/** O minimo para criar uma skill: o resto tem default no banco. */
|
|
54
|
+
export type NovaSkillInicial = {
|
|
55
|
+
slug: string;
|
|
56
|
+
descricao: string;
|
|
57
|
+
conteudo: string;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Semeia as skills iniciais — so num projeto que ainda nao tem nenhuma.
|
|
61
|
+
*
|
|
62
|
+
* A checagem e "nenhuma skill", e nao "esta skill especifica": quem apagou
|
|
63
|
+
* `como-desenvolver` tomou uma decisao, e recria-la a cada `start` a desfaria em silencio.
|
|
64
|
+
* Projeto com qualquer skill propria ja passou do ponto de ser semeado.
|
|
65
|
+
*
|
|
66
|
+
* Devolve o que criou. Nunca lanca por falha de rede: semear e conveniencia de projeto
|
|
67
|
+
* novo, e um `start` nao pode falhar porque o mimo nao pode ser entregue.
|
|
68
|
+
*/
|
|
69
|
+
export declare function semeiaSkills(raiz: string, iniciais: NovaSkillInicial[]): Promise<{
|
|
70
|
+
criadas: string[];
|
|
71
|
+
}>;
|
package/dist/skill.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { cabecalhos, credencial, pede, recusa } from "./api.js";
|
|
4
|
+
// --- rede ---
|
|
5
|
+
export async function leSkills(raiz) {
|
|
6
|
+
const { config, token } = await credencial(raiz);
|
|
7
|
+
const url = new URL(`${config.api}/api/v1/skills`);
|
|
8
|
+
url.searchParams.set("tenant", config.tenant);
|
|
9
|
+
url.searchParams.set("projeto", config.projeto);
|
|
10
|
+
const resposta = await pede(url, { headers: cabecalhos(token) });
|
|
11
|
+
if (!resposta.ok)
|
|
12
|
+
await recusa(resposta);
|
|
13
|
+
return (await resposta.json()).skills;
|
|
14
|
+
}
|
|
15
|
+
export async function leSkill(raiz, slug) {
|
|
16
|
+
const { config, token } = await credencial(raiz);
|
|
17
|
+
const url = new URL(`${config.api}/api/v1/skills/${encodeURIComponent(slug)}`);
|
|
18
|
+
url.searchParams.set("tenant", config.tenant);
|
|
19
|
+
url.searchParams.set("projeto", config.projeto);
|
|
20
|
+
const resposta = await pede(url, { headers: cabecalhos(token) });
|
|
21
|
+
if (!resposta.ok)
|
|
22
|
+
await recusa(resposta);
|
|
23
|
+
return (await resposta.json()).skill;
|
|
24
|
+
}
|
|
25
|
+
export async function criaSkill(raiz,
|
|
26
|
+
// Só o que a API aceita na criação: `atualizada_em` é do servidor, e aceitá-la no tipo
|
|
27
|
+
// prometeria um campo que o Zod descarta em silêncio.
|
|
28
|
+
dados) {
|
|
29
|
+
const { config, token } = await credencial(raiz);
|
|
30
|
+
const resposta = await pede(`${config.api}/api/v1/skills`, {
|
|
31
|
+
method: "POST",
|
|
32
|
+
headers: cabecalhos(token, true),
|
|
33
|
+
body: JSON.stringify({ tenant: config.tenant, projeto: config.projeto, ...dados }),
|
|
34
|
+
});
|
|
35
|
+
if (!resposta.ok)
|
|
36
|
+
await recusa(resposta);
|
|
37
|
+
return (await resposta.json());
|
|
38
|
+
}
|
|
39
|
+
// --- o ponteiro em disco (puro, para ser testado sem rede) ---
|
|
40
|
+
/**
|
|
41
|
+
* Escapa o que quebraria o YAML do frontmatter.
|
|
42
|
+
*
|
|
43
|
+
* A descricao vem de texto livre editado na interface: `: ` no meio dela faz o parser ler
|
|
44
|
+
* uma chave nova, e uma quebra de linha encerra o valor. Aspas duplas com escape resolvem
|
|
45
|
+
* os dois — e um frontmatter quebrado nao da erro visivel, a skill simplesmente some da
|
|
46
|
+
* lista.
|
|
47
|
+
*/
|
|
48
|
+
function comoEscalarYaml(texto) {
|
|
49
|
+
return `"${texto.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\r?\n/g, " ")}"`;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* O SKILL.md que vai para o disco: frontmatter de verdade, corpo que aponta para o MCP.
|
|
53
|
+
*
|
|
54
|
+
* `allowed-tools` sempre inclui a ferramenta que busca o procedimento — sem ela o ponteiro
|
|
55
|
+
* pediria permissao para ler a propria skill, e um "permitir?" antes de cada invocacao
|
|
56
|
+
* ensina a desligar o mecanismo.
|
|
57
|
+
*/
|
|
58
|
+
export function ponteiroDaSkill(skill) {
|
|
59
|
+
const frontmatter = [
|
|
60
|
+
"---",
|
|
61
|
+
`name: ${skill.slug}`,
|
|
62
|
+
`description: ${comoEscalarYaml(skill.descricao)}`,
|
|
63
|
+
];
|
|
64
|
+
if (skill.so_por_comando)
|
|
65
|
+
frontmatter.push("disable-model-invocation: true");
|
|
66
|
+
if (skill.dica_de_argumento) {
|
|
67
|
+
frontmatter.push(`argument-hint: ${comoEscalarYaml(skill.dica_de_argumento)}`);
|
|
68
|
+
}
|
|
69
|
+
const ferramentas = ["mcp__dd-harness__ler_skill", ...skill.ferramentas];
|
|
70
|
+
frontmatter.push(`allowed-tools: ${ferramentas.join(", ")}`);
|
|
71
|
+
if (skill.caminhos.length > 0) {
|
|
72
|
+
frontmatter.push(`paths: ${skill.caminhos.join(", ")}`);
|
|
73
|
+
}
|
|
74
|
+
frontmatter.push("---");
|
|
75
|
+
return `${frontmatter.join("\n")}
|
|
76
|
+
|
|
77
|
+
# ${skill.slug}
|
|
78
|
+
|
|
79
|
+
O procedimento desta skill vive no dd-harness, não neste arquivo.
|
|
80
|
+
|
|
81
|
+
**Chame a ferramenta MCP \`ler_skill\` com \`slug: "${skill.slug}"\` e siga o que ela
|
|
82
|
+
devolver.** É a primeira ação — não tente executar a skill a partir deste arquivo, que é
|
|
83
|
+
só o ponteiro.
|
|
84
|
+
|
|
85
|
+
Se a ferramenta não existir, o servidor MCP do dd-harness não está declarado: avise o
|
|
86
|
+
usuário em vez de improvisar um procedimento.
|
|
87
|
+
`;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Escreve os ponteiros de todas as skills do projeto.
|
|
91
|
+
*
|
|
92
|
+
* NAO apaga diretorio que sobrou: uma skill apagada no servico deixa o ponteiro orfao, e
|
|
93
|
+
* varrer `.claude/skills/` inteiro levaria junto as skills que a pessoa escreveu a mao —
|
|
94
|
+
* que sao legitimas e nao passam por aqui. O DELETE da API avisa o que apagar; a decisao
|
|
95
|
+
* de apagar arquivo alheio nao e nossa.
|
|
96
|
+
*/
|
|
97
|
+
export async function escrevePonteirosDeSkills(raiz, skills) {
|
|
98
|
+
let escritos = 0;
|
|
99
|
+
for (const skill of skills) {
|
|
100
|
+
const dir = join(raiz, ".claude", "skills", skill.slug);
|
|
101
|
+
const caminho = join(dir, "SKILL.md");
|
|
102
|
+
const novo = ponteiroDaSkill(skill);
|
|
103
|
+
// Só escreve quando muda: reescrever igual sujaria o `git status` a cada `start`.
|
|
104
|
+
const atual = await readFile(caminho, "utf8").catch(() => null);
|
|
105
|
+
if (atual === novo)
|
|
106
|
+
continue;
|
|
107
|
+
await mkdir(dir, { recursive: true });
|
|
108
|
+
await writeFile(caminho, novo, "utf8");
|
|
109
|
+
escritos++;
|
|
110
|
+
}
|
|
111
|
+
return { escritos };
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Semeia as skills iniciais — so num projeto que ainda nao tem nenhuma.
|
|
115
|
+
*
|
|
116
|
+
* A checagem e "nenhuma skill", e nao "esta skill especifica": quem apagou
|
|
117
|
+
* `como-desenvolver` tomou uma decisao, e recria-la a cada `start` a desfaria em silencio.
|
|
118
|
+
* Projeto com qualquer skill propria ja passou do ponto de ser semeado.
|
|
119
|
+
*
|
|
120
|
+
* Devolve o que criou. Nunca lanca por falha de rede: semear e conveniencia de projeto
|
|
121
|
+
* novo, e um `start` nao pode falhar porque o mimo nao pode ser entregue.
|
|
122
|
+
*/
|
|
123
|
+
export async function semeiaSkills(raiz, iniciais) {
|
|
124
|
+
try {
|
|
125
|
+
const existentes = await leSkills(raiz);
|
|
126
|
+
if (existentes.length > 0)
|
|
127
|
+
return { criadas: [] };
|
|
128
|
+
const criadas = [];
|
|
129
|
+
for (const nova of iniciais) {
|
|
130
|
+
try {
|
|
131
|
+
await criaSkill(raiz, nova);
|
|
132
|
+
criadas.push(nova.slug);
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
// Uma skill que nao entrou nao impede as outras — nem o `start`.
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return { criadas };
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
return { criadas: [] };
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { NovaSkillInicial } from "./skill.js";
|
|
2
|
+
/**
|
|
3
|
+
* As skills que todo projeto novo nasce tendo.
|
|
4
|
+
*
|
|
5
|
+
* Mesma ideia da politica e do briefing: projeto recem-criado ja vem com o minimo para
|
|
6
|
+
* trabalhar, em vez de comecar vazio esperando que alguem lembre de escrever. A diferenca
|
|
7
|
+
* e que skill e EDITAVEL na interface — o que entra aqui e ponto de partida do projeto,
|
|
8
|
+
* nao regra do produto, e ajusta-la e o uso esperado.
|
|
9
|
+
*
|
|
10
|
+
* Semeadas so quando o projeto NAO TEM skill alguma. Um projeto que ja tem as suas nao
|
|
11
|
+
* recebe nada: se alguem apagou uma delas, foi decisao — e recria-la a cada `start`
|
|
12
|
+
* desfaria a decisao em silencio, toda vez.
|
|
13
|
+
*
|
|
14
|
+
* GERADO a partir dos SKILL.md em .claude/skills deste repositorio. Corrija la e
|
|
15
|
+
* regenere; editar este arquivo a mao faz a correcao se perder na proxima geracao.
|
|
16
|
+
*/
|
|
17
|
+
export type SkillInicial = NovaSkillInicial & {
|
|
18
|
+
ferramentas?: string[];
|
|
19
|
+
};
|
|
20
|
+
export declare const SKILLS_INICIAIS: SkillInicial[];
|
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
export const SKILLS_INICIAIS = [
|
|
2
|
+
{
|
|
3
|
+
slug: "como-desenvolver",
|
|
4
|
+
descricao: `Como desenvolver neste projeto — simplicidade, modularização, mudanças cirúrgicas e execução orientada a objetivos verificados de fato. Invoque ao escrever ou editar código para garantir qualidade. Não cobre segurança nem o protocolo de briefing (isso é sempre obrigatório, vive no CLAUDE.md).`,
|
|
5
|
+
ferramentas: [],
|
|
6
|
+
conteudo: `# Como Desenvolver
|
|
7
|
+
|
|
8
|
+
Aplique ao escrever ou editar código. Quando duas seções parecerem puxar em direções opostas numa mesma tarefa, a nota de precedência na Seção 4 resolve o conflito mais comum — não decida em silêncio.
|
|
9
|
+
|
|
10
|
+
## 1. Simplicidade Primeiro
|
|
11
|
+
|
|
12
|
+
**Mínimo de código que resolve o problema pedido. Nada especulativo.**
|
|
13
|
+
|
|
14
|
+
- Sem funcionalidades além do que foi pedido.
|
|
15
|
+
- Sem abstrações para código de uso único.
|
|
16
|
+
- Sem "flexibilidade" ou "configurabilidade" que ninguém pediu.
|
|
17
|
+
- Sem tratamento de erro para cenários impossíveis.
|
|
18
|
+
- Se escreveu 200 linhas e dava em 50, reescreva.
|
|
19
|
+
|
|
20
|
+
**Anti-padrão: gold-plating** — entregar mais do que foi pedido porque "já que estava ali" (um parâmetro extra sem uso, um \`try/catch\` para erro impossível, uma opção com um único valor possível). Perceber isso no que acabou de escrever é sinal de cortar antes de seguir.
|
|
21
|
+
|
|
22
|
+
O teste: para cada função, parâmetro ou arquivo novo, aponte a frase do pedido que o justifica. Se não conseguir apontar, corte.
|
|
23
|
+
|
|
24
|
+
## 2. Modularização e Componentização
|
|
25
|
+
|
|
26
|
+
**Prefira peças pequenas e com responsabilidade única a um monólito grande.** Vale para qualquer código — front-end, back-end, scripts.
|
|
27
|
+
|
|
28
|
+
- **Uma responsabilidade por unidade.** Se você descreve o que ela faz usando "e" várias vezes, ela faz coisa demais.
|
|
29
|
+
- **Separe as camadas.** Não misture lógica de negócio, acesso a dados e apresentação no mesmo lugar.
|
|
30
|
+
- **Extraia o que repete ou o que cresce dentro do código que a tarefa já toca** — só depois que existe de fato. Regra prática: na segunda vez que o mesmo trecho aparece nesse território, considere extrair; na terceira, extraia. Repetição pré-existente que a tarefa não tocaria por si só não conta — mexer nela é a Seção 3 quem decide, não esta.
|
|
31
|
+
- **No front-end:** componente que rola por centenas de linhas quase sempre é vários componentes disfarçados.
|
|
32
|
+
|
|
33
|
+
O teste: *"Eu preciso rolar muito para entender esta unidade, ou guardar várias coisas na cabeça ao mesmo tempo?"* Se sim, divida.
|
|
34
|
+
|
|
35
|
+
**Mas não fragmente à toa.** Dividir peças minúsculas demais cria o problema oposto — saltar entre dez arquivos para seguir uma linha de raciocínio. Divida por responsabilidade, não por contagem de linhas.
|
|
36
|
+
|
|
37
|
+
## 3. Mudanças Cirúrgicas
|
|
38
|
+
|
|
39
|
+
**Toque apenas no necessário. Limpe apenas sua própria bagunça.**
|
|
40
|
+
|
|
41
|
+
Ao editar código existente:
|
|
42
|
+
- Não "melhore" código adjacente, comentários ou formatação fora da tarefa.
|
|
43
|
+
- Não refatore o que não está quebrado.
|
|
44
|
+
- Mantenha o estilo existente, mesmo que você faria diferente.
|
|
45
|
+
- Viu código morto não relacionado? Cite no seu relatório final ao usuário — não apague, e não anote isso dentro do arquivo (a anotação em si já seria uma edição fora do pedido).
|
|
46
|
+
|
|
47
|
+
Quando suas mudanças criam órfãos:
|
|
48
|
+
- Remova imports/variáveis/funções que **suas** mudanças tornaram inúteis.
|
|
49
|
+
- Não remova código morto pré-existente sem ser solicitado.
|
|
50
|
+
|
|
51
|
+
**Quando a mudança toca um contrato usado em mais de um lugar** (assinatura, formato de dado, schema, campo de API, nome exportado): antes de considerar pronto, busque quem mais depende daquilo — não só o ponto que motivou a tarefa. Um chamador desatualizado é a mesma tarefa, incompleta.
|
|
52
|
+
|
|
53
|
+
O teste: releia o diff final. Cada linha alterada deve rastrear diretamente à solicitação do usuário — a que não rastreia, reverta.
|
|
54
|
+
|
|
55
|
+
## 4. Execução Orientada a Objetivos
|
|
56
|
+
|
|
57
|
+
**Defina critérios de sucesso verificáveis. Itere até verificar de fato — não até parecer pronto.**
|
|
58
|
+
|
|
59
|
+
Transforme tarefas vagas em objetivos verificáveis:
|
|
60
|
+
- "Adicionar validação" → "Escreva testes para entradas inválidas, depois faça-os passar."
|
|
61
|
+
- "Corrigir o bug" → "Escreva um teste que o reproduza, depois faça-o passar."
|
|
62
|
+
|
|
63
|
+
Para tarefas com múltiplos passos, declare um plano breve:
|
|
64
|
+
\`\`\`
|
|
65
|
+
1. [Passo] → verificar: [checagem]
|
|
66
|
+
2. [Passo] → verificar: [checagem]
|
|
67
|
+
\`\`\`
|
|
68
|
+
|
|
69
|
+
**Verificar é executar, não ler.** Compilar ou "parecer certo" na leitura não é verificação. Antes de declarar algo pronto, rode o código (teste, script, rota, comando) e observe o resultado real. Sem como executar no ambiente disponível, diga isso explicitamente em vez de presumir que passaria.
|
|
70
|
+
|
|
71
|
+
**Nem tudo precisa de teste escrito — tudo precisa de execução verificada.** Escreva teste para lógica com ramificação, cálculo, estado, ou um bug que já aconteceu (para não voltar). Para o que só delega ou é trivialmente correto pela leitura, não escreva teste — mas rode uma vez para confirmar.
|
|
72
|
+
|
|
73
|
+
**Se o critério declarado falhar, é sinal para diagnosticar — não obstáculo para contornar.** Nunca afrouxe a asserção, pule o teste incômodo, ou tente de novo sem entender a causa. Se a conclusão for que o critério original estava errado (não o código), diga isso antes de trocá-lo.
|
|
74
|
+
|
|
75
|
+
**Precedência com a Seção 1:** o critério de verificação em si (o teste, a checagem) não é "funcionalidade especulativa" mesmo sem pedido explícito de testes — verificar o próprio trabalho é parte de executar a tarefa. Mas o critério cobre exatamente o que foi pedido, nem mais: um teste que cobre casos fora do escopo volta a violar a Seção 1.
|
|
76
|
+
|
|
77
|
+
Critérios fortes deixam você iterar sozinho. Critérios fracos ("faça funcionar") forçam esclarecimentos constantes.`,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
slug: "gravar-com-ancora",
|
|
81
|
+
descricao: `Grava uma memória no Brain amarrada ao ponto do código de que ela fala. Invoque quando for gravar memória que descreve um arquivo, função, chave de config ou trecho específico — a âncora é o que faz a memória ser entregue a quem mexer ali depois. Não use para memória que fala do projeto como um todo.`,
|
|
82
|
+
ferramentas: ["mcp__dd-harness__sugerir_ancoras", "mcp__dd-harness__gravar_memoria", "mcp__dd-harness__buscar_memoria", "Read", "Grep"],
|
|
83
|
+
conteudo: `# Gravar memória com âncora
|
|
84
|
+
|
|
85
|
+
Memória sem âncora só aparece se alguém lembrar de buscar — e é justamente quando o
|
|
86
|
+
agente *acha que sabe* que ele não busca. A âncora inverte isso: ela faz a memória chegar
|
|
87
|
+
antes da edição, sem ninguém pedir.
|
|
88
|
+
|
|
89
|
+
Este procedimento é sobre **escolher o alvo certo**. Gravar é uma chamada; escolher a
|
|
90
|
+
âncora é a decisão que faz a memória proteger algo ou nascer inerte.
|
|
91
|
+
|
|
92
|
+
## 1. Confira que ela não existe
|
|
93
|
+
|
|
94
|
+
Chame \`buscar_memoria\` com a pergunta que a memória responderia. Se já houver uma que diz
|
|
95
|
+
a mesma coisa, **edite aquela** em vez de criar uma segunda — duas memórias dizendo o
|
|
96
|
+
mesmo se contradizem no dia em que uma for corrigida.
|
|
97
|
+
|
|
98
|
+
## 2. Descubra o que a sessão tocou
|
|
99
|
+
|
|
100
|
+
\`sugerir_ancoras\` devolve os arquivos modificados. Isso é **matéria-prima, não escolha**:
|
|
101
|
+
a lista mostra onde o trabalho aconteceu, e a âncora certa é o ponto de que a *memória*
|
|
102
|
+
fala — que pode ser um arquivo que você só leu.
|
|
103
|
+
|
|
104
|
+
Lista vazia (projeto sem git) não é impedimento: pergunte ao usuário qual ponto a memória
|
|
105
|
+
guarda.
|
|
106
|
+
|
|
107
|
+
## 3. Escolha o alvo — a decisão que importa
|
|
108
|
+
|
|
109
|
+
**Prefira sempre \`arquivo#trecho\` a \`arquivo\` inteiro.**
|
|
110
|
+
|
|
111
|
+
Medido neste projeto: duas memórias ancoradas no mesmo \`config/limites.json\` eram **as
|
|
112
|
+
duas** sinalizadas quando só uma das chaves mudava. Alerta que dispara no lugar errado
|
|
113
|
+
ensina a ignorar o mecanismo — e aí ele deixa de proteger.
|
|
114
|
+
|
|
115
|
+
O trecho precisa ser **estável e específico**:
|
|
116
|
+
|
|
117
|
+
| Bom alvo | Por quê |
|
|
118
|
+
|---|---|
|
|
119
|
+
| \`SECURITY DEFINER\` | é a decisão em si; some só se a decisão mudar |
|
|
120
|
+
| \`poolConexoesPostgres\` | nome de chave, sobrevive a reformatação |
|
|
121
|
+
| \`processarPagamento\` | nome de função, muda só em rename deliberado |
|
|
122
|
+
|
|
123
|
+
| Alvo ruim | Por quê |
|
|
124
|
+
|---|---|
|
|
125
|
+
| \`const x = 3\` | a próxima refatoração reescreve |
|
|
126
|
+
| \`// TODO\` | aparece em toda parte do arquivo |
|
|
127
|
+
| uma linha inteira de código | qualquer espaço a mais quebra o casamento |
|
|
128
|
+
|
|
129
|
+
**Confirme antes de gravar:** abra o arquivo e veja quantas linhas contêm o texto do alvo.
|
|
130
|
+
Se aparecer em dez lugares, o alvo é largo demais. Se não aparecer em nenhum, a âncora
|
|
131
|
+
nasce quebrada — e ninguém descobre, porque alvo ausente parece deriva legítima.
|
|
132
|
+
|
|
133
|
+
## 4. Âncora de diretório: quando cabe
|
|
134
|
+
|
|
135
|
+
\`supabase/migrations\` faz sentido para memória que fala da pasta inteira ("toda migration
|
|
136
|
+
precisa de X"). Não use diretório quando a memória fala de um arquivo dentro dele.
|
|
137
|
+
|
|
138
|
+
## 5. Proponha ao humano, com o alvo explícito
|
|
139
|
+
|
|
140
|
+
Diga qual âncora você escolheu **e por quê aquele trecho**. Se você não consegue explicar
|
|
141
|
+
por que o alvo é estável, provavelmente ele não é.
|
|
142
|
+
|
|
143
|
+
Só então chame \`gravar_memoria\`.
|
|
144
|
+
|
|
145
|
+
## Se a memória não tem ponto no código
|
|
146
|
+
|
|
147
|
+
Grave sem âncora, e **diga isso ao usuário**: ela só será encontrada por busca, e ninguém
|
|
148
|
+
será avisado ao mexer em nada. Para memória sobre um acordo com cliente ou uma regra da
|
|
149
|
+
organização, isso é o correto — não force uma âncora que não existe só para ter uma.`,
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
slug: "resolver-deriva",
|
|
153
|
+
descricao: `Decide o que fazer quando o check acusa deriva numa memória — se ela ainda vale, se a âncora mudou de lugar, ou se o mundo mudou e ela morreu. Invoque ao ver ALVO AUSENTE ou alvo alterado no resultado de dd-harness check, ou quando o status mostrar observações esperando julgamento.`,
|
|
154
|
+
ferramentas: ["mcp__dd-harness__ler_memoria", "mcp__dd-harness__editar_memoria", "mcp__dd-harness__arquivar_memoria", "Read", "Grep", "Bash(dd-harness check:*)", "Bash(dd-harness status:*)", "Bash(dd-harness reancorar:*)", "Bash(git log:*)", "Bash(git diff:*)"],
|
|
155
|
+
conteudo: `# Resolver deriva
|
|
156
|
+
|
|
157
|
+
Deriva é a memória avisando que o mundo que ela descreve mudou. Há **três** respostas
|
|
158
|
+
possíveis, e o atalho tentador é sempre o mesmo — marcar "ainda vale" e fechar o alarme.
|
|
159
|
+
|
|
160
|
+
É assim que um Brain apodrece parecendo saudável: nenhuma observação aberta, e metade das
|
|
161
|
+
memórias descrevendo um código que não existe mais.
|
|
162
|
+
|
|
163
|
+
## Primeiro: qual é o tipo?
|
|
164
|
+
|
|
165
|
+
\`dd-harness check\` distingue dois, e eles pedem coisas diferentes.
|
|
166
|
+
|
|
167
|
+
**ALVO AUSENTE** — o arquivo ou trecho não existe mais no caminho ancorado.
|
|
168
|
+
**Alterado** — o alvo existe e o conteúdo mudou desde a linha de base.
|
|
169
|
+
|
|
170
|
+
## ALVO AUSENTE: quase sempre é reancorar
|
|
171
|
+
|
|
172
|
+
O arquivo mudou de lugar. Se houve rename no commit, o \`check --commit\` já imprime o
|
|
173
|
+
comando \`dd-harness reancorar\` pronto — **confira o destino antes de rodar**: rename por
|
|
174
|
+
similaridade é heurística do git, e reancorar para o lugar errado move a memória em
|
|
175
|
+
silêncio, o que é pior que o alvo ausente.
|
|
176
|
+
|
|
177
|
+
Se não houve rename, procure o conteúdo (\`Grep\` pelo trecho ancorado). Três desfechos:
|
|
178
|
+
|
|
179
|
+
- **achou noutro lugar** → reancore para lá
|
|
180
|
+
- **o trecho foi renomeado** (a função virou outra coisa) → reancore para o nome novo, e
|
|
181
|
+
confira se a memória ainda descreve o que aquele código faz
|
|
182
|
+
- **sumiu de vez** → o mundo mudou; siga para "a memória morreu"
|
|
183
|
+
|
|
184
|
+
## Alterado: leia a memória ANTES de decidir
|
|
185
|
+
|
|
186
|
+
Isto é o passo que o atalho pula. Abra a memória com \`ler_memoria\` e leia o **porquê** —
|
|
187
|
+
não o título. A pergunta é:
|
|
188
|
+
|
|
189
|
+
> A razão que fez esta memória existir continua verdadeira?
|
|
190
|
+
|
|
191
|
+
Compare com o que mudou (\`git diff\` no alvo). Três desfechos, e cada um tem uma ação
|
|
192
|
+
diferente:
|
|
193
|
+
|
|
194
|
+
**1. A memória ainda vale** — o código mudou por perto, mas a razão externa continua de
|
|
195
|
+
pé (o fornecedor continua limitando, o compliance continua exigindo). Resolva como "ainda
|
|
196
|
+
vale": a linha de base passa a ser o estado atual.
|
|
197
|
+
|
|
198
|
+
**2. A memória vale, mas o texto envelheceu** — a razão é a mesma, a descrição não bate
|
|
199
|
+
mais com o código. **Edite a memória** e depois resolva. Resolver sem editar guarda uma
|
|
200
|
+
memória que vai confundir quem a ler.
|
|
201
|
+
|
|
202
|
+
**3. A razão deixou de existir** — o fornecedor mudou o limite, a lib foi trocada, a
|
|
203
|
+
decisão foi revertida. Aí não é "ainda vale": é \`arquivar_memoria\` com motivo
|
|
204
|
+
\`obsoleta\`. Se outra memória tomou o lugar, use \`--substituida-por\`.
|
|
205
|
+
|
|
206
|
+
## Nunca faça
|
|
207
|
+
|
|
208
|
+
**Não resolva em lote.** Cinco observações abertas são cinco perguntas diferentes; marcar
|
|
209
|
+
todas como "ainda vale" é o mesmo que não ter medido nada.
|
|
210
|
+
|
|
211
|
+
**Não decida sozinho pelo arquivamento.** Resolver "ainda vale" é reversível — a próxima
|
|
212
|
+
deriva reabre. Arquivar tira a memória de circulação, e quem vier depois não saberá que
|
|
213
|
+
ela existiu se você errar. Proponha ao humano.
|
|
214
|
+
|
|
215
|
+
**Não edite o código para fechar a deriva.** Se o alvo mudou porque alguém corrigiu um
|
|
216
|
+
bug, o código está certo e a memória é que precisa acompanhar.
|
|
217
|
+
|
|
218
|
+
## O caso que parece deriva e não é
|
|
219
|
+
|
|
220
|
+
Se o primeiro \`check\` de um projeto acusa tudo, isso não é deriva: é a **linha de base
|
|
221
|
+
sendo adotada**. Medido numa rodada de teste — rodar o primeiro \`check\` com o código já
|
|
222
|
+
alterado fixa o estado errado como base, e reverter para o valor correto passa a ser "a
|
|
223
|
+
mudança". Rode \`check\` antes de editar, não depois.`,
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
slug: "promover-ou-nao",
|
|
227
|
+
descricao: `Decide se uma memória deve valer para todo projeto do espaço, ou continuar só neste. Invoque ao gravar ou revisar uma memória que parece falar de algo maior que o projeto — um limite de fornecedor, uma regra da organização, uma armadilha da linguagem. Também ao revisar as globais existentes.`,
|
|
228
|
+
ferramentas: ["mcp__dd-harness__ler_memoria", "mcp__dd-harness__buscar_memoria", "mcp__dd-harness__promover_memoria"],
|
|
229
|
+
conteudo: `# Promover, ou não
|
|
230
|
+
|
|
231
|
+
Memória global vale para **todo projeto do espaço, inclusive os que ainda não existem**.
|
|
232
|
+
Ela chega ao contexto de qualquer sessão e dispara o alerta antes da edição, como as do
|
|
233
|
+
próprio projeto.
|
|
234
|
+
|
|
235
|
+
Isso é força e é risco: memória global errada **erra em escala**.
|
|
236
|
+
|
|
237
|
+
## O teste, em uma pergunta
|
|
238
|
+
|
|
239
|
+
> Se o próximo projeto repetir este erro, esta memória o teria evitado?
|
|
240
|
+
|
|
241
|
+
Se a resposta for sim, é global. Se você precisa construir um cenário para chegar ao sim,
|
|
242
|
+
não é.
|
|
243
|
+
|
|
244
|
+
## O que passa
|
|
245
|
+
|
|
246
|
+
A razão vem de **fora deste projeto** e continuaria valendo se o projeto não existisse:
|
|
247
|
+
|
|
248
|
+
- **limite de fornecedor** — "o gateway recusa mais de N tentativas por contrato"
|
|
249
|
+
- **regra da organização** — "todo repositório aqui exige revisão de dois"
|
|
250
|
+
- **armadilha da linguagem ou da ferramenta** — "\`REVOKE\` por coluna não tem efeito
|
|
251
|
+
quando existe \`GRANT\` na tabela inteira"
|
|
252
|
+
- **bug de terceiro com workaround** — "a versão X da lib quebra em Y; não subir"
|
|
253
|
+
|
|
254
|
+
Repare no padrão: nenhuma delas menciona um arquivo deste repositório.
|
|
255
|
+
|
|
256
|
+
## O que NÃO passa
|
|
257
|
+
|
|
258
|
+
- **decisão deste projeto** — "aqui usamos Drizzle em vez de Prisma". Vale para um
|
|
259
|
+
projeto; noutro a escolha pode ser outra, e a memória global mentiria.
|
|
260
|
+
- **acordo com um cliente específico** — é do projeto daquele cliente.
|
|
261
|
+
- **algo ancorado num arquivo deste repositório** — se a âncora aponta para
|
|
262
|
+
\`src/pagamento.js\`, a memória fala deste código. Para alcançar alguns projetos
|
|
263
|
+
nomeados, existe \`projetos:\` no frontmatter, que é uma **lista** — diferente de global,
|
|
264
|
+
que é uma propriedade.
|
|
265
|
+
- **"é interessante para todos saberem"** — interessante não é o critério. O critério é
|
|
266
|
+
o erro que ela evita.
|
|
267
|
+
|
|
268
|
+
## A ordem importa
|
|
269
|
+
|
|
270
|
+
**Grave no projeto primeiro. Promova depois.**
|
|
271
|
+
|
|
272
|
+
A memória nasce onde a lição apareceu, e continua morando lá mesmo depois de promovida —
|
|
273
|
+
a origem é metade do porquê. *"Descobrimos isto no projeto de pagamentos"* explica a
|
|
274
|
+
lição de um jeito que uma origem apagada não explicaria.
|
|
275
|
+
|
|
276
|
+
## Proponha, não promova
|
|
277
|
+
|
|
278
|
+
Promover multiplica o alcance. Leve ao humano com:
|
|
279
|
+
|
|
280
|
+
1. **o teste respondido** — qual erro futuro ela evita, em que projeto plausível
|
|
281
|
+
2. **por que não é deste projeto** — a razão externa, nomeada
|
|
282
|
+
3. **o que acontece se estiver errada** — ela vai chegar a todo projeto do espaço
|
|
283
|
+
|
|
284
|
+
Só então \`promover_memoria\`.
|
|
285
|
+
|
|
286
|
+
## Revisar as globais
|
|
287
|
+
|
|
288
|
+
É reversível: \`promover_memoria\` com \`global: false\` traz de volta. Ao revisar o acervo
|
|
289
|
+
global, reaplique o teste em cada uma — uma memória que era global e virou específica
|
|
290
|
+
(porque o fornecedor mudou, porque a regra caiu) deve voltar ao projeto, não ficar.
|
|
291
|
+
|
|
292
|
+
**Uma trava a conhecer:** uma global sem vínculo com projeto algum **não pode ser
|
|
293
|
+
despromovida** — isso a deixaria invisível. Vincule-a a um projeto antes, ou apague.`,
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
slug: "escrever-skill",
|
|
297
|
+
descricao: `Escreve uma skill que o modelo de fato invoca e segue, em vez de uma que existe e nunca dispara. Invoque ao criar ou revisar uma skill deste projeto — ou quando notar que um procedimento já foi explicado três vezes e devia estar escrito.`,
|
|
298
|
+
ferramentas: ["mcp__dd-harness__listar_skills", "Read"],
|
|
299
|
+
conteudo: `# Escrever uma skill que funciona
|
|
300
|
+
|
|
301
|
+
A falha mais comum de skill não é estar errada — é **nunca disparar**. Ela existe, o
|
|
302
|
+
procedimento está certo, e o modelo nunca a invoca. Não há erro, ninguém descobre, e o
|
|
303
|
+
esforço de escrevê-la some.
|
|
304
|
+
|
|
305
|
+
O segundo modo de falha é ser prosa: um texto que descreve o assunto em vez de dar os
|
|
306
|
+
passos, e que o modelo lê sem mudar nada do que ia fazer.
|
|
307
|
+
|
|
308
|
+
## Antes: isto é mesmo uma skill?
|
|
309
|
+
|
|
310
|
+
Três filtros, **conjuntivos**. Falhou um, não escreva.
|
|
311
|
+
|
|
312
|
+
| # | Filtro | A pergunta | É skill se |
|
|
313
|
+
|---|---|---|---|
|
|
314
|
+
| 1 | **Repetição consumada** | Isto já foi executado três vezes, em sessões diferentes? | sim — a terceira, não a segunda |
|
|
315
|
+
| 2 | **Passos, não conhecimento** | É uma sequência que se executa, ou um fato que se sabe? | passos — fato é memória |
|
|
316
|
+
| 3 | **Estável** | Os passos seriam os mesmos daqui a três meses? | sim — o que muda a cada uso é prosa |
|
|
317
|
+
|
|
318
|
+
"Seria conveniente ter" não é repetição. "Fiz duas vezes" não é três.
|
|
319
|
+
|
|
320
|
+
Chame \`listar_skills\` antes: se já existe uma que cobre o assunto, **edite aquela**. Duas
|
|
321
|
+
skills parecidas competem pela invocação, e o modelo escolhe mal entre elas.
|
|
322
|
+
|
|
323
|
+
## A descrição é metade do trabalho
|
|
324
|
+
|
|
325
|
+
É o **único campo que o modelo lê antes de decidir** invocar, e ele fica no contexto de
|
|
326
|
+
toda sessão — inclusive nas que não usam a skill.
|
|
327
|
+
|
|
328
|
+
**Escreva o gatilho, não o resumo.** A pergunta que a descrição responde é *"quando eu
|
|
329
|
+
deveria parar e usar isto?"*, não *"o que isto é?"*.
|
|
330
|
+
|
|
331
|
+
| Descrição ruim | Por quê |
|
|
332
|
+
|---|---|
|
|
333
|
+
| "Ajuda com memórias" | não diz quando; nunca dispara |
|
|
334
|
+
| "Procedimento de gravação" | descreve a skill, não a situação |
|
|
335
|
+
| "Use esta skill para gravar" | circular |
|
|
336
|
+
|
|
337
|
+
| Descrição boa | Por quê |
|
|
338
|
+
|---|---|
|
|
339
|
+
| "Invoque ao gravar memória que descreve um arquivo, função ou chave de config" | nomeia a situação |
|
|
340
|
+
| "Invoque ao ver ALVO AUSENTE no resultado de \`dd-harness check\`" | nomeia o gatilho literal |
|
|
341
|
+
|
|
342
|
+
**Diga também quando NÃO usar**, se houver confusão provável com outra skill. Uma linha de
|
|
343
|
+
exclusão evita a skill errada disparar.
|
|
344
|
+
|
|
345
|
+
## O corpo: passos e decisões, não explicação
|
|
346
|
+
|
|
347
|
+
O corpo só entra no contexto quando a skill é invocada — aqui cabe detalhe. Mas detalhe
|
|
348
|
+
não é prosa.
|
|
349
|
+
|
|
350
|
+
- **Numere o que tem ordem.** Se a ordem não importa, não numere.
|
|
351
|
+
- **Escreva a decisão, não só a ação.** "Escolha o alvo" não ajuda; "prefira
|
|
352
|
+
\`arquivo#trecho\` porque âncora de arquivo dispara para a memória errada" ajuda.
|
|
353
|
+
- **Nomeie o caminho errado atraente.** Toda skill que vale existe porque há um atalho
|
|
354
|
+
tentador — diga qual é e por que ele custa caro. Sem isso o modelo pega o atalho.
|
|
355
|
+
- **Traga a medição, quando houver.** "Medido: duas memórias no mesmo arquivo eram as
|
|
356
|
+
duas sinalizadas" vale mais que "pode gerar ruído".
|
|
357
|
+
- **Tabelas para distinguir casos.** Bom/ruim lado a lado decide mais rápido que dois
|
|
358
|
+
parágrafos.
|
|
359
|
+
|
|
360
|
+
Se o corpo só repete o que a descrição de uma ferramenta MCP já diz, **não escreva a
|
|
361
|
+
skill** — a ferramenta já ensina isso, e melhor.
|
|
362
|
+
|
|
363
|
+
## O frontmatter faz diferença
|
|
364
|
+
|
|
365
|
+
- **\`allowed-tools\`** — as ferramentas que a skill usa. Sem isso ela para a cada passo
|
|
366
|
+
pedindo permissão, e a pessoa aprende a desligar o mecanismo.
|
|
367
|
+
- **\`paths\`** — globs que carregam a skill só quando o trabalho toca aqueles arquivos. É
|
|
368
|
+
a mesma ideia da âncora, aplicada ao procedimento. Glob largo demais é uma skill sempre
|
|
369
|
+
presente, que era o que o campo existia para evitar.
|
|
370
|
+
- **\`disable-model-invocation: true\`** (na interface, "só por comando") — para skill com
|
|
371
|
+
efeito colateral, onde o momento é de quem digita.
|
|
372
|
+
- **\`argument-hint\`** — só quando a skill recebe argumento.
|
|
373
|
+
|
|
374
|
+
## Depois de escrever
|
|
375
|
+
|
|
376
|
+
**Releia a descrição sozinha**, sem o corpo. Se você não souber dizer em que momento
|
|
377
|
+
invocá-la, o modelo também não saberá.
|
|
378
|
+
|
|
379
|
+
Skill deste projeto vive no serviço: o repositório guarda só o ponteiro, e o
|
|
380
|
+
\`dd-harness start\` o reescreve. Se você mudou a descrição, rode-o — o frontmatter em
|
|
381
|
+
disco não muda sozinho.`,
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
slug: "propor-ferramentas",
|
|
385
|
+
descricao: `Pesquisa e propõe MCP servers, skills e bibliotecas que combinem com a stack deste projeto, validando cada candidato contra fonte antes de sugerir. Invoque logo após concluir o briefing — é quando a stack acabou de ser descrita — ou quando a stack mudar. Só propõe: nada é instalado sem OK, e recusar tudo é resposta válida.`,
|
|
386
|
+
ferramentas: ["mcp__dd-harness__ler_artefato", "mcp__dd-harness__listar_skills", "mcp__dd-harness__buscar_memoria", "Read", "Glob", "Grep", "WebSearch", "WebFetch"],
|
|
387
|
+
conteudo: `# Propor ferramentas para este projeto
|
|
388
|
+
|
|
389
|
+
O briefing acabou de descrever a stack e as restrições. Este é o único momento em que
|
|
390
|
+
esse contexto está fresco — depois dele, ninguém pergunta de novo, e uma ferramenta que
|
|
391
|
+
faria diferença aqui nunca é considerada.
|
|
392
|
+
|
|
393
|
+
Roda em subagente (\`context: fork\`): o conteúdo bruto das buscas fica lá, e só a proposta
|
|
394
|
+
volta. Por isso leia tudo do serviço no passo 1 — não conte com o histórico da conversa.
|
|
395
|
+
|
|
396
|
+
> **Só PROPÕE.** Não instala MCP, não cria skill, não adiciona dependência. Cada adoção é
|
|
397
|
+
> uma tarefa nova, com plano e OK. **Recusar tudo é resposta válida e esperada** — se o
|
|
398
|
+
> usuário não quiser nada, não insista.
|
|
399
|
+
|
|
400
|
+
## 1. O que já existe (antes de buscar qualquer coisa)
|
|
401
|
+
|
|
402
|
+
Propor o que o projeto já tem gasta a confiança na proposta inteira.
|
|
403
|
+
|
|
404
|
+
- **Stack, objetivo e restrições:** \`ler_artefato\` com \`tipo: "briefing"\`. As restrições
|
|
405
|
+
são **filtro duro** — sugestão que as viola precisa ser marcada como tal, ou descartada.
|
|
406
|
+
- **Skills já existentes:** \`listar_skills\`. Duas skills parecidas competem pela
|
|
407
|
+
invocação, e o modelo escolhe mal entre elas.
|
|
408
|
+
- **MCP e hooks já ligados:** leia \`.mcp.json\` e \`.claude/settings.json\` do repositório —
|
|
409
|
+
esses continuam em disco, porque é deles que o host lê.
|
|
410
|
+
- **O que já foi rejeitado:** \`buscar_memoria\` pelo nome de cada candidato antes de
|
|
411
|
+
propô-lo. Pode haver memória dizendo "não usar X porque Y" — repropor o que foi
|
|
412
|
+
descartado é o erro que mais rápido faz a proposta ser ignorada.
|
|
413
|
+
|
|
414
|
+
Briefing vazio: **pare e avise**. Sem a stack, a busca não tem filtro e a proposta vira
|
|
415
|
+
lista genérica — que é pior que nenhuma.
|
|
416
|
+
|
|
417
|
+
## 2. Pesquise com a stack no filtro
|
|
418
|
+
|
|
419
|
+
Busque informação **atual** (\`WebSearch\`, \`WebFetch\`), não memória. Três categorias:
|
|
420
|
+
|
|
421
|
+
- **MCP servers** — o que daria ao agente acesso a algo que hoje ele não alcança neste
|
|
422
|
+
projeto (o banco, o deploy, o rastreador de issues).
|
|
423
|
+
- **Skills** — procedimento repetido que já exista escrito e testado por outros.
|
|
424
|
+
- **Libs e ferramentas** — do ecossistema da stack, para o problema que o projeto tem.
|
|
425
|
+
|
|
426
|
+
Ancore cada busca na stack real. "Melhores ferramentas de 2026" devolve lista de blog;
|
|
427
|
+
"MCP server para Postgres com RLS" devolve o que serve aqui.
|
|
428
|
+
|
|
429
|
+
## 3. Valide contra fonte — não contra memória
|
|
430
|
+
|
|
431
|
+
Para todo candidato, responda com link:
|
|
432
|
+
|
|
433
|
+
- **Existe de fato?** Repositório, doc ou pacote oficial.
|
|
434
|
+
- **É mantido?** Último release ou commit. Abandonado → descarte ou marque o risco.
|
|
435
|
+
- **Casa com a stack?** Versão da linguagem, do framework, do runtime.
|
|
436
|
+
- **Respeita as restrições?** Confronte com o briefing. Conflito → **diga**, não esconda.
|
|
437
|
+
- **Já não temos equivalente?** Confronte com o passo 1.
|
|
438
|
+
|
|
439
|
+
Candidato que não passa: **descarte e diga por quê, em uma linha.** Não infle a lista —
|
|
440
|
+
três propostas boas valem mais que dez, e uma lista longa faz o usuário recusar tudo sem
|
|
441
|
+
ler.
|
|
442
|
+
|
|
443
|
+
## 4. Apresente
|
|
444
|
+
|
|
445
|
+
Para cada proposta: **o que é**, **o problema deste projeto que ela resolve**, **o custo**
|
|
446
|
+
(dependência nova, chave de API, processo a manter) e o **link**. Nessa ordem — o
|
|
447
|
+
problema antes da solução, senão parece catálogo.
|
|
448
|
+
|
|
449
|
+
Separe o que é **recomendação** do que é **possibilidade**. Se nada passou na validação,
|
|
450
|
+
diga isso: "não achei nada que valha para esta stack agora" é uma resposta honesta e
|
|
451
|
+
útil.
|
|
452
|
+
|
|
453
|
+
## 5. Se algo for adotado
|
|
454
|
+
|
|
455
|
+
Adotar é tarefa nova: plano curto, OK do usuário, e o protocolo normal do projeto.
|
|
456
|
+
|
|
457
|
+
Se a decisão vier com uma razão que sobrevive ao dia de hoje — "escolhemos X porque o
|
|
458
|
+
fornecedor limita Y" —, isso pode ser memória. Passe pelos três filtros antes de gravar;
|
|
459
|
+
"experimentamos e gostamos" não passa em nenhum deles.`,
|
|
460
|
+
},
|
|
461
|
+
];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dd-harness",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Cliente do dd-harness: politica no inicio da sessao, e memoria por busca — nada em disco. Sem dependencia: fetch, crypto e fs sao do Node.",
|
|
6
6
|
"license": "UNLICENSED",
|