dd-harness 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/check.js +2 -1
- package/dist/gravar.js +111 -0
- package/dist/index.js +26 -1
- package/package.json +11 -3
package/dist/check.js
CHANGED
|
@@ -72,7 +72,7 @@ export async function check(raiz, commit) {
|
|
|
72
72
|
? memoriasTocadas(brain, await caminhosDoCommit(raiz, commit))
|
|
73
73
|
: [];
|
|
74
74
|
if (medicoes.length === 0) {
|
|
75
|
-
return { medidas: 0, ausentes: [], novas: 0, base: 0, jaAbertas: 0, tocadas };
|
|
75
|
+
return { medidas: 0, ausentes: [], novas: 0, base: 0, jaAbertas: 0, fechadas: 0, tocadas };
|
|
76
76
|
}
|
|
77
77
|
const envio = await fetch(`${config.api}/api/v1/deriva`, {
|
|
78
78
|
method: "POST",
|
|
@@ -97,6 +97,7 @@ export async function check(raiz, commit) {
|
|
|
97
97
|
novas: julgamento.novas,
|
|
98
98
|
base: julgamento.base,
|
|
99
99
|
jaAbertas: julgamento.ja_abertas,
|
|
100
|
+
fechadas: julgamento.fechadas ?? 0,
|
|
100
101
|
tocadas,
|
|
101
102
|
};
|
|
102
103
|
}
|
package/dist/gravar.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { leConfigDoRepo, leToken } from "./config.js";
|
|
3
|
+
const OBRIGATORIOS = ["name", "titulo", "description", "pasta"];
|
|
4
|
+
/** Frontmatter simples: `chave: valor` por linha. Sem lib de YAML — nao ha aninhamento. */
|
|
5
|
+
function leFrontmatter(texto) {
|
|
6
|
+
const linhas = texto.replace(/\r\n/g, "\n").split("\n");
|
|
7
|
+
if (linhas[0]?.trim() !== "---") {
|
|
8
|
+
throw new Error("o arquivo precisa começar com `---` e um frontmatter.");
|
|
9
|
+
}
|
|
10
|
+
const fim = linhas.indexOf("---", 1);
|
|
11
|
+
if (fim === -1)
|
|
12
|
+
throw new Error("frontmatter sem `---` de fechamento.");
|
|
13
|
+
const campos = new Map();
|
|
14
|
+
for (const linha of linhas.slice(1, fim)) {
|
|
15
|
+
const corte = linha.indexOf(":");
|
|
16
|
+
if (corte === -1)
|
|
17
|
+
continue;
|
|
18
|
+
campos.set(linha.slice(0, corte).trim(), linha.slice(corte + 1).trim());
|
|
19
|
+
}
|
|
20
|
+
return { campos, resto: linhas.slice(fim + 1).join("\n") };
|
|
21
|
+
}
|
|
22
|
+
/** `**Dano:** texto` — devolve o texto, sem o rotulo. */
|
|
23
|
+
function filtro(corpo, rotulo) {
|
|
24
|
+
const achado = corpo.match(new RegExp(`\\*\\*${rotulo}:\\*\\*\\s*([\\s\\S]*?)(?=\\n\\s*\\n\\*\\*|\\n## |$)`, "i"));
|
|
25
|
+
const valor = achado?.[1]?.trim() ?? "";
|
|
26
|
+
if (!valor)
|
|
27
|
+
throw new Error(`falta o filtro **${rotulo}:** no arquivo.`);
|
|
28
|
+
return valor;
|
|
29
|
+
}
|
|
30
|
+
/** Itens de lista da secao de ancoras, com ou sem crase em volta. */
|
|
31
|
+
function ancorasDe(texto) {
|
|
32
|
+
const secao = texto.split(/\n##\s+[ÂA]ncoras\s*\n/i)[1];
|
|
33
|
+
if (!secao)
|
|
34
|
+
return [];
|
|
35
|
+
return secao
|
|
36
|
+
.split("\n")
|
|
37
|
+
.map((l) => l.match(/^\s*[-*]\s+(.+?)\s*$/)?.[1])
|
|
38
|
+
.filter((v) => Boolean(v))
|
|
39
|
+
.map((v) => v.replace(/^`|`$/g, "").trim())
|
|
40
|
+
.filter(Boolean);
|
|
41
|
+
}
|
|
42
|
+
export function interpreta(texto) {
|
|
43
|
+
const { campos, resto } = leFrontmatter(texto);
|
|
44
|
+
for (const chave of OBRIGATORIOS) {
|
|
45
|
+
if (!campos.get(chave))
|
|
46
|
+
throw new Error(`frontmatter sem \`${chave}\`.`);
|
|
47
|
+
}
|
|
48
|
+
// O corpo vai ate a secao dos filtros; dali para baixo e metadado, nao conteudo.
|
|
49
|
+
const corte = resto.search(/\n##\s+Os tr[êe]s filtros\s*\n/i);
|
|
50
|
+
if (corte === -1)
|
|
51
|
+
throw new Error("falta a seção `## Os três filtros`.");
|
|
52
|
+
const corpo = resto
|
|
53
|
+
.slice(0, corte)
|
|
54
|
+
.replace(/<!--[\s\S]*?-->/g, "")
|
|
55
|
+
.trim();
|
|
56
|
+
if (!corpo)
|
|
57
|
+
throw new Error("o corpo da memória está vazio.");
|
|
58
|
+
const cauda = resto.slice(corte);
|
|
59
|
+
const tambem = campos.get("projetos") ?? "";
|
|
60
|
+
return {
|
|
61
|
+
slug: campos.get("name"),
|
|
62
|
+
titulo: campos.get("titulo"),
|
|
63
|
+
resumo: campos.get("description"),
|
|
64
|
+
pasta: campos.get("pasta"),
|
|
65
|
+
corpo,
|
|
66
|
+
dano: filtro(cauda, "Dano"),
|
|
67
|
+
invisibilidade: filtro(cauda, "Invisibilidade"),
|
|
68
|
+
externalidade: filtro(cauda, "Externalidade"),
|
|
69
|
+
ancoras: ancorasDe(cauda),
|
|
70
|
+
tambemEm: tambem
|
|
71
|
+
.split(",")
|
|
72
|
+
.map((s) => s.trim())
|
|
73
|
+
.filter(Boolean),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
export async function grava(raiz, caminho) {
|
|
77
|
+
const config = await leConfigDoRepo(raiz);
|
|
78
|
+
const token = await leToken(config.api);
|
|
79
|
+
if (!token) {
|
|
80
|
+
throw new Error(`sem credencial para ${config.api}. Rode \`dd-harness login --token <token>\`.`);
|
|
81
|
+
}
|
|
82
|
+
const memoria = interpreta(await readFile(caminho, "utf8"));
|
|
83
|
+
const resposta = await fetch(`${config.api}/api/v1/memorias`, {
|
|
84
|
+
method: "POST",
|
|
85
|
+
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
|
|
86
|
+
body: JSON.stringify({
|
|
87
|
+
tenant: config.tenant,
|
|
88
|
+
projeto: config.projeto,
|
|
89
|
+
pasta: memoria.pasta,
|
|
90
|
+
slug: memoria.slug,
|
|
91
|
+
titulo: memoria.titulo,
|
|
92
|
+
resumo: memoria.resumo,
|
|
93
|
+
corpo: memoria.corpo,
|
|
94
|
+
dano: memoria.dano,
|
|
95
|
+
invisibilidade: memoria.invisibilidade,
|
|
96
|
+
externalidade: memoria.externalidade,
|
|
97
|
+
ancoras: memoria.ancoras,
|
|
98
|
+
tambem_em: memoria.tambemEm,
|
|
99
|
+
}),
|
|
100
|
+
});
|
|
101
|
+
if (!resposta.ok) {
|
|
102
|
+
const { erro } = (await resposta.json().catch(() => ({})));
|
|
103
|
+
throw new Error(erro ?? `a API respondeu ${resposta.status}.`);
|
|
104
|
+
}
|
|
105
|
+
const { endereco } = (await resposta.json());
|
|
106
|
+
return {
|
|
107
|
+
endereco,
|
|
108
|
+
ancoras: memoria.ancoras.length,
|
|
109
|
+
projetos: 1 + memoria.tambemEm.length,
|
|
110
|
+
};
|
|
111
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { check, status } from "./check.js";
|
|
3
3
|
import { leConfigDoRepo, guardaToken } from "./config.js";
|
|
4
|
+
import { grava } from "./gravar.js";
|
|
4
5
|
import { init } from "./init.js";
|
|
5
6
|
import { LINHA_DE_IMPORT } from "./materializa.js";
|
|
6
7
|
import { sync } from "./sync.js";
|
|
@@ -17,6 +18,7 @@ const AJUDA = `dd-harness — materializa política, briefing e Brain no reposit
|
|
|
17
18
|
dd-harness init --tenant <t> --projeto <p> [--api <url>]
|
|
18
19
|
prepara o repositório (config + CLAUDE.md)
|
|
19
20
|
dd-harness login --token <token> guarda a credencial desta máquina
|
|
21
|
+
dd-harness gravar <arquivo.md> registra uma memória a partir de um markdown
|
|
20
22
|
dd-harness sync escreve os artefatos em disco
|
|
21
23
|
dd-harness check [--commit <sha>] mede as âncoras e reporta a deriva
|
|
22
24
|
dd-harness status só lê: o que espera julgamento
|
|
@@ -142,7 +144,12 @@ async function comandoCheck(argv) {
|
|
|
142
144
|
if (r.jaAbertas > 0) {
|
|
143
145
|
console.log(`${r.jaAbertas} já estava(m) aberta(s) — nada novo, só o carimbo.`);
|
|
144
146
|
}
|
|
145
|
-
|
|
147
|
+
// Reverter uma mudanca fecha a observacao que ela abriu. Anunciar isso importa: sem a
|
|
148
|
+
// linha, a fila encolhe sem explicacao e quem olha o `status` acha que perdeu algo.
|
|
149
|
+
if (r.fechadas > 0) {
|
|
150
|
+
console.log(`${r.fechadas} deriva(s) fechada(s): o alvo voltou a bater com a linha de base.`);
|
|
151
|
+
}
|
|
152
|
+
if (!r.ausentes.length && r.novas === 0 && r.jaAbertas === 0 && r.fechadas === 0) {
|
|
146
153
|
console.log("nenhuma deriva: o mundo ainda bate com o que as memórias dizem.");
|
|
147
154
|
}
|
|
148
155
|
// O cruzamento com o diff e outra coisa que deriva: e "voce acabou de mexer no que
|
|
@@ -177,6 +184,22 @@ async function comandoStatus() {
|
|
|
177
184
|
console.log(` ${m.pasta}/${m.memoria} — ${m.titulo}`);
|
|
178
185
|
}
|
|
179
186
|
}
|
|
187
|
+
/**
|
|
188
|
+
* O agente escreve o arquivo — que e o que ele ja fazia no modelo file-based — e este
|
|
189
|
+
* comando o transforma em requisicao. O arquivo nao fica no repositorio: quem
|
|
190
|
+
* materializa e o `sync`, a partir do servico, para nao existir copia escrita a mao ao
|
|
191
|
+
* lado da copia gerada.
|
|
192
|
+
*/
|
|
193
|
+
async function comandoGravar(argv) {
|
|
194
|
+
const caminho = argv[0];
|
|
195
|
+
if (!caminho || caminho.startsWith("-")) {
|
|
196
|
+
throw new Error("uso: dd-harness gravar <arquivo.md>");
|
|
197
|
+
}
|
|
198
|
+
const r = await grava(process.cwd(), caminho);
|
|
199
|
+
console.log(`gravado ${r.endereco}`);
|
|
200
|
+
console.log(` ${r.ancoras} âncora(s), valendo para ${r.projetos} projeto(s).` +
|
|
201
|
+
" Rode `dd-harness sync` para materializar.");
|
|
202
|
+
}
|
|
180
203
|
async function principal() {
|
|
181
204
|
const [comando, ...resto] = process.argv.slice(2);
|
|
182
205
|
switch (comando) {
|
|
@@ -184,6 +207,8 @@ async function principal() {
|
|
|
184
207
|
return comandoInit(resto);
|
|
185
208
|
case "login":
|
|
186
209
|
return comandoLogin(resto);
|
|
210
|
+
case "gravar":
|
|
211
|
+
return comandoGravar(resto);
|
|
187
212
|
case "sync":
|
|
188
213
|
return comandoSync();
|
|
189
214
|
case "check":
|
package/package.json
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dd-harness",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Materializa politica, briefing e Brain do dd-harness no repositorio. Sem dependencia: fetch, crypto e fs sao do Node.",
|
|
6
6
|
"license": "UNLICENSED",
|
|
7
7
|
"author": "Diego Dias",
|
|
8
|
-
"keywords": [
|
|
8
|
+
"keywords": [
|
|
9
|
+
"claude-code",
|
|
10
|
+
"ai-agents",
|
|
11
|
+
"memory",
|
|
12
|
+
"brain",
|
|
13
|
+
"cli"
|
|
14
|
+
],
|
|
9
15
|
"homepage": "https://dd-harness.vercel.app",
|
|
10
16
|
"repository": {
|
|
11
17
|
"type": "git",
|
|
@@ -15,7 +21,9 @@
|
|
|
15
21
|
"bin": {
|
|
16
22
|
"dd-harness": "dist/index.js"
|
|
17
23
|
},
|
|
18
|
-
"files": [
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
19
27
|
"engines": {
|
|
20
28
|
"node": ">=20"
|
|
21
29
|
},
|