dd-harness 0.25.0 → 0.26.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/brain.d.ts CHANGED
@@ -18,6 +18,11 @@ export type Memoria = {
18
18
  resumo: string;
19
19
  corpo: string;
20
20
  status: "ativa" | "historico";
21
+ /**
22
+ * `global` = vale para todo projeto do tenant, inclusive os que ainda nao existem.
23
+ * Ausente em payload antigo, e ai a memoria e de projeto — que era o unico escopo.
24
+ */
25
+ escopo?: "projeto" | "global";
21
26
  dano: string;
22
27
  invisibilidade: string;
23
28
  externalidade: string;
package/dist/cinto.d.ts CHANGED
@@ -28,7 +28,7 @@ export declare function caminhoDoCache(raiz: string): string;
28
28
  */
29
29
  export type CacheDeAncoras = {
30
30
  gravado_em: string;
31
- memorias: Pick<Memoria, "pasta" | "slug" | "titulo" | "resumo" | "status" | "ancoras">[];
31
+ memorias: Pick<Memoria, "pasta" | "slug" | "titulo" | "resumo" | "status" | "ancoras" | "escopo">[];
32
32
  };
33
33
  /**
34
34
  * Acrescenta UMA memoria ao cache, sem esperar a proxima sessao.
@@ -44,7 +44,7 @@ export type CacheDeAncoras = {
44
44
  *
45
45
  * Idempotente pelo endereco: regravar a mesma memoria substitui a entrada, nunca duplica.
46
46
  */
47
- export declare function acrescentaAoCache(raiz: string, memoria: Pick<Memoria, "pasta" | "slug" | "titulo" | "resumo" | "status" | "ancoras">): Promise<void>;
47
+ export declare function acrescentaAoCache(raiz: string, memoria: Pick<Memoria, "pasta" | "slug" | "titulo" | "resumo" | "status" | "ancoras" | "escopo">): Promise<void>;
48
48
  /** Guarda as ancoras do Brain para o hook consultar sem rede. Falha em silencio: cache e otimizacao, nao contrato. */
49
49
  export declare function guardaCache(raiz: string, brain: Brain): Promise<void>;
50
50
  export declare function leCache(raiz: string): Promise<CacheDeAncoras | null>;
@@ -78,7 +78,7 @@ export declare function alerta(tocadas: {
78
78
  pasta: string;
79
79
  memoria: string;
80
80
  ancora: string;
81
- }[], resumos: Map<string, string>): string;
81
+ }[], resumos: Map<string, string>, globais?: Set<string>): string;
82
82
  /**
83
83
  * O hook inteiro: le a entrada, cruza, devolve o JSON que o Claude Code espera.
84
84
  *
package/dist/cinto.js CHANGED
@@ -66,13 +66,14 @@ export async function guardaCache(raiz, brain) {
66
66
  gravado_em: new Date().toISOString(),
67
67
  memorias: brain.memorias
68
68
  .filter((m) => m.status === "ativa" && m.ancoras.length > 0)
69
- .map(({ pasta, slug, titulo, resumo, status, ancoras }) => ({
69
+ .map(({ pasta, slug, titulo, resumo, status, ancoras, escopo }) => ({
70
70
  pasta,
71
71
  slug,
72
72
  titulo,
73
73
  resumo,
74
74
  status,
75
75
  ancoras,
76
+ escopo,
76
77
  })),
77
78
  };
78
79
  try {
@@ -143,7 +144,7 @@ export function caminhoRelativo(raiz, arquivo) {
143
144
  * Vazio e o caso comum e tem que sair barato: a maioria das edicoes nao toca ancora
144
145
  * alguma, e o silencio ai nao e falta de aviso, e a ausencia de motivo para avisar.
145
146
  */
146
- export function alerta(tocadas, resumos) {
147
+ export function alerta(tocadas, resumos, globais = new Set()) {
147
148
  if (tocadas.length === 0)
148
149
  return "";
149
150
  const linhas = [
@@ -152,7 +153,11 @@ export function alerta(tocadas, resumos) {
152
153
  ];
153
154
  for (const t of tocadas) {
154
155
  const endereco = `${t.pasta}/${t.memoria}`;
155
- linhas.push(`## ${t.titulo}`, `\`${endereco}\` âncora: \`${t.ancora}\``, "");
156
+ // Marcar a global muda o peso do que se le: a licao nao fala DESTE projeto, fala de
157
+ // todos. Sem a marca, o agente a avalia como decisao local e pode concluir que "aqui
158
+ // e diferente" — que e exatamente o raciocinio que a promocao existiu para vencer.
159
+ const marca = globais.has(endereco) ? " · MEMÓRIA GLOBAL" : "";
160
+ linhas.push(`## ${t.titulo}${marca}`, `\`${endereco}\` — âncora: \`${t.ancora}\``, "");
156
161
  const resumo = resumos.get(endereco);
157
162
  if (resumo)
158
163
  linhas.push(resumo, "");
@@ -203,11 +208,12 @@ export async function decideDoHook(entrada) {
203
208
  if (novas.length === 0)
204
209
  return permitir;
205
210
  const resumos = new Map(cache.memorias.map((m) => [`${m.pasta}/${m.slug}`, m.resumo]));
211
+ const globais = new Set(cache.memorias.filter((m) => m.escopo === "global").map((m) => `${m.pasta}/${m.slug}`));
206
212
  return JSON.stringify({
207
213
  hookSpecificOutput: {
208
214
  hookEventName: "PreToolUse",
209
215
  permissionDecision: "allow",
210
- additionalContext: alerta(novas, resumos),
216
+ additionalContext: alerta(novas, resumos, globais),
211
217
  },
212
218
  });
213
219
  }
package/dist/curar.d.ts CHANGED
@@ -54,3 +54,30 @@ export declare function arquiva(raiz: string, endereco: string, opcoes: Arquivam
54
54
  endereco: string;
55
55
  motivo: string;
56
56
  }>;
57
+ /**
58
+ * Promove a memoria a global — ou a traz de volta ao projeto de origem.
59
+ *
60
+ * Global vale para TODO projeto do espaco, inclusive os que ainda nao existem. E o degrau
61
+ * acima de `memory_projects`, que lista projetos nomeados: aqui o alcance deixa de ser
62
+ * uma lista e vira uma propriedade.
63
+ */
64
+ export declare function promove(raiz: string, endereco: string, global: boolean): Promise<{
65
+ escopo: string;
66
+ global: boolean;
67
+ tenant: string | null;
68
+ }>;
69
+ /**
70
+ * Apaga de verdade, em cascata — ancoras, deriva medida, vinculos, tudo.
71
+ *
72
+ * Diferente de `arquiva`, que e o caminho normal: arquivar guarda o conteudo porque o que
73
+ * a memoria dizia pode voltar a importar. Isto e para o que nunca deveria ter existido.
74
+ *
75
+ * Sem `confirmacao`, o servidor RECUSA e devolve o que a cascata levaria junto — e so
76
+ * entao se repete a chamada com o nome do espaco. Duas etapas de proposito: DELETE nao
77
+ * tem desfazer, e a cascata e invisivel de fora.
78
+ */
79
+ export declare function apaga(raiz: string, endereco: string, confirmacao?: string): Promise<{
80
+ apagou: boolean;
81
+ detalhe: string;
82
+ confirmacaoEsperada?: string;
83
+ }>;
package/dist/curar.js CHANGED
@@ -92,3 +92,52 @@ export async function arquiva(raiz, endereco, opcoes) {
92
92
  const lido = (await resposta.json());
93
93
  return lido;
94
94
  }
95
+ /**
96
+ * Promove a memoria a global — ou a traz de volta ao projeto de origem.
97
+ *
98
+ * Global vale para TODO projeto do espaco, inclusive os que ainda nao existem. E o degrau
99
+ * acima de `memory_projects`, que lista projetos nomeados: aqui o alcance deixa de ser
100
+ * uma lista e vira uma propriedade.
101
+ */
102
+ export async function promove(raiz, endereco, global) {
103
+ const { config, token } = await credencial(raiz);
104
+ const resposta = await pede(`${config.api}/api/v1/memorias/${endereco}/escopo`, {
105
+ method: "PUT",
106
+ headers: cabecalhos(token, true),
107
+ body: JSON.stringify({ tenant: config.tenant, projeto: config.projeto, global }),
108
+ });
109
+ if (!resposta.ok)
110
+ await recusa(resposta);
111
+ return (await resposta.json());
112
+ }
113
+ /**
114
+ * Apaga de verdade, em cascata — ancoras, deriva medida, vinculos, tudo.
115
+ *
116
+ * Diferente de `arquiva`, que e o caminho normal: arquivar guarda o conteudo porque o que
117
+ * a memoria dizia pode voltar a importar. Isto e para o que nunca deveria ter existido.
118
+ *
119
+ * Sem `confirmacao`, o servidor RECUSA e devolve o que a cascata levaria junto — e so
120
+ * entao se repete a chamada com o nome do espaco. Duas etapas de proposito: DELETE nao
121
+ * tem desfazer, e a cascata e invisivel de fora.
122
+ */
123
+ export async function apaga(raiz, endereco, confirmacao) {
124
+ const { config, token } = await credencial(raiz);
125
+ const resposta = await pede(`${config.api}/api/v1/memorias/${endereco}/apagar`, {
126
+ method: "POST",
127
+ headers: cabecalhos(token, true),
128
+ body: JSON.stringify({
129
+ tenant: config.tenant,
130
+ ...(confirmacao ? { confirmacao } : {}),
131
+ }),
132
+ });
133
+ // 409 aqui nao e falha: e "ainda nao" — falta confirmar, ou ha uma arquivada apontando
134
+ // para esta. O texto do servidor e a resposta, entao ele passa adiante em vez de virar
135
+ // excecao com mensagem generica.
136
+ if (resposta.status === 409) {
137
+ const lido = (await resposta.json());
138
+ return { apagou: false, detalhe: lido.erro, confirmacaoEsperada: lido.confirmacao_esperada };
139
+ }
140
+ if (!resposta.ok)
141
+ await recusa(resposta);
142
+ return (await resposta.json());
143
+ }
package/dist/index.js CHANGED
@@ -22,7 +22,7 @@ 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
24
  import { busca } from "./buscar.js";
25
- import { arquiva, edita, le } from "./curar.js";
25
+ import { apaga, arquiva, edita, le, promove } from "./curar.js";
26
26
  import { criaPasta } from "./pasta.js";
27
27
  import { criaProjeto, listaTenants } from "./projeto.js";
28
28
  import { reancora } from "./reancorar.js";
@@ -52,6 +52,15 @@ const AJUDA = `dd-harness — a política e o Brain do projeto, no serviço
52
52
  dd-harness arquivar <pasta>/<slug> --motivo <obsoleta|incorreta|fora_dos_filtros>
53
53
  [--substituida-por <pasta>/<slug>]
54
54
  tira de circulação sem apagar
55
+ dd-harness promover <pasta>/<slug>
56
+ torna a memória global: vale para TODO
57
+ projeto do espaço, inclusive os futuros
58
+ dd-harness despromover <pasta>/<slug>
59
+ traz de volta ao alcance dos vínculos
60
+ dd-harness apagar <pasta>/<slug> [--confirmar <espaço>]
61
+ apaga de vez, em cascata — sem desfazer.
62
+ Para tirar de circulação guardando o
63
+ conteúdo, use "arquivar"
55
64
  dd-harness buscar "<pergunta>" acha memória por relevância, não por arquivo
56
65
  dd-harness ler <pasta>/<slug> imprime a memória inteira, no formato de gravar
57
66
  dd-harness reancorar <pasta>/<slug> --de "<alvo>" --para "<alvo>"
@@ -520,6 +529,49 @@ async function comandoEditar(argv) {
520
529
  console.log(` ${r.ancoras} âncora(s).`);
521
530
  }
522
531
  const MOTIVOS = ["obsoleta", "incorreta", "fora_dos_filtros"];
532
+ /**
533
+ * `promover` / `despromover` — o alcance da memoria.
534
+ *
535
+ * Global vale para TODO projeto do espaco, inclusive os que ainda nao existem. Nao e o
536
+ * mesmo que `projetos:` no frontmatter, que lista projetos nomeados: la o alcance e uma
537
+ * lista, aqui e uma propriedade.
538
+ */
539
+ async function comandoPromover(argv, global) {
540
+ const endereco = argv[0];
541
+ const verbo = global ? "promover" : "despromover";
542
+ if (!endereco || endereco.startsWith("-") || !endereco.includes("/")) {
543
+ throw new Error(`uso: dd-harness ${verbo} <pasta>/<slug>`);
544
+ }
545
+ const r = await promove(process.cwd(), endereco, global);
546
+ console.log(global
547
+ ? `${endereco} agora é GLOBAL — vale para todo projeto de ${r.tenant}, inclusive os que ainda não existem.`
548
+ : `${endereco} voltou a valer só para os projetos a que está vinculada.`);
549
+ }
550
+ /**
551
+ * `apagar` — o unico caminho que destroi.
552
+ *
553
+ * Duas etapas, e a segunda pede o nome do espaco digitado. Nao e cerimonia: a cascata leva
554
+ * ancoras e toda a deriva medida delas, e nao ha desfazer. Arquivar continua sendo o
555
+ * caminho normal — isto e para o que nunca deveria ter existido.
556
+ */
557
+ async function comandoApagar(argv) {
558
+ const endereco = argv[0];
559
+ if (!endereco || endereco.startsWith("-") || !endereco.includes("/")) {
560
+ throw new Error("uso: dd-harness apagar <pasta>/<slug> [--confirmar <espaço>]");
561
+ }
562
+ const r = await apaga(process.cwd(), endereco, argumento(argv, "confirmar"));
563
+ if (!r.apagou) {
564
+ console.log(r.detalhe);
565
+ if (r.confirmacaoEsperada) {
566
+ console.log(`
567
+ dd-harness apagar ${endereco} --confirmar ${r.confirmacaoEsperada}`);
568
+ }
569
+ // Sem exit diferente de zero: recusar por falta de confirmacao nao e falha, e o
570
+ // caminho normal da primeira chamada.
571
+ return;
572
+ }
573
+ console.log(`${endereco} apagada — ${r.detalhe}`);
574
+ }
523
575
  async function comandoArquivar(argv) {
524
576
  const endereco = argv[0];
525
577
  const motivo = argumento(argv, "motivo");
@@ -995,6 +1047,12 @@ async function principal() {
995
1047
  return comandoEditar(resto);
996
1048
  case "arquivar":
997
1049
  return comandoArquivar(resto);
1050
+ case "promover":
1051
+ return comandoPromover(resto, true);
1052
+ case "despromover":
1053
+ return comandoPromover(resto, false);
1054
+ case "apagar":
1055
+ return comandoApagar(resto);
998
1056
  case "ler":
999
1057
  return comandoLer(resto);
1000
1058
  case "buscar":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-harness",
3
- "version": "0.25.0",
3
+ "version": "0.26.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",