wendkeep 0.8.0 → 0.9.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 +35 -0
- package/README.md +1 -1
- package/bin/wendkeep.mjs +1 -1
- package/hooks/linked-notes.mjs +62 -38
- package/package.json +2 -2
- package/src/init.mjs +13 -4
- package/src/sensors.mjs +48 -16
- package/src/skills-seed.mjs +146 -3
- package/src/vault-readme.mjs +86 -44
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,39 @@ 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.9.0] — 2026-07-06
|
|
8
|
+
|
|
9
|
+
Engineering debt: sensor editing + i18n coherence for auto-generated notes.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- **`wendkeep sensors add <id> "<command>"`** (`--severity` / `--type` / `--report` / `--name`
|
|
13
|
+
/ `--description`) — append a sensor to `wendkeep.sensors.json` (creates the file with
|
|
14
|
+
`$schema` when absent, dedups by id) instead of hand-editing JSON.
|
|
15
|
+
- **Locale-aware derived notes:** the auto-generated bug/decision/learning notes render their
|
|
16
|
+
headings + callout in the vault locale — an `en` vault no longer gets Portuguese headings.
|
|
17
|
+
|
|
18
|
+
### Deferred (with reason)
|
|
19
|
+
- `migrate-locale`: renaming a populated vault breaks every wikilink to the old folder names;
|
|
20
|
+
needs a backlink-repair pass — its own effort, not a patch.
|
|
21
|
+
- Code-hash verdict freshness: a change carries no file manifest, so "the code" is undefined;
|
|
22
|
+
the existing `tarefas.md` hash already blocks task drift.
|
|
23
|
+
|
|
24
|
+
## [0.8.1] — 2026-07-06
|
|
25
|
+
|
|
26
|
+
Polish: i18n coherence + presentation.
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
- **Locale-aware process skills + vault docs:** an `en` vault now seeds the `wk-*` skills,
|
|
30
|
+
the vault README, the change template, and the specs README in English (previously
|
|
31
|
+
Portuguese regardless of locale). Completes the `--locale en` promise.
|
|
32
|
+
- **`wendkeep.sensors.json` at the repo root** — the project gates itself with its own
|
|
33
|
+
test/check sensors (dogfooding the harness).
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
- npm `description` now describes the harness + a2 loop (was capture-only).
|
|
37
|
+
- CI: `actions/checkout` and `actions/setup-node` bumped to `v5` (v4 runner deprecation).
|
|
38
|
+
- README: the i18n "known limitation" is resolved.
|
|
39
|
+
|
|
7
40
|
## [0.8.0] — 2026-07-05
|
|
8
41
|
|
|
9
42
|
Reach: internationalization + agent-agnostic distribution.
|
|
@@ -180,6 +213,8 @@ Initial release — the capture engine, extracted from a system in daily product
|
|
|
180
213
|
- `wendkeep init` (cross-platform installer) + optional `@bitbonsai/mcpvault` MCP server.
|
|
181
214
|
|
|
182
215
|
<!-- Only v0.4.0+ is tagged in git (history starts here); older versions link to npm. -->
|
|
216
|
+
[0.9.0]: https://github.com/rogersialves/wendkeep/releases/tag/v0.9.0
|
|
217
|
+
[0.8.1]: https://github.com/rogersialves/wendkeep/releases/tag/v0.8.1
|
|
183
218
|
[0.8.0]: https://github.com/rogersialves/wendkeep/releases/tag/v0.8.0
|
|
184
219
|
[0.7.0]: https://github.com/rogersialves/wendkeep/releases/tag/v0.7.0
|
|
185
220
|
[0.6.1]: https://github.com/rogersialves/wendkeep/releases/tag/v0.6.1
|
package/README.md
CHANGED
|
@@ -136,7 +136,7 @@ The agent's settings.json points each hook at `npx wendkeep hook …`. On `Stop`
|
|
|
136
136
|
|
|
137
137
|
## Known limitations (`v0.1`)
|
|
138
138
|
|
|
139
|
-
- **Vault folder names
|
|
139
|
+
- **Vault folder names default to Portuguese** (`02-Sessões`, `04-Decisões`, …). Pass `wendkeep init --locale en` for an English vault (`02-Sessions`, `04-Decisions`, English scaffold/skills). The locale is a vault property, locked at init; parsers are bilingual so mixed content never breaks.
|
|
140
140
|
- **Search is keyword/frontmatter scoring**, not on‑device embeddings (that's on the roadmap).
|
|
141
141
|
- **Transcript formats are agent‑internal** and can change between agent versions; parsing is isolated but may need updates.
|
|
142
142
|
- Installer wires **Claude Code** settings + `.mcp.json`. Codex hooks run on the same scripts but are not auto‑wired yet.
|
package/bin/wendkeep.mjs
CHANGED
|
@@ -46,7 +46,7 @@ Usage:
|
|
|
46
46
|
wendkeep change <sub> Change lifecycle: new [--simple] | list | show | status |
|
|
47
47
|
done <id> | undone <id> | diff | archive [--force].
|
|
48
48
|
wendkeep spec <sub> Living specs: list | show <capability>.
|
|
49
|
-
wendkeep sensors list
|
|
49
|
+
wendkeep sensors <sub> list | add <id> "<command>" [--severity --type --report].
|
|
50
50
|
wendkeep verify [--change s] Run a change's task sensors + record evidence (the gate).
|
|
51
51
|
wendkeep lesson add "t" "l" Record a project-local lesson (injected at SessionStart).
|
|
52
52
|
wendkeep validate-memory [path] Validate .brain/CORE.md against the compaction
|
package/hooks/linked-notes.mjs
CHANGED
|
@@ -162,7 +162,28 @@ export function extractBugDetails(tx) {
|
|
|
162
162
|
};
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
-
|
|
165
|
+
// Locale labels for the auto-generated derived notes (0.9.0). Output-only — the extraction
|
|
166
|
+
// heuristics are untouched. Default pt-BR keeps existing behaviour for every legacy caller.
|
|
167
|
+
const NOTE_LABELS = {
|
|
168
|
+
'pt-BR': {
|
|
169
|
+
autoTag: 'Auto-gerada', autoLine: (p) => `Nota criada automaticamente pelo hook Stop do ${p}.`, session: 'Sessão',
|
|
170
|
+
verify: '_Extraído da sessão — verificar._', complete: '_Extraído da sessão — complementar._',
|
|
171
|
+
bug: { symptom: 'Sintoma', rootCause: 'Causa raiz', fix: 'Correção', files: 'Arquivos alterados', evidence: 'Evidência', lessons: 'Lições aprendidas', noFix: '_Nenhuma correção explícita detectada._', seeSession: '_Ver sessão vinculada._', addEvidence: '_Adicionar evidência empírica._' },
|
|
172
|
+
dec: { context: 'Contexto', decision: 'Decisão', consequences: 'Consequências', alternatives: 'Alternativas consideradas', noAlt: '_Nenhuma alternativa registrada automaticamente._' },
|
|
173
|
+
learn: { title: 'Aprendizado', context: 'Contexto', learned: 'O que aprendemos', future: 'Como aplicar no futuro', futureHint: '_Registrar como este conhecimento pode ser reutilizado._' },
|
|
174
|
+
},
|
|
175
|
+
en: {
|
|
176
|
+
autoTag: 'Auto-generated', autoLine: (p) => `Note created automatically by the ${p} Stop hook.`, session: 'Session',
|
|
177
|
+
verify: '_Extracted from the session — verify._', complete: '_Extracted from the session — complete._',
|
|
178
|
+
bug: { symptom: 'Symptom', rootCause: 'Root cause', fix: 'Fix', files: 'Changed files', evidence: 'Evidence', lessons: 'Lessons learned', noFix: '_No explicit fix detected._', seeSession: '_See the linked session._', addEvidence: '_Add empirical evidence._' },
|
|
179
|
+
dec: { context: 'Context', decision: 'Decision', consequences: 'Consequences', alternatives: 'Alternatives considered', noAlt: '_No alternative recorded automatically._' },
|
|
180
|
+
learn: { title: 'Learning', context: 'Context', learned: 'What we learned', future: 'How to apply in future', futureHint: '_Record how this knowledge can be reused._' },
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
function noteLabels(localeId) { return NOTE_LABELS[localeId] || NOTE_LABELS['pt-BR']; }
|
|
184
|
+
|
|
185
|
+
export function buildBugNoteContent(bug, issueRef, dateStr, sessionRel, provider = providerMeta(), contentKey = derivedContentKey(bug.rootCause), localeId = 'pt-BR') {
|
|
186
|
+
const L = noteLabels(localeId);
|
|
166
187
|
const title = issueRef
|
|
167
188
|
? `${issueRef} - ${normalizeInline(bug.rootCause, 80)}`
|
|
168
189
|
: normalizeInline(bug.rootCause, 80);
|
|
@@ -184,31 +205,31 @@ issue: ${yamlQuote(issueRef || '')}
|
|
|
184
205
|
|
|
185
206
|
# Bug - ${title}
|
|
186
207
|
|
|
187
|
-
> [!note]
|
|
188
|
-
>
|
|
189
|
-
>
|
|
208
|
+
> [!note] ${L.autoTag}
|
|
209
|
+
> ${L.autoLine(provider.label)}
|
|
210
|
+
> ${L.session}: ${wikilinkFromRel(sessionRel)}
|
|
190
211
|
|
|
191
|
-
##
|
|
212
|
+
## ${L.bug.symptom}
|
|
192
213
|
|
|
193
|
-
${bug.symptom ||
|
|
214
|
+
${bug.symptom || L.verify}
|
|
194
215
|
|
|
195
|
-
##
|
|
216
|
+
## ${L.bug.rootCause}
|
|
196
217
|
|
|
197
218
|
${bug.rootCause}
|
|
198
219
|
|
|
199
|
-
##
|
|
220
|
+
## ${L.bug.fix}
|
|
200
221
|
|
|
201
|
-
${markdownList(bug.fixes,
|
|
222
|
+
${markdownList(bug.fixes, L.bug.noFix)}
|
|
202
223
|
|
|
203
|
-
##
|
|
224
|
+
## ${L.bug.files}
|
|
204
225
|
|
|
205
|
-
${markdownList(bug.changedFiles.map((file) => `\`${file}\``),
|
|
226
|
+
${markdownList(bug.changedFiles.map((file) => `\`${file}\``), L.bug.seeSession)}
|
|
206
227
|
|
|
207
|
-
##
|
|
228
|
+
## ${L.bug.evidence}
|
|
208
229
|
|
|
209
|
-
${markdownList(bug.evidence,
|
|
230
|
+
${markdownList(bug.evidence, L.bug.addEvidence)}
|
|
210
231
|
|
|
211
|
-
##
|
|
232
|
+
## ${L.bug.lessons}
|
|
212
233
|
|
|
213
234
|
${bug.lessons}
|
|
214
235
|
`;
|
|
@@ -309,7 +330,8 @@ export function extractDecisionDetails(tx) {
|
|
|
309
330
|
};
|
|
310
331
|
}
|
|
311
332
|
|
|
312
|
-
export function buildDecisionNoteContent(decision, adrNum, dateStr, sessionRel, provider = providerMeta(), contentKey = derivedContentKey(decision.title)) {
|
|
333
|
+
export function buildDecisionNoteContent(decision, adrNum, dateStr, sessionRel, provider = providerMeta(), contentKey = derivedContentKey(decision.title), localeId = 'pt-BR') {
|
|
334
|
+
const L = noteLabels(localeId);
|
|
313
335
|
const adrId = `ADR-${String(adrNum).padStart(3, '0')}`;
|
|
314
336
|
return `---
|
|
315
337
|
type: decision
|
|
@@ -327,25 +349,25 @@ superseded_by: ""
|
|
|
327
349
|
|
|
328
350
|
# ${adrId} - ${decision.title}
|
|
329
351
|
|
|
330
|
-
> [!note]
|
|
331
|
-
>
|
|
332
|
-
>
|
|
352
|
+
> [!note] ${L.autoTag}
|
|
353
|
+
> ${L.autoLine(provider.label)}
|
|
354
|
+
> ${L.session}: ${wikilinkFromRel(sessionRel)}
|
|
333
355
|
|
|
334
|
-
##
|
|
356
|
+
## ${L.dec.context}
|
|
335
357
|
|
|
336
|
-
${decision.context ||
|
|
358
|
+
${decision.context || L.complete}
|
|
337
359
|
|
|
338
|
-
##
|
|
360
|
+
## ${L.dec.decision}
|
|
339
361
|
|
|
340
362
|
${decision.detail}
|
|
341
363
|
|
|
342
|
-
##
|
|
364
|
+
## ${L.dec.consequences}
|
|
343
365
|
|
|
344
366
|
${decision.consequences}
|
|
345
367
|
|
|
346
|
-
##
|
|
368
|
+
## ${L.dec.alternatives}
|
|
347
369
|
|
|
348
|
-
${markdownList(decision.alternatives,
|
|
370
|
+
${markdownList(decision.alternatives, L.dec.noAlt)}
|
|
349
371
|
`;
|
|
350
372
|
}
|
|
351
373
|
|
|
@@ -413,7 +435,8 @@ export function extractLearningDetails(tx, bugDetails) {
|
|
|
413
435
|
return learnings.length ? learnings.slice(0, 5) : null;
|
|
414
436
|
}
|
|
415
437
|
|
|
416
|
-
export function buildLearningNoteContent(learning, dateStr, sessionRel, provider = providerMeta(), contentKey = derivedContentKey(learning.title)) {
|
|
438
|
+
export function buildLearningNoteContent(learning, dateStr, sessionRel, provider = providerMeta(), contentKey = derivedContentKey(learning.title), localeId = 'pt-BR') {
|
|
439
|
+
const L = noteLabels(localeId);
|
|
417
440
|
return `---
|
|
418
441
|
type: learning
|
|
419
442
|
date: ${dateStr}
|
|
@@ -427,23 +450,23 @@ tags:
|
|
|
427
450
|
${yamlTags(learning.tags.map((tag) => (tag === 'codex' ? provider.tag : tag)))}
|
|
428
451
|
---
|
|
429
452
|
|
|
430
|
-
#
|
|
453
|
+
# ${L.learn.title} - ${learning.title}
|
|
431
454
|
|
|
432
|
-
> [!note]
|
|
433
|
-
>
|
|
434
|
-
>
|
|
455
|
+
> [!note] ${L.autoTag}
|
|
456
|
+
> ${L.autoLine(provider.label)}
|
|
457
|
+
> ${L.session}: ${wikilinkFromRel(sessionRel)}
|
|
435
458
|
|
|
436
|
-
##
|
|
459
|
+
## ${L.learn.context}
|
|
437
460
|
|
|
438
|
-
${learning.context ||
|
|
461
|
+
${learning.context || L.complete}
|
|
439
462
|
|
|
440
|
-
##
|
|
463
|
+
## ${L.learn.learned}
|
|
441
464
|
|
|
442
465
|
${learning.content}
|
|
443
466
|
|
|
444
|
-
##
|
|
467
|
+
## ${L.learn.future}
|
|
445
468
|
|
|
446
|
-
|
|
469
|
+
${L.learn.futureHint}
|
|
447
470
|
`;
|
|
448
471
|
}
|
|
449
472
|
|
|
@@ -478,7 +501,8 @@ function alreadyHasKey(keys, candidate) {
|
|
|
478
501
|
export function createLinkedNotes(vaultBase, dateStr, sessionRel, tx, options = {}) {
|
|
479
502
|
const linked = { decisions: [], bugs: [], learnings: [] };
|
|
480
503
|
const provider = providerMeta(options.provider);
|
|
481
|
-
const
|
|
504
|
+
const loc = getLocale(vaultBase);
|
|
505
|
+
const locF = loc.folders;
|
|
482
506
|
const bugsDir = join(vaultBase, monthFolderRelFromDateStr(locF.bugs, dateStr, vaultBase));
|
|
483
507
|
const decisionsDir = join(vaultBase, monthFolderRelFromDateStr(locF.decisions, dateStr, vaultBase));
|
|
484
508
|
const learningsDir = join(vaultBase, monthFolderRelFromDateStr(locF.learnings, dateStr, vaultBase));
|
|
@@ -497,7 +521,7 @@ export function createLinkedNotes(vaultBase, dateStr, sessionRel, tx, options =
|
|
|
497
521
|
const causeSlug = slugify(bugDetails.rootCause.slice(0, 40), 'bug');
|
|
498
522
|
const fileName = issueRef ? `${issueRef}-${causeSlug}.md` : `${dateStr}-bug-${causeSlug}.md`;
|
|
499
523
|
const filePath = join(bugsDir, fileName);
|
|
500
|
-
if (!existsSync(filePath)) writeFileSync(filePath, buildBugNoteContent(bugDetails, issueRef, dateStr, sessionRel, provider, bugKey), 'utf-8');
|
|
524
|
+
if (!existsSync(filePath)) writeFileSync(filePath, buildBugNoteContent(bugDetails, issueRef, dateStr, sessionRel, provider, bugKey, loc.id), 'utf-8');
|
|
501
525
|
linked.bugs.push(toVaultRelative(vaultBase, filePath));
|
|
502
526
|
existingKeys.bugs.push(bugKey);
|
|
503
527
|
}
|
|
@@ -513,7 +537,7 @@ export function createLinkedNotes(vaultBase, dateStr, sessionRel, tx, options =
|
|
|
513
537
|
const filePath = join(decisionsDir, fileName);
|
|
514
538
|
if (!existsSync(filePath)) {
|
|
515
539
|
const adrNum = Number(fileName.match(/^ADR-(\d+)/i)?.[1]) || getNextAdrNumber(vaultBase);
|
|
516
|
-
writeFileSync(filePath, buildDecisionNoteContent(decisionDetails, adrNum, dateStr, sessionRel, provider, decisionKey), 'utf-8');
|
|
540
|
+
writeFileSync(filePath, buildDecisionNoteContent(decisionDetails, adrNum, dateStr, sessionRel, provider, decisionKey, loc.id), 'utf-8');
|
|
517
541
|
}
|
|
518
542
|
linked.decisions.push(toVaultRelative(vaultBase, filePath));
|
|
519
543
|
existingKeys.decisions.push(decisionKey);
|
|
@@ -528,7 +552,7 @@ export function createLinkedNotes(vaultBase, dateStr, sessionRel, tx, options =
|
|
|
528
552
|
const learningSlug = slugify(learning.title.slice(0, 40), 'aprendizado');
|
|
529
553
|
const fileName = `${dateStr}-${learningSlug}.md`;
|
|
530
554
|
const filePath = join(learningsDir, fileName);
|
|
531
|
-
if (!existsSync(filePath)) writeFileSync(filePath, buildLearningNoteContent(learning, dateStr, sessionRel, provider, learningKey), 'utf-8');
|
|
555
|
+
if (!existsSync(filePath)) writeFileSync(filePath, buildLearningNoteContent(learning, dateStr, sessionRel, provider, learningKey, loc.id), 'utf-8');
|
|
532
556
|
linked.learnings.push(toVaultRelative(vaultBase, filePath));
|
|
533
557
|
existingKeys.learnings.push(learningKey);
|
|
534
558
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wendkeep",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"description": "A persistent-memory harness for AI coding agents on your Obsidian vault: turn-by-turn session capture plus a native, zero-dependency spec→change→verify→archive loop (sensor-gated, independent verdict, mutation discrimination). Local-first, agent-agnostic (Claude Code, Codex, Cursor…).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"wendkeep": "bin/wendkeep.mjs",
|
package/src/init.mjs
CHANGED
|
@@ -282,7 +282,7 @@ export async function runInit(argv) {
|
|
|
282
282
|
const readmePath = join(vaultPath, 'README.md');
|
|
283
283
|
let readmeNote = '';
|
|
284
284
|
if (!existsSync(readmePath)) {
|
|
285
|
-
writeFileSync(readmePath, renderVaultReadme({ projectName: basename(projectPath), vaultPath, withMcp: args.mcp }), 'utf8');
|
|
285
|
+
writeFileSync(readmePath, renderVaultReadme({ projectName: basename(projectPath), vaultPath, withMcp: args.mcp, locale: loc.id }), 'utf8');
|
|
286
286
|
readmeNote = ', README.md created';
|
|
287
287
|
}
|
|
288
288
|
// Seed the curated memory layer (CORE.md) + the compaction-protocol doc. The
|
|
@@ -297,12 +297,21 @@ export async function runInit(argv) {
|
|
|
297
297
|
// Seed the definitions layer (.brain/agents + .brain/skills): versioned source of
|
|
298
298
|
// truth for custom agents/skills. `wendkeep sync-defs` copies them to the agent dirs.
|
|
299
299
|
seedDefinitions(brainDir);
|
|
300
|
-
seedWkSkills(brainDir); // Pilar A: native process skills
|
|
300
|
+
seedWkSkills(brainDir, loc.id); // Pilar A: native process skills, in the vault locale.
|
|
301
301
|
// Seed the change/spec layer starters (Pilar B) — non-destructive.
|
|
302
|
+
const en = loc.id === 'en';
|
|
302
303
|
const specsReadme = join(vaultPath, loc.folders.specs, 'README.md');
|
|
303
|
-
if (!existsSync(specsReadme))
|
|
304
|
+
if (!existsSync(specsReadme)) {
|
|
305
|
+
writeFileSync(specsReadme, en
|
|
306
|
+
? `# Specs — living contract\n\nThe project's capabilities (requirements/scenarios). Changes in \`${loc.folders.changes}/\` promote deltas here on \`wendkeep change archive\`.\n`
|
|
307
|
+
: `# Specs — contrato vivo\n\nCapacidades do projeto (requisitos/cenários). Changes em \`${loc.folders.changes}/\` promovem deltas aqui no \`wendkeep change archive\`.\n`, 'utf8');
|
|
308
|
+
}
|
|
304
309
|
const changeTpl = join(vaultPath, 'Templates', 'Change.md');
|
|
305
|
-
if (!existsSync(changeTpl))
|
|
310
|
+
if (!existsSync(changeTpl)) {
|
|
311
|
+
writeFileSync(changeTpl, en
|
|
312
|
+
? '---\ntype: change\nstatus: active\ncssclasses:\n - topic-change\n---\n\n# <slug>\n\n## Why\n\n## What changes\n'
|
|
313
|
+
: '---\ntype: change\nstatus: active\ncssclasses:\n - topic-change\n---\n\n# <slug>\n\n## Por quê\n\n## O que muda\n', 'utf8');
|
|
314
|
+
}
|
|
306
315
|
// Seed the native sensor config (Pilar C) at project root — non-destructive.
|
|
307
316
|
const sensorsFile = join(projectPath, 'wendkeep.sensors.json');
|
|
308
317
|
if (!existsSync(sensorsFile)) {
|
package/src/sensors.mjs
CHANGED
|
@@ -1,23 +1,55 @@
|
|
|
1
|
-
// `wendkeep sensors list
|
|
2
|
-
import {
|
|
1
|
+
// `wendkeep sensors <list|add>` — view/edit wendkeep.sensors.json (0.7.0 / 0.9.0).
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { join, resolve } from 'node:path';
|
|
3
4
|
import { loadSensors } from '../hooks/sensors-core.mjs';
|
|
4
5
|
|
|
6
|
+
const SCHEMA = 'https://raw.githubusercontent.com/rogersialves/wendkeep/main/schema/wendkeep.sensors.schema.json';
|
|
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
|
+
function resolveProject(rest) {
|
|
16
|
+
return resolve(opt(rest, '--project') || process.cwd());
|
|
17
|
+
}
|
|
18
|
+
|
|
5
19
|
export function runSensors(argv) {
|
|
6
20
|
const [sub, ...rest] = argv;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
if (rest[i] === '--project') project = rest[++i];
|
|
14
|
-
else if (rest[i].startsWith('--project=')) project = rest[i].slice(10);
|
|
21
|
+
|
|
22
|
+
if (sub === 'list') {
|
|
23
|
+
const sensors = loadSensors(resolveProject(rest));
|
|
24
|
+
if (!sensors.length) { process.stdout.write('sensors: (nenhum — crie wendkeep.sensors.json na raiz)\n'); process.exit(0); }
|
|
25
|
+
for (const s of sensors) process.stdout.write(`${s.id}: ${s.type || 'command'} · ${s.severity || 'critical'} · ${s.command}\n`);
|
|
26
|
+
process.exit(0);
|
|
15
27
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
28
|
+
|
|
29
|
+
if (sub === 'add') {
|
|
30
|
+
const flags = new Set(['--severity', '--type', '--report', '--name', '--description', '--project']);
|
|
31
|
+
const positional = rest.filter((a, i) => !a.startsWith('--') && !flags.has(rest[i - 1]));
|
|
32
|
+
const [id, command] = positional;
|
|
33
|
+
if (!id || !command) { process.stderr.write('wendkeep sensors add: precisa <id> "<command>"\n'); process.exit(2); }
|
|
34
|
+
const projectRoot = resolveProject(rest);
|
|
35
|
+
const path = join(projectRoot, 'wendkeep.sensors.json');
|
|
36
|
+
let cfg = { $schema: SCHEMA, version: 1, source: 'manual', sensors: [] };
|
|
37
|
+
if (existsSync(path)) {
|
|
38
|
+
try { cfg = JSON.parse(readFileSync(path, 'utf8')); } catch { process.stderr.write('wendkeep sensors add: wendkeep.sensors.json inválido\n'); process.exit(2); }
|
|
39
|
+
if (!Array.isArray(cfg.sensors)) cfg.sensors = [];
|
|
40
|
+
if (!cfg.$schema) cfg.$schema = SCHEMA;
|
|
41
|
+
}
|
|
42
|
+
if (cfg.sensors.some((s) => s.id === id)) { process.stderr.write(`wendkeep sensors add: sensor "${id}" já existe\n`); process.exit(2); }
|
|
43
|
+
const severity = opt(rest, '--severity') === 'warning' ? 'warning' : 'critical';
|
|
44
|
+
const type = opt(rest, '--type') === 'mutation' ? 'mutation' : 'command';
|
|
45
|
+
const sensor = { id, name: opt(rest, '--name') || id, description: opt(rest, '--description') || command, severity, type, command };
|
|
46
|
+
if (type === 'mutation') sensor.report = opt(rest, '--report') || 'reports/mutation/mutation.json';
|
|
47
|
+
cfg.sensors.push(sensor);
|
|
48
|
+
writeFileSync(path, `${JSON.stringify(cfg, null, 2)}\n`, 'utf8');
|
|
49
|
+
process.stdout.write(`sensor added: ${id} (${type} · ${severity})\n`);
|
|
50
|
+
process.exit(0);
|
|
21
51
|
}
|
|
22
|
-
|
|
52
|
+
|
|
53
|
+
process.stderr.write(`wendkeep sensors: unknown subcommand "${sub}". Known: list, add.\n`);
|
|
54
|
+
process.exit(2);
|
|
23
55
|
}
|
package/src/skills-seed.mjs
CHANGED
|
@@ -203,7 +203,135 @@ nunca tivesse visto a implementação. Contexto fresco, read-only.
|
|
|
203
203
|
- O gate do \`archive\` **exige** \`verdict.json\` com \`ok\` cobrindo todo \`[req:]\`. Sem isso, não arquiva.
|
|
204
204
|
`;
|
|
205
205
|
|
|
206
|
-
|
|
206
|
+
const WORKFLOW_EN = `# The a2 loop — wendkeep's work cycle
|
|
207
|
+
|
|
208
|
+
Use it when starting any non-trivial change. The loop keeps memory (vault) and proof
|
|
209
|
+
(sensors) together, wikilinked in the Obsidian graph.
|
|
210
|
+
|
|
211
|
+
## Steps
|
|
212
|
+
|
|
213
|
+
1. **Explore** — understand the problem before proposing.
|
|
214
|
+
2. **Propose** — \`wendkeep change new <slug>\` scaffolds \`08-Changes/<slug>/\`
|
|
215
|
+
(proposta/design/tarefas + a \`specs/\` delta). The change becomes *active* and is
|
|
216
|
+
injected at the next SessionStart.
|
|
217
|
+
3. **Apply** — implement each task in tarefas.md with **wk-tdd** (red test first). Tag tasks:
|
|
218
|
+
\`[sensor:<id>]\` (automated proof) and \`[req:<ID>]\` (the spec requirement it satisfies).
|
|
219
|
+
4. **Verify** — \`wendkeep verify\` runs the sensors; then \`wendkeep verify --deep\` builds
|
|
220
|
+
the verification package.
|
|
221
|
+
5. **Verify deep** — the **wk-verify** skill (fresh, author≠verifier) writes \`verdict.json\`.
|
|
222
|
+
A trivial change (no \`[req:]\`) gets an auto verdict.
|
|
223
|
+
6. **Archive** — \`wendkeep change archive <slug>\`. The gate needs green sensors AND a
|
|
224
|
+
verdict AND no open tasks. It promotes the delta into \`07-Specs\` and mints an ADR.
|
|
225
|
+
|
|
226
|
+
## Rules
|
|
227
|
+
- One active change at a time. Finish (archive) before starting another.
|
|
228
|
+
- No \`[sensor:]\` on a task = no automated gate for it. No \`[req:]\` = no independent verdict.
|
|
229
|
+
- The graph links session ↔ change ↔ requirement ↔ decision. That is the point.
|
|
230
|
+
`;
|
|
231
|
+
|
|
232
|
+
const TDD_EN = `# TDD — Red, Green, Refactor (tests that discriminate)
|
|
233
|
+
|
|
234
|
+
Never write production code without a red test asking for it — and never write a test that
|
|
235
|
+
would pass under the wrong implementation.
|
|
236
|
+
|
|
237
|
+
## The cycle
|
|
238
|
+
1. **Red** — the smallest test that fails for the missing behaviour.
|
|
239
|
+
2. **See it fail** for the right reason (not an import/typo).
|
|
240
|
+
3. **Green** — the minimal code to pass.
|
|
241
|
+
4. **Refactor** with the greens protecting you.
|
|
242
|
+
|
|
243
|
+
## Derive from the spec, not the code
|
|
244
|
+
Write assertions from the requirement's acceptance criteria (\`07-Specs\`), not by reading the
|
|
245
|
+
implementation. Reading the code to write the test = it only confirms what the code already does.
|
|
246
|
+
|
|
247
|
+
## Non-shallow litmus
|
|
248
|
+
Reject an assertion that would pass under a wrong implementation. Assert a **value or persisted
|
|
249
|
+
state**, never "the mock was called". "Tested later" is rejected.
|
|
250
|
+
|
|
251
|
+
## Adequacy (necessary and sufficient)
|
|
252
|
+
Every acceptance criterion has an assertion with \`file:line\` evidence; every test traces to a
|
|
253
|
+
requirement (\`[req:ID]\`); covers what the spec asks, nothing more.
|
|
254
|
+
|
|
255
|
+
## Learn the project
|
|
256
|
+
Sample 5–10 existing tests for style/location/framework/depth (treat that depth as a floor).
|
|
257
|
+
Read \`AGENTS.md\` / \`.cursor/rules\` / CI configs for declared standards.
|
|
258
|
+
`;
|
|
259
|
+
|
|
260
|
+
const DEBUGGING_EN = `# Systematic debugging
|
|
261
|
+
|
|
262
|
+
Use it when something fails or behaves wrong. One hypothesis at a time — no shotgun changes.
|
|
263
|
+
|
|
264
|
+
1. **Reproduce** — a minimal deterministic case. Without it you can't know you fixed anything.
|
|
265
|
+
2. **One hypothesis** — the most likely cause, specific enough to test.
|
|
266
|
+
3. **Isolate** — confirm or kill the hypothesis BEFORE fixing (bisect, log, comment half). Prove
|
|
267
|
+
where it is, not where you think it is.
|
|
268
|
+
4. **Fix the root**, not the symptom.
|
|
269
|
+
5. **Verify** — the repro is gone AND the suite stays green. No regression.
|
|
270
|
+
|
|
271
|
+
Changed several things and it "worked"? You don't know what fixed it — revert, isolate one
|
|
272
|
+
variable. Read the whole error + stack — the decisive line is usually there.
|
|
273
|
+
`;
|
|
274
|
+
|
|
275
|
+
const BRAINSTORMING_EN = `# Brainstorming — idea into design
|
|
276
|
+
|
|
277
|
+
Use it when the idea is still vague. Turn it into an approved design BEFORE writing code.
|
|
278
|
+
|
|
279
|
+
1. **Explore context** — files, docs, relevant history.
|
|
280
|
+
2. **One question at a time** — purpose, constraints, success criteria. Prefer multiple choice.
|
|
281
|
+
3. **Propose 2–3 approaches** with trade-offs and your recommendation.
|
|
282
|
+
4. **Present the design in sections**, confirm each.
|
|
283
|
+
|
|
284
|
+
## Closure gate — no dangling ambiguity
|
|
285
|
+
Resolve every gray area: decide with the user, or log a **signed-off assumption** ("assuming X
|
|
286
|
+
because Y — correct me if wrong"). A declined gray area is recorded, not silently dropped.
|
|
287
|
+
|
|
288
|
+
## Out-of-scope table
|
|
289
|
+
List explicitly what the change does **not** do. Undeclared scope becomes creep.
|
|
290
|
+
|
|
291
|
+
## Hard gate
|
|
292
|
+
No code / scaffold / implementation action until a design is presented and approved. Then go to
|
|
293
|
+
**wk-planning**.
|
|
294
|
+
`;
|
|
295
|
+
|
|
296
|
+
const PLANNING_EN = `# Planning — design into a task plan
|
|
297
|
+
|
|
298
|
+
Use it after an approved design. Produce a plan an engineer with no project context can execute.
|
|
299
|
+
|
|
300
|
+
## Before tasks
|
|
301
|
+
Map the files: what to create/modify and each one's responsibility. Files that change together
|
|
302
|
+
live together; one file, one responsibility.
|
|
303
|
+
|
|
304
|
+
## Bite-sized tasks (TDD)
|
|
305
|
+
Each task ends in an independently testable deliverable. Each step is a 2–5 min action:
|
|
306
|
+
write the failing test (show code) → run and see it fail (exact command + expected) → minimal
|
|
307
|
+
implementation (show code) → run and see it pass → checkpoint: suite green.
|
|
308
|
+
|
|
309
|
+
## Rules
|
|
310
|
+
- Exact file paths, always. Real code in each step — no "TODO", "handle errors appropriately",
|
|
311
|
+
"similar to Task N". DRY, YAGNI. Consistent names/signatures across tasks.
|
|
312
|
+
`;
|
|
313
|
+
|
|
314
|
+
const VERIFY_EN = `# Independent verification — the fresh pass
|
|
315
|
+
|
|
316
|
+
Use it in the *verify deep* step, after \`wendkeep verify --deep\`. You are the **verifier**, not
|
|
317
|
+
the author — even if you wrote the code, enter as if you'd never seen it. Fresh context, read-only.
|
|
318
|
+
|
|
319
|
+
## What to do
|
|
320
|
+
1. Read the package \`08-Changes/<slug>/verificacao.json\` (requirements, tasks, evidence).
|
|
321
|
+
2. **Re-derive coverage from \`07-Specs\`** — for each \`[req:ID]\`, check the requirement's behaviour
|
|
322
|
+
is covered by a test that discriminates (wouldn't pass under a wrong impl). \`file:line\` evidence.
|
|
323
|
+
3. Spec-anchored outcome check: does the observable result match the acceptance criterion?
|
|
324
|
+
4. Write \`08-Changes/<slug>/verdict.json\`:
|
|
325
|
+
\`{ "slug": "...", "ok": true, "coverage": [{ "req": "GATE-1", "covered": true, "evidence": "file:line" }], "tasksHash": "<copy from the package>", "notes": [] }\`.
|
|
326
|
+
|
|
327
|
+
## Rules
|
|
328
|
+
- **Author ≠ verifier.** On Claude, spawn a read-only sub-agent for real isolation.
|
|
329
|
+
- \`ok: false\` if any requirement lacks discriminating coverage. A gap is red, not "almost".
|
|
330
|
+
- Don't fix here — a gap becomes a fix task; re-verify after.
|
|
331
|
+
- The archive gate **requires** a fresh \`verdict.json\` (matching \`tasksHash\`) covering every \`[req:]\`.
|
|
332
|
+
`;
|
|
333
|
+
|
|
334
|
+
const WK_SKILLS_PT = [
|
|
207
335
|
skill('wk-workflow', 'Use ao começar qualquer mudança não-trivial — orquestra o loop a2 (explore, propose, apply, verify, archive) nos comandos wendkeep.', WORKFLOW),
|
|
208
336
|
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),
|
|
209
337
|
skill('wk-debugging', 'Use quando algo falha ou quebra — depuração sistemática por hipótese antes de corrigir.', DEBUGGING),
|
|
@@ -212,10 +340,25 @@ export const WK_SKILLS = [
|
|
|
212
340
|
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),
|
|
213
341
|
];
|
|
214
342
|
|
|
343
|
+
const WK_SKILLS_EN = [
|
|
344
|
+
skill('wk-workflow', 'Use when starting any non-trivial change — orchestrates the a2 loop (explore, propose, apply, verify, archive) over the wendkeep commands.', WORKFLOW_EN),
|
|
345
|
+
skill('wk-tdd', 'Use when implementing any behaviour — Red/Green/Refactor with tests that discriminate (spec-derived, non-shallow litmus, adequacy).', TDD_EN),
|
|
346
|
+
skill('wk-debugging', 'Use when something fails or breaks — systematic hypothesis-driven debugging before fixing.', DEBUGGING_EN),
|
|
347
|
+
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),
|
|
348
|
+
skill('wk-planning', 'Use after an approved design — decomposes it into a bite-sized TDD task plan.', PLANNING_EN),
|
|
349
|
+
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),
|
|
350
|
+
];
|
|
351
|
+
|
|
352
|
+
// Skill set for a locale. WK_SKILLS stays the pt-BR set for back-compat.
|
|
353
|
+
export function wkSkills(localeId = 'pt-BR') {
|
|
354
|
+
return localeId === 'en' ? WK_SKILLS_EN : WK_SKILLS_PT;
|
|
355
|
+
}
|
|
356
|
+
export const WK_SKILLS = WK_SKILLS_PT;
|
|
357
|
+
|
|
215
358
|
// Seed each skill into <brainDir>/skills/<name>/SKILL.md if absent (non-destructive).
|
|
216
|
-
export function seedWkSkills(brainDir) {
|
|
359
|
+
export function seedWkSkills(brainDir, localeId = 'pt-BR') {
|
|
217
360
|
const created = [];
|
|
218
|
-
for (const s of
|
|
361
|
+
for (const s of wkSkills(localeId)) {
|
|
219
362
|
const dir = join(brainDir, 'skills', s.name);
|
|
220
363
|
mkdirSync(dir, { recursive: true });
|
|
221
364
|
const f = join(dir, 'SKILL.md');
|
package/src/vault-readme.mjs
CHANGED
|
@@ -1,46 +1,90 @@
|
|
|
1
1
|
// Renders a neutral, project-templated README for a freshly created wendkeep vault.
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
'
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
2
|
+
// Locale-aware (0.8.1): the structure table + prose follow the vault locale; folder names
|
|
3
|
+
// come from the locale so they can never drift from what `wendkeep init` creates.
|
|
4
|
+
import { LOCALES, vaultFolders } from '../hooks/locale.mjs';
|
|
5
|
+
|
|
6
|
+
// Folder descriptions keyed by locale folder KEY (not literal name), per locale.
|
|
7
|
+
const DESC = {
|
|
8
|
+
'pt-BR': {
|
|
9
|
+
inbox: 'Notas rápidas, captura sem classificação',
|
|
10
|
+
project: 'Arquitetura, padrões, memória do projeto',
|
|
11
|
+
sessions: 'Uma nota por sessão de trabalho com o agente IA',
|
|
12
|
+
linear: 'Espelho de issues/tarefas do seu tracker (Linear, Jira, GitHub Issues…)',
|
|
13
|
+
decisions: 'Architecture Decision Records (ADRs)',
|
|
14
|
+
bugs: 'Bugs resolvidos com causa raiz documentada',
|
|
15
|
+
learnings: 'Lições extraídas das sessões',
|
|
16
|
+
specs: 'Contrato vivo: capacidades do projeto (requisitos)',
|
|
17
|
+
changes: 'Mudanças em andamento (proposta/design/tarefas/spec-delta)',
|
|
18
|
+
Templates: 'Templates para novas notas',
|
|
19
|
+
'.brain': 'Memória canônica: CORE.md (curado) + DIGEST.md (auto), injetada no SessionStart',
|
|
20
|
+
},
|
|
21
|
+
en: {
|
|
22
|
+
inbox: 'Quick notes, unclassified capture',
|
|
23
|
+
project: 'Architecture, patterns, project memory',
|
|
24
|
+
sessions: 'One note per AI-agent work session',
|
|
25
|
+
linear: 'Mirror of issues/tasks from your tracker (Linear, Jira, GitHub Issues…)',
|
|
26
|
+
decisions: 'Architecture Decision Records (ADRs)',
|
|
27
|
+
bugs: 'Fixed bugs with documented root cause',
|
|
28
|
+
learnings: 'Lessons extracted from sessions',
|
|
29
|
+
specs: 'Living contract: the project capabilities (requirements)',
|
|
30
|
+
changes: 'Changes in flight (proposal/design/tasks/spec-delta)',
|
|
31
|
+
Templates: 'Templates for new notes',
|
|
32
|
+
'.brain': 'Canonical memory: CORE.md (curated) + DIGEST.md (auto), injected at SessionStart',
|
|
33
|
+
},
|
|
16
34
|
};
|
|
17
35
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
});
|
|
23
|
-
return ['| Pasta | Conteúdo |', '| --- | --- |', ...rows].join('\n');
|
|
36
|
+
// folder literal -> key, for description lookup.
|
|
37
|
+
function keyForFolder(loc, folder) {
|
|
38
|
+
const entry = Object.entries(loc.folders).find(([, name]) => name === folder);
|
|
39
|
+
return entry ? entry[0] : folder; // Templates / .brain fall through as themselves
|
|
24
40
|
}
|
|
25
41
|
|
|
26
|
-
export function renderVaultReadme({ projectName, vaultPath, withMcp = true }) {
|
|
27
|
-
const
|
|
28
|
-
const
|
|
42
|
+
export function renderVaultReadme({ projectName, vaultPath, withMcp = true, locale = 'pt-BR' }) {
|
|
43
|
+
const loc = LOCALES[locale] || LOCALES['pt-BR'];
|
|
44
|
+
const en = loc.id === 'en';
|
|
45
|
+
const desc = DESC[loc.id] || DESC['pt-BR'];
|
|
46
|
+
const name = projectName || (en ? 'project' : 'projeto');
|
|
47
|
+
|
|
48
|
+
const rows = vaultFolders(loc).map((f) => `| \`${f}/\` | ${desc[keyForFolder(loc, f)] || (en ? 'Project notes' : 'Notas do projeto')} |`);
|
|
49
|
+
const table = [en ? '| Folder | Contents |' : '| Pasta | Conteúdo |', '| --- | --- |', ...rows].join('\n');
|
|
50
|
+
|
|
51
|
+
if (en) {
|
|
52
|
+
const mcpIntro = withMcp ? ', and read/written by the **MCPVault** MCP server' : '';
|
|
53
|
+
const access = [`- **Obsidian:** open this folder with "Open folder as vault" → \`${vaultPath}\``];
|
|
54
|
+
if (withMcp) access.push('- **Agent (MCP):** the `wendkeep-vault` server (MCPVault) points at this vault (set in `.mcp.json`), giving the agent read/write on the notes.');
|
|
55
|
+
access.push('- **Hooks:** `settings.json` calls `npx wendkeep hook <name>`; the vault is located via `OBSIDIAN_VAULT_PATH` (also written by `wendkeep init`).');
|
|
56
|
+
return `# Obsidian vault — ${name}
|
|
57
|
+
|
|
58
|
+
> Knowledge base of **${name}**, captured automatically by wendkeep from AI coding-agent
|
|
59
|
+
> sessions (**Claude Code**, **Codex**) via session hooks${mcpIntro}.
|
|
60
|
+
|
|
61
|
+
## Structure
|
|
62
|
+
|
|
63
|
+
${table}
|
|
64
|
+
|
|
65
|
+
## Access
|
|
29
66
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
67
|
+
${access.join('\n')}
|
|
68
|
+
|
|
69
|
+
## Rules
|
|
70
|
+
|
|
71
|
+
1. Every relevant work session produces a note in \`${loc.folders.sessions}/\` (automatic, via hooks).
|
|
72
|
+
2. Architecture decisions become ADRs in \`${loc.folders.decisions}/\`.
|
|
73
|
+
3. Bugs with a root cause go to \`${loc.folders.bugs}/\`; lessons to \`${loc.folders.learnings}/\`.
|
|
74
|
+
4. Canonical memory: \`.brain/CORE.md\` (curated) + \`.brain/DIGEST.md\` (auto) — injected at SessionStart.
|
|
75
|
+
5. Tags in YAML frontmatter, not inline. Wikilinks \`[[note]]\` to connect notes.
|
|
76
|
+
|
|
77
|
+
## Notes
|
|
78
|
+
|
|
79
|
+
- **Locale:** this vault is \`en\` (folder names in English). Set at \`wendkeep init --locale\`; a vault is never renamed after.
|
|
80
|
+
- **Versioning:** session notes may contain transcript excerpts — review before committing this vault to a shared repo.
|
|
81
|
+
`;
|
|
38
82
|
}
|
|
39
|
-
access.push(
|
|
40
|
-
'- **Hooks:** `settings.json` chama `npx wendkeep hook <name>`; o vault é localizado\n' +
|
|
41
|
-
' via a env `OBSIDIAN_VAULT_PATH` (também gravada pelo `wendkeep init`).',
|
|
42
|
-
);
|
|
43
83
|
|
|
84
|
+
const mcpIntro = withMcp ? ', e lida/escrita pelo MCP server **MCPVault**' : '';
|
|
85
|
+
const access = [`- **Obsidian:** abra esta pasta com "Open folder as vault" → \`${vaultPath}\``];
|
|
86
|
+
if (withMcp) access.push('- **Agente (MCP):** o servidor `wendkeep-vault` (MCPVault) é apontado para este vault pelo `wendkeep init` (em `.mcp.json`), dando ao agente leitura/escrita das notas.');
|
|
87
|
+
access.push('- **Hooks:** `settings.json` chama `npx wendkeep hook <name>`; o vault é localizado via a env `OBSIDIAN_VAULT_PATH` (também gravada pelo `wendkeep init`).');
|
|
44
88
|
return `# Vault Obsidian — ${name}
|
|
45
89
|
|
|
46
90
|
> Base de conhecimento de **${name}**, capturada automaticamente pelo wendkeep a
|
|
@@ -49,7 +93,7 @@ export function renderVaultReadme({ projectName, vaultPath, withMcp = true }) {
|
|
|
49
93
|
|
|
50
94
|
## Estrutura
|
|
51
95
|
|
|
52
|
-
${
|
|
96
|
+
${table}
|
|
53
97
|
|
|
54
98
|
## Acesso
|
|
55
99
|
|
|
@@ -57,17 +101,15 @@ ${access.join('\n')}
|
|
|
57
101
|
|
|
58
102
|
## Regras
|
|
59
103
|
|
|
60
|
-
1. Toda sessão de trabalho relevante gera nota em
|
|
61
|
-
2. Decisões de arquitetura viram ADR em
|
|
62
|
-
3. Bugs com causa raiz vão para
|
|
104
|
+
1. Toda sessão de trabalho relevante gera nota em \`${loc.folders.sessions}/\` (automático, pelos hooks).
|
|
105
|
+
2. Decisões de arquitetura viram ADR em \`${loc.folders.decisions}/\`.
|
|
106
|
+
3. Bugs com causa raiz vão para \`${loc.folders.bugs}/\`; lições para \`${loc.folders.learnings}/\`.
|
|
63
107
|
4. Memória canônica: \`.brain/CORE.md\` (curado) + \`.brain/DIGEST.md\` (auto) — injetados no SessionStart.
|
|
64
|
-
5. Tags em frontmatter YAML, não inline.
|
|
65
|
-
6. Wikilinks \`[[nota]]\` para conectar notas entre si.
|
|
108
|
+
5. Tags em frontmatter YAML, não inline. Wikilinks \`[[nota]]\` para conectar notas.
|
|
66
109
|
|
|
67
110
|
## Notas
|
|
68
111
|
|
|
69
|
-
- **
|
|
70
|
-
- **Versionamento:** as notas de sessão podem conter trechos de transcript — avalie
|
|
71
|
-
antes de commitar este vault em um repositório compartilhado.
|
|
112
|
+
- **Locale:** este vault é \`pt-BR\` (nomes de pasta em português). Definido em \`wendkeep init --locale\`; o vault nunca é renomeado depois.
|
|
113
|
+
- **Versionamento:** as notas de sessão podem conter trechos de transcript — avalie antes de commitar este vault em um repositório compartilhado.
|
|
72
114
|
`;
|
|
73
115
|
}
|