wendkeep 0.62.0 → 0.64.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 CHANGED
@@ -4,6 +4,55 @@ 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.64.0] — 2026-07-28
8
+
9
+ ### Added
10
+
11
+ - **O workspace privado CLI passa a ter runtime canônico em `packages/cli/src/index.mjs`.** Help,
12
+ versão, seleção de Vault, apresentação de erros e dispatch lazy agora pertencem à fronteira
13
+ `@wendkeep/cli`, com import sem autoexecução.
14
+ - **O tarball instalado prova a CLI fora do checkout.** A distribuição inclui o runtime novo e
15
+ exercita os aliases `wendkeep` e `wk` em um consumidor temporário, mantendo o pacote raiz como
16
+ a única unidade npm.
17
+
18
+ ### Changed
19
+
20
+ - **`bin/wendkeep.mjs` agora é uma fachada executável fina.** O arquivo preserva o shebang e
21
+ apenas invoca `runCli()`, enquanto comandos, flags, help antes do Vault, streams, mensagens e
22
+ códigos de saída mantêm o comportamento anterior.
23
+ - **A CLI programática permanece privada durante a migração.** Não há export raiz
24
+ `wendkeep/cli`; as superfícies públicas continuam sendo os binários, `wendkeep/harness` e
25
+ `wendkeep/vault`.
26
+
27
+ ## [0.63.0] — 2026-07-28
28
+
29
+ ### Added
30
+
31
+ - **O Vault público passa a expor locale e taxonomia de pastas, e o Harness passa a expor o
32
+ store durável de FLOW.** As novas superfícies canônicas estão disponíveis por
33
+ wendkeep/vault e wendkeep/harness e são validadas a partir de um tarball instalado em consumidor
34
+ isolado, inclusive com um ciclo persistido completo.
35
+
36
+ ### Changed
37
+
38
+ - **Locale agora pertence a packages/vault/src e o store de FLOW a packages/harness/src.**
39
+ hooks/locale.mjs e hooks/vault-runtime-store.mjs permanecem como fachadas puras, preservando por
40
+ identidade todas as constantes e funções usadas por consumidores existentes.
41
+ - **A extração stateful não exige migração.** Paths em .brain/runtime, schemas, ids, locks,
42
+ tentativas, recibos, reservas, promoções, atomicidade, contenção multiprocesso e validação física
43
+ de paths mantêm o contrato anterior.
44
+ - **A direção Harness → Vault é verificada pelo índice público canônico.** Vault continua proibido
45
+ de depender do Harness, enquanto CLI, MCP, Integrations, Pi e flow-core permanecem fora deste
46
+ corte modular.
47
+
48
+ ### Fixed
49
+
50
+ - **Promoções concorrentes no Windows/Node 22 não confundem a liberação transitória do lock com
51
+ um path inseguro.** O Vault agora revalida, com backoff curto e limitado, o lock público que
52
+ desaparece entre `lstat` e `realpath` e leituras `ENOENT` de owner/lease. A aquisição compartilha
53
+ um único budget/deadline e a liberação limpa o lock sem resíduo; junctions, reparse points, links
54
+ dangling, erros não transitórios e locks privados `.pending` continuam falhando fechado.
55
+
7
56
  ## [0.62.0] — 2026-07-28
8
57
 
9
58
  ### Added
package/README.en.md CHANGED
@@ -16,13 +16,29 @@
16
16
  **Persistent memory for AI coding agents, built on your Obsidian vault.** Every Claude Code **and Codex** session is captured turn by turn into local Markdown — `init` wires both (Codex asks you to approve its hooks once; `import` backfills past sessions either way) — with token/cost tracking and automatically extracted decisions, bugs, and learnings. That always-on plane is **Keep Core**. On top of it, **Wend Runtime** provides a native, zero-dependency lifecycle (spec → change → TDD → sensor-gated archive), selected through the `OFF`, `FLOW`, `GUIDE`, `GOVERN`, and `ASSURE` Operating Profiles. 100% local, open-core.
17
17
 
18
18
  The runtime is being separated into six physical boundaries — `cli`, `harness`, `vault`, `mcp`,
19
- `integrations`, and `pi` — without fragmenting installation. The private `harness` and `vault`
20
- workspaces now canonically own Operating Profiles/the sensor engine and safe binding/the Shared
21
- Project Memory v2 kernel, respectively. The root package exposes those surfaces through
22
- `wendkeep/harness` and `wendkeep/vault`; historical imports keep working through compatibility
19
+ `integrations`, and `pi` — without fragmenting installation. The private `cli`, `harness`, and
20
+ `vault` workspaces now canonically own the executable runtime, Operating Profiles/the sensor
21
+ engine, and safe binding/the Shared Project Memory v2 kernel, respectively. The root package
22
+ exposes Harness and Vault through `wendkeep/harness` and `wendkeep/vault`; CLI remains public only
23
+ through the `wendkeep`/`wk` binaries. Historical imports keep working through compatibility
23
24
  facades and no session data needs migration;
24
25
  see the [modular architecture](docs/en/architecture.md).
25
26
 
27
+ In the **0.64 CLI Runtime** phase, `packages/cli/src/index.mjs` owns help, version reporting, Vault
28
+ selection, error presentation, and lazy dispatch. `bin/wendkeep.mjs` is reduced to the shebang and
29
+ a `runCli()` invocation. The tarball remains a single artifact and proves both aliases in an
30
+ isolated consumer; there is no public `wendkeep/cli` subpath in this phase.
31
+
32
+ In the **0.63 Harness FLOW Store** phase, `packages/vault/src/locale.mjs` becomes the canonical
33
+ source for Vault locale and taxonomy, while `packages/harness/src/flow-store.mjs` becomes the
34
+ canonical durable FLOW store. The historical `hooks/locale.mjs` and
35
+ `hooks/vault-runtime-store.mjs` facades preserve export identity. Harness depends only on Vault's
36
+ public index — never the reverse — with no migration of paths, schemas, or locks; the tarball
37
+ remains a single artifact published by the root `wendkeep` package.
38
+ Under multiprocess contention, transient release of the public lock and its owner/lease metadata
39
+ is revalidated with a bounded budget/deadline, including final cleanup; junctions, reparse points,
40
+ dangling locks, and non-transient errors are still rejected before any write.
41
+
26
42
  ```bash
27
43
  npm i -D wendkeep && npx wendkeep init # captures from the next session on
28
44
  npx wendkeep import # backfill past Claude + Codex sessions
package/README.md CHANGED
@@ -16,13 +16,29 @@
16
16
  **Persistent memory for AI coding agents, built on your Obsidian vault.** Every Claude Code **and Codex** session is captured turn by turn into local Markdown — `init` wires both (Codex asks you to approve its hooks once; `import` backfills past sessions either way) — with token/cost tracking and automatically extracted decisions, bugs, and learnings. That always-on plane is **Keep Core**. On top of it, **Wend Runtime** provides a native, zero-dependency lifecycle (spec → change → TDD → sensor-gated archive), selected through the `OFF`, `FLOW`, `GUIDE`, `GOVERN`, and `ASSURE` Operating Profiles. 100% local, open-core.
17
17
 
18
18
  The runtime is being separated into six physical boundaries — `cli`, `harness`, `vault`, `mcp`,
19
- `integrations`, and `pi` — without fragmenting installation. The private `harness` and `vault`
20
- workspaces now canonically own Operating Profiles/the sensor engine and safe binding/the Shared
21
- Project Memory v2 kernel, respectively. The root package exposes those surfaces through
22
- `wendkeep/harness` and `wendkeep/vault`; historical imports keep working through compatibility
19
+ `integrations`, and `pi` — without fragmenting installation. The private `cli`, `harness`, and
20
+ `vault` workspaces now canonically own the executable runtime, Operating Profiles/the sensor
21
+ engine, and safe binding/the Shared Project Memory v2 kernel, respectively. The root package
22
+ exposes Harness and Vault through `wendkeep/harness` and `wendkeep/vault`; CLI remains public only
23
+ through the `wendkeep`/`wk` binaries. Historical imports keep working through compatibility
23
24
  facades and no session data needs migration;
24
25
  see the [modular architecture](docs/en/architecture.md).
25
26
 
27
+ In the **0.64 CLI Runtime** phase, `packages/cli/src/index.mjs` owns help, version reporting, Vault
28
+ selection, error presentation, and lazy dispatch. `bin/wendkeep.mjs` is reduced to the shebang and
29
+ a `runCli()` invocation. The tarball remains a single artifact and proves both aliases in an
30
+ isolated consumer; there is no public `wendkeep/cli` subpath in this phase.
31
+
32
+ In the **0.63 Harness FLOW Store** phase, `packages/vault/src/locale.mjs` becomes the canonical
33
+ source for Vault locale and taxonomy, while `packages/harness/src/flow-store.mjs` becomes the
34
+ canonical durable FLOW store. The historical `hooks/locale.mjs` and
35
+ `hooks/vault-runtime-store.mjs` facades preserve export identity. Harness depends only on Vault's
36
+ public index — never the reverse — with no migration of paths, schemas, or locks; the tarball
37
+ remains a single artifact published by the root `wendkeep` package.
38
+ Under multiprocess contention, transient release of the public lock and its owner/lease metadata
39
+ is revalidated with a bounded budget/deadline, including final cleanup; junctions, reparse points,
40
+ dangling locks, and non-transient errors are still rejected before any write.
41
+
26
42
  ```bash
27
43
  npm i -D wendkeep && npx wendkeep init # captures from the next session on
28
44
  npx wendkeep import # backfill past Claude + Codex sessions
package/bin/wendkeep.mjs CHANGED
@@ -1,346 +1,4 @@
1
1
  #!/usr/bin/env node
2
- // wendkeep CLI single entrypoint.
3
- // wendkeep init [--vault <path>] [--project <path>] [--no-mcp] [--yes] [--force]
4
- // wendkeep hook <name> (invoked by the agent's settings.json; pipes stdin/stdout)
5
- // wendkeep doctor [--vault <path>]
6
- // wendkeep --version | --help
7
- import { spawnSync } from 'node:child_process';
8
- import { existsSync, readFileSync } from 'node:fs';
9
- import { dirname, join } from 'node:path';
10
- import { fileURLToPath } from 'node:url';
11
- import { RUNNABLE_HOOKS } from '../src/taxonomy.mjs';
2
+ import { runCli } from '../packages/cli/src/index.mjs';
12
3
 
13
- const here = dirname(fileURLToPath(import.meta.url));
14
- const pkgRoot = join(here, '..');
15
- const hooksDir = join(pkgRoot, 'hooks');
16
-
17
- function version() {
18
- try {
19
- return JSON.parse(readFileSync(join(pkgRoot, 'package.json'), 'utf8')).version;
20
- } catch {
21
- return '0.0.0';
22
- }
23
- }
24
-
25
- const HELP = `wendkeep ${version()} — keep durable AI sessions in an Obsidian vault, with optional governance.
26
-
27
- Usage:
28
- wendkeep init [options] Set up wendkeep in a project (cross-platform).
29
- --vault <path> Obsidian vault folder (default: <project>/.<project-name>-vault).
30
- --project <path> Project root to wire (default: current directory).
31
- --profile <name> Operating profile: OFF, FLOW, GUIDE, GOVERN (default), or ASSURE.
32
- --no-mcp Do not add the mcpvault MCP server to .mcp.json.
33
- --companions <csv> Companion plugins/MCP to pin: context-mode,caveman,understand-anything
34
- (default: none — opt in explicitly). dotcontext is legacy — the native a2 loop replaces it.
35
- --no-companions Skip companion plugins/MCP entirely.
36
- --no-colors Skip the Obsidian color system (.obsidian snippet + graph groups).
37
- --dotcontext-mcp <v> dotcontext MCP placement: auto (default; skip project entry
38
- if already global), project, or none.
39
- --dotcontext-hooks <v> dotcontext hooks: full (default), light (no PostToolUse), none.
40
- --yes, -y Non-interactive; accept defaults.
41
- --force Overwrite existing wendkeep config blocks.
42
-
43
- wendkeep hook <name> Run a session hook (used by settings.json). Reads the
44
- agent's JSON on stdin. Names: ${RUNNABLE_HOOKS.join(', ')}.
45
-
46
- wendkeep sync [--project P] Run init -> sync-defs -> doctor on the CURRENT project, in one
47
- command — the three steps that repeat identically after every
48
- package update. Stops at the first failing step. Install the
49
- package first (npm i -D wendkeep@latest); a running process
50
- cannot replace itself. · --vault P · --profile <name> · --yes.
51
-
52
- wendkeep doctor [--vault P] Run a vault health check.
53
- wendkeep change <sub> Change lifecycle: new [--simple] | use | bind <slug> --session <id> | continue | list | show |
54
- status | done <id> | undone <id> | diff | archive [--force] | abandon | relink | backlink.
55
- archive exige verdict (rode verify --deep); abandon descarta sem ADR.
56
- backlink [--apply]: injeta o backlink pro proposta em design/tarefas/spec órfãos (open + _arquivo).
57
- wendkeep theme sync Re-aplica o color system (snippet CSS + graph color groups) num vault
58
- existente — recupera o grafo cinza sem re-init. --vault P.
59
- wendkeep session <sub> Session registry: list | show <id> | use <id>.
60
- wendkeep profile <sub> Operating profile: status | use <OFF|FLOW|GUIDE|GOVERN|ASSURE>.
61
- --session <id> sets an audited session override; otherwise changes
62
- the project default. The Vault/session/memory core is always active.
63
- wendkeep flow <sub> Low-ceremony E -> V contract: start | status | show | finish | promote.
64
- FLOW records scope, sensors and a receipt without creating a change.
65
- wendkeep spec <sub> Specs: list | show | effective [--change] [--json] | migrate | rebase.
66
- wendkeep sensors <sub> list | add <id> "<command>" [--severity --type --report].
67
- wendkeep cost [opts] Aggregate AI-coding spend across the vault's sessions.
68
- --since <date> · --top [N] (priciest) · --trend [day|week|month]
69
- (+ run-rate projection) · --write (generate 00-Custo.md) · --json.
70
- wendkeep cost rebuild Recalculate historical parent + subagent costs from SESSION_REGISTRY.
71
- Dry-run by default · --apply writes notes + .brain/COST_REBUILD.json
72
- · --session <id|file> · --limit N · --json.
73
- wendkeep stats [--vault P] One shareable line: sessions · prompts · spend · span · models (--json).
74
- wendkeep import [opts] Backfill: import this project's past Claude + Codex sessions into
75
- the vault (deduped by session_id). --source all|claude|codex (default
76
- all) · --stamp-ids (backfill session_id in existing notes) ·
77
- --rescan-decisions (capture prose decisions from already-imported transcripts) ·
78
- --from <dir> · --codex-from <dir> · --since <date> · --limit N ·
79
- --dry-run · --json.
80
- wendkeep verify [--deep] [--change s] Run a change's task sensors + record evidence (the gate);
81
- --deep assembles the verification package for the wk-verify pass.
82
- wendkeep dashboard [--force] (Re)generate the vault's folder-filtered Bases + 00-Dashboard MOC.
83
- wendkeep renumber-decisions Renumber 04-Decisões to ADR-<NNNN>-<slug> in chronological order,
84
- renaming files + rewriting every wikilink. Preview by default; --apply to
85
- write. --vault P · --json.
86
- wendkeep renumber-bugs Renumber 05-Bugs to BUG-<NNNN>-<slug> chronologically, moving notes
87
- out of legacy "DIA N" subfolders into the month folder and rewriting
88
- wikilinks. Preview by default; --apply · --vault P · --json.
89
- wendkeep renumber-learnings Same for 06-Aprendizados/06-Learnings with APR-<NNNN>-<slug>.
90
- wendkeep note new --type bug|learning "<título>" Create a numbered derived note (BUG-/APR-NNNN)
91
- in the month folder and print its vault path. --date YYYY-MM-DD · --vault P.
92
- wendkeep note relink [--apply] Backfill orphan derived notes (BUG/APR without a source session),
93
- linking each to the modal source session of its type/month cohort. Dry-run
94
- by default; --apply writes; skips notes with no sibling to infer from.
95
- wendkeep note repair-frontmatter [--apply] Merge stacked frontmatter blocks in session notes
96
- (damage from pre-lock concurrent writes) into a single block: base keys
97
- from the original block, values from the newest. Dry-run by default;
98
- --apply writes under the same lock as the hooks · --json.
99
- wendkeep note repair-sections [--apply] Rebuild the derived sections (decisions/bugs/learnings)
100
- in session notes from the linked derived notes — the body used to lag
101
- behind the closing block. Dry-run by default · --apply · --json.
102
- wendkeep lesson add "t" "l" Record a project-local lesson (injected at SessionStart).
103
- wendkeep memory <sub> Shared memory v2: status | migrate [--apply] | repair |
104
- reconcile <session> --by-session <session> --reason <text> [--apply] |
105
- promote <candidate> | reject <candidate>. --vault P.
106
- Reconcile is dry-run by default; the original attempt remains audited.
107
- wendkeep validate-memory [path] Validate .brain/CORE.md against the compaction
108
- protocol (cap 25, 3 sections, no secrets/PII).
109
- --vault <path> validates the complete v2 bundle.
110
- wendkeep sync-defs [opts] Copy versioned defs from the vault's .brain into the
111
- project: .brain/agents/*.toml -> .codex/agents,
112
- .brain/skills/<name> -> .claude/skills + .agents/skills. --vault P --project P.
113
- --reseed re-semeia as skills wk-* com os seeds da versão instalada
114
- (sobrescreve edições manuais nas wk-*) antes de copiar.
115
- --check detecta drift sem modificar arquivos.
116
- wendkeep --version Print version.
117
- wendkeep --help Show this help.
118
- `;
119
-
120
- function runHook(name) {
121
- if (!name) {
122
- process.stderr.write('wendkeep hook: missing hook name\n');
123
- process.exit(2);
124
- }
125
- if (!RUNNABLE_HOOKS.includes(name)) {
126
- process.stderr.write(`wendkeep hook: unknown hook "${name}". Known: ${RUNNABLE_HOOKS.join(', ')}\n`);
127
- process.exit(2);
128
- }
129
- const file = join(hooksDir, `${name}.mjs`);
130
- if (!existsSync(file)) {
131
- process.stderr.write(`wendkeep hook: hook file not found: ${file}\n`);
132
- process.exit(2);
133
- }
134
- // Spawn exactly as the agent would run `node <hook>.mjs`: stdio inherited so the
135
- // hook's stdin (agent JSON) and stdout (hookSpecificOutput) pass through untouched.
136
- const r = spawnSync(process.execPath, [file], { stdio: 'inherit' });
137
- process.exit(r.status ?? 0);
138
- }
139
-
140
- function optionValue(argv, name) {
141
- const index = argv.indexOf(name);
142
- if (index >= 0) return argv[index + 1] || '';
143
- return argv.find((item) => item.startsWith(`${name}=`))?.slice(name.length + 1) || '';
144
- }
145
-
146
- async function preferProjectVault(argv) {
147
- // Existing command modules still consume OBSIDIAN_VAULT_PATH internally. Populate it
148
- // only inside this CLI process from the provider-neutral project binding, overriding
149
- // any inherited machine-global value. An explicit --vault remains authoritative.
150
- if (optionValue(argv, '--vault')) return;
151
- try {
152
- const { resolveProjectVault } = await import('../src/project-vault.mjs');
153
- // A sensor may itself invoke WendKeep. `verify`/`flow finish` already selected the
154
- // authoritative Vault explicitly, so preserve that choice across the child process
155
- // instead of letting the sensor cwd's project binding redirect it.
156
- const sensorVault = process.env.WENDKEEP_SENSOR_VAULT;
157
- if (sensorVault) {
158
- const selected = resolveProjectVault({
159
- startDir: optionValue(argv, '--project') || process.cwd(),
160
- explicitVault: sensorVault,
161
- });
162
- process.env.OBSIDIAN_VAULT_PATH = selected.base;
163
- return;
164
- }
165
- const resolved = resolveProjectVault({ startDir: optionValue(argv, '--project') || process.cwd() });
166
- process.env.OBSIDIAN_VAULT_PATH = resolved.base;
167
- } catch (error) {
168
- // Backward-compatible manual CLI behavior: individual commands still explain
169
- // --vault / legacy env only when no project binding exists. A configured but
170
- // corrupt/missing/mismatched binding must abort before dispatch to another Vault.
171
- if (error?.code !== 'WENDKEEP_VAULT_UNCONFIGURED') throw error;
172
- }
173
- }
174
-
175
- async function main() {
176
- const [cmd, ...rest] = process.argv.slice(2);
177
- // Universal --help: any subcommand with --help/-h prints usage and never executes.
178
- // Intercepted BEFORE vault resolution so it works anywhere — help must never depend
179
- // on project state, and no command may treat --help as a runnable default.
180
- if (cmd && (rest.includes('--help') || rest.includes('-h'))) {
181
- if (cmd === 'flow') {
182
- const { FLOW_HELP } = await import('../src/flow.mjs');
183
- process.stdout.write(FLOW_HELP);
184
- } else if (cmd === 'profile') {
185
- const { PROFILE_HELP } = await import('../src/profile.mjs');
186
- process.stdout.write(PROFILE_HELP);
187
- } else {
188
- process.stdout.write(HELP);
189
- }
190
- process.exit(0);
191
- }
192
- const validatesStandaloneCore = cmd === 'validate-memory'
193
- && !rest.includes('--vault')
194
- && !rest.some((item) => item.startsWith('--vault='));
195
- if (cmd
196
- && !validatesStandaloneCore
197
- // `sync` starts with `init` and resolves the freshly bound Vault itself. Pre-resolving
198
- // here would prevent that repair step from reporting a corrupt binding as its own
199
- // first-stage failure (and could never make it as far as the guarded init).
200
- && !['init', 'sync', 'hook', '--version', '-v', '--help', '-h', 'help'].includes(cmd)) {
201
- await preferProjectVault(rest);
202
- }
203
- switch (cmd) {
204
- case 'init': {
205
- const { runInit } = await import('../src/init.mjs');
206
- await runInit(rest);
207
- break;
208
- }
209
- case 'hook':
210
- runHook(rest[0]);
211
- break;
212
- case 'doctor': {
213
- const { runDoctor } = await import('../src/doctor.mjs');
214
- process.exit(runDoctor(rest));
215
- break;
216
- }
217
- case 'sync': {
218
- const { runSync } = await import('../src/sync.mjs');
219
- process.exit(await runSync(rest));
220
- break;
221
- }
222
- case 'validate-memory': {
223
- if (rest.includes('--vault') || rest.some((item) => item.startsWith('--vault='))) {
224
- const { runValidateMemoryBundle } = await import('../src/memory.mjs');
225
- runValidateMemoryBundle(rest);
226
- } else {
227
- const { runValidateMemory } = await import('../src/validate-core.mjs');
228
- runValidateMemory(rest);
229
- }
230
- break;
231
- }
232
- case 'memory': {
233
- const { runMemory } = await import('../src/memory.mjs');
234
- runMemory(rest);
235
- break;
236
- }
237
- case 'sync-defs': {
238
- const { runSyncDefs } = await import('../src/sync-defs.mjs');
239
- process.exit(runSyncDefs(rest));
240
- break;
241
- }
242
- case 'change': {
243
- const { runChange } = await import('../src/change.mjs');
244
- runChange(rest);
245
- break;
246
- }
247
- case 'session': {
248
- const { runSession } = await import('../src/session.mjs');
249
- runSession(rest);
250
- break;
251
- }
252
- case 'profile': {
253
- const { runProfile } = await import('../src/profile.mjs');
254
- process.exit(runProfile(rest));
255
- break;
256
- }
257
- case 'flow': {
258
- const { runFlow } = await import('../src/flow.mjs');
259
- process.exit(await runFlow(rest));
260
- break;
261
- }
262
- case 'theme': {
263
- const { runTheme } = await import('../src/theme.mjs');
264
- runTheme(rest);
265
- break;
266
- }
267
- case 'verify': {
268
- const { runVerify } = await import('../src/verify.mjs');
269
- runVerify(rest);
270
- break;
271
- }
272
- case 'lesson': {
273
- const { runLesson } = await import('../src/lessons.mjs');
274
- runLesson(rest);
275
- break;
276
- }
277
- case 'spec': {
278
- const { runSpec } = await import('../src/spec.mjs');
279
- runSpec(rest);
280
- break;
281
- }
282
- case 'sensors': {
283
- const { runSensors } = await import('../src/sensors.mjs');
284
- runSensors(rest);
285
- break;
286
- }
287
- case 'cost': {
288
- const { runCost } = await import('../src/cost.mjs');
289
- runCost(rest);
290
- break;
291
- }
292
- case 'stats': {
293
- const { runStats } = await import('../src/stats.mjs');
294
- runStats(rest);
295
- break;
296
- }
297
- case 'import': {
298
- const { runImportCli } = await import('../src/import.mjs');
299
- runImportCli(rest);
300
- break;
301
- }
302
- case 'dashboard': {
303
- const { runDashboard } = await import('../src/vault-views.mjs');
304
- runDashboard(rest);
305
- break;
306
- }
307
- case 'renumber-decisions': {
308
- const { runRenumberDecisions } = await import('../src/renumber.mjs');
309
- runRenumberDecisions(rest);
310
- break;
311
- }
312
- case 'renumber-bugs': {
313
- const { runRenumberBugs } = await import('../src/renumber.mjs');
314
- runRenumberBugs(rest);
315
- break;
316
- }
317
- case 'renumber-learnings': {
318
- const { runRenumberLearnings } = await import('../src/renumber.mjs');
319
- runRenumberLearnings(rest);
320
- break;
321
- }
322
- case 'note': {
323
- const { runNote } = await import('../src/note.mjs');
324
- runNote(rest);
325
- break;
326
- }
327
- case '--version':
328
- case '-v':
329
- process.stdout.write(`${version()}\n`);
330
- break;
331
- case undefined:
332
- case '--help':
333
- case '-h':
334
- case 'help':
335
- process.stdout.write(HELP);
336
- break;
337
- default:
338
- process.stderr.write(`wendkeep: unknown command "${cmd}"\n\n${HELP}`);
339
- process.exit(2);
340
- }
341
- }
342
-
343
- main().catch((err) => {
344
- process.stderr.write(`wendkeep: ${err?.stack || err}\n`);
345
- process.exit(1);
346
- });
4
+ await runCli();
package/hooks/locale.mjs CHANGED
@@ -1,73 +1 @@
1
- // hooks/locale.mjs vault locale (i18n, 0.8.0). The locale is a property of the VAULT,
2
- // stored at <vault>/.brain/config.json ({ "locale": "en" }); absent = pt-BR (full backward
3
- // compat). Parsers stay bilingual everywhere; only RENDERING follows the locale.
4
- import { readFileSync } from 'node:fs';
5
- import { join } from 'node:path';
6
-
7
- export const LOCALES = {
8
- 'pt-BR': {
9
- id: 'pt-BR',
10
- folders: {
11
- inbox: '00-Inbox',
12
- project: '01-Projeto',
13
- sessions: '02-Sessões',
14
- linear: '03-Linear',
15
- decisions: '04-Decisões',
16
- bugs: '05-Bugs',
17
- learnings: '06-Aprendizados',
18
- specs: '07-Specs',
19
- changes: '08-Mudanças',
20
- },
21
- months: ['01-JAN', '02-FEV', '03-MAR', '04-ABR', '05-MAI', '06-JUN', '07-JUL', '08-AGO', '09-SET', '10-OUT', '11-NOV', '12-DEZ'],
22
- reqHeading: 'Requisito',
23
- fixTaskVerb: 'mata mutante',
24
- coreSections: ['Preferências do Usuário', 'Padrões Ativos', 'Pendências Abertas'],
25
- },
26
- en: {
27
- id: 'en',
28
- folders: {
29
- inbox: '00-Inbox',
30
- project: '01-Project',
31
- sessions: '02-Sessions',
32
- linear: '03-Linear',
33
- decisions: '04-Decisions',
34
- bugs: '05-Bugs',
35
- learnings: '06-Learnings',
36
- specs: '07-Specs',
37
- changes: '08-Changes',
38
- },
39
- months: ['01-JAN', '02-FEB', '03-MAR', '04-APR', '05-MAY', '06-JUN', '07-JUL', '08-AUG', '09-SEP', '10-OCT', '11-NOV', '12-DEC'],
40
- reqHeading: 'Requirement',
41
- fixTaskVerb: 'kill mutant',
42
- coreSections: ['User Preferences', 'Active Patterns', 'Open Items'],
43
- },
44
- };
45
-
46
- export const DEFAULT_LOCALE = 'pt-BR';
47
-
48
- // Per-process cache: one vault per process (hooks + CLI), reads are hot paths.
49
- const cache = new Map();
50
-
51
- export function getLocale(vaultBase) {
52
- if (!vaultBase) return LOCALES[DEFAULT_LOCALE];
53
- const key = String(vaultBase);
54
- if (cache.has(key)) return cache.get(key);
55
- let id = DEFAULT_LOCALE;
56
- try {
57
- const data = JSON.parse(readFileSync(join(key, '.brain', 'config.json'), 'utf8'));
58
- if (data.locale && LOCALES[data.locale]) id = data.locale;
59
- } catch { /* sem config = pt-BR */ }
60
- const loc = LOCALES[id];
61
- cache.set(key, loc);
62
- return loc;
63
- }
64
-
65
- // Test hook: drop the memoized entry (tests rewrite config.json under one tmpdir).
66
- export function clearLocaleCache() {
67
- cache.clear();
68
- }
69
-
70
- // The full vault taxonomy for a locale (folders + fixed entries), in creation order.
71
- export function vaultFolders(loc) {
72
- return [...Object.values(loc.folders), 'Templates', '.brain'];
73
- }
1
+ export * from '../packages/vault/src/locale.mjs';