wendkeep 0.61.0 → 0.63.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 +48 -0
- package/README.en.md +18 -7
- package/README.md +18 -7
- package/docs/en/commands/operating-profiles.md +26 -4
- package/docs/pt-BR/commands/operating-profiles.md +26 -3
- package/hooks/locale.mjs +1 -73
- package/hooks/sensors-core.mjs +1 -102
- package/hooks/vault-runtime-store.mjs +1 -558
- package/package.json +4 -2
- package/packages/harness/package.json +2 -1
- package/packages/harness/src/flow-store.mjs +558 -0
- package/packages/harness/src/index.mjs +3 -0
- package/packages/harness/src/operating-profile.mjs +133 -0
- package/packages/harness/src/sensors-core.mjs +102 -0
- package/packages/vault/src/index.mjs +1 -0
- package/packages/vault/src/locale.mjs +74 -0
- package/packages/vault/src/vault-path-safety.mjs +321 -152
- package/src/operating-profile.mjs +1 -133
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,54 @@ 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.63.0] — 2026-07-28
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **O Vault público passa a expor locale e taxonomia de pastas, e o Harness passa a expor o
|
|
12
|
+
store durável de FLOW.** As novas superfícies canônicas estão disponíveis por
|
|
13
|
+
wendkeep/vault e wendkeep/harness e são validadas a partir de um tarball instalado em consumidor
|
|
14
|
+
isolado, inclusive com um ciclo persistido completo.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- **Locale agora pertence a packages/vault/src e o store de FLOW a packages/harness/src.**
|
|
19
|
+
hooks/locale.mjs e hooks/vault-runtime-store.mjs permanecem como fachadas puras, preservando por
|
|
20
|
+
identidade todas as constantes e funções usadas por consumidores existentes.
|
|
21
|
+
- **A extração stateful não exige migração.** Paths em .brain/runtime, schemas, ids, locks,
|
|
22
|
+
tentativas, recibos, reservas, promoções, atomicidade, contenção multiprocesso e validação física
|
|
23
|
+
de paths mantêm o contrato anterior.
|
|
24
|
+
- **A direção Harness → Vault é verificada pelo índice público canônico.** Vault continua proibido
|
|
25
|
+
de depender do Harness, enquanto CLI, MCP, Integrations, Pi e flow-core permanecem fora deste
|
|
26
|
+
corte modular.
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- **Promoções concorrentes no Windows/Node 22 não confundem a liberação transitória do lock com
|
|
31
|
+
um path inseguro.** O Vault agora revalida, com backoff curto e limitado, o lock público que
|
|
32
|
+
desaparece entre `lstat` e `realpath` e leituras `ENOENT` de owner/lease. A aquisição compartilha
|
|
33
|
+
um único budget/deadline e a liberação limpa o lock sem resíduo; junctions, reparse points, links
|
|
34
|
+
dangling, erros não transitórios e locks privados `.pending` continuam falhando fechado.
|
|
35
|
+
|
|
36
|
+
## [0.62.0] — 2026-07-28
|
|
37
|
+
|
|
38
|
+
### Added
|
|
39
|
+
|
|
40
|
+
- **`wendkeep/harness` passa a expor o policy kernel reutilizável.** Os contratos dos cinco
|
|
41
|
+
Perfis de Operação e a engine de sensores podem ser importados pela superfície pública do pacote
|
|
42
|
+
raiz, com identidade validada a partir de um tarball realmente instalado.
|
|
43
|
+
|
|
44
|
+
### Changed
|
|
45
|
+
|
|
46
|
+
- **As implementações canônicas de perfis e sensores agora pertencem a
|
|
47
|
+
`packages/harness/src`.** `src/operating-profile.mjs` e `hooks/sensors-core.mjs` permanecem como
|
|
48
|
+
fachadas de reexport, preservando funções, constantes, objetos congelados e consumidores atuais.
|
|
49
|
+
- **A direção modular do Harness virou contrato estrutural.** O workspace aceita somente built-ins,
|
|
50
|
+
módulos internos e a superfície pública do Vault; Vault continua proibido de depender do
|
|
51
|
+
Harness, e CLI, MCP, Integrations e Pi permanecem adaptadores superiores.
|
|
52
|
+
- **OFF preserva a semântica publicada:** Keep Core/Vault continua ativo e a governança automática
|
|
53
|
+
fica desligada; comandos explícitos do operador não ganham bloqueios novos nesta extração.
|
|
54
|
+
|
|
7
55
|
## [0.61.0] — 2026-07-28
|
|
8
56
|
|
|
9
57
|
### Added
|
package/README.en.md
CHANGED
|
@@ -16,13 +16,23 @@
|
|
|
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 `
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
|
23
|
+
facades and no session data needs migration;
|
|
24
24
|
see the [modular architecture](docs/en/architecture.md).
|
|
25
25
|
|
|
26
|
+
In the **0.63 Harness FLOW Store** phase, `packages/vault/src/locale.mjs` becomes the canonical
|
|
27
|
+
source for Vault locale and taxonomy, while `packages/harness/src/flow-store.mjs` becomes the
|
|
28
|
+
canonical durable FLOW store. The historical `hooks/locale.mjs` and
|
|
29
|
+
`hooks/vault-runtime-store.mjs` facades preserve export identity. Harness depends only on Vault's
|
|
30
|
+
public index — never the reverse — with no migration of paths, schemas, or locks; the tarball
|
|
31
|
+
remains a single artifact published by the root `wendkeep` package.
|
|
32
|
+
Under multiprocess contention, transient release of the public lock and its owner/lease metadata
|
|
33
|
+
is revalidated with a bounded budget/deadline, including final cleanup; junctions, reparse points,
|
|
34
|
+
dangling locks, and non-transient errors are still rejected before any write.
|
|
35
|
+
|
|
26
36
|
```bash
|
|
27
37
|
npm i -D wendkeep && npx wendkeep init # captures from the next session on
|
|
28
38
|
npx wendkeep import # backfill past Claude + Codex sessions
|
|
@@ -189,8 +199,9 @@ Operations that deserve step-by-step guidance: [verify and exits 0/1/2](https://
|
|
|
189
199
|
## Operating Profiles — Keep Core is always active
|
|
190
200
|
|
|
191
201
|
**Keep Core is always active**: the Vault, sessions, identity, CORE/SHARED, lessons, costs,
|
|
192
|
-
and persistence do not turn off with the harness. **Wend Runtime** controls only the
|
|
193
|
-
layer
|
|
202
|
+
and persistence do not turn off with the harness. **Wend Runtime** controls only the automatic
|
|
203
|
+
governance layer. Even under `OFF`, explicit WendKeep commands remain available; invoking one is a
|
|
204
|
+
deliberate opt-in and runs that command's own validations:
|
|
194
205
|
|
|
195
206
|
| Profile | Route | Use |
|
|
196
207
|
|---|---|---|
|
package/README.md
CHANGED
|
@@ -16,13 +16,23 @@
|
|
|
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 `
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
|
23
|
+
facades and no session data needs migration;
|
|
24
24
|
see the [modular architecture](docs/en/architecture.md).
|
|
25
25
|
|
|
26
|
+
In the **0.63 Harness FLOW Store** phase, `packages/vault/src/locale.mjs` becomes the canonical
|
|
27
|
+
source for Vault locale and taxonomy, while `packages/harness/src/flow-store.mjs` becomes the
|
|
28
|
+
canonical durable FLOW store. The historical `hooks/locale.mjs` and
|
|
29
|
+
`hooks/vault-runtime-store.mjs` facades preserve export identity. Harness depends only on Vault's
|
|
30
|
+
public index — never the reverse — with no migration of paths, schemas, or locks; the tarball
|
|
31
|
+
remains a single artifact published by the root `wendkeep` package.
|
|
32
|
+
Under multiprocess contention, transient release of the public lock and its owner/lease metadata
|
|
33
|
+
is revalidated with a bounded budget/deadline, including final cleanup; junctions, reparse points,
|
|
34
|
+
dangling locks, and non-transient errors are still rejected before any write.
|
|
35
|
+
|
|
26
36
|
```bash
|
|
27
37
|
npm i -D wendkeep && npx wendkeep init # captures from the next session on
|
|
28
38
|
npx wendkeep import # backfill past Claude + Codex sessions
|
|
@@ -189,8 +199,9 @@ Operations that deserve step-by-step guidance: [verify and exits 0/1/2](https://
|
|
|
189
199
|
## Operating Profiles — Keep Core is always active
|
|
190
200
|
|
|
191
201
|
**Keep Core is always active**: the Vault, sessions, identity, CORE/SHARED, lessons, costs,
|
|
192
|
-
and persistence do not turn off with the harness. **Wend Runtime** controls only the
|
|
193
|
-
layer
|
|
202
|
+
and persistence do not turn off with the harness. **Wend Runtime** controls only the automatic
|
|
203
|
+
governance layer. Even under `OFF`, explicit WendKeep commands remain available; invoking one is a
|
|
204
|
+
deliberate opt-in and runs that command's own validations:
|
|
194
205
|
|
|
195
206
|
| Profile | Route | Use |
|
|
196
207
|
|---|---|---|
|
|
@@ -8,6 +8,10 @@ Choose how much Wend Runtime governance an execution needs without disabling **K
|
|
|
8
8
|
Keep Core is always active: the Vault, session, identity, CORE/SHARED, lessons, costs, and
|
|
9
9
|
persistence integrations continue working under every profile.
|
|
10
10
|
|
|
11
|
+
The `OFF` profile disables automatic governance activation, not the CLI: explicit commands such as
|
|
12
|
+
`profile`, `flow`, `change`, `verify`, and `sensors` remain available. Invoking one is a deliberate
|
|
13
|
+
opt-in and runs that command's own validations and gates.
|
|
14
|
+
|
|
11
15
|
## When to use
|
|
12
16
|
|
|
13
17
|
Use `profile` to inspect or explicitly select an Operating Profile. Use `FLOW` for local,
|
|
@@ -44,11 +48,28 @@ Every FLOW subcommand also accepts `--project <path>`, `--vault <path>`, and `--
|
|
|
44
48
|
provided, `--session` scopes ID-based reads and mutations to the session that owns the FLOW; an
|
|
45
49
|
ID from another session fails without mutation.
|
|
46
50
|
|
|
51
|
+
## Ownership and programmatic surface
|
|
52
|
+
|
|
53
|
+
The private `packages/harness` workspace canonically owns Operating Profile resolution/policy and
|
|
54
|
+
the sensor engine. Programmatic consumers use the public root-package subpath:
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
import {
|
|
58
|
+
resolveOperatingProfile,
|
|
59
|
+
runSensors,
|
|
60
|
+
evaluateGate,
|
|
61
|
+
} from 'wendkeep/harness';
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`src/operating-profile.mjs` and `hooks/sensors-core.mjs` are compatibility facades only. Dependency
|
|
65
|
+
direction is `adapters (cli/mcp/integrations/pi) -> Harness -> Vault`; Vault never depends on
|
|
66
|
+
Harness. The workspaces remain private and are not published as independent npm packages.
|
|
67
|
+
|
|
47
68
|
## Options and exit codes
|
|
48
69
|
|
|
49
70
|
| Profile | Route | Contract |
|
|
50
71
|
|---|---|---|
|
|
51
|
-
| `OFF` | LLM-native harness |
|
|
72
|
+
| `OFF` | LLM-native harness | Automatic governance off; Keep Core and explicit commands available. |
|
|
52
73
|
| `FLOW` | E → V | Microcontract with Git baseline, allowlist, sensors, and receipt, without a change. |
|
|
53
74
|
| `GUIDE` | P → E → V | Compact change; policy recognized for compatible evolution. |
|
|
54
75
|
| `GOVERN` | P → R → E → V | Current a2 loop and conservative fallback. |
|
|
@@ -145,9 +166,10 @@ npx wendkeep flow promote $flowId --change-slug another-slug
|
|
|
145
166
|
## Expected result
|
|
146
167
|
|
|
147
168
|
Changing profile neither creates a new session nor interrupts the Vault. In `OFF`, memory and
|
|
148
|
-
lessons are still injected and Stop still persists the session/memory lifecycle, while
|
|
149
|
-
router, skill gate, change context/warn/nag/guard, and plan capture are inactive.
|
|
150
|
-
|
|
169
|
+
lessons are still injected and Stop still persists the session/memory lifecycle, while automatic
|
|
170
|
+
router, skill gate, change context/warn/nag/guard, and plan capture are inactive. Explicit commands
|
|
171
|
+
remain available and run their own contracts. A completed FLOW leaves a durable, inspectable
|
|
172
|
+
receipt; a promoted FLOW enters the normal change lifecycle.
|
|
151
173
|
|
|
152
174
|
## Common errors and diagnosis
|
|
153
175
|
|
|
@@ -8,6 +8,10 @@ Escolher quanta governança do Wend Runtime uma execução precisa sem desligar
|
|
|
8
8
|
O Keep Core permanece sempre ativo: Vault, sessão, identidade, CORE/SHARED, lessons, custos e
|
|
9
9
|
integrações de persistência continuam funcionando em todos os perfis.
|
|
10
10
|
|
|
11
|
+
O perfil `OFF` desativa a ativação automática da governança, não a CLI: comandos explícitos como
|
|
12
|
+
`profile`, `flow`, `change`, `verify` e `sensors` continuam disponíveis. Invocá-los é um opt-in
|
|
13
|
+
deliberado e executa as validações e gates próprios daquele comando.
|
|
14
|
+
|
|
11
15
|
## Quando usar
|
|
12
16
|
|
|
13
17
|
Use `profile` para consultar ou selecionar explicitamente um Perfil de Operação. Use `FLOW` para
|
|
@@ -44,11 +48,29 @@ Todos os subcomandos FLOW também aceitam `--project <path>`, `--vault <path>` e
|
|
|
44
48
|
Quando informado, `--session` restringe inclusive consultas e mutações por ID à sessão dona do
|
|
45
49
|
FLOW; um ID de outra sessão falha sem mutação.
|
|
46
50
|
|
|
51
|
+
## Ownership e superfície programática
|
|
52
|
+
|
|
53
|
+
O workspace privado `packages/harness` é o dono canônico da resolução/política dos Perfis de
|
|
54
|
+
Operação e da engine de sensores. Consumidores programáticos usam o subpath público do pacote raiz:
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
import {
|
|
58
|
+
resolveOperatingProfile,
|
|
59
|
+
runSensors,
|
|
60
|
+
evaluateGate,
|
|
61
|
+
} from 'wendkeep/harness';
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`src/operating-profile.mjs` e `hooks/sensors-core.mjs` são somente fachadas de compatibilidade. A
|
|
65
|
+
direção de dependências é `adapters (cli/mcp/integrations/pi) -> Harness -> Vault`; o Vault nunca
|
|
66
|
+
depende do Harness. Os workspaces continuam privados e não são publicados como pacotes npm
|
|
67
|
+
independentes.
|
|
68
|
+
|
|
47
69
|
## Opções e códigos de saída
|
|
48
70
|
|
|
49
71
|
| Perfil | Rota | Contrato |
|
|
50
72
|
|---|---|---|
|
|
51
|
-
| `OFF` | harness nativo da LLM |
|
|
73
|
+
| `OFF` | harness nativo da LLM | Governança automática desligada; Keep Core e comandos explícitos disponíveis. |
|
|
52
74
|
| `FLOW` | E → V | Microcontrato com baseline Git, allowlist, sensores e recibo, sem change. |
|
|
53
75
|
| `GUIDE` | P → E → V | Change compacta; política reconhecida para evolução compatível. |
|
|
54
76
|
| `GOVERN` | P → R → E → V | Loop a2 atual e fallback conservador. |
|
|
@@ -144,8 +166,9 @@ npx wendkeep flow promote $flowId --change-slug outro-slug
|
|
|
144
166
|
|
|
145
167
|
Trocar o perfil não cria outra sessão nem interrompe o Vault. Em `OFF`, a memória e as lessons
|
|
146
168
|
continuam injetadas e o Stop continua persistindo sessão/memória, mas router, skill gate,
|
|
147
|
-
change context/warn/nag/guard e captura de plano ficam inativos.
|
|
148
|
-
|
|
169
|
+
change context/warn/nag/guard e captura de plano automáticos ficam inativos. Os comandos explícitos
|
|
170
|
+
continuam disponíveis e executam seus próprios contratos. Um FLOW concluído deixa recibo durável e
|
|
171
|
+
consultável; um FLOW promovido passa a seguir o lifecycle normal de change.
|
|
149
172
|
|
|
150
173
|
## Erros comuns e diagnóstico
|
|
151
174
|
|
package/hooks/locale.mjs
CHANGED
|
@@ -1,73 +1 @@
|
|
|
1
|
-
|
|
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';
|
package/hooks/sensors-core.mjs
CHANGED
|
@@ -1,102 +1 @@
|
|
|
1
|
-
|
|
2
|
-
// Pure-ish: `spawn` is injectable so runs are testable without a shell. Config lives
|
|
3
|
-
// at the PROJECT ROOT (wendkeep.sensors.json); evidence lives per-change in the vault.
|
|
4
|
-
import { spawnSync } from 'node:child_process';
|
|
5
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
6
|
-
import { dirname, join, resolve } from 'node:path';
|
|
7
|
-
|
|
8
|
-
export const SENSOR_VAULT_ENV = 'WENDKEEP_SENSOR_VAULT';
|
|
9
|
-
|
|
10
|
-
export function sensorProcessEnv(vaultBase, inherited = process.env) {
|
|
11
|
-
return {
|
|
12
|
-
...inherited,
|
|
13
|
-
OBSIDIAN_VAULT_PATH: vaultBase,
|
|
14
|
-
[SENSOR_VAULT_ENV]: vaultBase,
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function loadSensors(projectRoot, file = 'wendkeep.sensors.json') {
|
|
19
|
-
return loadSensorsDetailed(projectRoot, file).sensors;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// Missing config and broken config are different failures: absent file usually means
|
|
23
|
-
// wrong cwd (subdirectory), broken JSON means the config itself needs fixing. Collapsing
|
|
24
|
-
// both into [] made every sensor report "sensor não definido" — a misleading diagnosis.
|
|
25
|
-
export function loadSensorsDetailed(projectRoot, file = 'wendkeep.sensors.json') {
|
|
26
|
-
const path = join(projectRoot, file);
|
|
27
|
-
if (!existsSync(path)) return { sensors: [], missing: true, error: null, path };
|
|
28
|
-
try {
|
|
29
|
-
const data = JSON.parse(readFileSync(path, 'utf8'));
|
|
30
|
-
return { sensors: Array.isArray(data.sensors) ? data.sensors : [], missing: false, error: null, path };
|
|
31
|
-
} catch (e) {
|
|
32
|
-
return { sensors: [], missing: false, error: e.message, path };
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Climb the directory tree looking for a project marker (wendkeep.sensors.json or
|
|
37
|
-
// .wendkeep.json), like git does with .git — shells in agent harnesses keep their cwd
|
|
38
|
-
// across commands, so verify is often run from a subdirectory.
|
|
39
|
-
export function findProjectRoot(startDir) {
|
|
40
|
-
let dir = resolve(startDir);
|
|
41
|
-
for (;;) {
|
|
42
|
-
if (existsSync(join(dir, 'wendkeep.sensors.json')) || existsSync(join(dir, '.wendkeep.json'))) return dir;
|
|
43
|
-
const parent = dirname(dir);
|
|
44
|
-
if (parent === dir) return null;
|
|
45
|
-
dir = parent;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function requiredSensors(tasks) {
|
|
50
|
-
return [...new Set((tasks || []).flatMap((task) => (
|
|
51
|
-
Array.isArray(task.sensors) && task.sensors.length ? task.sensors : [task.sensor]
|
|
52
|
-
)).filter(Boolean))];
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export function runSensors(sensors, ids, { spawn = spawnSync, cwd, env, now } = {}) {
|
|
56
|
-
const byId = Object.fromEntries((sensors || []).map((s) => [s.id, s]));
|
|
57
|
-
const ts = now || new Date().toISOString();
|
|
58
|
-
const evidence = [];
|
|
59
|
-
for (const id of ids) {
|
|
60
|
-
const s = byId[id];
|
|
61
|
-
if (!s) { evidence.push({ id, status: 'red', ts, severity: 'critical', note: 'sensor não definido' }); continue; }
|
|
62
|
-
const r = spawn(s.command, [], { cwd, shell: true, stdio: 'ignore', ...(env ? { env } : {}) });
|
|
63
|
-
const entry = { id, status: (r.status ?? 1) === 0 ? 'green' : 'red', ts, severity: s.severity || 'critical' };
|
|
64
|
-
if (s.type === 'mutation' && s.report) {
|
|
65
|
-
// Delegated mutation (Wave B): read the tool's mutation-testing-elements report and
|
|
66
|
-
// attach surviving mutants so verify can turn them into fix tasks.
|
|
67
|
-
try { entry.survivors = parseMutationReport(JSON.parse(readFileSync(join(cwd || '.', s.report), 'utf8'))); }
|
|
68
|
-
catch { /* report ausente/ilegível — segue só com o exit code */ }
|
|
69
|
-
}
|
|
70
|
-
evidence.push(entry);
|
|
71
|
-
}
|
|
72
|
-
return evidence;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Parse a mutation-testing-elements report (Stryker et al.): return surviving mutants
|
|
76
|
-
// (Survived | NoCoverage) as {file, line, mutator}.
|
|
77
|
-
export function parseMutationReport(json) {
|
|
78
|
-
const out = [];
|
|
79
|
-
const files = json && json.files ? json.files : {};
|
|
80
|
-
for (const [file, data] of Object.entries(files)) {
|
|
81
|
-
for (const m of (data && data.mutants) || []) {
|
|
82
|
-
if (m.status === 'Survived' || m.status === 'NoCoverage') {
|
|
83
|
-
out.push({ file, line: m.location && m.location.start ? m.location.start.line : null, mutator: m.mutatorName || 'unknown' });
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return out;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// A required sensor blocks the gate when it is missing (never verified) or red at a
|
|
91
|
-
// non-warning severity. Warnings are advisory: a red warning does not block archive.
|
|
92
|
-
// Severity comes from the evidence entry (written by runSensors); absent -> critical.
|
|
93
|
-
export function evaluateGate(evidence, requiredIds) {
|
|
94
|
-
const byId = Object.fromEntries((evidence || []).map((e) => [e.id, e]));
|
|
95
|
-
const failing = (requiredIds || []).filter((id) => {
|
|
96
|
-
const e = byId[id];
|
|
97
|
-
if (!e) return true; // never verified
|
|
98
|
-
if (e.status === 'green') return false;
|
|
99
|
-
return (e.severity || 'critical') !== 'warning';
|
|
100
|
-
});
|
|
101
|
-
return { ok: failing.length === 0, failing };
|
|
102
|
-
}
|
|
1
|
+
export * from '../packages/harness/src/sensors-core.mjs';
|