wendkeep 0.38.0 → 0.38.2

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/CHANGELOG.md CHANGED
@@ -4,6 +4,19 @@ All notable changes to **wendkeep** are documented here. Format based on
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this project follows
5
5
  [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.38.2] — 2026-07-12
8
+
9
+ ### Fixed
10
+
11
+ - Observabilidade de sessão: o `Effort` do Claude Code passa a derivar da **presença** de blocos `thinking` (a `signature` persiste mesmo quando o Claude Code redige o texto do pensamento), não da estimativa por caracteres — que dava `0` e marcava a sessão como `unknown` mesmo com o extended thinking ativo (visto em 42/43 chamadas do transcript principal). Rótulo binário `thinking`/`none`, desacoplado da contagem de reasoning tokens. Subagents com mesmo modelo/estado deixam de se dividir em linhas `unknown` + `thinking ~Nk` e agrupam corretamente. Reasoning do Claude vira estimativa-piso do texto sobrevivente (não determina mais o effort). Caminho Codex inalterado.
12
+
13
+ ## [0.38.1] — 2026-07-12
14
+
15
+ ### Fixed
16
+
17
+ - Sessão Claude nova não perde mais o 1º turno: `resolveSessionIdentity` usa o `session_id` do hook como identidade canônica quando o transcript ainda não materializou em disco, em vez de adiar a criação da nota. Codex mantém a exigência de rollout/registry — a barreira anti-contaminação do 0.38.0 segue intacta.
18
+ - `task-log` e `subagent-stop` passam a honrar `input.provider` ao resolver a sessão (paridade com `decision-capture`), evitando deferimento falso quando o provider do ambiente diverge do transcript.
19
+
7
20
  ## [0.38.0] — 2026-07-12
8
21
 
9
22
  ### Added
@@ -33,6 +46,12 @@ All notable changes to **wendkeep** are documented here. Format based on
33
46
  - Migração remove os headings legados sem perder reaberturas ou iterações mal posicionadas.
34
47
  - Totais combinados de tokens/custo são atualizados também no `SubagentStop` e persistidos em campos compatíveis com os dashboards existentes.
35
48
 
49
+ ## [0.36.0] — 2026-07-11
50
+
51
+ _Publicada no npm sem changelog dedicado (bump de versão não foi commitado à época);
52
+ registrada retroativamente para paridade npm ↔ GitHub. As mudanças reais desta faixa
53
+ estão consolidadas entre 0.35.0 e 0.38.1._
54
+
36
55
  ## [0.35.0] — 2026-07-11
37
56
 
38
57
  ### Fixed
@@ -1,92 +1,92 @@
1
- // .agent/hooks/brain-inject.mjs
2
- // Injeção da camada quente no SessionStart (Claude/Codex/Copilot): CORE curado +
1
+ // .agent/hooks/brain-inject.mjs
2
+ // Injeção da camada quente no SessionStart (Claude/Codex/Copilot): CORE curado +
3
3
  // DIGEST auto + 1-linha pointer do recall + backlog completo de changes. Nunca derruba o hook.
4
- // Uso (hook): node .agent/hooks/brain-inject.mjs (input JSON via stdin)
5
- import { readFileSync } from 'node:fs';
6
- import { join } from 'node:path';
7
- import { pathToFileURL } from 'node:url';
8
- import { getVaultBase, readHookInput, writeHookOutput } from './obsidian-common.mjs';
9
- import { brainDir } from './brain-core.mjs';
10
- import { buildActiveChangeInjection, changeCtxState, writeSentinel } from './change-core.mjs';
11
- import { buildLessonsInjection } from './lessons-core.mjs';
4
+ // Uso (hook): node .agent/hooks/brain-inject.mjs (input JSON via stdin)
5
+ import { readFileSync } from 'node:fs';
6
+ import { join } from 'node:path';
7
+ import { pathToFileURL } from 'node:url';
8
+ import { getVaultBase, readHookInput, writeHookOutput } from './obsidian-common.mjs';
9
+ import { brainDir } from './brain-core.mjs';
10
+ import { buildActiveChangeInjection, changeCtxState, writeSentinel } from './change-core.mjs';
11
+ import { buildLessonsInjection } from './lessons-core.mjs';
12
12
  import { getLocale } from './locale.mjs';
13
13
  import { resolveSessionEntry } from './session-identity.mjs';
14
-
15
- // The process ROUTER — the enforcement layer. The wk-* skills are passive files; without a
16
- // standing instruction the model plans in chat, leaves the change scaffold raw and forces the
17
- // gate (seen in production: change archived with `(primeira tarefa)` open, via --force). This
18
- // block is injected EVERY session so planning always routes through the a2 loop.
19
- function processRouter(localeId) {
20
- if (localeId === 'en') {
21
- return [
22
- '<wk_process>',
23
- 'Spec-driven process (mandatory for any non-trivial task): INVOKE the wk-workflow Skill BEFORE editing any file.',
24
- '1. Plan: invoke the wk-brainstorming Skill (approved design) → wk-planning (task plan).',
14
+
15
+ // The process ROUTER — the enforcement layer. The wk-* skills are passive files; without a
16
+ // standing instruction the model plans in chat, leaves the change scaffold raw and forces the
17
+ // gate (seen in production: change archived with `(primeira tarefa)` open, via --force). This
18
+ // block is injected EVERY session so planning always routes through the a2 loop.
19
+ function processRouter(localeId) {
20
+ if (localeId === 'en') {
21
+ return [
22
+ '<wk_process>',
23
+ 'Spec-driven process (mandatory for any non-trivial task): INVOKE the wk-workflow Skill BEFORE editing any file.',
24
+ '1. Plan: invoke the wk-brainstorming Skill (approved design) → wk-planning (task plan).',
25
25
  '2. Record: `wendkeep change new <slug>` and FILL proposta/design/tasks. Resolve `spec_impact`: `required` needs `specs/<capability>/spec.md` + [req:ID]; `none` needs a reason. Never leave pending/placeholders.',
26
- '3. Implement: wk-tdd per task; tick `- [x]` as you finish. Something broke? wk-debugging.',
27
- '4. Close: `wendkeep verify` (+ `--deep` + the wk-verify Skill) → `wendkeep change archive`.',
28
- 'NEVER `archive --force` on your own — a red gate means pending work; --force is the user\'s call, not yours. Dead end? `wendkeep change abandon`.',
29
- '</wk_process>',
30
- ].join('\n');
31
- }
32
- return [
33
- '<wk_process>',
34
- 'Processo spec-driven (obrigatório em tarefa não-trivial): INVOQUE a Skill wk-workflow ANTES de editar qualquer arquivo.',
35
- '1. Planejar: invoque a Skill wk-brainstorming (design aprovado) → wk-planning (plano de tarefas).',
26
+ '3. Implement: wk-tdd per task; tick `- [x]` as you finish. Something broke? wk-debugging.',
27
+ '4. Close: `wendkeep verify` (+ `--deep` + the wk-verify Skill) → `wendkeep change archive`.',
28
+ 'NEVER `archive --force` on your own — a red gate means pending work; --force is the user\'s call, not yours. Dead end? `wendkeep change abandon`.',
29
+ '</wk_process>',
30
+ ].join('\n');
31
+ }
32
+ return [
33
+ '<wk_process>',
34
+ 'Processo spec-driven (obrigatório em tarefa não-trivial): INVOQUE a Skill wk-workflow ANTES de editar qualquer arquivo.',
35
+ '1. Planejar: invoque a Skill wk-brainstorming (design aprovado) → wk-planning (plano de tarefas).',
36
36
  '2. Registrar: `wendkeep change new <slug>` e PREENCHA proposta/design/tarefas. Resolva `spec_impact`: `required` exige `specs/<capability>/spec.md` + [req:ID]; `none` exige justificativa. Nunca deixe pending/placeholders.',
37
- '3. Implementar: wk-tdd por tarefa; marque `- [x]` ao concluir. Quebrou algo? wk-debugging.',
38
- '4. Fechar: `wendkeep verify` (+ `--deep` + Skill wk-verify) → `wendkeep change archive`.',
39
- 'PROIBIDO `archive --force` por conta própria — gate vermelho significa trabalho pendente; --force é decisão do usuário, não sua. Beco sem saída? `wendkeep change abandon`.',
40
- '</wk_process>',
41
- ].join('\n');
42
- }
43
-
44
- const MAX_LINES = 45; // CORE ≤25 + DIGEST ≤15 + folga; salvaguarda se o CORE crescer à mão
45
-
37
+ '3. Implementar: wk-tdd por tarefa; marque `- [x]` ao concluir. Quebrou algo? wk-debugging.',
38
+ '4. Fechar: `wendkeep verify` (+ `--deep` + Skill wk-verify) → `wendkeep change archive`.',
39
+ 'PROIBIDO `archive --force` por conta própria — gate vermelho significa trabalho pendente; --force é decisão do usuário, não sua. Beco sem saída? `wendkeep change abandon`.',
40
+ '</wk_process>',
41
+ ].join('\n');
42
+ }
43
+
44
+ const MAX_LINES = 45; // CORE ≤25 + DIGEST ≤15 + folga; salvaguarda se o CORE crescer à mão
45
+
46
46
  export function buildInjection(vaultBase, input = {}) {
47
- const dir = brainDir(vaultBase);
48
- const read = (name) => {
49
- try { return readFileSync(join(dir, name), 'utf8').trim(); } catch { return ''; }
50
- };
51
- const pointer = 'Memória profunda sob demanda: /brain-recall <tópico> (índice .brain/index.jsonl).';
52
- // Quando CORE e DIGEST não existem, ''.split('\n') vira [''] — o filter derruba essa
53
- // linha vazia para o caso "só pointer" ficar com exatamente 3 linhas.
54
- let lines = [read('CORE.md'), read('DIGEST.md')].filter(Boolean).join('\n\n').split('\n').filter((l, i, a) => a.length > 1 || l);
55
- if (lines.length > MAX_LINES) {
56
- lines = lines.slice(0, MAX_LINES);
57
- lines.push('*…truncado pelo budget — fonte completa: .brain/CORE.md + .brain/DIGEST.md*');
58
- }
59
- const brain = ['<brain_memory>', ...lines, pointer, '</brain_memory>'].join('\n');
60
- const router = processRouter(getLocale(vaultBase).id);
61
- const change = buildActiveChangeInjection(vaultBase);
47
+ const dir = brainDir(vaultBase);
48
+ const read = (name) => {
49
+ try { return readFileSync(join(dir, name), 'utf8').trim(); } catch { return ''; }
50
+ };
51
+ const pointer = 'Memória profunda sob demanda: /brain-recall <tópico> (índice .brain/index.jsonl).';
52
+ // Quando CORE e DIGEST não existem, ''.split('\n') vira [''] — o filter derruba essa
53
+ // linha vazia para o caso "só pointer" ficar com exatamente 3 linhas.
54
+ let lines = [read('CORE.md'), read('DIGEST.md')].filter(Boolean).join('\n\n').split('\n').filter((l, i, a) => a.length > 1 || l);
55
+ if (lines.length > MAX_LINES) {
56
+ lines = lines.slice(0, MAX_LINES);
57
+ lines.push('*…truncado pelo budget — fonte completa: .brain/CORE.md + .brain/DIGEST.md*');
58
+ }
59
+ const brain = ['<brain_memory>', ...lines, pointer, '</brain_memory>'].join('\n');
60
+ const router = processRouter(getLocale(vaultBase).id);
61
+ const change = buildActiveChangeInjection(vaultBase);
62
62
  const lessons = buildLessonsInjection(vaultBase);
63
63
  const { identity, entry } = resolveSessionEntry(vaultBase, input);
64
64
  const focus = identity.state === 'resolved' && entry?.change_slug
65
65
  ? `<session_change>Change vinculada a esta sessão: ${entry.change_slug}. Este vínculo prevalece para writes automáticos; todas as pendências continuam visíveis acima.</session_change>`
66
66
  : '';
67
67
  return [brain, router, focus, change, lessons].filter(Boolean).join('\n');
68
- }
69
-
70
- if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
71
- try {
72
- const input = readHookInput();
73
- const vaultBase = getVaultBase(input);
74
- writeHookOutput({
75
- hookSpecificOutput: {
76
- hookEventName: 'SessionStart',
68
+ }
69
+
70
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
71
+ try {
72
+ const input = readHookInput();
73
+ const vaultBase = getVaultBase(input);
74
+ writeHookOutput({
75
+ hookSpecificOutput: {
76
+ hookEventName: 'SessionStart',
77
77
  additionalContext: buildInjection(vaultBase, input),
78
- },
79
- });
78
+ },
79
+ });
80
80
  // Sentinela do change-context: o backlog completo acabou de ser injetado aqui, então o hook
81
81
  // UserPromptSubmit não precisa re-pingar no 1º prompt. Bônus — nunca derruba a injeção.
82
- try {
83
- const st = changeCtxState(vaultBase);
82
+ try {
83
+ const st = changeCtxState(vaultBase);
84
84
  const { identity } = resolveSessionEntry(vaultBase, input);
85
85
  const sid = identity.state === 'resolved' ? identity.canonicalConversationId : (input.session_id || input.sessionId || '');
86
86
  if (st) writeSentinel(vaultBase, 'ctx', sid, st.hash);
87
- } catch { /* sentinela é bônus */ }
88
- } catch (error) {
89
- process.stderr.write(`[brain] inject falhou: ${error.message}\n`);
90
- writeHookOutput({});
91
- }
92
- }
87
+ } catch { /* sentinela é bônus */ }
88
+ } catch (error) {
89
+ process.stderr.write(`[brain] inject falhou: ${error.message}\n`);
90
+ writeHookOutput({});
91
+ }
92
+ }