wendkeep 0.29.2 → 0.31.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 +89 -0
- package/bin/wendkeep.mjs +12 -1
- package/hooks/brain-inject.mjs +17 -10
- package/hooks/change-context.mjs +61 -0
- package/hooks/change-core.mjs +116 -11
- package/hooks/change-guard.mjs +59 -0
- package/hooks/change-nag.mjs +31 -0
- package/hooks/change-warn.mjs +48 -0
- package/hooks/decision-capture.mjs +37 -9
- package/hooks/linked-notes.mjs +3 -3
- package/hooks/plan-capture.mjs +116 -0
- package/hooks/renumber-decisions.mjs +198 -0
- package/hooks/session-stop.mjs +11 -1
- package/hooks/spec-core.mjs +25 -1
- package/hooks/subagent-usage.mjs +3 -1
- package/package.json +1 -1
- package/src/change.mjs +44 -13
- package/src/init.mjs +34 -13
- package/src/renumber.mjs +32 -0
- package/src/skills-seed.mjs +30 -13
- package/src/sync-defs.mjs +8 -0
- package/src/taxonomy.mjs +32 -0
package/src/init.mjs
CHANGED
|
@@ -9,9 +9,12 @@ import { createInterface } from 'node:readline/promises';
|
|
|
9
9
|
import {
|
|
10
10
|
VAULT_FOLDERS,
|
|
11
11
|
SESSION_HOOKS,
|
|
12
|
+
CHANGE_NUDGE_HOOKS,
|
|
13
|
+
CHANGE_GATE_HOOKS,
|
|
12
14
|
MCP_SERVER_KEY,
|
|
13
15
|
mcpServerEntry,
|
|
14
16
|
hookCommand,
|
|
17
|
+
hookCommandLocal,
|
|
15
18
|
deriveVaultDirName,
|
|
16
19
|
selectableCompanions,
|
|
17
20
|
resolveCompanions,
|
|
@@ -91,27 +94,45 @@ function backup(path) {
|
|
|
91
94
|
|
|
92
95
|
// --- merges -----------------------------------------------------------------
|
|
93
96
|
|
|
94
|
-
|
|
97
|
+
// Comando preferido para um hook: node-direto quando o projeto tem o pacote local (alta
|
|
98
|
+
// frequência sem cold-start de npx — ver R3 do design 0.31.0); senão o npx portátil.
|
|
99
|
+
export function hookCommandFor(name, projectPath) {
|
|
100
|
+
try {
|
|
101
|
+
if (projectPath && existsSync(join(projectPath, 'node_modules', 'wendkeep', 'hooks', `${name}.mjs`))) {
|
|
102
|
+
return hookCommandLocal(name);
|
|
103
|
+
}
|
|
104
|
+
} catch { /* fs indisponível — npx */ }
|
|
105
|
+
return hookCommand(name);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function mergeSettings(existing, { vaultPath, withMcp, force, companions = [], skipMcp = [], dotcontextHookLevel = 'full', projectPath = '' }) {
|
|
95
109
|
const s = existing && typeof existing === 'object' ? { ...existing } : {};
|
|
96
110
|
s.hooks = { ...(s.hooks || {}) };
|
|
97
111
|
let added = 0;
|
|
98
|
-
// wendkeep's own session hooks +
|
|
99
|
-
// `order` (default 0) so higher-order companion
|
|
100
|
-
// fold AFTER wendkeep's own within each event. A spec
|
|
101
|
-
// (dotcontext dispatch) instead of a wendkeep hook `name`.
|
|
102
|
-
const allSpecs = [
|
|
103
|
-
|
|
104
|
-
|
|
112
|
+
// wendkeep's own session hooks + the change-lifecycle hooks (0.31.0, default) + any
|
|
113
|
+
// companion-authored hooks. Stable-sort by `order` (default 0) so higher-order companion
|
|
114
|
+
// hooks (e.g. dotcontext's order 100) fold AFTER wendkeep's own within each event. A spec
|
|
115
|
+
// may carry its own `command` (dotcontext dispatch) instead of a wendkeep hook `name`.
|
|
116
|
+
const allSpecs = [
|
|
117
|
+
...SESSION_HOOKS,
|
|
118
|
+
...CHANGE_NUDGE_HOOKS,
|
|
119
|
+
...CHANGE_GATE_HOOKS,
|
|
120
|
+
...companionHookSpecs(companions, { dotcontextHookLevel }),
|
|
121
|
+
].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
105
122
|
for (const h of allSpecs) {
|
|
106
|
-
const command = h.command ?? hookCommand(h.name);
|
|
123
|
+
const command = h.command ?? (h.preferLocal ? hookCommandFor(h.name, projectPath) : hookCommand(h.name));
|
|
124
|
+
// Dual-recognition: um hook nomeado é reconhecido tanto na forma npx quanto na node-direta,
|
|
125
|
+
// para que trocar a forma preferida (ou re-initar noutra máquina) nunca duplique o grupo.
|
|
126
|
+
const candidates = h.command ? [h.command] : [hookCommand(h.name), hookCommandLocal(h.name)];
|
|
107
127
|
const groups = Array.isArray(s.hooks[h.event]) ? [...s.hooks[h.event]] : [];
|
|
108
|
-
const owning = groups.find((g) => (g.hooks || []).some((x) => x.command
|
|
128
|
+
const owning = groups.find((g) => (g.hooks || []).some((x) => candidates.includes(x.command)));
|
|
109
129
|
if (owning) {
|
|
110
130
|
// Already wired: never add a duplicate group (the old `if (present && !force)` fell through
|
|
111
131
|
// under --force and appended a second identical group). Under --force, refresh the managed
|
|
112
132
|
// entry's fields in place — without disturbing any sibling hooks the user grouped with it.
|
|
113
133
|
if (force) {
|
|
114
|
-
const hk = owning.hooks.find((x) => x.command
|
|
134
|
+
const hk = owning.hooks.find((x) => candidates.includes(x.command));
|
|
135
|
+
hk.command = command;
|
|
115
136
|
hk.timeout = h.timeout;
|
|
116
137
|
if (h.statusMessage) hk.statusMessage = h.statusMessage;
|
|
117
138
|
if (h.matcher && (owning.hooks || []).length === 1) owning.matcher = h.matcher;
|
|
@@ -477,12 +498,12 @@ export async function runInit(argv) {
|
|
|
477
498
|
const settingsRead = readJsonSafe(settingsPath);
|
|
478
499
|
if (!settingsRead.ok) {
|
|
479
500
|
// Unparseable existing file — never clobber. Drop a .new for manual merge.
|
|
480
|
-
const fresh = mergeSettings(null, { vaultPath, withMcp: args.mcp, force: true, companions, skipMcp, dotcontextHookLevel }).settings;
|
|
501
|
+
const fresh = mergeSettings(null, { vaultPath, withMcp: args.mcp, force: true, companions, skipMcp, dotcontextHookLevel, projectPath }).settings;
|
|
481
502
|
writeJson(`${settingsPath}.new`, fresh);
|
|
482
503
|
log(M.settingsBadJson(settingsPath));
|
|
483
504
|
} else {
|
|
484
505
|
const hadFile = settingsRead.data !== null;
|
|
485
|
-
const { settings, added } = mergeSettings(settingsRead.data, { vaultPath, withMcp: args.mcp, force: args.force, companions, skipMcp, dotcontextHookLevel });
|
|
506
|
+
const { settings, added } = mergeSettings(settingsRead.data, { vaultPath, withMcp: args.mcp, force: args.force, companions, skipMcp, dotcontextHookLevel, projectPath });
|
|
486
507
|
if (hadFile) backup(settingsPath);
|
|
487
508
|
writeJson(settingsPath, settings);
|
|
488
509
|
log(M.settings(hadFile ? M.merged : M.created, added, hadFile ? M.bakSaved : ''));
|
package/src/renumber.mjs
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// `wendkeep renumber-decisions` — retroactive ADR fix. Renumbers every note in 04-Decisões to
|
|
2
|
+
// `ADR-<NNNN>-<slug>` in chronological order, renames the files, and rewrites every wikilink to
|
|
3
|
+
// them across the vault. Preview by default; pass --apply to write. Idempotent.
|
|
4
|
+
import { existsSync } from 'node:fs';
|
|
5
|
+
import { isAbsolute, resolve } from 'node:path';
|
|
6
|
+
import { renumberDecisions } from '../hooks/renumber-decisions.mjs';
|
|
7
|
+
|
|
8
|
+
function opt(argv, name) {
|
|
9
|
+
const i = argv.indexOf(name);
|
|
10
|
+
if (i >= 0) return argv[i + 1];
|
|
11
|
+
const eq = argv.find((a) => a.startsWith(`${name}=`));
|
|
12
|
+
return eq ? eq.slice(name.length + 1) : undefined;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function runRenumberDecisions(argv) {
|
|
16
|
+
const vaultRaw = opt(argv, '--vault') || process.env.OBSIDIAN_VAULT_PATH;
|
|
17
|
+
if (!vaultRaw) { process.stderr.write('wendkeep renumber-decisions: no vault (--vault or OBSIDIAN_VAULT_PATH).\n'); process.exit(2); }
|
|
18
|
+
const vaultBase = isAbsolute(vaultRaw) ? vaultRaw : resolve(process.cwd(), vaultRaw);
|
|
19
|
+
if (!existsSync(vaultBase)) { process.stderr.write(`wendkeep renumber-decisions: vault not found: ${vaultBase}\n`); process.exit(2); }
|
|
20
|
+
|
|
21
|
+
const apply = argv.includes('--apply');
|
|
22
|
+
const report = renumberDecisions(vaultBase, { apply });
|
|
23
|
+
|
|
24
|
+
if (argv.includes('--json')) { process.stdout.write(`${JSON.stringify(report, null, 2)}\n`); process.exit(0); }
|
|
25
|
+
|
|
26
|
+
process.stdout.write(`${report.total} decisão(ões) · ${report.renamed} a renomear${apply ? ` · ${report.filesTouched} arquivo(s) com links atualizados` : ''}\n`);
|
|
27
|
+
for (const p of report.plan.filter((x) => x.renamed)) {
|
|
28
|
+
process.stdout.write(` ADR-${String(p.num).padStart(4, '0')} ${p.from}\n → ${p.to}\n`);
|
|
29
|
+
}
|
|
30
|
+
if (!apply) process.stdout.write('\nNada foi escrito (preview). Rode com --apply para renomear e atualizar os wikilinks.\n');
|
|
31
|
+
process.exit(0);
|
|
32
|
+
}
|
package/src/skills-seed.mjs
CHANGED
|
@@ -16,6 +16,12 @@ const WORKFLOW = `# Loop a2 — o ciclo de trabalho do wendkeep
|
|
|
16
16
|
Use ao começar qualquer mudança não-trivial. O loop mantém memória (vault) e prova
|
|
17
17
|
(sensores) juntas, tudo linkado no grafo do Obsidian.
|
|
18
18
|
|
|
19
|
+
<HARD-GATE>
|
|
20
|
+
NÃO edite arquivos de código antes do passo 2 (Propose / \`wendkeep change new\`).
|
|
21
|
+
Toda tarefa não-trivial passa pelo loop — planejar no chat e sair editando deixa o
|
|
22
|
+
vault cego. Exceção única: mudança trivial (typo, 1 linha).
|
|
23
|
+
</HARD-GATE>
|
|
24
|
+
|
|
19
25
|
## Os passos
|
|
20
26
|
|
|
21
27
|
1. **Explore** — entenda o problema antes de propor. Leia o código/contexto relevante.
|
|
@@ -222,6 +228,12 @@ const WORKFLOW_EN = `# The a2 loop — wendkeep's work cycle
|
|
|
222
228
|
Use it when starting any non-trivial change. The loop keeps memory (vault) and proof
|
|
223
229
|
(sensors) together, wikilinked in the Obsidian graph.
|
|
224
230
|
|
|
231
|
+
<HARD-GATE>
|
|
232
|
+
Do NOT edit code files before step 2 (Propose / \`wendkeep change new\`).
|
|
233
|
+
Every non-trivial task goes through the loop — planning in chat and editing right away
|
|
234
|
+
leaves the vault blind. Single exception: a trivial change (typo, one line).
|
|
235
|
+
</HARD-GATE>
|
|
236
|
+
|
|
225
237
|
## Steps
|
|
226
238
|
|
|
227
239
|
1. **Explore** — understand the problem before proposing.
|
|
@@ -518,21 +530,24 @@ size of the problem>
|
|
|
518
530
|
<verifiable criteria: for each requirement, the test that fails → passes>
|
|
519
531
|
`;
|
|
520
532
|
|
|
533
|
+
// As descriptions são o gatilho de ativação: o harness casa a description com o PEDIDO do
|
|
534
|
+
// usuário ("implementa X", "corrige Y"), não com abstrações ("mudança não-trivial"). Gatilhos
|
|
535
|
+
// concretos + instrução imperativa = a skill dispara sozinha (paridade Superpowers).
|
|
521
536
|
const WK_SKILLS_PT = [
|
|
522
|
-
skill('wk-workflow', 'Use
|
|
537
|
+
skill('wk-workflow', 'Use SEMPRE que o usuário pedir para implementar, criar, corrigir, refatorar, adicionar ou alterar código — qualquer tarefa de código não-trivial. Invoque ANTES de editar qualquer arquivo: orquestra o loop a2 (wendkeep change new → tarefas → verify → archive) e registra tudo no vault.', WORKFLOW),
|
|
523
538
|
skill('wk-tdd', 'Use ao implementar qualquer comportamento — Red/Green/Refactor com testes que discriminam (derivados do spec, litmus não-raso, adequação).', TDD),
|
|
524
|
-
skill('wk-debugging', 'Use quando algo falha ou
|
|
525
|
-
skill('wk-brainstorming', 'Use quando a ideia ainda é vaga — vira design aprovado, com closure gate e tabela out-of-scope, antes de código.', BRAINSTORMING, [{ name: 'design-template.md', content: DESIGN_TEMPLATE_PT }]),
|
|
526
|
-
skill('wk-planning', 'Use após um design aprovado — decompõe em plano de tarefas TDD bite-sized.', PLANNING, [{ name: 'plan-template.md', content: PLAN_TEMPLATE_PT }]),
|
|
539
|
+
skill('wk-debugging', 'Use quando algo falha, quebra, dá erro ou regride — depuração sistemática por hipótese antes de corrigir.', DEBUGGING),
|
|
540
|
+
skill('wk-brainstorming', 'Use quando a ideia ainda é vaga ou o usuário quer discutir/planejar uma feature (inclusive em plan mode) — vira design aprovado, com closure gate e tabela out-of-scope, antes de código.', BRAINSTORMING, [{ name: 'design-template.md', content: DESIGN_TEMPLATE_PT }]),
|
|
541
|
+
skill('wk-planning', 'Use após um design aprovado ou um plano aceito (inclusive plan mode) — decompõe em plano de tarefas TDD bite-sized e registra na change ativa.', PLANNING, [{ name: 'plan-template.md', content: PLAN_TEMPLATE_PT }]),
|
|
527
542
|
skill('wk-verify', 'Use no verify deep — passe independente read-only (autor≠verificador) que re-deriva a cobertura do spec e grava verdict.json.', VERIFY, [{ name: 'spec-reviewer-prompt.md', content: REVIEWER_PROMPT_PT }, { name: 'verdict-template.json', content: VERDICT_TEMPLATE }]),
|
|
528
543
|
];
|
|
529
544
|
|
|
530
545
|
const WK_SKILLS_EN = [
|
|
531
|
-
skill('wk-workflow', 'Use
|
|
546
|
+
skill('wk-workflow', 'Use WHENEVER the user asks to implement, create, fix, refactor, add or change code — any non-trivial coding task. Invoke BEFORE editing any file: it orchestrates the a2 loop (wendkeep change new → tasks → verify → archive) and records everything in the vault.', WORKFLOW_EN),
|
|
532
547
|
skill('wk-tdd', 'Use when implementing any behaviour — Red/Green/Refactor with tests that discriminate (spec-derived, non-shallow litmus, adequacy).', TDD_EN),
|
|
533
|
-
skill('wk-debugging', 'Use when something fails or
|
|
534
|
-
skill('wk-brainstorming', 'Use when the idea is still vague — turns it into an approved design, with a closure gate and out-of-scope table, before code.', BRAINSTORMING_EN, [{ name: 'design-template.md', content: DESIGN_TEMPLATE_EN }]),
|
|
535
|
-
skill('wk-planning', 'Use after an approved design — decomposes it into a bite-sized TDD task plan.', PLANNING_EN, [{ name: 'plan-template.md', content: PLAN_TEMPLATE_EN }]),
|
|
548
|
+
skill('wk-debugging', 'Use when something fails, breaks, errors or regresses — systematic hypothesis-driven debugging before fixing.', DEBUGGING_EN),
|
|
549
|
+
skill('wk-brainstorming', 'Use when the idea is still vague or the user wants to discuss/plan a feature (plan mode included) — turns it into an approved design, with a closure gate and out-of-scope table, before code.', BRAINSTORMING_EN, [{ name: 'design-template.md', content: DESIGN_TEMPLATE_EN }]),
|
|
550
|
+
skill('wk-planning', 'Use after an approved design or an accepted plan (plan mode included) — decomposes it into a bite-sized TDD task plan recorded in the active change.', PLANNING_EN, [{ name: 'plan-template.md', content: PLAN_TEMPLATE_EN }]),
|
|
536
551
|
skill('wk-verify', 'Use in verify deep — an independent read-only pass (author≠verifier) that re-derives spec coverage and writes verdict.json.', VERIFY_EN, [{ name: 'spec-reviewer-prompt.md', content: REVIEWER_PROMPT_EN }, { name: 'verdict-template.json', content: VERDICT_TEMPLATE }]),
|
|
537
552
|
];
|
|
538
553
|
|
|
@@ -545,20 +560,22 @@ export const WK_SKILLS = WK_SKILLS_PT;
|
|
|
545
560
|
// Seed each skill into <brainDir>/skills/<name>/ if absent (non-destructive): SKILL.md plus any
|
|
546
561
|
// bundled template/prompt files. Existing files are never overwritten, so re-seeding an older
|
|
547
562
|
// install just fills in the new template files alongside its SKILL.md.
|
|
548
|
-
|
|
563
|
+
// { refresh: true } (sync-defs --reseed) SOBRESCREVE as wk-* com os seeds atuais — é como um
|
|
564
|
+
// vault existente recebe descriptions/HARD-GATE novos (edições manuais nas wk-* são perdidas).
|
|
565
|
+
export function seedWkSkills(brainDir, localeId = 'pt-BR', { refresh = false } = {}) {
|
|
549
566
|
const created = [];
|
|
550
567
|
for (const s of wkSkills(localeId)) {
|
|
551
568
|
const dir = join(brainDir, 'skills', s.name);
|
|
552
569
|
mkdirSync(dir, { recursive: true });
|
|
553
|
-
const
|
|
570
|
+
const write = (name, content) => {
|
|
554
571
|
const f = join(dir, name);
|
|
555
|
-
if (!existsSync(f)) {
|
|
572
|
+
if (refresh || !existsSync(f)) {
|
|
556
573
|
writeFileSync(f, content, 'utf8');
|
|
557
574
|
created.push(f);
|
|
558
575
|
}
|
|
559
576
|
};
|
|
560
|
-
|
|
561
|
-
for (const file of s.files || [])
|
|
577
|
+
write('SKILL.md', s.body);
|
|
578
|
+
for (const file of s.files || []) write(file.name, file.content);
|
|
562
579
|
}
|
|
563
580
|
return created;
|
|
564
581
|
}
|
package/src/sync-defs.mjs
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
// cross-platform robustness.
|
|
8
8
|
import { copyFileSync, cpSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs';
|
|
9
9
|
import { isAbsolute, join, resolve } from 'node:path';
|
|
10
|
+
import { seedWkSkills } from './skills-seed.mjs';
|
|
11
|
+
import { getLocale } from '../hooks/locale.mjs';
|
|
10
12
|
|
|
11
13
|
// Managed AGENTS.md section (0.8.0): the agent-agnostic distribution channel. Codex, Amp,
|
|
12
14
|
// Cursor, Zed et al. read AGENTS.md — one file covers them all. Only the content between
|
|
@@ -114,6 +116,12 @@ export function runSyncDefs(argv) {
|
|
|
114
116
|
}
|
|
115
117
|
const vaultBase = isAbsolute(base) ? base : resolve(process.cwd(), base);
|
|
116
118
|
const projectPath = resolve(project || process.cwd());
|
|
119
|
+
// --reseed (0.31.0): sobrescreve as wk-* de .brain/skills com os seeds da versão instalada
|
|
120
|
+
// ANTES de copiar — é como um vault existente recebe descriptions/HARD-GATE novos.
|
|
121
|
+
if (argv.includes('--reseed')) {
|
|
122
|
+
const n = seedWkSkills(join(vaultBase, '.brain'), getLocale(vaultBase).id, { refresh: true });
|
|
123
|
+
process.stdout.write(`wendkeep sync-defs: ${n.length} arquivo(s) de skill re-semeados em .brain/skills\n`);
|
|
124
|
+
}
|
|
117
125
|
const r = syncDefs(vaultBase, projectPath);
|
|
118
126
|
process.stdout.write(
|
|
119
127
|
`wendkeep sync-defs: ${r.agents.length} agent(s) -> .codex/agents, ${r.skills.length} skill(s) -> .claude/skills\n`,
|
package/src/taxonomy.mjs
CHANGED
|
@@ -45,10 +45,16 @@ export const HOOK_FILES = [
|
|
|
45
45
|
'session-backfill.mjs',
|
|
46
46
|
'import-sessions.mjs',
|
|
47
47
|
'decision-capture.mjs',
|
|
48
|
+
'renumber-decisions.mjs',
|
|
48
49
|
'subagent-stop.mjs',
|
|
49
50
|
'task-log.mjs',
|
|
50
51
|
'vault-health.mjs',
|
|
51
52
|
'understand-inject.mjs',
|
|
53
|
+
'change-context.mjs',
|
|
54
|
+
'change-warn.mjs',
|
|
55
|
+
'change-guard.mjs',
|
|
56
|
+
'change-nag.mjs',
|
|
57
|
+
'plan-capture.mjs',
|
|
52
58
|
];
|
|
53
59
|
|
|
54
60
|
// Hook scripts that are safe to invoke directly via `wendkeep hook <name>`.
|
|
@@ -67,6 +73,11 @@ export const RUNNABLE_HOOKS = [
|
|
|
67
73
|
'brain-reindex',
|
|
68
74
|
'vault-health',
|
|
69
75
|
'understand-inject',
|
|
76
|
+
'change-context',
|
|
77
|
+
'change-warn',
|
|
78
|
+
'change-guard',
|
|
79
|
+
'change-nag',
|
|
80
|
+
'plan-capture',
|
|
70
81
|
];
|
|
71
82
|
|
|
72
83
|
// The MCP server entry wendkeep wires into .mcp.json so the agent can read/write the
|
|
@@ -106,6 +117,27 @@ export function hookCommand(name) {
|
|
|
106
117
|
return `npx wendkeep hook ${name}`;
|
|
107
118
|
}
|
|
108
119
|
|
|
120
|
+
// Forma node-direta do comando de hook: 1 processo (~100-250ms) em vez dos 3 do npx (cold-start
|
|
121
|
+
// de segundos no Windows). Usada pelos hooks de ALTA FREQUÊNCIA (por prompt / por tool-call)
|
|
122
|
+
// quando o projeto tem wendkeep instalado localmente; o init decide (hookCommandFor).
|
|
123
|
+
export function hookCommandLocal(name) {
|
|
124
|
+
return `node node_modules/wendkeep/hooks/${name}.mjs`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Hooks do lifecycle de change (0.31.0) — enforcement do loop a2. Nudges (contexto/aviso/
|
|
128
|
+
// cobrança/captura de plano) e gate (deny/ask no Bash). Separados em dois grupos para
|
|
129
|
+
// preservar a opção futura de gates opt-in; hoje o init wira TODOS por default.
|
|
130
|
+
// preferLocal: alta frequência → invocação node-direta quando houver instalação local.
|
|
131
|
+
export const CHANGE_NUDGE_HOOKS = [
|
|
132
|
+
{ event: 'UserPromptSubmit', matcher: null, name: 'change-context', timeout: 15, order: 10, preferLocal: true, statusMessage: 'wendkeep: change ping' },
|
|
133
|
+
{ event: 'PostToolUse', matcher: 'Edit|Write|MultiEdit', name: 'change-warn', timeout: 10, order: 10, preferLocal: true, statusMessage: 'wendkeep: change warn' },
|
|
134
|
+
{ event: 'PostToolUse', matcher: 'ExitPlanMode', name: 'plan-capture', timeout: 15, order: 10, preferLocal: true, statusMessage: 'wendkeep: capturing approved plan' },
|
|
135
|
+
{ event: 'Stop', matcher: null, name: 'change-nag', timeout: 15, order: 10, preferLocal: true, statusMessage: 'wendkeep: open tasks check' },
|
|
136
|
+
];
|
|
137
|
+
export const CHANGE_GATE_HOOKS = [
|
|
138
|
+
{ event: 'PreToolUse', matcher: 'Bash', name: 'change-guard', timeout: 10, order: 10, preferLocal: true, statusMessage: 'wendkeep: change gate' },
|
|
139
|
+
];
|
|
140
|
+
|
|
109
141
|
// --- companion plugins / MCP --------------------------------------------------
|
|
110
142
|
// Optional tools wendkeep init can pin alongside the vault. Each is wired through
|
|
111
143
|
// the MOST agent-agnostic mechanism it supports; the Claude Code plugin entry
|