wendkeep 0.86.0 → 0.88.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.
Files changed (55) hide show
  1. package/.githooks/commit-msg +16 -0
  2. package/.githooks/prepare-commit-msg +16 -0
  3. package/CHANGELOG.md +32 -0
  4. package/README.en.md +3 -1
  5. package/README.md +3 -1
  6. package/docs/en/commands/commit.md +159 -0
  7. package/docs/en/commands/observer-security.md +154 -0
  8. package/docs/en/commands/observer.md +30 -12
  9. package/docs/pt-BR/commands/commit.md +159 -0
  10. package/docs/pt-BR/commands/observer-security.md +154 -0
  11. package/docs/pt-BR/commands/observer.md +30 -12
  12. package/hooks/observer-publish.mjs +3 -1
  13. package/package.json +6 -2
  14. package/packages/cli/src/index.mjs +11 -1
  15. package/packages/commit/package.json +6 -0
  16. package/packages/commit/src/cli.mjs +89 -0
  17. package/packages/commit/src/commit-input.mjs +181 -0
  18. package/packages/commit/src/commit-message.mjs +51 -0
  19. package/packages/commit/src/commit-policy.mjs +144 -0
  20. package/packages/commit/src/git-runtime.mjs +428 -0
  21. package/packages/commit/src/index.mjs +28 -0
  22. package/packages/commit/src/proof-validation.mjs +443 -0
  23. package/packages/mcp/src/executor.mjs +35 -2
  24. package/packages/observer/package.json +16 -0
  25. package/packages/observer/src/audit.mjs +1 -0
  26. package/packages/observer/src/authz.mjs +38 -0
  27. package/packages/observer/src/encryption.mjs +75 -0
  28. package/packages/observer/src/index.mjs +7 -0
  29. package/packages/observer/src/policy.mjs +305 -0
  30. package/packages/observer/src/purge.mjs +100 -0
  31. package/packages/observer/src/redaction.mjs +54 -0
  32. package/packages/observer/src/retention.mjs +39 -0
  33. package/packages/observer/src/token-registry.mjs +122 -0
  34. package/schema/commit-message-v1.schema.json +75 -0
  35. package/schema/observer/006-observer-security.sql +64 -0
  36. package/schema/observer-policy-v1.schema.json +63 -0
  37. package/schema/sync-event-v1.schema.json +10 -0
  38. package/scripts/validate-commit-range.mjs +244 -0
  39. package/src/doctor.mjs +7 -0
  40. package/src/git-commit-hooks.mjs +112 -0
  41. package/src/init.mjs +13 -0
  42. package/src/observer-auth.mjs +8 -0
  43. package/src/observer-privacy.mjs +7 -3
  44. package/src/observer-publish.mjs +31 -0
  45. package/src/observer-server.mjs +179 -20
  46. package/src/observer-sql-migrate.mjs +5 -2
  47. package/src/observer-sql-publish.mjs +114 -39
  48. package/src/observer-sql-store.mjs +299 -45
  49. package/src/observer-transcript-store.mjs +23 -8
  50. package/src/observer.mjs +145 -12
  51. package/src/skills-seed.mjs +79 -0
  52. package/src/sync-protocol.mjs +20 -0
  53. package/web/observer/app.mjs +107 -31
  54. package/web/observer/index.html +7 -0
  55. package/web/observer/styles.css +5 -0
@@ -0,0 +1,159 @@
1
+ # Commits baseados em evidências
2
+
3
+ **PT-BR** · [English](../../en/commands/commit.md)
4
+
5
+ ## Objetivo
6
+
7
+ Produzir a mesma mensagem auditável em Codex, Claude Code ou outro cliente Git, usando somente
8
+ entrada tipada, referências públicas e o resumo do index staged. O kernel é determinístico e não
9
+ lê o Vault, `.brain`, registros de sessão ou rede.
10
+
11
+ ## Quando usar
12
+
13
+ Use antes de commits de implementação `feat`, `fix`, `refactor` ou `perf` que precisam registrar
14
+ autoridade causal, tarefas, testes, escopo e evidência verificável de forma equivalente entre
15
+ harnesses.
16
+
17
+ ## Quando não usar
18
+
19
+ Não use para inventar prova, publicar conteúdo privado, reescrever histórico ou automatizar push.
20
+ Commits `docs`, `test` e `chore` dispensam contexto somente quando todos os arquivos alterados são
21
+ objetivamente documentação/testes. Alteração de produto exige o corpo governado mesmo com outro tipo.
22
+
23
+ ## Pré-requisitos
24
+
25
+ Execute dentro de um repositório Git, com o WendKeep instalado localmente e os arquivos do produto
26
+ já selecionados no index staged.
27
+
28
+ ## Sintaxe
29
+
30
+ ```bash
31
+ npx --no-install wendkeep commit context --input <json|-> [--json]
32
+ npx --no-install wendkeep commit context --clear [--json]
33
+ npx --no-install wendkeep commit render --input <json|->
34
+ npx --no-install wendkeep commit prepare --message-file <path> [--source <source>]
35
+ npx --no-install wendkeep commit validate --message-file <path> [--json]
36
+ ```
37
+
38
+ ## Opções e códigos de saída
39
+
40
+ - Exit `0`: contexto escrito/limpo ou mensagem válida.
41
+ - Exit `1`: mensagem governada inválida.
42
+ - Exit `2`: argumento, JSON, Git, privacidade ou contexto inválido/stale.
43
+ - `--consume-context` é reservado ao wrapper `commit-msg`; remove o contexto após validação verde.
44
+
45
+ ## Instalação opt-in
46
+
47
+ Os hooks Git não são ativados pelo `init` padrão. Para copiar os wrappers portáteis e configurar
48
+ `core.hooksPath=.githooks` somente neste repositório:
49
+
50
+ ```bash
51
+ npx --no-install wendkeep init --git-commit-hooks --yes
52
+ ```
53
+
54
+ Hooks personalizados nunca são sobrescritos silenciosamente. Se o `init` encontrar conflito, ele
55
+ preserva o arquivo. Revise-o e, somente se quiser substituí-lo, execute novamente com `--force`; o
56
+ arquivo anterior fica em `.bak`.
57
+ Um `core.hooksPath` customizado também é conflito: sem `--force` ele permanece intocado.
58
+
59
+ ## Exemplos
60
+
61
+ ### Preparar um commit
62
+
63
+ Crie um JSON conforme `schema/commit-message-v1.schema.json`. Declare autoridade e referências de
64
+ evidência, mas não envie `tasks`, `tests`, `fresh` ou `verified`. O runtime deriva tarefas do
65
+ checklist canônico com Task Contracts concluídos. Testes vêm somente de sensores declarados por
66
+ `[sensor:<id>]` e executados pelo coletor; `[phase:verify]`
67
+ sozinho nunca é resultado. Sensors declarados no Envelope devem corresponder exatamente em IDs,
68
+ configuração, comando, severidade e resultado à reexecução canônica; apenas essa reexecução gera a
69
+ linha `Tests`. O gate remoto reexecuta o sensor no checkout do SHA correspondente.
70
+ Cada referência publicada recebe digest SHA-256 rederivado. ADR/design validam
71
+ ID/path/artefato. Tasks com `[req:]` exigem uma referência `spec` versionada e sanitizada que defina
72
+ cada requisito. Evidence Envelope, Verdict, receipt e TDD attestation podem participar da validação
73
+ local, mas são omitidos da Evidence remota: não há publicação de IDs de worktree/sessão/branch nem
74
+ promoção de consistência autocontida para prova. Se uma mensagem os alegar como `fresh`/`verified`,
75
+ o range rejeita `WENDKEEP_COMMIT_REMOTE_PROOF_UNAVAILABLE`. Os trailers fixos
76
+ `Remote-Proof-Scope: git,authority,tasks,spec,sensors` e `Local-Causal-Proof: unpublished` tornam
77
+ essa fronteira explícita. No range, authority/artefatos, task/spec, `Scope` Git e config/sensors são
78
+ rederivados do SHA, e apenas a reexecução canônica gera `Tests`. `Co-Authored-By` é
79
+ omitido enquanto não houver identidade registrada confiável.
80
+
81
+ A autoridade normal é a ADR causal:
82
+
83
+ ```json
84
+ { "authority": { "kind": "adr", "adr": "ADR-1234", "ref": "docs/ADR-1234.md", "issue": "#123" } }
85
+ ```
86
+
87
+ Somente quando não existe change/ADR causal, o harness nativo pode declarar o fallback abaixo.
88
+ `issue` deve ser `#NNN` e `design` precisa estar versionado no mesmo commit sob
89
+ `docs/superpowers/specs/` ou `plans/`:
90
+
91
+ ```json
92
+ {
93
+ "authority": {
94
+ "kind": "native",
95
+ "issue": "#40",
96
+ "design": "docs/superpowers/specs/design-aprovado.md"
97
+ }
98
+ }
99
+ ```
100
+
101
+ O runtime confirma perfil efetivo `OFF`, ausência de context/change/lease e ADR causal reais, além
102
+ da issue no design. Esse modo gera trailers únicos `Authority: native-no-causal-change`, `Issue` e `Design`. Texto solto,
103
+ design não versionado, prova stale/unverified, corpo ou testes ausentes falham fechados.
104
+
105
+ ```bash
106
+ git add <arquivos-do-produto>
107
+ npx --no-install wendkeep commit context --input commit-input.json
108
+ git commit -m "feat(escopo): rascunho"
109
+ ```
110
+
111
+ `commit context` calcula o hash SHA-256 do diff staged e guarda o contexto sanitizado em
112
+ `.git/wendkeep-commit-input.json`, fora do working tree. `prepare-commit-msg` substitui o rascunho
113
+ pela mensagem canônica; `commit-msg` valida e consome o contexto. Se o index mudar, o contexto fica
114
+ stale e deve ser recriado.
115
+ O `commit-msg` relê o contexto, compara a mensagem inteira e o hash/files staged, e só consome o
116
+ contexto após sucesso. `merge`, `squash` e amend limpam contexto incompatível para não contaminar o
117
+ commit seguinte. `--message-file` fica contido no repositório ou git-dir.
118
+
119
+ Outros comandos:
120
+
121
+ ```bash
122
+ npx --no-install wendkeep commit render --input commit-input.json
123
+ npx --no-install wendkeep commit validate --message-file .git/COMMIT_EDITMSG
124
+ npx --no-install wendkeep commit context --clear
125
+ ```
126
+
127
+ Commits realmente triviais permanecem intactos. Commits de implementação
128
+ `feat`, `fix`, `refactor` e `perf` exigem assunto Conventional Commit com ADR ou o fallback nativo
129
+ restrito acima, seções Capability, Evidence, Tasks, Tests e Scope, hash staged e trailer
130
+ `WendKeep-Commit: v1`. Amend, merge e squash não recebem corpo duplicado ou prova inventada.
131
+
132
+ ## Privacidade e falha segura
133
+
134
+ - Caminhos absolutos mesmo embutidos, qualquer Vault configurado/default, `.brain`, registros de sessão, PII e segredos são
135
+ rejeitados antes da persistência.
136
+ - Evidência `reported`, `legacy-unbound`, `stale` ou `unproven` não pode ser apresentada como prova.
137
+ - O contexto contém referências sanitizadas e metadados do diff, nunca o conteúdo privado do Vault.
138
+ - `--no-verify` não é um fluxo aceito: o CI valida cada commit novo, inclusive merges e resoluções inéditas.
139
+
140
+ ## Resultado esperado
141
+
142
+ Uma mensagem determinística, autocontida, sem material privado, com hash do mesmo index que foi
143
+ commitado e trailers causais coerentes.
144
+
145
+ ## Erros comuns e diagnóstico
146
+
147
+ `wendkeep doctor` mostra `[commit-hooks] healthy`, `disabled`, `missing` ou `drift` e permanece
148
+ read-only. Para recuperar arquivos ausentes ou divergentes após revisão:
149
+
150
+ ```bash
151
+ npx --no-install wendkeep init --git-commit-hooks --force --yes
152
+ ```
153
+
154
+ Se um commit for abandonado, limpe apenas o contexto transitório com
155
+ `wendkeep commit context --clear`. Nenhum comando reescreve histórico ou faz push automaticamente.
156
+
157
+ ## Próximos passos
158
+
159
+ Revise a mensagem gerada, faça o commit e deixe o gate de range do PR validar qualquer bypass local.
@@ -0,0 +1,154 @@
1
+ # Segurança do Observer
2
+
3
+ **PT-BR** · [English](../../en/commands/observer-security.md)
4
+
5
+ ## Objetivo
6
+
7
+ O Observer é um read model local ou de equipe, nunca uma nova autoridade sobre Vault, spec, memória
8
+ ou sync. O modelo de ameaça considera host remoto comprometido, token roubado, operador curioso,
9
+ banco/outbox copiados, payload adversarial e purge interrompido. Host/Origin continuam validados;
10
+ mutações e leituras sensíveis falham fechadas, inclusive no loopback.
11
+
12
+ | Classe | Padrão | Risco principal |
13
+ |---|---|---|
14
+ | documento | `metadata` | memória/decisões integrais |
15
+ | transcript | `metadata` | conversa e ferramentas |
16
+ | prompt/resposta | `redacted` | PII e segredos |
17
+ | uso | `aggregate` | custo e identidade operacional |
18
+ | audit/receipt | metadados mínimos | apagar a própria prova |
19
+
20
+ A policy v1 restringe por classe, `project_id`, glob de path e `entity_type`. Regras mais tardias
21
+ vencem apenas no projeto correspondente. Redaction cobre Bearer, credenciais em URL/connection
22
+ string, access keys, e-mail, telefone e regexes configuráveis seguras. O schema é
23
+ `schema/observer-policy-v1.schema.json`.
24
+ Em `transcript_capture: messages`, arrays, JSONL e o envelope canônico `{messages:[...]}` preservam
25
+ somente mensagens `user|assistant|system` com `role`/`content` string após redaction; campos extras,
26
+ tools e entradas malformadas são descartados ou falham fechados.
27
+ A policy explícita é a única autoridade de captura do publisher; `WENDKEEP_OBSERVER_CAPTURE_LEVEL`
28
+ é apenas compatibilidade traduzida para policy quando nenhum arquivo de policy foi fornecido e
29
+ nunca eleva nem suprime `none|metadata|messages|full` ou documentos `selected` explícitos.
30
+ Nos upserts de documento/transcript, `content_hash` sempre representa o conteúdo final após captura
31
+ e redaction; captura metadata/selected usa o SHA-256 do conteúdo vazio. Exclusões de documento
32
+ continuam efetivas mesmo com captura `none`, preservam path/revision/operação e nunca transportam
33
+ conteúdo ou hash obsoleto.
34
+ A redaction nunca reescreve campos validados de identidade estrutural, como IDs de projeto/evento/
35
+ entidade, paths lógicos, revisions ou operações. A privacidade do path é aplicada de modo fail-closed
36
+ pelas regras de captura por projeto/path, não pela renomeação da chave de storage por uma regra de
37
+ redaction de conteúdo.
38
+ O contrato estrutural por evento também preserva aliases snake/camel aceitos, chaves de documento/
39
+ sessão/agente/call/transcript/rollup, timestamps, roles, status, coverage, dimensões de modelo/preço,
40
+ workflow e proveniência de source. `title`, `summary`, `agent_name`, conteúdo, prompt/resposta e
41
+ metadata continuam como campos de display/conteúdo sujeitos a redaction.
42
+ Na publicação incremental, timestamps de turn ausentes ou vazios herdam o instante canônico do
43
+ lote, epoch numérico em milissegundos é normalizado para ISO 8601 e valor não vazio inválido falha
44
+ fechado antes da policy/store; evento e payload usam o mesmo instante.
45
+
46
+ ## Quando usar
47
+
48
+ Use ao habilitar o Observer para dados reais, cadastrar ou revogar credenciais, restringir captura,
49
+ proteger SQLite/outbox, definir retenção ou eliminar dados com prova verificável.
50
+
51
+ ## Quando não usar
52
+
53
+ Não use como KMS/secret manager corporativo, para publicar Vault/runtime, para substituir a
54
+ autoridade local ou para apagar manualmente tabelas e índices. Captura `full` continua opt-in e
55
+ sujeita à policy/redaction.
56
+
57
+ ## Pré-requisitos
58
+
59
+ Use Node.js 22.13+, mantenha o bind no loopback e injete tokens/chaves somente por variáveis de
60
+ ambiente. O token de bootstrap é registrado somente pelo hash, exige projetos explícitos e
61
+ expiração finita; não é um admin wildcard fora do registry. Para Docker, defina também
62
+ `WENDKEEP_OBSERVER_BOOTSTRAP_PROJECTS`, `WENDKEEP_OBSERVER_BOOTSTRAP_EXPIRES_AT` e uma chave de
63
+ 32 bytes em hex/base64 em `WENDKEEP_OBSERVER_ENCRYPTION_KEY`. O operador guarda a chave e receipts externos.
64
+
65
+ ## Sintaxe
66
+
67
+ ```bash
68
+ npx wendkeep observer serve --token <token> --bootstrap-projects <p1,p2> --bootstrap-expires-at <ISO> [--bootstrap-token-id <id>] [--require-loopback-auth] [--require-encryption]
69
+ npx wendkeep observer security token create --project-id <projeto> --role <role> --scopes <scopes> --token-env <env> --expires-at <ISO>
70
+ npx wendkeep observer security token rotate --project-id <projeto> --token-id <id> --token-env <env> --expires-at <ISO> [--new-token-id <id>]
71
+ npx wendkeep observer security token revoke --project-id <projeto> --token-id <id>
72
+ npx wendkeep observer security policy set --project-id <projeto> --file <policy.json>
73
+ npx wendkeep observer security policy show --project-id <projeto>
74
+ npx wendkeep observer security purge --project-id <projeto> --before <ISO> --classes <classes> [--dry-run]
75
+ npx wendkeep observer security retention run --project-id <projeto> [--dry-run] [--operation-id <id>]
76
+ ```
77
+
78
+ ## Opções e códigos de saída
79
+
80
+ - `viewer` lê metadata/agregados; `auditor` pode receber scopes sensíveis; `publisher` ingere;
81
+ `admin` administra policy, purge e recovery. Role, scope e projeto precisam autorizar juntos.
82
+ - Tokens são persistidos somente como SHA-256; expiração, rotação e revogação valem sem restart.
83
+ - Após rotacionar o bootstrap, atualize token e token ID no ambiente; reiniciar nunca reativa a
84
+ credencial antiga revogada ou expirada.
85
+ - `--token-env` nomeia a variável com o segredo; o comando nunca imprime o valor.
86
+ - `--require-loopback-auth` protege toda a API; reads sensíveis exigem token mesmo sem a flag.
87
+ - `--require-encryption` falha se a chave externa estiver ausente ou inválida.
88
+ - `WENDKEEP_OBSERVER_REQUIRE_ENCRYPTION=1` aplica a mesma falha fechada a `status`, `security`,
89
+ `register`, `publish` e `reconcile`; com chave configurada, todo primeiro upgrade v5 usa apenas
90
+ `.bak.enc` + manifest antes de qualquer leitura/backfill.
91
+ - Exit `0` indica operação concluída; exit `1` indica configuração, autorização, policy, chave ou
92
+ operação inválida. O hook mantém exit `0` fail-open para o fluxo local, mas aborta antes de
93
+ persistir conteúdo inseguro.
94
+
95
+ O audit guarda capability, resultado, rota e horário, nunca Bearer, prompt, resposta ou payload.
96
+
97
+ ## Exemplos
98
+
99
+ Recovery offline explícito e auditado:
100
+
101
+ ```powershell
102
+ $env:OBSERVER_RECOVERY_TOKEN = '<segredo-forte-temporário>'
103
+ npx wendkeep observer security token create --data-dir C:\WendKeepObserver `
104
+ --project-id project-a --role admin --scopes '*' --token-env OBSERVER_RECOVERY_TOKEN `
105
+ --expires-at 2026-09-29T12:00:00Z --reason 'offline recovery' --json
106
+ npx wendkeep observer security token revoke --data-dir C:\WendKeepObserver `
107
+ --project-id project-a --token-id <id> --reason 'recovery complete' --json
108
+ ```
109
+
110
+ Sempre faça dry-run antes do purge. O runner de retenção é explícito/idempotente (CLI ou
111
+ `POST /v1/projects/:id/security/retention`), sem timer oculto:
112
+
113
+ ```powershell
114
+ npx wendkeep observer security purge --data-dir C:\WendKeepObserver `
115
+ --project-id project-a --before 2026-08-01T00:00:00Z `
116
+ --classes documents,calls,transcripts --dry-run --json
117
+ ```
118
+
119
+ ```powershell
120
+ npx wendkeep observer security retention run --data-dir C:\WendKeepObserver `
121
+ --project-id project-a --operation-id scheduled-2026-08-29 --dry-run --json
122
+ ```
123
+
124
+ ## Resultado esperado
125
+
126
+ TTL é independente para documentos, calls e transcripts. Contagens, remoção de projeções/FTS,
127
+ eventos e receipt usam a mesma transação; retry é idempotente e dado antigo tardio gera nova prova.
128
+
129
+ AES-256-GCM usa AAD por projeto/classe/registro/campo e `keyProvider` externo. O backfill v6 remove
130
+ plaintext e índices derivados antes de liberar leituras; chave errada falha sem revelar conteúdo.
131
+ A migration estrutural `006-observer-security.sql` cria backup, valida checksum, faz rollback e
132
+ permite retry. Em modo at-rest obrigatório, o backup é `.bak.enc`, tem manifest/key ID/permissão
133
+ restrita e restauração falha com chave errada; nenhum `.bak` plaintext permanece.
134
+
135
+ O hook aplica policy metadata/redacted por padrão; `WENDKEEP_OBSERVER_POLICY_FILE` seleciona policy
136
+ explícita. `WENDKEEP_OBSERVER_OUTBOX_KEY_ENV` nomeia a variável da chave da outbox e
137
+ `WENDKEEP_OBSERVER_OUTBOX_KEY_ID` identifica a chave. O Compose exige autenticação e criptografia.
138
+ O painel guarda Bearer somente em memória, exporta cópia sanitizada e expõe Segurança. MCP exige
139
+ scope para calls/busca integral. Sync leva apenas `policy_ref`, sem duplicar tokens ou autoridade.
140
+
141
+ ## Erros comuns e diagnóstico
142
+
143
+ - `observer_token_missing|expired|revoked`: crie/rotacione um token escopado ou faça recovery offline.
144
+ - `observer_project_forbidden|role_forbidden|scope_forbidden`: confira a interseção projeto/role/scope.
145
+ - `observer_encryption_key_unavailable|observer_decryption_failed`: confira key ID e material externo;
146
+ nunca enfraqueça o modo obrigatório.
147
+ - `observer_policy_invalid`: valide campos/captures e remova regex inválida ou explosiva.
148
+ - Falha de migration v6: preserve `.pre-006-*.bak.enc` e seu manifest, corrija a causa e repita.
149
+
150
+ ## Próximos passos
151
+
152
+ Leia [Observer local](observer.md), faça um dry-run de retenção, valide token revogado/expirado e
153
+ guarde o receipt fora do banco quando precisar de prova externa. Nunca publique banco, backup,
154
+ outbox, chave, token ou `/data`.
@@ -26,7 +26,9 @@ pelos hooks e pelo WendKeep local.
26
26
 
27
27
  Tenha Node.js 22.13 ou mais recente para executar o Observer SQL. O Keep Core e os demais comandos
28
28
  continuam compatíveis com Node.js 18 ou mais recente. Registre explicitamente cada projeto e defina
29
- `WENDKEEP_OBSERVER_TOKEN`; leituras no loopback permanecem abertas, mas toda mutação exige Bearer.
29
+ `WENDKEEP_OBSERVER_TOKEN`. Toda mutação e toda leitura de conteúdo sensível exigem Bearer, inclusive
30
+ no loopback; metadados e agregados podem permanecer abertos localmente quando
31
+ `--require-loopback-auth` não é usado. Veja [Segurança do Observer](observer-security.md).
30
32
 
31
33
  ## Sintaxe
32
34
 
@@ -36,7 +38,7 @@ npx wendkeep observer register --project <projeto> --vault <vault> --data-dir <d
36
38
  npx wendkeep observer publish --project <projeto> --vault <vault> --data-dir <diretório>
37
39
  npx wendkeep observer reconcile --project <projeto> --vault <vault> --data-dir <diretório> [--url http://127.0.0.1:8787]
38
40
  npx wendkeep observer memory import --project <projeto> --vault <vault> --url http://127.0.0.1:8787 --token <token> --json
39
- npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir <diretório> --token <token>
41
+ npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir <diretório> --token <token> --bootstrap-projects <p1,p2> --bootstrap-expires-at <ISO> [--require-loopback-auth] [--require-encryption]
40
42
  ```
41
43
 
42
44
  ## Opções e códigos de saída
@@ -46,7 +48,13 @@ npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir <diretório>
46
48
  - `--project` e `--vault` identificam o projeto nos comandos `register`, `publish`, `reconcile` e `memory import`.
47
49
  - `--host` aceita somente `127.0.0.1`, `localhost` ou `::1`; outros hosts são recusados antes do
48
50
  listen.
49
- - `--token` ou `WENDKEEP_OBSERVER_TOKEN` autentica mutações; `--allow-non-loopback` falha sem token.
51
+ - `--token` ou `WENDKEEP_OBSERVER_TOKEN` fornece o segredo de bootstrap hash-only; projetos
52
+ explícitos e expiração finita são obrigatórios, e toda mutação/leitura sensível passa pelo registry;
53
+ `--allow-non-loopback` falha sem token.
54
+ - `--require-loopback-auth` exige Bearer também para metadados e agregados locais e ativa a policy
55
+ segura do projeto na ingestão.
56
+ - `--require-encryption` exige `WENDKEEP_OBSERVER_ENCRYPTION_KEY` com 32 bytes em hex/base64; use
57
+ `WENDKEEP_OBSERVER_ENCRYPTION_KEY_ID` para identificar a chave externa.
50
58
  - `WENDKEEP_OBSERVER_CAPTURE_LEVEL` aceita `metadata` (padrão, sem mensagens), `messages` ou
51
59
  `full-transcript`. Caminhos locais absolutos nunca são publicados.
52
60
  - Exit `0` indica sucesso; exit `1` indica falha de configuração ou operação; o hook publisher
@@ -57,7 +65,10 @@ npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir <diretório>
57
65
  ```powershell
58
66
  npx wendkeep observer register --project C:\GitHub\WendKeep --vault C:\GitHub\WendKeep\.WendKeep-vault --data-dir C:\WendKeepObserver
59
67
  $env:WENDKEEP_OBSERVER_TOKEN = '<token-local-forte>'
60
- npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir C:\WendKeepObserver --token $env:WENDKEEP_OBSERVER_TOKEN
68
+ $env:WENDKEEP_OBSERVER_BOOTSTRAP_PROJECTS = 'project-a'
69
+ $env:WENDKEEP_OBSERVER_BOOTSTRAP_EXPIRES_AT = '2026-09-29T12:00:00Z'
70
+ $env:WENDKEEP_OBSERVER_ENCRYPTION_KEY = '<32-bytes-em-hex-ou-base64>'
71
+ npx wendkeep observer serve --host 127.0.0.1 --port 8787 --data-dir C:\WendKeepObserver --token $env:WENDKEEP_OBSERVER_TOKEN --require-loopback-auth
61
72
  $env:WENDKEEP_OBSERVER_URL = 'http://127.0.0.1:8787'
62
73
  ```
63
74
 
@@ -67,15 +78,19 @@ Para Docker local:
67
78
  docker compose -f docker/wendkeep-observer/compose.yaml up -d --build
68
79
  ```
69
80
 
81
+ O Compose exige token, allowlist/expiração do bootstrap e chave; inicia com autenticação integral
82
+ e criptografia obrigatória. A policy `encryption_required` recusa ingestão e outbox plaintext.
83
+
70
84
  ## Painel web local
71
85
 
72
86
  Com o servidor em execução, abra [http://127.0.0.1:8787/](http://127.0.0.1:8787/) no navegador.
73
- O painel é servido pelo mesmo processo e abre diretamente para consultas, sem formulário ou token. A porta fica
74
- presa ao loopback do computador; não coloque o endereço em uma interface de rede.
87
+ O painel é servido pelo mesmo processo. Informe o token no formulário local: ele fica somente na
88
+ memória da página, segue como Bearer nas consultas e é descartado ao recarregar. A porta fica presa
89
+ ao loopback do computador; não coloque o endereço em uma interface de rede.
75
90
 
76
91
  O painel mostra a lista multi-projeto, versão, saúde, sessão mais recente, change ativa, contagem
77
92
  de changes e data da última captura. Ao abrir um projeto, o workspace oferece Overview, Consumo,
78
- Sessões, Memória, Changes e Sincronização. A aba Consumo mostra custo total, tokens por categoria,
93
+ Sessões, Memória, Changes, Sincronização e Segurança. A aba Consumo mostra custo total, tokens por categoria,
79
94
  agentes principais, subagentes, provedores, modelos, tendência diária, cobertura histórica e
80
95
  chamadas conforme o nível de captura escolhido. Os estados de carregamento, vazio, servidor
81
96
  indisponível, conflito, modelo sem tarifa e dados desatualizados ficam visíveis, e a atualização
@@ -160,18 +175,21 @@ corte. As telas do Observer não concluem, arquivam, reparam ou promovem estado.
160
175
  - `POST /v1/projects/:project_id/ingest` — lote idempotente de documentos, sessões, agentes, rollups,
161
176
  chamadas e transcripts.
162
177
  - `GET /v1/projects/:project_id/memory/tree` — árvore e metadados dos documentos.
163
- - `GET /v1/projects/:project_id/memory/document?path=...` — conteúdo Markdown integral.
178
+ - `GET /v1/projects/:project_id/memory/document?path=...` — conteúdo Markdown integral; exige Bearer.
164
179
  - `GET /v1/projects/:project_id/memory/search?q=...` — busca ranqueada por chunks, com trecho do
165
- match e proveniência; usa fallback lexical quando FTS5 não está disponível.
180
+ match e proveniência; usa fallback lexical quando FTS5 não está disponível e exige Bearer.
166
181
  - `GET /v1/projects/:project_id/sync` — modo, contagem, conflitos e último evento.
167
182
  - `PUT /v1/projects/:project_id/sync` — compatibilidade de configuração; a autoridade continua SQL.
168
- - `GET /v1/projects/:project_id/memory/export` — exportação read-only com conteúdo completo.
183
+ - `GET /v1/projects/:project_id/memory/export` — exportação read-only sanitizada por padrão; exige Bearer.
169
184
  - `POST /v1/projects/:project_id/memory/events` — ingestão idempotente em lote.
170
185
  - `GET /v1/projects/:project_id/usage/summary` — totais filtráveis por período, change, sessão,
171
186
  agente, provedor, modelo e papel.
172
187
  - `GET /v1/projects/:project_id/usage/breakdown` — hierarquia de agentes, subagentes e modelos.
173
- - `GET /v1/projects/:project_id/usage/calls` — chamadas individuais com prompt e resposta.
174
- - `GET /v1/projects/:project_id/transcripts/:transcript_id` — transcript comprimido, validado por hash.
188
+ - `GET /v1/projects/:project_id/usage/calls` — chamadas individuais com prompt e resposta; exige Bearer.
189
+ - `GET /v1/projects/:project_id/transcripts/:transcript_id` — transcript comprimido, validado por hash; exige Bearer.
190
+ - `GET /v1/projects/:project_id/security` — policy, contagens de tokens e audit sanitizado; exige admin.
191
+ - `PUT /v1/projects/:project_id/security/policy` — atualiza a policy efetiva sem restart; exige admin.
192
+ - `POST /v1/projects/:project_id/security/purge` — dry-run/purge transacional com receipt; exige admin.
175
193
 
176
194
  As rotas `/v1` rejeitam corpo transportado ou expandido acima do limite e validam projeto, caminho,
177
195
  revisão, hash, idempotência e isolamento antes de gravar o conteúdo no SQLite. Para preservar uma
@@ -1,15 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  import { pathToFileURL } from 'node:url';
3
3
  import { debugLog, readHookInput, resolveVault } from './obsidian-common.mjs';
4
- import { publishObserverSnapshot } from '../src/observer-publish.mjs';
4
+ import { publishObserverSnapshot, resolveObserverPublisherSecurity } from '../src/observer-publish.mjs';
5
5
 
6
6
  async function main() {
7
7
  const input = readHookInput();
8
8
  const resolved = resolveVault(input);
9
+ const publisherSecurity = resolveObserverPublisherSecurity();
9
10
  const result = await publishObserverSnapshot({
10
11
  vaultBase: resolved.base,
11
12
  projectRoot: resolved.projectRoot,
12
13
  input,
14
+ ...publisherSecurity,
13
15
  });
14
16
  if (!result.ok && result.error) debugLog('Observer publish fail-open:', result.error);
15
17
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wendkeep",
3
- "version": "0.86.0",
3
+ "version": "0.88.0",
4
4
  "description": "Vault-first persistent memory for AI coding agents, with an optional profile-aware governance runtime: OFF, FLOW, GUIDE, GOVERN, or ASSURE. Local-first and agent-agnostic (Claude Code, Codex, Cursor…).",
5
5
  "type": "module",
6
6
  "workspaces": [
@@ -9,6 +9,7 @@
9
9
  "exports": {
10
10
  "./harness": "./packages/harness/src/index.mjs",
11
11
  "./vault": "./packages/vault/src/index.mjs",
12
+ "./commit": "./packages/commit/src/index.mjs",
12
13
  "./hooks/*": "./hooks/*",
13
14
  "./src/*": "./src/*",
14
15
  "./bin/*": "./bin/*",
@@ -28,8 +29,10 @@
28
29
  "bin",
29
30
  "src",
30
31
  "hooks",
32
+ ".githooks",
31
33
  "packages",
32
34
  "schema",
35
+ "scripts/validate-commit-range.mjs",
33
36
  "web/observer",
34
37
  "docs/pt-BR/commands/*.md",
35
38
  "docs/en/commands/*.md",
@@ -42,7 +45,8 @@
42
45
  },
43
46
  "scripts": {
44
47
  "precheck": "node --check src/capabilities.mjs && node --check src/host-capabilities.mjs && node --check src/task-contracts.mjs && node --check src/task-leases.mjs && node --check src/task.mjs && node --check src/change.mjs && node --check src/archive-operation-lock.mjs && node --check src/worktree.mjs && node --check src/worktree-cleanup.mjs && node --check src/provenance-gate.mjs && node --check src/provenance-sources.mjs && node --check src/receipt-ledger.mjs && node --check src/evidence-envelope.mjs && node --check src/context.mjs && node --check src/active-context-health.mjs && node --check src/active-context-runtime.mjs && node --check hooks/active-context-store.mjs && node --check hooks/change-core.mjs && node --check hooks/brain-inject.mjs && node --check hooks/change-context.mjs && node --check hooks/session-stop.mjs && node --check packages/vault/src/worktree-metadata.mjs && node --check packages/vault/src/evidence-envelope.mjs && node --check packages/vault/src/memory-handoff.mjs && node --check packages/integrations/src/capabilities.mjs && node --check packages/pi/src/index.mjs && node --check src/sync-protocol.mjs && node --check src/sync-outbox.mjs && node --check src/sync-adapters.mjs && node --check src/sync-protocol-cli.mjs && node --check packages/mcp/src/sync.mjs && node --check src/portable.mjs && node --check src/tdd.mjs && node --check src/tdd-attestation.mjs && node --check src/tdd-attestation-store.mjs",
45
- "check": "node --check scripts/release.mjs && node --check scripts/release-plan.mjs && node --check scripts/release-provenance.mjs && node --check scripts/run-scope.mjs && node --check src/release-provenance.mjs && node --check bin/wendkeep.mjs && node --check packages/cli/src/index.mjs && node --check src/mcp.mjs && node --check src/init.mjs && node --check src/doctor.mjs && node --check src/active-context-health.mjs && node --check src/project-vault.mjs && node --check src/observer-auth.mjs && node --check src/observer-privacy.mjs && node --check src/observer-snapshot.mjs && node --check src/observer-store.mjs && node --check src/observer-memory.mjs && node --check src/observer-memory-publish.mjs && node --check src/observer-sql-store.mjs && node --check src/observer-sql-migrate.mjs && node --check src/observer-sql-publish.mjs && node --check src/observer-transcript-store.mjs && node --check src/observer-server.mjs && node --check src/observer.mjs && node --check src/observer-publish.mjs && node --check src/operating-profile.mjs && node --check src/profile.mjs && node --check src/flow.mjs && node --check src/work-kind.mjs && node --check src/delivery.mjs && node --check web/observer/app.mjs && node --check hooks/observer-publish.mjs && node --check hooks/evidence-context.mjs && node --check hooks/active-context-handoff-evidence.mjs && node --check hooks/evidence-recall.mjs && node --check hooks/memory-scope.mjs && node --check hooks/operating-profile-runtime.mjs && node --check hooks/operating-profile-task-store.mjs && node --check hooks/flow-core.mjs && node --check hooks/flow-protected-policy.mjs && node --check hooks/git-snapshot.mjs && node --check hooks/vault-path-safety.mjs && node --check hooks/vault-runtime-store.mjs && node --check packages/harness/src/index.mjs && node --check packages/harness/src/flow-store.mjs && node --check packages/harness/src/operating-profile.mjs && node --check packages/harness/src/sensors-core.mjs && node --check packages/integrations/src/host-hooks.mjs && node --check packages/integrations/src/hook-envelope.mjs && node --check packages/integrations/src/prompt-content.mjs && node --check packages/integrations/src/transcript-usage.mjs && node --check packages/integrations/src/transcripts.mjs && node --check packages/integrations/src/session-identity.mjs && node --check packages/integrations/src/index.mjs && node --check packages/mcp/src/audit.mjs && node --check packages/mcp/src/cli.mjs && node --check packages/mcp/src/config.mjs && node --check packages/mcp/src/effects.mjs && node --check packages/mcp/src/executor.mjs && node --check packages/mcp/src/server.mjs && node --check packages/mcp/src/stdio.mjs && node --check packages/mcp/src/index.mjs && node --check packages/vault/src/index.mjs && node --check packages/vault/src/project-vault.mjs && node --check packages/vault/src/vault-path-safety.mjs && node --check packages/vault/src/locale.mjs && node --check packages/vault/src/memory-schema.mjs && node --check packages/vault/src/memory-mode.mjs && node --check packages/vault/src/memory-scope.mjs && node --check packages/vault/src/memory-candidate-policy.mjs && node --check packages/vault/src/evidence-recall.mjs && node --check packages/vault/src/memory-handoff.mjs && node --check packages/vault/src/memory-store.mjs && node --check packages/vault/src/validate-core.mjs && node --check packages/vault/src/validate-memory.mjs",
48
+ "check": "node --check scripts/validate-commit-range.mjs && node --check packages/commit/src/index.mjs && node --check packages/commit/src/cli.mjs && node --check packages/commit/src/git-runtime.mjs && node --check src/git-commit-hooks.mjs && node --check scripts/release.mjs && node --check scripts/release-plan.mjs && node --check scripts/release-provenance.mjs && node --check scripts/run-scope.mjs && node --check src/release-provenance.mjs && node --check bin/wendkeep.mjs && node --check packages/cli/src/index.mjs && node --check src/mcp.mjs && node --check src/init.mjs && node --check src/doctor.mjs && node --check src/active-context-health.mjs && node --check src/project-vault.mjs && node --check src/observer-auth.mjs && node --check src/observer-privacy.mjs && node --check src/observer-snapshot.mjs && node --check src/observer-store.mjs && node --check src/observer-memory.mjs && node --check src/observer-memory-publish.mjs && node --check src/observer-sql-store.mjs && node --check src/observer-sql-migrate.mjs && node --check src/observer-sql-publish.mjs && node --check src/observer-transcript-store.mjs && node --check src/observer-server.mjs && node --check src/observer.mjs && node --check src/observer-publish.mjs && node --check src/operating-profile.mjs && node --check src/profile.mjs && node --check src/flow.mjs && node --check src/work-kind.mjs && node --check src/delivery.mjs && node --check web/observer/app.mjs && node --check hooks/observer-publish.mjs && node --check hooks/evidence-context.mjs && node --check hooks/active-context-handoff-evidence.mjs && node --check hooks/evidence-recall.mjs && node --check hooks/memory-scope.mjs && node --check hooks/operating-profile-runtime.mjs && node --check hooks/operating-profile-task-store.mjs && node --check hooks/flow-core.mjs && node --check hooks/flow-protected-policy.mjs && node --check hooks/git-snapshot.mjs && node --check hooks/vault-path-safety.mjs && node --check hooks/vault-runtime-store.mjs && node --check packages/harness/src/index.mjs && node --check packages/harness/src/flow-store.mjs && node --check packages/harness/src/operating-profile.mjs && node --check packages/harness/src/sensors-core.mjs && node --check packages/integrations/src/host-hooks.mjs && node --check packages/integrations/src/hook-envelope.mjs && node --check packages/integrations/src/prompt-content.mjs && node --check packages/integrations/src/transcript-usage.mjs && node --check packages/integrations/src/transcripts.mjs && node --check packages/integrations/src/session-identity.mjs && node --check packages/integrations/src/index.mjs && node --check packages/mcp/src/audit.mjs && node --check packages/mcp/src/cli.mjs && node --check packages/mcp/src/config.mjs && node --check packages/mcp/src/effects.mjs && node --check packages/mcp/src/executor.mjs && node --check packages/mcp/src/server.mjs && node --check packages/mcp/src/stdio.mjs && node --check packages/mcp/src/index.mjs && node --check packages/vault/src/index.mjs && node --check packages/vault/src/project-vault.mjs && node --check packages/vault/src/vault-path-safety.mjs && node --check packages/vault/src/locale.mjs && node --check packages/vault/src/memory-schema.mjs && node --check packages/vault/src/memory-mode.mjs && node --check packages/vault/src/memory-scope.mjs && node --check packages/vault/src/memory-candidate-policy.mjs && node --check packages/vault/src/evidence-recall.mjs && node --check packages/vault/src/memory-handoff.mjs && node --check packages/vault/src/memory-store.mjs && node --check packages/vault/src/validate-core.mjs && node --check packages/vault/src/validate-memory.mjs",
49
+ "postcheck": "node --check packages/observer/src/index.mjs && node --check packages/observer/src/policy.mjs && node --check packages/observer/src/redaction.mjs && node --check packages/observer/src/authz.mjs && node --check packages/observer/src/token-registry.mjs && node --check packages/observer/src/encryption.mjs && node --check packages/observer/src/retention.mjs && node --check packages/observer/src/purge.mjs && node --check packages/observer/src/audit.mjs",
46
50
  "test": "node --test --test-concurrency=2",
47
51
  "test:core": "node scripts/run-scope.mjs core",
48
52
  "release": "node scripts/release.mjs",
@@ -34,6 +34,7 @@ Usage:
34
34
  --no-companions Skip companion plugins/MCP entirely.
35
35
  --no-colors Skip the Obsidian color system (.obsidian snippet + graph groups).
36
36
  --vscode-worktree-tasks Create local, Git-excluded VS Code tasks for managed worktrees.
37
+ --git-commit-hooks Opt in to .githooks + local core.hooksPath for evidence commits.
37
38
  --dotcontext-mcp <v> dotcontext MCP placement: auto (default; skip project entry
38
39
  if already global), project, or none.
39
40
  --dotcontext-hooks <v> dotcontext hooks: full (default), light (no PostToolUse), none.
@@ -53,6 +54,7 @@ Usage:
53
54
  Uses explicit CAS/conflicts and an offline outbox; --remote path or --url HTTPS.
54
55
 
55
56
  wendkeep doctor [--vault P] Health check. --scope core|runtime · --strict for CI/release.
57
+ wendkeep commit <sub> Evidence-based Git commits: context | render | prepare | validate.
56
58
  wendkeep portable <sub> Shared authored state: status | export | import | diff.
57
59
  Default file: .wendkeep/portable/state.json; private runtime stays local.
58
60
  wendkeep mcp <serve|config> Native semantic MCP over stdio, or client config generation.
@@ -232,6 +234,9 @@ async function main(argv) {
232
234
  } else if (cmd === 'capabilities') {
233
235
  const { CAPABILITIES_HELP } = await import('../../../src/capabilities.mjs');
234
236
  process.stdout.write(CAPABILITIES_HELP);
237
+ } else if (cmd === 'commit') {
238
+ const { COMMIT_HELP } = await import('../../commit/src/cli.mjs');
239
+ process.stdout.write(COMMIT_HELP);
235
240
  } else {
236
241
  process.stdout.write(HELP);
237
242
  }
@@ -245,7 +250,7 @@ async function main(argv) {
245
250
  // `sync` starts with `init` and resolves the freshly bound Vault itself. Pre-resolving
246
251
  // here would prevent that repair step from reporting a corrupt binding as its own
247
252
  // first-stage failure (and could never make it as far as the guarded init).
248
- && !['init', 'sync', 'worktree', 'hook', 'observer', 'mcp', 'capabilities', '--version', '-v', '--help', '-h', 'help'].includes(cmd)) {
253
+ && !['init', 'sync', 'worktree', 'hook', 'observer', 'mcp', 'capabilities', 'commit', '--version', '-v', '--help', '-h', 'help'].includes(cmd)) {
249
254
  await preferProjectVault(rest);
250
255
  }
251
256
  switch (cmd) {
@@ -262,6 +267,11 @@ async function main(argv) {
262
267
  process.exit(runDoctor(rest));
263
268
  break;
264
269
  }
270
+ case 'commit': {
271
+ const { runCommit } = await import('../../commit/src/cli.mjs');
272
+ process.exit(runCommit(rest));
273
+ break;
274
+ }
265
275
  case 'portable': {
266
276
  const { runPortable } = await import('../../../src/portable.mjs');
267
277
  process.exit(runPortable(rest));
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "@wendkeep/commit",
3
+ "private": true,
4
+ "type": "module",
5
+ "exports": "./src/index.mjs"
6
+ }
@@ -0,0 +1,89 @@
1
+ import { readFileSync } from 'node:fs';
2
+
3
+ import { renderCommitMessage } from './commit-message.mjs';
4
+ import {
5
+ buildCommitInput,
6
+ clearCommitContext,
7
+ prepareCommitMessageFile,
8
+ validateCommitMessageFile,
9
+ writeCommitContext,
10
+ } from './git-runtime.mjs';
11
+
12
+ export const COMMIT_HELP = `wendkeep commit — evidence-based Git commit policy
13
+
14
+ Usage:
15
+ wendkeep commit context --input <json|-> [--json]
16
+ wendkeep commit context --clear [--json]
17
+ wendkeep commit render --input <json|->
18
+ wendkeep commit prepare --message-file <path> [--source <source>]
19
+ wendkeep commit validate --message-file <path> [--consume-context] [--json]
20
+
21
+ The context is stored under the repository Git directory, never in the Vault or working tree.
22
+ `;
23
+
24
+ function option(argv, name) {
25
+ const index = argv.indexOf(name);
26
+ if (index >= 0) return argv[index + 1] || '';
27
+ return argv.find((item) => item.startsWith(`${name}=`))?.slice(name.length + 1) || '';
28
+ }
29
+
30
+ function readInput(path) {
31
+ if (!path) throw Object.assign(new Error('--input is required'), { code: 'WENDKEEP_COMMIT_ARGUMENT' });
32
+ const source = path === '-' ? readFileSync(0, 'utf8') : readFileSync(path, 'utf8');
33
+ return JSON.parse(source);
34
+ }
35
+
36
+ function outputJson(value) {
37
+ process.stdout.write(`${JSON.stringify(value)}\n`);
38
+ }
39
+
40
+ export function runCommit(argv, { cwd = process.cwd() } = {}) {
41
+ const [subcommand, ...rest] = argv;
42
+ const json = rest.includes('--json');
43
+ try {
44
+ if (!subcommand || subcommand === 'help' || rest.includes('--help') || rest.includes('-h')) {
45
+ process.stdout.write(COMMIT_HELP);
46
+ return 0;
47
+ }
48
+ if (subcommand === 'context') {
49
+ const result = rest.includes('--clear')
50
+ ? clearCommitContext({ cwd })
51
+ : writeCommitContext(readInput(option(rest, '--input')), { cwd });
52
+ if (json) outputJson(rest.includes('--clear') ? result : { path: result.path, schema_version: 1 });
53
+ else process.stdout.write(`${rest.includes('--clear') ? 'cleared' : 'written'}: ${result.path}\n`);
54
+ return 0;
55
+ }
56
+ if (subcommand === 'render') {
57
+ const input = buildCommitInput(readInput(option(rest, '--input')), { cwd });
58
+ process.stdout.write(renderCommitMessage(input));
59
+ return 0;
60
+ }
61
+ if (subcommand === 'prepare') {
62
+ const result = prepareCommitMessageFile({
63
+ messageFile: option(rest, '--message-file'),
64
+ source: option(rest, '--source'),
65
+ cwd,
66
+ });
67
+ if (json) outputJson(result);
68
+ return 0;
69
+ }
70
+ if (subcommand === 'validate') {
71
+ const result = validateCommitMessageFile({
72
+ messageFile: option(rest, '--message-file'),
73
+ consumeContext: rest.includes('--consume-context'),
74
+ cwd,
75
+ });
76
+ if (json) outputJson(result);
77
+ if (!result.ok) {
78
+ process.stderr.write(`WENDKEEP_COMMIT_MESSAGE_INVALID\n${result.errors.map((error) => `- ${error}`).join('\n')}\n`);
79
+ return 1;
80
+ }
81
+ return 0;
82
+ }
83
+ process.stderr.write(`wendkeep commit: unknown subcommand "${subcommand}"\n`);
84
+ return 2;
85
+ } catch (error) {
86
+ process.stderr.write(`${error.code || 'WENDKEEP_COMMIT_ERROR'}: ${error.message}\n`);
87
+ return 2;
88
+ }
89
+ }