wendkeep 0.38.1 → 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 +12 -0
- package/hooks/session-observability.mjs +1 -1
- package/hooks/token-usage.mjs +20 -15
- package/package.json +7 -2
- package/src/release-changelog.mjs +38 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@ 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
|
+
|
|
7
13
|
## [0.38.1] — 2026-07-12
|
|
8
14
|
|
|
9
15
|
### Fixed
|
|
@@ -40,6 +46,12 @@ All notable changes to **wendkeep** are documented here. Format based on
|
|
|
40
46
|
- Migração remove os headings legados sem perder reaberturas ou iterações mal posicionadas.
|
|
41
47
|
- Totais combinados de tokens/custo são atualizados também no `SubagentStop` e persistidos em campos compatíveis com os dashboards existentes.
|
|
42
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
|
+
|
|
43
55
|
## [0.35.0] — 2026-07-11
|
|
44
56
|
|
|
45
57
|
### Fixed
|
|
@@ -11,7 +11,7 @@ const usd = (n) => `$${(Number(n) || 0).toFixed(4)}`;
|
|
|
11
11
|
const round4 = (n) => Math.round((Number(n) || 0) * 10000) / 10000;
|
|
12
12
|
const effort = (value) => {
|
|
13
13
|
const normalized = String(value || '').trim().toLowerCase();
|
|
14
|
-
return ['none', 'low', 'medium', 'high', 'xhigh'].includes(normalized) ? normalized : (normalized || 'unknown');
|
|
14
|
+
return ['none', 'low', 'medium', 'high', 'xhigh', 'thinking'].includes(normalized) ? normalized : (normalized || 'unknown');
|
|
15
15
|
};
|
|
16
16
|
const usageTotal = (u = {}) => Number(u.total || 0) || (Number(u.input || 0) + Number(u.cached || 0) + Number(u.cacheWrite || 0) + Number(u.output || 0));
|
|
17
17
|
|
package/hooks/token-usage.mjs
CHANGED
|
@@ -454,6 +454,7 @@ function parseClaudeLines(lines, result) {
|
|
|
454
454
|
const seenThinking = new Set();
|
|
455
455
|
let thinkingChars = 0;
|
|
456
456
|
const thinkingCharsByModel = new Map();
|
|
457
|
+
let sawThinking = false;
|
|
457
458
|
let latestPrompt = '';
|
|
458
459
|
|
|
459
460
|
for (const line of lines) {
|
|
@@ -483,12 +484,18 @@ function parseClaudeLines(lines, result) {
|
|
|
483
484
|
result.toolCalls += 1;
|
|
484
485
|
addUnique(result.tools, block.name || 'tool_use');
|
|
485
486
|
}
|
|
486
|
-
if (block?.type === 'thinking'
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
487
|
+
if (block?.type === 'thinking') {
|
|
488
|
+
// Presença = extended thinking ATIVO. A `signature` persiste mesmo quando o Claude
|
|
489
|
+
// Code redige o texto (`thinking: ''`) — é o único sinal confiável do effort.
|
|
490
|
+
sawThinking = true;
|
|
491
|
+
// O texto só sobrevive à redação às vezes; quando sobrevive, estima reasoning tokens.
|
|
492
|
+
if (block.thinking) {
|
|
493
|
+
const thinkKey = `${msg.id || ''}:${block.thinking.slice(0, 60)}`;
|
|
494
|
+
if (!seenThinking.has(thinkKey)) {
|
|
495
|
+
seenThinking.add(thinkKey);
|
|
496
|
+
thinkingChars += block.thinking.length;
|
|
497
|
+
thinkingCharsByModel.set(model, (thinkingCharsByModel.get(model) || 0) + block.thinking.length);
|
|
498
|
+
}
|
|
492
499
|
}
|
|
493
500
|
}
|
|
494
501
|
}
|
|
@@ -510,12 +517,16 @@ function parseClaudeLines(lines, result) {
|
|
|
510
517
|
result.model = model;
|
|
511
518
|
}
|
|
512
519
|
|
|
513
|
-
//
|
|
514
|
-
//
|
|
520
|
+
// Effort observável no Claude: presença de blocos thinking (signature), não o texto — o
|
|
521
|
+
// nível low/medium/high não é gravado no transcript. Rótulo binário: thinking/none.
|
|
522
|
+
result.pensamento = sawThinking ? 'thinking' : 'none';
|
|
523
|
+
|
|
524
|
+
// Reasoning: estimativa-piso ~3,5 chars/token dos textos que escaparam da redação (quase
|
|
525
|
+
// sempre 0 no thread principal). Já contido em output_tokens — nunca somado de novo. NÃO
|
|
526
|
+
// determina o effort (desacoplado do pensamento acima).
|
|
515
527
|
const thinkingTokens = Math.round(thinkingChars / 3.5);
|
|
516
528
|
if (thinkingTokens > 0) {
|
|
517
529
|
result.totals.reasoning = thinkingTokens;
|
|
518
|
-
result.pensamento = `thinking ~${formatTokensShort(thinkingTokens)}`;
|
|
519
530
|
for (const [model, chars] of thinkingCharsByModel) {
|
|
520
531
|
const entry = result.byModel.get(`anthropic:${model}`);
|
|
521
532
|
if (entry) entry.usage.reasoning = Math.round(chars / 3.5);
|
|
@@ -525,12 +536,6 @@ function parseClaudeLines(lines, result) {
|
|
|
525
536
|
return result;
|
|
526
537
|
}
|
|
527
538
|
|
|
528
|
-
function formatTokensShort(n) {
|
|
529
|
-
if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;
|
|
530
|
-
if (n >= 1000) return `${(n / 1000).toFixed(1)}k`;
|
|
531
|
-
return String(n);
|
|
532
|
-
}
|
|
533
|
-
|
|
534
539
|
function detectTranscriptFormat(lines) {
|
|
535
540
|
for (const line of lines.slice(0, 20)) {
|
|
536
541
|
const event = parseJsonLine(line);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wendkeep",
|
|
3
|
-
"version": "0.38.
|
|
3
|
+
"version": "0.38.2",
|
|
4
4
|
"description": "A persistent-memory harness for AI coding agents on your Obsidian vault: turn-by-turn session capture plus a native, zero-dependency spec→change→verify→archive loop (sensor-gated, independent verdict, mutation discrimination). Local-first, agent-agnostic (Claude Code, Codex, Cursor…).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -20,7 +20,9 @@
|
|
|
20
20
|
},
|
|
21
21
|
"scripts": {
|
|
22
22
|
"check": "node --check bin/wendkeep.mjs && node --check src/init.mjs && node --check src/doctor.mjs",
|
|
23
|
-
"test": "node --test"
|
|
23
|
+
"test": "node --test",
|
|
24
|
+
"release": "node scripts/release.mjs",
|
|
25
|
+
"release:dry": "node scripts/release.mjs --dry-run"
|
|
24
26
|
},
|
|
25
27
|
"keywords": [
|
|
26
28
|
"claude-code",
|
|
@@ -42,5 +44,8 @@
|
|
|
42
44
|
"homepage": "https://github.com/rogersialves/wendkeep#readme",
|
|
43
45
|
"bugs": {
|
|
44
46
|
"url": "https://github.com/rogersialves/wendkeep/issues"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"wendkeep": "^0.38.1"
|
|
45
50
|
}
|
|
46
51
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Pure helper: extract a single version's release notes from a Keep-a-Changelog
|
|
2
|
+
// file. Reused by scripts/release.mjs and .github/workflows/release.yml so the
|
|
3
|
+
// GitHub Release body always matches the committed CHANGELOG.
|
|
4
|
+
|
|
5
|
+
const HEADER_RE = /^##\s*\[([^\]]+)\]\s*[—–-]\s*(.+?)\s*$/;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @param {string} changelogText Full CHANGELOG.md contents.
|
|
9
|
+
* @param {string} version Version to extract (with or without leading "v").
|
|
10
|
+
* @returns {{ version: string, date: string, notes: string }}
|
|
11
|
+
* @throws if the version has no section.
|
|
12
|
+
*/
|
|
13
|
+
export function extractReleaseNotes(changelogText, version) {
|
|
14
|
+
const target = String(version).replace(/^v/i, '').trim();
|
|
15
|
+
const lines = String(changelogText).split(/\r?\n/);
|
|
16
|
+
|
|
17
|
+
let start = -1;
|
|
18
|
+
let date = '';
|
|
19
|
+
for (let i = 0; i < lines.length; i++) {
|
|
20
|
+
const m = lines[i].match(HEADER_RE);
|
|
21
|
+
if (m && m[1].trim() === target) {
|
|
22
|
+
start = i;
|
|
23
|
+
date = m[2].trim();
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (start === -1) {
|
|
28
|
+
throw new Error(`CHANGELOG: versão ${version} não encontrada`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const body = [];
|
|
32
|
+
for (let j = start + 1; j < lines.length; j++) {
|
|
33
|
+
if (HEADER_RE.test(lines[j])) break;
|
|
34
|
+
body.push(lines[j]);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return { version: target, date, notes: body.join('\n').trim() };
|
|
38
|
+
}
|