dd-harness 0.36.0 → 0.37.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/atualizar.d.ts +12 -0
- package/dist/atualizar.js +93 -44
- package/dist/index.js +15 -5
- package/dist/skills-iniciais.js +30 -2
- package/package.json +1 -1
package/dist/atualizar.d.ts
CHANGED
|
@@ -21,6 +21,18 @@ export type Item = {
|
|
|
21
21
|
* maquina, nao por projeto).
|
|
22
22
|
*/
|
|
23
23
|
export declare function diagnosticaAtualizacao(raiz: string, versaoInstalada: string, versaoPublicada: string | null): Promise<Item[]>;
|
|
24
|
+
/**
|
|
25
|
+
* Incorpora ao projeto as skills iniciais escolhidas pelo usuario.
|
|
26
|
+
*
|
|
27
|
+
* Recebe os slugs em vez de criar tudo que falta, e essa e a diferenca que importa: skill
|
|
28
|
+
* pode ter sido apagada de proposito, e criar a lista inteira desfaria a decisao de quem
|
|
29
|
+
* apagou. Quem chama aqui e a skill `atualizar-harness`, depois do "sim" a cada uma.
|
|
30
|
+
*/
|
|
31
|
+
export declare function incorporaSkills(raiz: string, slugs: string[]): Promise<{
|
|
32
|
+
criadas: string[];
|
|
33
|
+
falharam: string[];
|
|
34
|
+
ponteiros: string;
|
|
35
|
+
}>;
|
|
24
36
|
/**
|
|
25
37
|
* Aplica so os itens marcados `aplicar`. Os de `perguntar` ficam intactos de proposito —
|
|
26
38
|
* quem decide sobre arquivo editado a mao e o dono dele.
|
package/dist/atualizar.js
CHANGED
|
@@ -74,66 +74,113 @@ export async function diagnosticaAtualizacao(raiz, versaoInstalada, versaoPublic
|
|
|
74
74
|
// daquela epoca, e as que vieram depois so existiam em projeto criado do zero. Este item
|
|
75
75
|
// e o caminho que faltava.
|
|
76
76
|
//
|
|
77
|
-
//
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
// e o `criaSkill` erra de proposito se o slug ja existir.
|
|
77
|
+
// `perguntar`, nunca `aplicar`: skill PODE ser apagada, entao do lado de fora "nasceu
|
|
78
|
+
// depois deste projeto" e "eu apaguei porque nao quero" sao indistinguiveis — e recriar
|
|
79
|
+
// sozinho desfaria a segunda em silencio, toda vez que alguem rodasse o comando. Quem
|
|
80
|
+
// sabe qual dos dois e o dono; o diagnostico so mostra o que falta e para que serve.
|
|
81
|
+
let doServico = null;
|
|
83
82
|
try {
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
doServico = await leSkills(raiz);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// So a REDE cai aqui. O que vem depois e disco, e misturar os dois no mesmo try faz um
|
|
87
|
+
// erro de leitura de arquivo ser reportado como "o serviço não respondeu" — mentira que
|
|
88
|
+
// manda procurar o problema no lugar errado.
|
|
89
|
+
itens.push({ o_que: "skills", situacao: "não consegui consultar o serviço", acao: "manual",
|
|
90
|
+
detalhe: "confira com `dd-harness skills` quando o serviço responder" });
|
|
91
|
+
}
|
|
92
|
+
if (doServico) {
|
|
93
|
+
const existentes = new Set(doServico.map(s => s.slug));
|
|
94
|
+
const faltando = SKILLS_INICIAIS.filter(s => !existentes.has(s.slug));
|
|
95
|
+
// Sem ponteiro em disco, a skill EXISTE no servico e nenhum host a descobre — o Claude
|
|
96
|
+
// Code le `.claude/skills/*/SKILL.md` na abertura da sessao. Some por apagar o .md ou
|
|
97
|
+
// por renomear a pasta, e some CALADA: `dd-harness skills` continua listando a skill,
|
|
98
|
+
// entao a unica pista e ela nunca ser invocada. Caso diferente do de baixo, e este se
|
|
99
|
+
// resolve sozinho: reescrever o ponteiro nao desfaz decisao nenhuma sobre a skill.
|
|
100
|
+
const destinos = [...new Set((await hostsInstalados(raiz))
|
|
101
|
+
.map(h => h === "claude" ? ".claude" : ".agents"))];
|
|
102
|
+
const semPonteiro = [];
|
|
103
|
+
for (const skill of doServico) {
|
|
104
|
+
for (const destino of destinos.length ? destinos : [".claude"]) {
|
|
105
|
+
// No `.agents` a skill so-por-comando nao ganha ponteiro de proposito: ausencia ali
|
|
106
|
+
// e desenho, nao perda.
|
|
107
|
+
if (destino === ".agents" && skill.so_por_comando)
|
|
108
|
+
continue;
|
|
109
|
+
// So ENOENT conta como "sem ponteiro". Permissao negada ou disco com erro nao e
|
|
110
|
+
// ponteiro faltando, e tratar como se fosse mandaria reescrever por cima de um
|
|
111
|
+
// problema que e outro.
|
|
112
|
+
const existe = await readFile(join(raiz, destino, "skills", skill.slug, "SKILL.md"), "utf8")
|
|
113
|
+
.then(() => true)
|
|
114
|
+
.catch((e) => { if (e.code === "ENOENT")
|
|
115
|
+
return false; throw e; });
|
|
116
|
+
if (!existe) {
|
|
117
|
+
semPonteiro.push(`${destino}/skills/${skill.slug}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (semPonteiro.length) {
|
|
122
|
+
itens.push({
|
|
123
|
+
o_que: "ponteiros de skill",
|
|
124
|
+
situacao: `${semPonteiro.length} skill(s) do serviço sem arquivo em disco`,
|
|
125
|
+
acao: "aplicar",
|
|
126
|
+
detalhe: `${semPonteiro.join(", ")} — o host não descobre a skill sem o ponteiro; será reescrito`,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
86
129
|
itens.push(faltando.length === 0
|
|
87
130
|
? { o_que: "skills", situacao: `${existentes.size} no projeto; nenhuma inicial faltando`, acao: "em-dia" }
|
|
88
131
|
: {
|
|
89
132
|
o_que: "skills",
|
|
90
|
-
situacao: `${faltando.length} skill(s) inicial(is)
|
|
91
|
-
acao: "
|
|
92
|
-
|
|
133
|
+
situacao: `${faltando.length} skill(s) inicial(is) não estão neste projeto`,
|
|
134
|
+
acao: "perguntar",
|
|
135
|
+
// A descricao inteira, nao so o slug: e o campo que diz QUANDO a skill serve, e
|
|
136
|
+
// sem ele a pergunta vira "quer `revisar-memoria`?" — que ninguem responde bem.
|
|
137
|
+
detalhe: faltando.map(s => `${s.slug}: ${s.descricao}`).join("\n"),
|
|
93
138
|
});
|
|
94
139
|
}
|
|
95
|
-
catch {
|
|
96
|
-
// Sem rede ou sem credencial: o resto do diagnostico continua valendo, e skill faltando
|
|
97
|
-
// nao e urgente o bastante para derrubar a checagem inteira.
|
|
98
|
-
itens.push({ o_que: "skills", situacao: "não consegui consultar o serviço", acao: "manual",
|
|
99
|
-
detalhe: "confira com `dd-harness skills` quando o serviço responder" });
|
|
100
|
-
}
|
|
101
140
|
return itens;
|
|
102
141
|
}
|
|
103
|
-
/**
|
|
104
|
-
async function
|
|
105
|
-
const
|
|
106
|
-
|
|
142
|
+
/** Reescreve em disco os ponteiros das skills que o serviço tem. */
|
|
143
|
+
async function reescrevePonteiros(raiz) {
|
|
144
|
+
const destinos = [...new Set((await hostsInstalados(raiz))
|
|
145
|
+
.map(h => h === "claude" ? ".claude" : ".agents"))];
|
|
146
|
+
const r = await escrevePonteirosDeSkills(raiz, await leSkills(raiz), destinos.length ? destinos : [".claude"]);
|
|
147
|
+
return `ponteiros de skill: ${r.escritos} reescrito(s)` +
|
|
148
|
+
(r.conflitos.length ? `; ${r.conflitos.length} preservado(s) por edição local` : "");
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Incorpora ao projeto as skills iniciais escolhidas pelo usuario.
|
|
152
|
+
*
|
|
153
|
+
* Recebe os slugs em vez de criar tudo que falta, e essa e a diferenca que importa: skill
|
|
154
|
+
* pode ter sido apagada de proposito, e criar a lista inteira desfaria a decisao de quem
|
|
155
|
+
* apagou. Quem chama aqui e a skill `atualizar-harness`, depois do "sim" a cada uma.
|
|
156
|
+
*/
|
|
157
|
+
export async function incorporaSkills(raiz, slugs) {
|
|
107
158
|
const criadas = [];
|
|
108
159
|
const falharam = [];
|
|
109
|
-
for (const
|
|
160
|
+
for (const slug of slugs) {
|
|
161
|
+
const inicial = SKILLS_INICIAIS.find(s => s.slug === slug);
|
|
162
|
+
if (!inicial) {
|
|
163
|
+
falharam.push(`${slug} (não é uma skill inicial)`);
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
110
166
|
try {
|
|
111
167
|
await criaSkill(raiz, inicial);
|
|
112
|
-
criadas.push(
|
|
168
|
+
criadas.push(slug);
|
|
113
169
|
}
|
|
114
|
-
catch {
|
|
115
|
-
// Uma que nao entrou nao impede as outras: metade
|
|
116
|
-
//
|
|
117
|
-
falharam.push(
|
|
170
|
+
catch (erro) {
|
|
171
|
+
// Uma que nao entrou nao impede as outras: metade incorporada e melhor que nenhuma,
|
|
172
|
+
// e o retorno diz qual faltou e por que.
|
|
173
|
+
falharam.push(`${slug} (${erro instanceof Error ? erro.message : String(erro)})`);
|
|
118
174
|
}
|
|
119
175
|
}
|
|
176
|
+
// Sem o ponteiro em disco a skill existe no servico e host nenhum a descobre; criar e
|
|
177
|
+
// parar aqui entregaria uma skill invisivel ate o boot seguinte.
|
|
120
178
|
let ponteiros = "";
|
|
121
|
-
// A skill no servico nao e descoberta por host nenhum sem o ponteiro em disco: o Claude
|
|
122
|
-
// Code le `.claude/skills/*/SKILL.md` na abertura da sessao, e MCP nao fornece skill.
|
|
123
|
-
// Criar no servico e parar aqui entregaria uma skill que so aparece no boot seguinte.
|
|
124
179
|
if (criadas.length) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const destinos = [...new Set(hosts.map(h => h === "claude" ? ".claude" : ".agents"))];
|
|
128
|
-
const r = await escrevePonteirosDeSkills(raiz, await leSkills(raiz), destinos.length ? destinos : [".claude"]);
|
|
129
|
-
ponteiros = `; ${r.escritos} ponteiro(s) em disco`;
|
|
130
|
-
}
|
|
131
|
-
catch {
|
|
132
|
-
ponteiros = "; ponteiros NÃO escritos — rode `dd-harness integrar` ou reabra a sessão";
|
|
133
|
-
}
|
|
180
|
+
ponteiros = await reescrevePonteiros(raiz)
|
|
181
|
+
.catch(() => "ponteiros NÃO escritos — rode `dd-harness atualizar --aplicar` ou reabra a sessão");
|
|
134
182
|
}
|
|
135
|
-
return
|
|
136
|
-
(falharam.length ? `; FALHARAM: ${falharam.join(", ")}` : "") + ponteiros;
|
|
183
|
+
return { criadas, falharam, ponteiros };
|
|
137
184
|
}
|
|
138
185
|
/**
|
|
139
186
|
* Aplica so os itens marcados `aplicar`. Os de `perguntar` ficam intactos de proposito —
|
|
@@ -152,12 +199,14 @@ export async function aplicaAtualizacao(raiz, itens) {
|
|
|
152
199
|
// um projeto que continua sem o cinto.
|
|
153
200
|
feitos.push(r.ok ? `hooks: ${r.estado}` : `hooks: NÃO aplicado (${r.motivo})`);
|
|
154
201
|
}
|
|
155
|
-
else if (item.o_que === "
|
|
202
|
+
else if (item.o_que === "ponteiros de skill") {
|
|
203
|
+
// Seguro no `--aplicar`: reescrever o ponteiro de uma skill que o servico tem nao
|
|
204
|
+
// decide nada sobre a skill — so devolve ao host a chance de descobri-la.
|
|
156
205
|
try {
|
|
157
|
-
feitos.push(await
|
|
206
|
+
feitos.push(await reescrevePonteiros(raiz));
|
|
158
207
|
}
|
|
159
208
|
catch (erro) {
|
|
160
|
-
feitos.push(`
|
|
209
|
+
feitos.push(`ponteiros de skill: NÃO aplicado (${erro instanceof Error ? erro.message : String(erro)})`);
|
|
161
210
|
}
|
|
162
211
|
}
|
|
163
212
|
}
|
package/dist/index.js
CHANGED
|
@@ -545,7 +545,17 @@ async function comandoAtualizar(argv) {
|
|
|
545
545
|
const itens = await diagnosticaAtualizacao(raiz, await versaoInstalada(import.meta.url), await versaoPublicada(AbortSignal.timeout(6000)));
|
|
546
546
|
const simbolo = { "em-dia": "ok ", aplicar: "-> ", perguntar: "?? ", manual: "! " };
|
|
547
547
|
for (const i of itens) {
|
|
548
|
-
|
|
548
|
+
// Detalhe multilinha (as descrições das skills que faltam) vira bloco indentado: numa
|
|
549
|
+
// linha só, a descrição de cada skill some no meio das outras, e é justamente ela que
|
|
550
|
+
// responde se a skill vale para este projeto.
|
|
551
|
+
if (i.detalhe?.includes("\n")) {
|
|
552
|
+
console.log(`${simbolo[i.acao]}${i.o_que}: ${i.situacao}`);
|
|
553
|
+
for (const linha of i.detalhe.split("\n"))
|
|
554
|
+
console.log(` ${linha}`);
|
|
555
|
+
}
|
|
556
|
+
else {
|
|
557
|
+
console.log(`${simbolo[i.acao]}${i.o_que}: ${i.situacao}${i.detalhe ? ` — ${i.detalhe}` : ""}`);
|
|
558
|
+
}
|
|
549
559
|
}
|
|
550
560
|
const aAplicar = itens.filter(i => i.acao === "aplicar");
|
|
551
561
|
const aPerguntar = itens.filter(i => i.acao === "perguntar");
|
|
@@ -559,13 +569,13 @@ async function comandoAtualizar(argv) {
|
|
|
559
569
|
for (const linha of await aplicaAtualizacao(raiz, itens))
|
|
560
570
|
console.log(linha);
|
|
561
571
|
}
|
|
562
|
-
// Estes ficam de fora do `--aplicar` de proposito: o arquivo foi editado a mao,
|
|
563
|
-
//
|
|
572
|
+
// Estes ficam de fora do `--aplicar` de proposito: o arquivo foi editado a mao, ou a
|
|
573
|
+
// skill pode ter sido apagada de proposito — e so o dono sabe qual dos dois.
|
|
564
574
|
if (aPerguntar.length) {
|
|
565
575
|
console.log(`\n${aPerguntar.length} item(ns) NÃO foram tocados por exigirem decisão sua:`);
|
|
566
576
|
for (const i of aPerguntar)
|
|
567
|
-
console.log(` ${i.o_que}: ${i.
|
|
568
|
-
console.log(" Use a skill atualizar-harness
|
|
577
|
+
console.log(` ${i.o_que}: ${i.situacao}`);
|
|
578
|
+
console.log(" Use a skill atualizar-harness: ela apresenta cada caso e pergunta.");
|
|
569
579
|
}
|
|
570
580
|
}
|
|
571
581
|
async function comandoInit(argv) {
|
package/dist/skills-iniciais.js
CHANGED
|
@@ -543,9 +543,37 @@ cria o arquivo que não existe. O que está fora do bloco não é lido nem reesc
|
|
|
543
543
|
|
|
544
544
|
Os itens \`??\` ficam intactos de propósito — o comando não os toca.
|
|
545
545
|
|
|
546
|
-
##
|
|
546
|
+
## 4a. Skills que faltam: apresente e pergunte, uma a uma
|
|
547
547
|
|
|
548
|
-
|
|
548
|
+
O item \`?? skills\` lista as skills iniciais que **não estão** neste projeto. Elas caem em
|
|
549
|
+
dois casos que, de fora, são **indistinguíveis**:
|
|
550
|
+
|
|
551
|
+
- **Nasceu depois deste projeto.** A semeadura só roda em projeto sem skill alguma, então
|
|
552
|
+
skill nova nunca chega sozinha a um projeto que já existe.
|
|
553
|
+
- **Foi apagada de propósito.** Skill é apagável, e quem apagou tomou uma decisão.
|
|
554
|
+
|
|
555
|
+
**Nunca recrie sem perguntar.** Incorporar a lista inteira desfaz a segunda decisão em
|
|
556
|
+
silêncio, e desfaz de novo a cada vez que alguém rodar o comando.
|
|
557
|
+
|
|
558
|
+
Para cada skill faltante, apresente ao usuário:
|
|
559
|
+
|
|
560
|
+
1. **O nome e o que ela faz** — o diagnóstico já imprime a descrição completa; ela é o
|
|
561
|
+
campo que diz *quando* a skill serve
|
|
562
|
+
2. **Por que ela pode importar aqui**, com base neste projeto: a stack, o que existe no
|
|
563
|
+
repositório, o que o BRIEFING diz. "Este projeto tem Brain com âncoras, então
|
|
564
|
+
\`resolver-deriva\` teria uso" vale mais que repetir a descrição
|
|
565
|
+
3. **A pergunta**: incorporar esta?
|
|
566
|
+
|
|
567
|
+
Aceite "não" sem insistir. Uma skill recusada é uma decisão do dono, não um item pendente —
|
|
568
|
+
e se ele disser "não" para a mesma skill de novo no mês que vem, isso é sinal de que a
|
|
569
|
+
resposta já está dada.
|
|
570
|
+
|
|
571
|
+
Com a lista do que ele aceitou, incorpore só essas. Os ponteiros em disco são escritos
|
|
572
|
+
junto; sem eles a skill existe no serviço e host nenhum a descobre.
|
|
573
|
+
|
|
574
|
+
## 4b. Conduza os outros casos que exigem decisão
|
|
575
|
+
|
|
576
|
+
Para cada item \`??\` restante, faça o trabalho que o comando não pode fazer:
|
|
549
577
|
|
|
550
578
|
1. **Mostre ao usuário o que está lá** e o que o molde novo traz.
|
|
551
579
|
2. **Diga o que você acha que aconteceu** — "parece que você acrescentou esta linha
|
package/package.json
CHANGED