wendkeep 0.58.1 → 0.58.3
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 +47 -0
- package/README.en.md +28 -36
- package/README.md +28 -36
- package/docs/en/commands/changes-and-verification.md +79 -0
- package/docs/en/commands/costs-and-observability.md +65 -0
- package/docs/en/commands/getting-started.md +82 -0
- package/docs/en/commands/maintenance-and-diagnostics.md +77 -0
- package/docs/en/commands/memory-migration.md +73 -0
- package/docs/en/commands/memory.md +84 -0
- package/docs/en/commands/notes-and-knowledge.md +70 -0
- package/docs/en/commands/retroactive-import.md +67 -0
- package/docs/en/commands/sessions-and-import.md +85 -0
- package/docs/en/commands/verify.md +86 -0
- package/docs/pt-BR/commands/changes-and-verification.md +80 -0
- package/docs/pt-BR/commands/costs-and-observability.md +65 -0
- package/docs/pt-BR/commands/getting-started.md +83 -0
- package/docs/pt-BR/commands/maintenance-and-diagnostics.md +77 -0
- package/docs/pt-BR/commands/memory-migration.md +73 -0
- package/docs/pt-BR/commands/memory.md +83 -0
- package/docs/pt-BR/commands/notes-and-knowledge.md +69 -0
- package/docs/pt-BR/commands/retroactive-import.md +67 -0
- package/docs/pt-BR/commands/sessions-and-import.md +85 -0
- package/docs/pt-BR/commands/verify.md +87 -0
- package/hooks/obsidian-common.mjs +80 -29
- package/hooks/session-ensure.mjs +15 -8
- package/hooks/session-memory-lifecycle.mjs +330 -0
- package/hooks/session-stop.mjs +118 -42
- package/hooks/vault-health.mjs +101 -4
- package/package.json +3 -1
- package/src/taxonomy.mjs +1 -0
package/hooks/session-stop.mjs
CHANGED
|
@@ -16,6 +16,11 @@ import { buildSessionMemoryEvents, collectLifecycleEvidence } from './memory-han
|
|
|
16
16
|
import { enqueueMemoryEvent, projectMemoryOutbox } from './memory-store.mjs';
|
|
17
17
|
import { detectMemoryMode } from './memory-mode.mjs';
|
|
18
18
|
import { sanitizeMemoryText } from './memory-schema.mjs';
|
|
19
|
+
import {
|
|
20
|
+
projectStopMemoryAttempt,
|
|
21
|
+
recordStopMemoryOutcome,
|
|
22
|
+
stageStopMemoryAttempt,
|
|
23
|
+
} from './session-memory-lifecycle.mjs';
|
|
19
24
|
import {
|
|
20
25
|
ensureDir,
|
|
21
26
|
findActiveSessionByTranscript,
|
|
@@ -527,6 +532,26 @@ export function parseTranscript(transcriptPath) {
|
|
|
527
532
|
return parseCodexTranscript(transcriptPath);
|
|
528
533
|
}
|
|
529
534
|
|
|
535
|
+
export function resolveTurnIdentity(transcript, requestedTurnId = '') {
|
|
536
|
+
const turns = Array.isArray(transcript?.turns) ? transcript.turns : [];
|
|
537
|
+
const requested = String(requestedTurnId || '');
|
|
538
|
+
let index = requested
|
|
539
|
+
? turns.findIndex((turn) => String(turn?.turnId || '') === requested)
|
|
540
|
+
: -1;
|
|
541
|
+
if (requested && index < 0) return null;
|
|
542
|
+
if (index < 0 && transcript?.latestTurnId) {
|
|
543
|
+
index = turns.findIndex((turn) => String(turn?.turnId || '') === String(transcript.latestTurnId));
|
|
544
|
+
}
|
|
545
|
+
if (index < 0) index = turns.length - 1;
|
|
546
|
+
const turn = turns[index];
|
|
547
|
+
if (!turn?.turnId) return null;
|
|
548
|
+
return {
|
|
549
|
+
id: String(turn.turnId),
|
|
550
|
+
order: index + 1,
|
|
551
|
+
observedAt: String(turn.timestamp || ''),
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
|
|
530
555
|
function compactText(text, max = 600) {
|
|
531
556
|
const clean = redactSecrets(String(text || ''))
|
|
532
557
|
.replace(/\r/g, '\n')
|
|
@@ -1095,7 +1120,18 @@ function pingObsidianVault(apiKey) {
|
|
|
1095
1120
|
} catch {}
|
|
1096
1121
|
}
|
|
1097
1122
|
|
|
1098
|
-
function
|
|
1123
|
+
export function shouldAbortStopAfterStaging(causalStop, memoryAttempt) {
|
|
1124
|
+
const rejectedByV2Revalidation = memoryAttempt?.memory_mode === 'v2'
|
|
1125
|
+
&& memoryAttempt?.state === 'skipped';
|
|
1126
|
+
if (rejectedByV2Revalidation) return true;
|
|
1127
|
+
return Boolean(
|
|
1128
|
+
causalStop
|
|
1129
|
+
&& !causalStop.canPromoteMemory
|
|
1130
|
+
&& memoryAttempt?.state !== 'enqueued'
|
|
1131
|
+
);
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
export function main({ stageMemory = stageStopMemoryAttempt } = {}) {
|
|
1099
1135
|
const input = readHookInput();
|
|
1100
1136
|
if (input.stop_hook_active) {
|
|
1101
1137
|
writeHookOutput({});
|
|
@@ -1132,15 +1168,34 @@ function main() {
|
|
|
1132
1168
|
return;
|
|
1133
1169
|
}
|
|
1134
1170
|
|
|
1135
|
-
const tx = parseTranscript(input.transcript_path || input.transcriptPath);
|
|
1136
|
-
const
|
|
1171
|
+
const tx = parseTranscript(identity.transcriptPath || input.transcript_path || input.transcriptPath);
|
|
1172
|
+
const requestedTurnId = String(input.turn_id || input.turnId || '');
|
|
1137
1173
|
const sessionId = identity.canonicalConversationId;
|
|
1138
1174
|
const finalizing = shouldFinalizeSession();
|
|
1175
|
+
const turnIdentity = resolveTurnIdentity(tx, requestedTurnId);
|
|
1176
|
+
if (!turnIdentity) {
|
|
1177
|
+
if (finalizing) {
|
|
1178
|
+
const activeId = String(entry.active_activation_id || '');
|
|
1179
|
+
const active = entry.activations?.[activeId] || {};
|
|
1180
|
+
stageMemory(vaultBase, {
|
|
1181
|
+
sessionId,
|
|
1182
|
+
activationId: activeId,
|
|
1183
|
+
activationEpoch: Number(active.epoch || entry.activation_epoch || 0),
|
|
1184
|
+
turnId: requestedTurnId || 'unresolved-turn',
|
|
1185
|
+
turnSequence: Number(entry.last_turn_sequence || 0),
|
|
1186
|
+
disposition: 'ambiguous',
|
|
1187
|
+
observedAt: new Date(0).toISOString(),
|
|
1188
|
+
});
|
|
1189
|
+
}
|
|
1190
|
+
const message = 'wendkeep: Stop ambiguous; o turno solicitado não foi provado pelo transcript.';
|
|
1191
|
+
process.stderr.write(`[wendkeep] ${message}\n`);
|
|
1192
|
+
writeHookOutput({ systemMessage: message });
|
|
1193
|
+
return;
|
|
1194
|
+
}
|
|
1195
|
+
const turnId = turnIdentity.id;
|
|
1139
1196
|
const now = finalizing ? new Date() : null;
|
|
1140
1197
|
const endedAt = finalizing ? formatLocalIso(now) : '';
|
|
1141
|
-
const stopTurnSequence =
|
|
1142
|
-
? Number(input.turn_sequence)
|
|
1143
|
-
: Number(entry.last_turn_sequence || 0);
|
|
1198
|
+
const stopTurnSequence = turnIdentity.order;
|
|
1144
1199
|
const causalStop = finalizing
|
|
1145
1200
|
? mutateSessionRegistry(vaultBase, (registry) => {
|
|
1146
1201
|
const activationId = resolveStopActivation(registry, {
|
|
@@ -1152,6 +1207,7 @@ function main() {
|
|
|
1152
1207
|
const cas = applyStopActivation(registry, {
|
|
1153
1208
|
session_id: sessionId,
|
|
1154
1209
|
activation_id: activationId,
|
|
1210
|
+
turn_id: turnId,
|
|
1155
1211
|
turn_sequence: stopTurnSequence,
|
|
1156
1212
|
ended_at: endedAt,
|
|
1157
1213
|
});
|
|
@@ -1176,8 +1232,44 @@ function main() {
|
|
|
1176
1232
|
};
|
|
1177
1233
|
})
|
|
1178
1234
|
: null;
|
|
1179
|
-
|
|
1180
|
-
|
|
1235
|
+
let memoryHandoff = null;
|
|
1236
|
+
let memoryAttempt = null;
|
|
1237
|
+
if (finalizing) {
|
|
1238
|
+
let projectId = '';
|
|
1239
|
+
try {
|
|
1240
|
+
projectId = JSON.parse(readFileSync(join(vaultBase, '.brain', 'PROJECT.json'), 'utf8')).projectId || '';
|
|
1241
|
+
} catch { /* the staging validator exposes an observable failure below */ }
|
|
1242
|
+
const finalSummary = sessionFinalSummary(tx);
|
|
1243
|
+
const memoryEvidence = collectLifecycleEvidence(vaultBase, {
|
|
1244
|
+
changeSlug: entry.change_slug,
|
|
1245
|
+
summary: finalSummary,
|
|
1246
|
+
noteRel: sessionRel,
|
|
1247
|
+
});
|
|
1248
|
+
memoryHandoff = {
|
|
1249
|
+
projectId,
|
|
1250
|
+
identity,
|
|
1251
|
+
activation: {
|
|
1252
|
+
id: causalStop?.activationId || '',
|
|
1253
|
+
epoch: Number(causalStop?.activation?.epoch || entry.activation_epoch || 0),
|
|
1254
|
+
},
|
|
1255
|
+
turn: { id: turnId, sequence: stopTurnSequence },
|
|
1256
|
+
noteRel: sessionRel,
|
|
1257
|
+
observedAt: turnIdentity.observedAt || new Date(0).toISOString(),
|
|
1258
|
+
summary: finalSummary,
|
|
1259
|
+
evidence: memoryEvidence,
|
|
1260
|
+
};
|
|
1261
|
+
memoryAttempt = stageMemory(vaultBase, {
|
|
1262
|
+
handoff: memoryHandoff,
|
|
1263
|
+
disposition: causalStop?.stopDisposition || 'ambiguous',
|
|
1264
|
+
});
|
|
1265
|
+
}
|
|
1266
|
+
if (shouldAbortStopAfterStaging(causalStop, memoryAttempt)) {
|
|
1267
|
+
const disposition = memoryAttempt?.disposition || causalStop?.stopDisposition || 'ambiguous';
|
|
1268
|
+
if (disposition === 'duplicate' && memoryAttempt?.state === 'duplicate') {
|
|
1269
|
+
writeHookOutput({});
|
|
1270
|
+
return;
|
|
1271
|
+
}
|
|
1272
|
+
const message = `wendkeep: Stop ${disposition}; uma activation mais nova foi preservada e a memória não foi promovida.`;
|
|
1181
1273
|
process.stderr.write(`[wendkeep] ${message}\n`);
|
|
1182
1274
|
writeHookOutput({ systemMessage: message });
|
|
1183
1275
|
return;
|
|
@@ -1252,40 +1344,24 @@ function main() {
|
|
|
1252
1344
|
last_logged_turn_id: turnId,
|
|
1253
1345
|
});
|
|
1254
1346
|
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
noteRel: sessionRel,
|
|
1274
|
-
observedAt: new Date().toISOString(),
|
|
1275
|
-
summary: finalSummary,
|
|
1276
|
-
evidence: memoryEvidence,
|
|
1277
|
-
});
|
|
1278
|
-
mutateSessionRegistry(vaultBase, (registry) => {
|
|
1279
|
-
const current = registry.sessions[sessionId];
|
|
1280
|
-
if (!current) return null;
|
|
1281
|
-
registry.sessions[sessionId] = {
|
|
1282
|
-
...current,
|
|
1283
|
-
memory_status: memoryResult.status,
|
|
1284
|
-
memory_activation_id: causalStop.activationId,
|
|
1285
|
-
...(memoryResult.checkpoint ? { memory_checkpoint: memoryResult.checkpoint } : {}),
|
|
1286
|
-
};
|
|
1287
|
-
return null;
|
|
1288
|
-
});
|
|
1347
|
+
const memoryResult = projectStopMemoryAttempt(vaultBase, memoryAttempt);
|
|
1348
|
+
if (memoryResult.status === 'legacy') {
|
|
1349
|
+
mutateSessionRegistry(vaultBase, (registry) => {
|
|
1350
|
+
const current = registry.sessions[sessionId];
|
|
1351
|
+
const active = current?.activations?.[current.active_activation_id || ''];
|
|
1352
|
+
if (!current
|
|
1353
|
+
|| current.active_activation_id !== memoryAttempt.activation_id
|
|
1354
|
+
|| Number(active?.epoch || 0) !== Number(memoryAttempt.activation_epoch || 0)) return null;
|
|
1355
|
+
registry.sessions[sessionId] = {
|
|
1356
|
+
...current,
|
|
1357
|
+
memory_status: 'legacy',
|
|
1358
|
+
memory_activation_id: memoryAttempt.activation_id,
|
|
1359
|
+
};
|
|
1360
|
+
return null;
|
|
1361
|
+
});
|
|
1362
|
+
} else {
|
|
1363
|
+
recordStopMemoryOutcome(vaultBase, memoryAttempt, memoryResult);
|
|
1364
|
+
}
|
|
1289
1365
|
|
|
1290
1366
|
// Reconstrói índice (camada fria) + digest (camada quente) ao finalizar. Nunca derruba o Stop.
|
|
1291
1367
|
try {
|
package/hooks/vault-health.mjs
CHANGED
|
@@ -121,27 +121,117 @@ function readJsonLines(path, label) {
|
|
|
121
121
|
|
|
122
122
|
function inspectOutbox(vaultBase, projectId) {
|
|
123
123
|
const dir = join(vaultBase, '.brain', 'memory-outbox');
|
|
124
|
-
if (!existsSync(dir)) return { count: 0, errors: [] };
|
|
124
|
+
if (!existsSync(dir)) return { count: 0, errors: [], eventIds: new Set() };
|
|
125
125
|
const files = readdirSync(dir).filter((name) => name.endsWith('.json')).sort();
|
|
126
126
|
const errors = [];
|
|
127
|
+
const eventIds = new Set();
|
|
127
128
|
for (const name of files) {
|
|
128
129
|
const path = join(dir, name);
|
|
129
130
|
try {
|
|
130
131
|
const event = JSON.parse(readFileSync(path, 'utf8'));
|
|
131
132
|
const validation = validateMemoryEvent(event, projectId ? { projectId } : {});
|
|
132
133
|
if (!validation.ok) errors.push(`${name}: ${validation.errors.join(' ')}`);
|
|
134
|
+
else eventIds.add(event.event_id);
|
|
133
135
|
} catch (error) {
|
|
134
136
|
errors.push(`${name}: JSON inválido: ${error.message}`);
|
|
135
137
|
}
|
|
136
138
|
}
|
|
137
|
-
return { count: files.length, errors };
|
|
139
|
+
return { count: files.length, errors, eventIds };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function checkpointMatchesLedgerPrefix(checkpoint, eventIds, ledgerEvents) {
|
|
143
|
+
if (!checkpoint || typeof checkpoint !== 'object') return false;
|
|
144
|
+
if (!Number.isInteger(checkpoint.revision) || checkpoint.revision < 0) return false;
|
|
145
|
+
if (typeof checkpoint.event_cursor !== 'string' || !checkpoint.event_cursor) return false;
|
|
146
|
+
if (typeof checkpoint.state_hash !== 'string' || !checkpoint.state_hash) return false;
|
|
147
|
+
|
|
148
|
+
const cursorIndex = ledgerEvents.findIndex((event) => event?.event_id === checkpoint.event_cursor);
|
|
149
|
+
if (cursorIndex < 0) return false;
|
|
150
|
+
const prefix = ledgerEvents.slice(0, cursorIndex + 1);
|
|
151
|
+
const prefixIds = new Set(prefix.map((event) => event.event_id));
|
|
152
|
+
if (eventIds.some((eventId) => !prefixIds.has(eventId))) return false;
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
const replay = reduceMemoryEvents(prefix);
|
|
156
|
+
return checkpoint.revision === replay.revision
|
|
157
|
+
&& checkpoint.event_cursor === replay.eventCursor
|
|
158
|
+
&& checkpoint.state_hash === replay.stateHash;
|
|
159
|
+
} catch {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function checkMemoryAttempts(registry, { ledgerEvents = [], outboxEventIds = new Set() } = {}) {
|
|
165
|
+
const failures = [];
|
|
166
|
+
const warnings = [];
|
|
167
|
+
const ledgerEventIds = new Set(ledgerEvents.map((event) => event?.event_id).filter(Boolean));
|
|
168
|
+
const attempts = Object.values(registry?.sessions || {})
|
|
169
|
+
.map((entry) => entry?.last_memory_attempt)
|
|
170
|
+
.filter((attempt) => attempt && typeof attempt === 'object' && attempt.memory_mode === 'v2');
|
|
171
|
+
|
|
172
|
+
for (const attempt of attempts) {
|
|
173
|
+
const state = String(attempt.state || '');
|
|
174
|
+
const disposition = String(attempt.disposition || '');
|
|
175
|
+
const eventIds = Array.isArray(attempt.event_ids)
|
|
176
|
+
? [...new Set(attempt.event_ids.filter((eventId) => typeof eventId === 'string' && eventId))]
|
|
177
|
+
: [];
|
|
178
|
+
|
|
179
|
+
if (state === 'skipped' && disposition === 'ambiguous') {
|
|
180
|
+
failures.push(`Lifecycle de memória v2 ambíguo: Stop pulou a publicação sem identidade causal suficiente. Inspecione com: ${MEMORY_STATUS_COMMAND}.`);
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (state === 'skipped' && ['stale_turn', 'superseded'].includes(disposition)) {
|
|
185
|
+
if (eventIds.length) {
|
|
186
|
+
failures.push(`Stop stale/superseded emitiu event_ids apesar da rejeição causal. Inspecione com: ${MEMORY_STATUS_COMMAND}.`);
|
|
187
|
+
} else {
|
|
188
|
+
warnings.push('Stop stale/superseded foi descartado sem publicar memória.');
|
|
189
|
+
}
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (disposition !== 'applied') {
|
|
194
|
+
if (state === 'skipped') warnings.push('Attempt de memória v2 foi descartado sem publicação.');
|
|
195
|
+
else failures.push(`Attempt de memória v2 possui disposition não reconhecida para o estado informado. Inspecione com: ${MEMORY_STATUS_COMMAND}.`);
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (!eventIds.length) {
|
|
200
|
+
failures.push(`Attempt v2 aplicado não declarou event_ids; publicação perdida. Inspecione com: ${MEMORY_STATUS_COMMAND}.`);
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (state === 'enqueued' || state === 'degraded') {
|
|
205
|
+
const missing = eventIds.filter((eventId) => !ledgerEventIds.has(eventId) && !outboxEventIds.has(eventId));
|
|
206
|
+
if (missing.length) {
|
|
207
|
+
failures.push(`Attempt v2 perdeu ${missing.length} evento(s): ausentes do ledger e da outbox. Inspecione com: ${MEMORY_STATUS_COMMAND}.`);
|
|
208
|
+
} else {
|
|
209
|
+
warnings.push(`Attempt de memória v2 ${state} permanece recuperável: ${eventIds.length} evento(s) durável(is) no ledger e/ou outbox.`);
|
|
210
|
+
}
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (state === 'projected') {
|
|
215
|
+
const outsideLedger = eventIds.filter((eventId) => !ledgerEventIds.has(eventId));
|
|
216
|
+
if (outsideLedger.length) {
|
|
217
|
+
failures.push(`Attempt projetado perdeu ${outsideLedger.length} evento(s) no ledger. Inspecione com: ${MEMORY_STATUS_COMMAND}.`);
|
|
218
|
+
} else if (!checkpointMatchesLedgerPrefix(attempt.checkpoint, eventIds, ledgerEvents)) {
|
|
219
|
+
failures.push(`Checkpoint do attempt projetado diverge do prefixo rederivado do ledger. Inspecione com: ${MEMORY_STATUS_COMMAND}.`);
|
|
220
|
+
}
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
failures.push(`Attempt de memória v2 possui state inválido. Inspecione com: ${MEMORY_STATUS_COMMAND}.`);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return { failures, warnings };
|
|
138
228
|
}
|
|
139
229
|
|
|
140
230
|
/**
|
|
141
231
|
* Read-only consistency check for the local memory-v2 bundle. It intentionally
|
|
142
232
|
* does not acquire MEMORY.lock or invoke the projector/repair paths.
|
|
143
233
|
*/
|
|
144
|
-
export function checkMemoryBundle(vaultBase) {
|
|
234
|
+
export function checkMemoryBundle(vaultBase, { registry } = {}) {
|
|
145
235
|
const brain = join(vaultBase, '.brain');
|
|
146
236
|
const mode = detectMemoryMode(vaultBase);
|
|
147
237
|
if (mode.mode === 'legacy') {
|
|
@@ -207,6 +297,13 @@ export function checkMemoryBundle(vaultBase) {
|
|
|
207
297
|
}
|
|
208
298
|
}
|
|
209
299
|
|
|
300
|
+
const lifecycle = checkMemoryAttempts(registry || readSessionRegistry(vaultBase), {
|
|
301
|
+
ledgerEvents: bundle.ledger?.events || [],
|
|
302
|
+
outboxEventIds: outbox.eventIds,
|
|
303
|
+
});
|
|
304
|
+
failures.push(...lifecycle.failures);
|
|
305
|
+
warnings.push(...lifecycle.warnings);
|
|
306
|
+
|
|
210
307
|
const unresolved = candidates.items.filter((item) => !['resolved', 'rejected', 'superseded'].includes(item?.status));
|
|
211
308
|
const activeConflicts = unresolved.filter((item) => item?.reason === 'conflict');
|
|
212
309
|
const ordinaryCandidates = unresolved.filter((item) => item?.reason !== 'conflict');
|
|
@@ -332,7 +429,7 @@ export function runVaultHealth({ vaultBase, session = '' }) {
|
|
|
332
429
|
];
|
|
333
430
|
let memory = { status: 'legacy', metrics: {} };
|
|
334
431
|
if (memoryMarkers.some((path) => existsSync(path))) {
|
|
335
|
-
memory = checkMemoryBundle(vaultBase);
|
|
432
|
+
memory = checkMemoryBundle(vaultBase, { registry });
|
|
336
433
|
failures.push(...memory.failures.map((item) => `Memória: ${item}`));
|
|
337
434
|
warnings.push(...memory.warnings.map((item) => `Memória: ${item}`));
|
|
338
435
|
} else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wendkeep",
|
|
3
|
-
"version": "0.58.
|
|
3
|
+
"version": "0.58.3",
|
|
4
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": {
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
"src",
|
|
13
13
|
"hooks",
|
|
14
14
|
"schema",
|
|
15
|
+
"docs/pt-BR/commands/*.md",
|
|
16
|
+
"docs/en/commands/*.md",
|
|
15
17
|
"README.md",
|
|
16
18
|
"README.en.md",
|
|
17
19
|
"CHANGELOG.md"
|