dd-harness 0.38.2 → 0.40.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/README.md +103 -103
- package/dist/artefato.d.ts +9 -4
- package/dist/artefato.js +16 -8
- package/dist/atualizar.d.ts +2 -0
- package/dist/atualizar.js +1 -1
- package/dist/brain.d.ts +20 -4
- package/dist/check.js +18 -4
- package/dist/curar.d.ts +6 -1
- package/dist/curar.js +6 -2
- package/dist/escreve-config.js +24 -24
- package/dist/gravar.d.ts +1 -0
- package/dist/gravar.js +1 -0
- package/dist/index.js +49 -12
- package/dist/init.js +45 -45
- package/dist/materializa.d.ts +70 -0
- package/dist/materializa.js +5 -0
- package/dist/moldes-historicos.js +70 -70
- package/dist/payload-cache.d.ts +31 -0
- package/dist/payload-cache.js +62 -0
- package/dist/politica.js +20 -3
- package/dist/reancorar.js +19 -1
- package/dist/skill.d.ts +5 -0
- package/dist/skill.js +37 -3
- package/dist/skills-iniciais.js +10 -4
- package/dist/sync.d.ts +59 -0
- package/dist/sync.js +2 -1
- package/package.json +1 -1
package/dist/skill.d.ts
CHANGED
|
@@ -31,6 +31,11 @@ export declare function leSkill(raiz: string, slug: string): Promise<SkillComple
|
|
|
31
31
|
export declare function criaSkill(raiz: string, dados: NovaSkillInicial & Partial<Pick<Skill, "so_por_comando" | "ferramentas" | "caminhos" | "dica_de_argumento">>): Promise<{
|
|
32
32
|
skill: Skill;
|
|
33
33
|
}>;
|
|
34
|
+
/** Só o que muda: o que não vier fica como está no serviço. */
|
|
35
|
+
export type EdicaoDeSkill = Partial<Pick<SkillCompleta, "descricao" | "conteudo" | "so_por_comando" | "ferramentas" | "caminhos" | "dica_de_argumento">>;
|
|
36
|
+
export declare function editaSkill(raiz: string, slug: string, dados: EdicaoDeSkill): Promise<{
|
|
37
|
+
skill: Skill;
|
|
38
|
+
}>;
|
|
34
39
|
/**
|
|
35
40
|
* O SKILL.md que vai para o disco: frontmatter de verdade, corpo que aponta para o MCP.
|
|
36
41
|
*
|
package/dist/skill.js
CHANGED
|
@@ -38,6 +38,17 @@ dados) {
|
|
|
38
38
|
await recusa(resposta);
|
|
39
39
|
return (await jsonDaEscrita(resposta));
|
|
40
40
|
}
|
|
41
|
+
export async function editaSkill(raiz, slug, dados) {
|
|
42
|
+
const { config, token } = await credencial(raiz);
|
|
43
|
+
const resposta = await pede(`${config.api}/api/v1/skills/${encodeURIComponent(slug)}`, {
|
|
44
|
+
method: "PATCH",
|
|
45
|
+
headers: cabecalhos(token, true),
|
|
46
|
+
body: JSON.stringify({ tenant: config.tenant, projeto: config.projeto, ...dados }),
|
|
47
|
+
});
|
|
48
|
+
if (!resposta.ok)
|
|
49
|
+
await recusa(resposta);
|
|
50
|
+
return (await jsonDaEscrita(resposta));
|
|
51
|
+
}
|
|
41
52
|
// --- o ponteiro em disco (puro, para ser testado sem rede) ---
|
|
42
53
|
/**
|
|
43
54
|
* Escapa o que quebraria o YAML do frontmatter.
|
|
@@ -50,6 +61,22 @@ dados) {
|
|
|
50
61
|
function comoEscalarYaml(texto) {
|
|
51
62
|
return `"${texto.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\r?\n/g, " ")}"`;
|
|
52
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Um item de `allowed-tools` ou `paths`, limpo do que quebraria o frontmatter.
|
|
66
|
+
*
|
|
67
|
+
* Estas duas chaves sao listas separadas por virgula, e o host as le assim — entao nao da
|
|
68
|
+
* para citar o valor inteiro como se faz com `description`: as aspas entrariam no nome da
|
|
69
|
+
* ferramenta. O que sobra e neutralizar os tres caracteres que mudam a ESTRUTURA do YAML:
|
|
70
|
+
* quebra de linha (encerra o valor e a proxima linha vira chave nova), `:` seguido de
|
|
71
|
+
* espaco (o parser le chave) e a virgula (inventa um item que ninguem pediu).
|
|
72
|
+
*
|
|
73
|
+
* Importa porque estes itens vem da API, que aceita qualquer string nao vazia: um item com
|
|
74
|
+
* `\n` faz a skill sumir da lista ou ganhar uma chave que o host obedece — e frontmatter
|
|
75
|
+
* quebrado nao da erro visivel, exatamente como na `description`.
|
|
76
|
+
*/
|
|
77
|
+
function comoItemDeLista(texto) {
|
|
78
|
+
return texto.replace(/[\r\n]+/g, " ").replace(/:\s/g, " ").replace(/,/g, " ").trim();
|
|
79
|
+
}
|
|
53
80
|
/**
|
|
54
81
|
* O SKILL.md que vai para o disco: frontmatter de verdade, corpo que aponta para o MCP.
|
|
55
82
|
*
|
|
@@ -68,11 +95,18 @@ export function ponteiroDaSkill(skill, formato = "claude") {
|
|
|
68
95
|
if (skill.dica_de_argumento) {
|
|
69
96
|
frontmatter.push(`argument-hint: ${comoEscalarYaml(skill.dica_de_argumento)}`);
|
|
70
97
|
}
|
|
71
|
-
const ferramentas = [
|
|
98
|
+
const ferramentas = [
|
|
99
|
+
"mcp__dd-harness__ler_skill",
|
|
100
|
+
...skill.ferramentas.map(comoItemDeLista).filter(Boolean),
|
|
101
|
+
];
|
|
72
102
|
if (formato === "claude")
|
|
73
103
|
frontmatter.push(`allowed-tools: ${ferramentas.join(", ")}`);
|
|
74
|
-
if (formato === "claude"
|
|
75
|
-
|
|
104
|
+
if (formato === "claude") {
|
|
105
|
+
const caminhos = skill.caminhos.map(comoItemDeLista).filter(Boolean);
|
|
106
|
+
// Depois da limpeza a lista pode ficar vazia (item que era so `\n`). `paths: ` sem
|
|
107
|
+
// valor nao e o mesmo que ausente: seria uma skill que nunca casa arquivo nenhum.
|
|
108
|
+
if (caminhos.length > 0)
|
|
109
|
+
frontmatter.push(`paths: ${caminhos.join(", ")}`);
|
|
76
110
|
}
|
|
77
111
|
frontmatter.push("---");
|
|
78
112
|
return `${frontmatter.join("\n")}
|
package/dist/skills-iniciais.js
CHANGED
|
@@ -295,7 +295,7 @@ despromovida** — isso a deixaria invisível. Vincule-a a um projeto antes, ou
|
|
|
295
295
|
{
|
|
296
296
|
slug: "escrever-skill",
|
|
297
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"],
|
|
298
|
+
ferramentas: ["mcp__dd-harness__listar_skills", "mcp__dd-harness__escrever_skill", "Read"],
|
|
299
299
|
conteudo: `# Escrever uma skill que funciona
|
|
300
300
|
|
|
301
301
|
A falha mais comum de skill não é estar errada — é **nunca disparar**. Ela existe, o
|
|
@@ -376,9 +376,15 @@ skill** — a ferramenta já ensina isso, e melhor.
|
|
|
376
376
|
**Releia a descrição sozinha**, sem o corpo. Se você não souber dizer em que momento
|
|
377
377
|
invocá-la, o modelo também não saberá.
|
|
378
378
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
379
|
+
## Gravar
|
|
380
|
+
|
|
381
|
+
Skill deste projeto vive no serviço: o repositório guarda só o ponteiro. **Mostre a
|
|
382
|
+
descrição e o corpo ao humano e grave só depois do OK**, com \`escrever_skill\` —
|
|
383
|
+
\`criar: true\` para uma nova; sem ele, edita a existente. Na edição, mande só o que muda;
|
|
384
|
+
\`conteudo\` substitui o procedimento inteiro, então leia antes com \`ler_skill\`.
|
|
385
|
+
|
|
386
|
+
A ferramenta reescreve o ponteiro em disco. O procedimento novo vale na hora; descrição,
|
|
387
|
+
ferramentas e caminhos são frontmatter, e o host só os relê na próxima sessão.`,
|
|
382
388
|
},
|
|
383
389
|
{
|
|
384
390
|
slug: "propor-ferramentas",
|
package/dist/sync.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Busca o Brain e escreve em disco. Uma direcao so.
|
|
3
|
+
*
|
|
4
|
+
* O manifesto guarda o hash de cada arquivo que este comando escreveu. Antes de
|
|
5
|
+
* sobrescrever, ele confere: hash igual ao guardado significa "isto e meu, posso
|
|
6
|
+
* reescrever"; hash diferente significa que alguem editou a mao, e ai o comando **para**.
|
|
7
|
+
* Deriva silenciosa entre repo e servico e exatamente o que este projeto existe para
|
|
8
|
+
* evitar — melhor falhar alto do que engolir a edicao de alguem.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Estado do ponteiro na raiz.
|
|
12
|
+
*
|
|
13
|
+
* `CLAUDE.md` nao e gerado — e do repositorio, e so precisa importar a politica. Mas o
|
|
14
|
+
* import falha em **silencio**: sem o arquivo alvo, ou sem a linha, a sessao abre e
|
|
15
|
+
* nada avisa que a politica nao veio junto. Foi medido, nao suposto. Como a sessao sem
|
|
16
|
+
* protocolo e a falha que o molde mais combate, o `sync` confere isso toda vez — mesmo
|
|
17
|
+
* quando o servico nao mudou, porque quem apaga a linha e quem mexe no repositorio.
|
|
18
|
+
*/
|
|
19
|
+
export type Ponteiro = "ok" | "sem-claude-md" | "sem-a-linha" | "sem-politica"
|
|
20
|
+
/**
|
|
21
|
+
* A linha de import existe, mas nao ha politica no servico para ela apontar. O `init`
|
|
22
|
+
* escreve a linha antes de existir politica, entao o repositorio novo cai aqui: import
|
|
23
|
+
* pendurado desde o primeiro dia, e o mesmo silencio de sempre.
|
|
24
|
+
*/
|
|
25
|
+
| "aponta-para-o-vazio";
|
|
26
|
+
type Resultado = {
|
|
27
|
+
ponteiro: Ponteiro;
|
|
28
|
+
} & ({
|
|
29
|
+
tipo: "sem-mudanca";
|
|
30
|
+
} | {
|
|
31
|
+
tipo: "sincronizado";
|
|
32
|
+
escritos: string[];
|
|
33
|
+
removidos: string[];
|
|
34
|
+
} | {
|
|
35
|
+
tipo: "editado-a-mao";
|
|
36
|
+
arquivos: string[];
|
|
37
|
+
});
|
|
38
|
+
/**
|
|
39
|
+
* Estado do que este comando escreveu, contra o hash guardado.
|
|
40
|
+
*
|
|
41
|
+
* Duas situacoes diferentes, e confundi-las custa caro: hash diferente e **edicao a
|
|
42
|
+
* mao**, e o comando tem que parar sem escrever; arquivo **ausente** nao e edicao — e
|
|
43
|
+
* trabalho a refazer, e o comando tem que reescrever.
|
|
44
|
+
*/
|
|
45
|
+
export declare function confereDisco(raiz: string, arquivos: Record<string, string>): Promise<{
|
|
46
|
+
editados: string[];
|
|
47
|
+
faltando: string[];
|
|
48
|
+
}>;
|
|
49
|
+
/**
|
|
50
|
+
* O `CLAUDE.md` da raiz existe e importa a politica?
|
|
51
|
+
*
|
|
52
|
+
* Sem politica no servico E sem a linha, nao ha o que apontar nem o que avisar — avisar
|
|
53
|
+
* ai seria ruido que ensina a ignorar aviso. Mas com a linha escrita e a politica vazia o
|
|
54
|
+
* import esta pendurado de verdade, e esse e o silencio que a memoria
|
|
55
|
+
* `import-do-claude-md-falha-calado` manda quebrar.
|
|
56
|
+
*/
|
|
57
|
+
export declare function confereOPonteiro(raiz: string, temPolitica: boolean): Promise<Ponteiro>;
|
|
58
|
+
export declare function sync(raiz: string): Promise<Resultado>;
|
|
59
|
+
export {};
|
package/dist/sync.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { pede } from "./api.js";
|
|
2
3
|
import { dirname, join } from "node:path";
|
|
3
4
|
import { gravaManifesto, hashDe, leConfigDoRepo, leManifesto, leToken, } from "./config.js";
|
|
4
5
|
import { LINHA_DE_IMPORT, materializa } from "./materializa.js";
|
|
@@ -31,7 +32,7 @@ export async function confereDisco(raiz, arquivos) {
|
|
|
31
32
|
}
|
|
32
33
|
async function buscaBrain(config, token, etag) {
|
|
33
34
|
const url = `${config.api}/api/v1/artefatos?tenant=${encodeURIComponent(config.tenant)}&projeto=${encodeURIComponent(config.projeto)}`;
|
|
34
|
-
const resposta = await
|
|
35
|
+
const resposta = await pede(url, {
|
|
35
36
|
headers: {
|
|
36
37
|
Authorization: `Bearer ${token}`,
|
|
37
38
|
...(etag ? { "If-None-Match": etag } : {}),
|
package/package.json
CHANGED