wendkeep 0.48.0 → 0.49.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/CHANGELOG.md +15 -0
- package/hooks/harness-doctor.mjs +38 -2
- package/hooks/linked-notes.mjs +763 -763
- package/package.json +1 -1
- package/src/doctor.mjs +21 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,21 @@ 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.49.0] — 2026-07-23
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **`wendkeep doctor` agora surfaça os órfãos do grafo com o comando de reparo.** Nova seção
|
|
12
|
+
`[links]`: conta notas derivadas sem sessão-fonte (→ `note relink`), artefatos de change
|
|
13
|
+
sem backlink (→ `change backlink`) e o estado das cores do grafo (→ `theme sync`); quando
|
|
14
|
+
tudo conectado, diz `grafo conectado`. Reusa as funções de reparo em dry-run — zero lógica
|
|
15
|
+
nova de detecção. Antes o dono do vault só descobria os órfãos olhando o grafo no Obsidian
|
|
16
|
+
e não sabia qual comando rodava.
|
|
17
|
+
- **Sessão não mente "inativa" com atividade recente.** Nova seção `[sessão]`: quando o
|
|
18
|
+
control marca `inactive` mas a nota da sessão foi escrita há pouco, o doctor sinaliza
|
|
19
|
+
possível workflow/subagente em segundo plano — o control só reflete o lifecycle da
|
|
20
|
+
sessão-mãe, não a atividade de background. Capability `vault-doctor` (DIAG-1..4).
|
|
21
|
+
|
|
7
22
|
## [0.48.0] — 2026-07-23
|
|
8
23
|
|
|
9
24
|
### Added
|
package/hooks/harness-doctor.mjs
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
// hooks/harness-doctor.mjs — integrity checks for the a2 harness state (Wave B).
|
|
2
2
|
// Pure-ish (fs reads only). `wendkeep doctor` reports errors (exit 1) + warnings.
|
|
3
|
-
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
3
|
+
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
|
-
import { activeChange, parseTasks } from './change-core.mjs';
|
|
5
|
+
import { activeChange, parseTasks, backfillArtifactLinks } from './change-core.mjs';
|
|
6
|
+
import { relinkDerivedNotes } from './linked-notes.mjs';
|
|
6
7
|
import { buildEffectiveRequirementPackage, checkSpecsState, evaluateVerdict, tasksHashOf, validateSpecImpact } from './spec-core.mjs';
|
|
7
8
|
import { getLocale } from './locale.mjs';
|
|
9
|
+
import { readControl } from './obsidian-common.mjs';
|
|
8
10
|
|
|
9
11
|
export function checkHarness(vaultBase, projectRoot) {
|
|
10
12
|
const loc = getLocale(vaultBase);
|
|
@@ -63,3 +65,37 @@ export function checkHarness(vaultBase, projectRoot) {
|
|
|
63
65
|
|
|
64
66
|
return { errors, warnings };
|
|
65
67
|
}
|
|
68
|
+
|
|
69
|
+
// --- diagnóstico de links do grafo (read-only, reusa os reparos em dry-run) -----
|
|
70
|
+
// Surfaça os órfãos que o doctor não enxergava: notas derivadas sem sessão-fonte,
|
|
71
|
+
// artefatos de change sem backlink, e o estado das cores do grafo. Cada não-zero tem um
|
|
72
|
+
// comando de reparo (note relink / change backlink / theme sync).
|
|
73
|
+
export function checkVaultLinks(vaultBase) {
|
|
74
|
+
let derivedOrphans = 0;
|
|
75
|
+
try { derivedOrphans = relinkDerivedNotes(vaultBase, {}).linked.length; } catch { /* sem notas derivadas */ }
|
|
76
|
+
let artifactOrphans = 0;
|
|
77
|
+
try { artifactOrphans = backfillArtifactLinks(vaultBase, {}).changed.length; } catch { /* sem changes */ }
|
|
78
|
+
let graphColors = null; // true=com grupos · false=vazio/ausente de cores · null=sem graph.json
|
|
79
|
+
try {
|
|
80
|
+
const g = JSON.parse(readFileSync(join(vaultBase, '.obsidian', 'graph.json'), 'utf8'));
|
|
81
|
+
graphColors = Array.isArray(g.colorGroups) && g.colorGroups.length > 0;
|
|
82
|
+
} catch { graphColors = null; }
|
|
83
|
+
return { derivedOrphans, artifactOrphans, graphColors };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const unquoteControl = (v) => String(v ?? '').replace(/^"(.*)"$/, '$1').trim();
|
|
87
|
+
|
|
88
|
+
// O control marca `inactive` quando a sessão-mãe encerra, mesmo com um workflow/subagente
|
|
89
|
+
// ainda vivo em background. Se a nota da sessão foi escrita há pouco apesar do `inactive`,
|
|
90
|
+
// sinaliza a atividade recente — o doctor deixa de dizer "inativa" quando não está.
|
|
91
|
+
export function checkSessionActivity(vaultBase, { now = Date.now(), windowMs = 5 * 60000 } = {}) {
|
|
92
|
+
const control = readControl(vaultBase);
|
|
93
|
+
const active = unquoteControl(control.status) === 'active';
|
|
94
|
+
const sessionRel = unquoteControl(active ? control.session_file : (control.last_session_file || control.session_file));
|
|
95
|
+
let ageMs = null;
|
|
96
|
+
if (sessionRel) {
|
|
97
|
+
try { ageMs = now - statSync(join(vaultBase, sessionRel)).mtimeMs; } catch { ageMs = null; }
|
|
98
|
+
}
|
|
99
|
+
const backgroundSuspected = !active && sessionRel !== '' && ageMs !== null && ageMs >= 0 && ageMs < windowMs;
|
|
100
|
+
return { lastSession: sessionRel, active, ageMs, backgroundSuspected };
|
|
101
|
+
}
|