wendkeep 0.59.0 → 0.60.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.
@@ -1,326 +1,2 @@
1
- import { randomUUID } from 'node:crypto';
2
- import {
3
- existsSync,
4
- mkdirSync,
5
- readFileSync,
6
- renameSync,
7
- statSync,
8
- writeFileSync,
9
- } from 'node:fs';
10
- import { basename, dirname, isAbsolute, join, parse, relative, resolve } from 'node:path';
11
-
12
- export const PROJECT_CONFIG_FILE = '.wendkeep.json';
13
- export const PROJECT_MARKER_REL = '.brain/PROJECT.json';
14
- export const PROJECT_CONFIG_SCHEMA = 1;
15
-
16
- const PROJECT_VAULT_INTEGRITY_CODES = new Set([
17
- 'WENDKEEP_VAULT_CONFIG_INVALID',
18
- 'WENDKEEP_VAULT_MARKER_MISSING',
19
- 'WENDKEEP_VAULT_PROJECT_MISMATCH',
20
- ]);
21
-
22
- export function isProjectVaultIntegrityError(error) {
23
- return PROJECT_VAULT_INTEGRITY_CODES.has(error?.code);
24
- }
25
-
26
- function json(path) {
27
- try { return JSON.parse(readFileSync(path, 'utf8')); }
28
- catch (error) {
29
- const wrapped = new Error(`Configuração WendKeep inválida em "${path}": ${error.message}`);
30
- wrapped.code = 'WENDKEEP_VAULT_CONFIG_INVALID';
31
- throw wrapped;
32
- }
33
- }
34
-
35
- function atomicJson(path, value) {
36
- mkdirSync(dirname(path), { recursive: true });
37
- const content = `${JSON.stringify(value, null, 2)}\n`;
38
- if (existsSync(path) && readFileSync(path, 'utf8') === content) return false;
39
- const temp = `${path}.${process.pid}.${Date.now()}.tmp`;
40
- writeFileSync(temp, content, 'utf8');
41
- renameSync(temp, path);
42
- return true;
43
- }
44
-
45
- function startDirectory(value) {
46
- const candidate = resolve(String(value || process.cwd()));
47
- try { return statSync(candidate).isFile() ? dirname(candidate) : candidate; }
48
- catch { return candidate; }
49
- }
50
-
51
- function walkParents(start) {
52
- const result = [];
53
- let current = startDirectory(start);
54
- const root = parse(current).root;
55
- while (true) {
56
- result.push(current);
57
- if (current === root) break;
58
- const parent = dirname(current);
59
- if (parent === current) break;
60
- current = parent;
61
- }
62
- return result;
63
- }
64
-
65
- function inputStart(input = {}, fallback = '') {
66
- return input.cwd
67
- || input.project_dir
68
- || input.projectDir
69
- || input.workspace?.cwd
70
- || process.env.CLAUDE_PROJECT_DIR
71
- || fallback
72
- || process.cwd();
73
- }
74
-
75
- function bindingDiagnostic(error) {
76
- return {
77
- code: error?.code || 'WENDKEEP_VAULT_CONFIG_INVALID',
78
- message: error?.message || 'Configuração WendKeep inválida.',
79
- };
80
- }
81
-
82
- function vaultFromConfig(projectRoot, config) {
83
- const valid = config
84
- && typeof config === 'object'
85
- && !Array.isArray(config)
86
- && config.schemaVersion === PROJECT_CONFIG_SCHEMA
87
- && typeof config.projectId === 'string'
88
- && config.projectId.trim()
89
- && typeof config.vault === 'string'
90
- && config.vault.trim();
91
- if (!valid) {
92
- const error = new Error(
93
- `Configuração incompleta em "${join(projectRoot, PROJECT_CONFIG_FILE)}". `
94
- + 'Rode `wendkeep init --project <path> --vault <path>`.',
95
- );
96
- error.code = 'WENDKEEP_VAULT_CONFIG_INVALID';
97
- throw error;
98
- }
99
- return isAbsolute(config.vault) ? resolve(config.vault) : resolve(projectRoot, config.vault);
100
- }
101
-
102
- export function readProjectBinding(projectRoot) {
103
- const root = resolve(projectRoot);
104
- const path = join(root, PROJECT_CONFIG_FILE);
105
- if (!existsSync(path)) return null;
106
- const config = json(path);
107
- return { config, configPath: path, projectRoot: root, base: vaultFromConfig(root, config) };
108
- }
109
-
110
- export function findProjectBinding(start) {
111
- for (const projectRoot of walkParents(start)) {
112
- const found = readProjectBinding(projectRoot);
113
- if (found) return found;
114
- }
115
- return null;
116
- }
117
-
118
- export function findLegacyProjectVault(start) {
119
- for (const projectRoot of walkParents(start)) {
120
- const settingsPath = join(projectRoot, '.claude', 'settings.json');
121
- if (!existsSync(settingsPath)) continue;
122
- try {
123
- const settings = JSON.parse(readFileSync(settingsPath, 'utf8'));
124
- const raw = settings?.env?.OBSIDIAN_VAULT_PATH;
125
- if (typeof raw === 'string' && raw.trim()) {
126
- return {
127
- base: isAbsolute(raw) ? resolve(raw) : resolve(projectRoot, raw),
128
- projectRoot,
129
- source: 'legacy-project-settings',
130
- configPath: settingsPath,
131
- projectId: '',
132
- config: null,
133
- };
134
- }
135
- } catch (error) {
136
- const wrapped = new Error(`Configuração WendKeep legada inválida em "${settingsPath}": ${error.message}`);
137
- wrapped.code = 'WENDKEEP_VAULT_CONFIG_INVALID';
138
- throw wrapped;
139
- }
140
- }
141
- return null;
142
- }
143
-
144
- export function readVaultMarker(vaultPath) {
145
- const markerPath = join(resolve(vaultPath), ...PROJECT_MARKER_REL.split('/'));
146
- if (!existsSync(markerPath)) return null;
147
- return { marker: json(markerPath), markerPath };
148
- }
149
-
150
- function validateMarker(result) {
151
- const found = readVaultMarker(result.base);
152
- if (!found) {
153
- const error = new Error(
154
- `O vault "${result.base}" ainda não possui ${PROJECT_MARKER_REL}. `
155
- + `Rode \`wendkeep init --project "${result.projectRoot}" --vault "${result.base}" --yes\`.`,
156
- );
157
- error.code = 'WENDKEEP_VAULT_MARKER_MISSING';
158
- throw error;
159
- }
160
- if (found.marker?.projectId !== result.projectId) {
161
- const error = new Error(
162
- `Vault de outro projeto: configuração "${result.projectId}" aponta para marcador `
163
- + `"${found.marker?.projectId || 'ausente'}" em "${found.markerPath}".`,
164
- );
165
- error.code = 'WENDKEEP_VAULT_PROJECT_MISMATCH';
166
- throw error;
167
- }
168
- }
169
-
170
- export function resolveProjectVault({
171
- input = {},
172
- startDir = '',
173
- explicitVault = '',
174
- allowLegacySettings = true,
175
- validateIdentity = true,
176
- } = {}) {
177
- const start = inputStart(input, startDir);
178
- const explicit = explicitVault || input?.obsidian_vault_path;
179
- if (explicit) {
180
- let bindingError = null;
181
- try { findProjectBinding(start); }
182
- catch (error) {
183
- if (error?.code !== 'WENDKEEP_VAULT_CONFIG_INVALID') throw error;
184
- bindingError = bindingDiagnostic(error);
185
- }
186
- return {
187
- base: isAbsolute(explicit) ? resolve(explicit) : resolve(startDirectory(start), explicit),
188
- source: explicitVault ? 'explicit' : 'payload',
189
- projectRoot: startDirectory(start),
190
- projectId: '',
191
- configPath: '',
192
- config: null,
193
- ...(bindingError ? { bindingError } : {}),
194
- };
195
- }
196
-
197
- let binding = null;
198
- let bindingFailure = null;
199
- try { binding = findProjectBinding(start); }
200
- catch (error) {
201
- if (error?.code !== 'WENDKEEP_VAULT_CONFIG_INVALID') throw error;
202
- bindingFailure = error;
203
- }
204
- if (binding) {
205
- const result = {
206
- base: binding.base,
207
- source: 'project-config',
208
- projectRoot: binding.projectRoot,
209
- projectId: binding.config.projectId,
210
- configPath: binding.configPath,
211
- config: binding.config,
212
- };
213
- if (validateIdentity) validateMarker(result);
214
- return result;
215
- }
216
-
217
- if (allowLegacySettings) {
218
- const legacy = findLegacyProjectVault(start);
219
- if (legacy) {
220
- return {
221
- ...legacy,
222
- ...(bindingFailure ? { bindingError: bindingDiagnostic(bindingFailure) } : {}),
223
- };
224
- }
225
- }
226
-
227
- if (bindingFailure) throw bindingFailure;
228
-
229
- const error = new Error(
230
- `Nenhum vault WendKeep vinculado ao projeto em "${startDirectory(start)}". `
231
- + `Crie ${PROJECT_CONFIG_FILE} com \`wendkeep init --project "${startDirectory(start)}" --vault <path> --yes\`.`,
232
- );
233
- error.code = 'WENDKEEP_VAULT_UNCONFIGURED';
234
- throw error;
235
- }
236
-
237
- function portableVaultPath(projectRoot, vaultPath) {
238
- const rel = relative(projectRoot, vaultPath);
239
- if (rel && !rel.startsWith('..') && !isAbsolute(rel)) return rel.replaceAll('\\', '/');
240
- return vaultPath;
241
- }
242
-
243
- function objectRecord(value) {
244
- return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
245
- }
246
-
247
- export function bindProjectVault({ projectRoot, vaultPath, configPatch = {} }) {
248
- const root = resolve(projectRoot);
249
- const base = isAbsolute(vaultPath) ? resolve(vaultPath) : resolve(root, vaultPath);
250
- const existing = readProjectBinding(root);
251
- const existingMarker = readVaultMarker(base);
252
- const projectId = existing?.config?.projectId || existingMarker?.marker?.projectId || randomUUID();
253
-
254
- if (existingMarker?.marker?.projectId && existingMarker.marker.projectId !== projectId) {
255
- const error = new Error(
256
- `Não é seguro vincular "${root}" ao vault de outro projeto: `
257
- + `esperado "${projectId}", encontrado "${existingMarker.marker.projectId}".`,
258
- );
259
- error.code = 'WENDKEEP_VAULT_PROJECT_MISMATCH';
260
- throw error;
261
- }
262
-
263
- mkdirSync(join(base, '.brain'), { recursive: true });
264
- const previousConfig = objectRecord(existing?.config);
265
- const patch = objectRecord(configPatch);
266
- const config = {
267
- ...previousConfig,
268
- ...patch,
269
- schemaVersion: PROJECT_CONFIG_SCHEMA,
270
- projectId,
271
- vault: portableVaultPath(root, base),
272
- };
273
- if (previousConfig.harness || patch.harness) {
274
- config.harness = {
275
- ...objectRecord(previousConfig.harness),
276
- ...objectRecord(patch.harness),
277
- };
278
- }
279
- const marker = {
280
- ...objectRecord(existingMarker?.marker),
281
- schemaVersion: PROJECT_CONFIG_SCHEMA,
282
- projectId,
283
- projectName: basename(root),
284
- };
285
- atomicJson(join(base, ...PROJECT_MARKER_REL.split('/')), marker);
286
- atomicJson(join(root, PROJECT_CONFIG_FILE), config);
287
- return { base, projectRoot: root, projectId, config, marker };
288
- }
289
-
290
- export function updateProjectBinding(projectRoot, updater) {
291
- const binding = readProjectBinding(projectRoot);
292
- if (!binding) {
293
- const error = new Error(
294
- `Nenhum binding WendKeep em "${resolve(projectRoot)}". Rode \`wendkeep init\` primeiro.`,
295
- );
296
- error.code = 'WENDKEEP_VAULT_UNCONFIGURED';
297
- throw error;
298
- }
299
- if (typeof updater !== 'function') {
300
- throw new TypeError('updateProjectBinding exige uma função updater.');
301
- }
302
-
303
- const current = {
304
- ...binding.config,
305
- ...(binding.config.harness ? { harness: { ...objectRecord(binding.config.harness) } } : {}),
306
- };
307
- const candidate = updater(current);
308
- if (!candidate || typeof candidate !== 'object' || Array.isArray(candidate)) {
309
- const error = new Error('Updater do binding WendKeep deve retornar um objeto de configuração.');
310
- error.code = 'WENDKEEP_VAULT_CONFIG_INVALID';
311
- throw error;
312
- }
313
-
314
- const config = {
315
- ...candidate,
316
- schemaVersion: PROJECT_CONFIG_SCHEMA,
317
- projectId: binding.config.projectId,
318
- vault: binding.config.vault,
319
- };
320
- atomicJson(binding.configPath, config);
321
- return {
322
- ...binding,
323
- config,
324
- base: vaultFromConfig(binding.projectRoot, config),
325
- };
326
- }
1
+ // Compatibility facade during the physical package migration.
2
+ export * from '../packages/vault/src/project-vault.mjs';