@ddtcorex/dsh-maestro-memory 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/LICENSE +21 -0
  3. package/README.md +448 -0
  4. package/cordis.patch.yml +13 -0
  5. package/lib/client.js +756 -0
  6. package/lib/index.d.ts +10 -0
  7. package/lib/index.d.ts.map +1 -0
  8. package/lib/index.js +627 -0
  9. package/lib/index.js.map +1 -0
  10. package/lib/memory/batch.d.ts +44 -0
  11. package/lib/memory/batch.d.ts.map +1 -0
  12. package/lib/memory/batch.js +75 -0
  13. package/lib/memory/batch.js.map +1 -0
  14. package/lib/memory/feedback.d.ts +18 -0
  15. package/lib/memory/feedback.d.ts.map +1 -0
  16. package/lib/memory/feedback.js +29 -0
  17. package/lib/memory/feedback.js.map +1 -0
  18. package/lib/memory/store.d.ts +154 -0
  19. package/lib/memory/store.d.ts.map +1 -0
  20. package/lib/memory/store.js +606 -0
  21. package/lib/memory/store.js.map +1 -0
  22. package/lib/migration/cli.d.ts +18 -0
  23. package/lib/migration/cli.d.ts.map +1 -0
  24. package/lib/migration/cli.js +143 -0
  25. package/lib/migration/cli.js.map +1 -0
  26. package/lib/migration/fixture.d.ts +45 -0
  27. package/lib/migration/fixture.d.ts.map +1 -0
  28. package/lib/migration/fixture.js +177 -0
  29. package/lib/migration/fixture.js.map +1 -0
  30. package/lib/migration/service.d.ts +75 -0
  31. package/lib/migration/service.d.ts.map +1 -0
  32. package/lib/migration/service.js +713 -0
  33. package/lib/migration/service.js.map +1 -0
  34. package/lib/prompt/snapshot.d.ts +26 -0
  35. package/lib/prompt/snapshot.d.ts.map +1 -0
  36. package/lib/prompt/snapshot.js +74 -0
  37. package/lib/prompt/snapshot.js.map +1 -0
  38. package/lib/review/queue.d.ts +72 -0
  39. package/lib/review/queue.d.ts.map +1 -0
  40. package/lib/review/queue.js +306 -0
  41. package/lib/review/queue.js.map +1 -0
  42. package/lib/skills-browser.d.ts +67 -0
  43. package/lib/skills-browser.d.ts.map +1 -0
  44. package/lib/skills-browser.js +185 -0
  45. package/lib/skills-browser.js.map +1 -0
  46. package/lib/storage/atomic-store.d.ts +211 -0
  47. package/lib/storage/atomic-store.d.ts.map +1 -0
  48. package/lib/storage/atomic-store.js +582 -0
  49. package/lib/storage/atomic-store.js.map +1 -0
  50. package/lib/storage/layout.d.ts +81 -0
  51. package/lib/storage/layout.d.ts.map +1 -0
  52. package/lib/storage/layout.js +162 -0
  53. package/lib/storage/layout.js.map +1 -0
  54. package/lib/storage/legacy-format.d.ts +104 -0
  55. package/lib/storage/legacy-format.d.ts.map +1 -0
  56. package/lib/storage/legacy-format.js +262 -0
  57. package/lib/storage/legacy-format.js.map +1 -0
  58. package/lib/sync/config.d.ts +22 -0
  59. package/lib/sync/config.d.ts.map +1 -0
  60. package/lib/sync/config.js +57 -0
  61. package/lib/sync/config.js.map +1 -0
  62. package/lib/sync/git.d.ts +97 -0
  63. package/lib/sync/git.d.ts.map +1 -0
  64. package/lib/sync/git.js +282 -0
  65. package/lib/sync/git.js.map +1 -0
  66. package/lib/sync/index.d.ts +6 -0
  67. package/lib/sync/index.d.ts.map +1 -0
  68. package/lib/sync/index.js +6 -0
  69. package/lib/sync/index.js.map +1 -0
  70. package/lib/sync/layout.d.ts +15 -0
  71. package/lib/sync/layout.d.ts.map +1 -0
  72. package/lib/sync/layout.js +33 -0
  73. package/lib/sync/layout.js.map +1 -0
  74. package/lib/sync/merge.d.ts +42 -0
  75. package/lib/sync/merge.d.ts.map +1 -0
  76. package/lib/sync/merge.js +159 -0
  77. package/lib/sync/merge.js.map +1 -0
  78. package/lib/sync/service.d.ts +70 -0
  79. package/lib/sync/service.d.ts.map +1 -0
  80. package/lib/sync/service.js +508 -0
  81. package/lib/sync/service.js.map +1 -0
  82. package/lib/todo/store.d.ts +142 -0
  83. package/lib/todo/store.d.ts.map +1 -0
  84. package/lib/todo/store.js +452 -0
  85. package/lib/todo/store.js.map +1 -0
  86. package/lib/types/client/index.d.ts +7 -0
  87. package/lib/types/client/index.d.ts.map +1 -0
  88. package/package.json +63 -0
  89. package/src/client/index.tsx +994 -0
  90. package/src/host/index.ts +591 -0
  91. package/src/host/memory/batch.ts +107 -0
  92. package/src/host/memory/feedback.ts +36 -0
  93. package/src/host/memory/store.ts +650 -0
  94. package/src/host/migration/cli.ts +139 -0
  95. package/src/host/migration/fixture.ts +218 -0
  96. package/src/host/migration/service.ts +786 -0
  97. package/src/host/prompt/snapshot.ts +95 -0
  98. package/src/host/review/queue.ts +325 -0
  99. package/src/host/skills-browser.ts +191 -0
  100. package/src/host/storage/atomic-store.ts +605 -0
  101. package/src/host/storage/layout.ts +191 -0
  102. package/src/host/storage/legacy-format.ts +308 -0
  103. package/src/host/sync/config.ts +67 -0
  104. package/src/host/sync/git.ts +280 -0
  105. package/src/host/sync/index.ts +5 -0
  106. package/src/host/sync/layout.ts +39 -0
  107. package/src/host/sync/merge.ts +188 -0
  108. package/src/host/sync/service.ts +494 -0
  109. package/src/host/todo/store.ts +480 -0
@@ -0,0 +1,582 @@
1
+ /**
2
+ * Atomic file store for Maestro memory — per-directory lock, temp-write/rename,
3
+ * reread validation, backups, duplicate detection.
4
+ *
5
+ * Each mutation:
6
+ * 1. Acquires a same-directory lock file (.maestro.lock) with stale detection
7
+ * (mtime timeout + pid liveness via kill(pid,0)).
8
+ * 2. Validates the on-disk content round-trips through parseEntries/serializeEntries.
9
+ * Non-canonical files are backed up to `<file>.bak.<timestamp>` before the
10
+ * operation is refused (drift guard).
11
+ * 3. Checks for exact duplicates (ID-stripped) so re-adds are no-ops.
12
+ * 4. Writes to a same-directory temp file (`.<uuid>.tmp`, mode 0o600, flag wx),
13
+ * fsyncs the temp, renames atomically over the target, fsyncs the parent
14
+ * directory on POSIX, then rereads and validates the result.
15
+ * 5. Cleans up the temp file on any failure; the previous file is never
16
+ * clobbered on error.
17
+ *
18
+ * Uses only `node:fs` / `node:fs/promises` atomic primitives: open(wx),
19
+ * writeFile, fsync, rename, unlink. No third-party dependencies.
20
+ *
21
+ * @module storage/atomic-store
22
+ */
23
+ import { createHash, randomUUID } from 'node:crypto';
24
+ import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, statSync, writeFileSync, } from 'node:fs';
25
+ import { mkdir, open, readFile, rename, rm, stat, writeFile, } from 'node:fs/promises';
26
+ import { dirname, join } from 'node:path';
27
+ // ---------------------------------------------------------------------------
28
+ // Delimiter + parse/serialize (pure, no I/O)
29
+ // ---------------------------------------------------------------------------
30
+ /** Entry delimiter, byte-compatible with Hermes MEMORY.md / USER.md. */
31
+ export const ENTRY_DELIMITER = '\n§\n';
32
+ /**
33
+ * Split raw file text into trimmed, non-empty entries.
34
+ * @param text - raw file content
35
+ * @returns entries
36
+ */
37
+ export function parseEntries(text) {
38
+ return text
39
+ .split(ENTRY_DELIMITER)
40
+ .map((e) => e.trim())
41
+ .filter((e) => e.length > 0);
42
+ }
43
+ /**
44
+ * Serialize entries into canonical file text.
45
+ * @param entries - entries to serialize
46
+ * @returns canonical file content (entries joined by delimiter + trailing newline)
47
+ */
48
+ export function serializeEntries(entries) {
49
+ return entries.length === 0 ? '' : entries.join(ENTRY_DELIMITER) + '\n';
50
+ }
51
+ /**
52
+ * Whether raw text is the canonical serialization of its own entries.
53
+ * Blank / whitespace-only text counts as canonical (empty store).
54
+ * @param text - raw file content
55
+ * @returns true when round-tripping preserves the text
56
+ */
57
+ export function isCanonical(text) {
58
+ return text.trim() === '' || serializeEntries(parseEntries(text)) === text;
59
+ }
60
+ // ---------------------------------------------------------------------------
61
+ // Duplicate detection (ID-aware)
62
+ // ---------------------------------------------------------------------------
63
+ /** Regex for cross-device entry ID prefix (space after colon allowed, case-insensitive). */
64
+ const ENTRY_ID_RE = /^\[id:\s*[0-9a-f]{8}\]\s*/i;
65
+ /**
66
+ * Strip the `[id:xxxxxxxx]` prefix from an entry, if present.
67
+ * @param entry - full entry text
68
+ * @returns text without the ID prefix
69
+ */
70
+ export function stripEntryId(entry) {
71
+ return String(entry).replace(ENTRY_ID_RE, '');
72
+ }
73
+ /**
74
+ * Check whether `candidate` already exists in `entries`, comparing after
75
+ * stripping IDs so a re-add with a new random ID is still detected as a
76
+ * duplicate (IDs are random per write, direct includes would never hit).
77
+ * @param entries - existing entries
78
+ * @param candidate - new entry text
79
+ * @returns true when a duplicate exists
80
+ */
81
+ export function isDuplicate(entries, candidate) {
82
+ const needle = stripEntryId(candidate);
83
+ return entries.some((e) => stripEntryId(e) === needle);
84
+ }
85
+ /**
86
+ * Find the exact index of `exact` in `entries`, ID-immune (strip-and-compare).
87
+ * Returns -1 when not found or when multiple hits make the match ambiguous.
88
+ * @param entries - existing entries
89
+ * @param exact - full entry text to find
90
+ * @returns index or -1
91
+ */
92
+ export function findExactIndex(entries, exact) {
93
+ const target = stripEntryId(exact);
94
+ let found = -1;
95
+ for (let i = 0; i < entries.length; i++) {
96
+ if (stripEntryId(entries[i]) === target) {
97
+ if (found !== -1)
98
+ return -1; // ambiguous
99
+ found = i;
100
+ }
101
+ }
102
+ return found;
103
+ }
104
+ // ---------------------------------------------------------------------------
105
+ // Lock (per-directory)
106
+ // ---------------------------------------------------------------------------
107
+ /** A lock file older than this is considered abandoned (stale). */
108
+ export const STALE_LOCK_MS = 10_000;
109
+ /** How long to keep waiting for the lock before failing. */
110
+ export const LOCK_TIMEOUT_MS = 5_000;
111
+ /** Spin interval while waiting for the lock. */
112
+ export const LOCK_RETRY_MS = 25;
113
+ /** Lock file name inside each directory. */
114
+ export const LOCK_FILE = '.maestro.lock';
115
+ /** Directories whose lock this process currently holds (reentrancy guard). */
116
+ const heldLocks = new Set();
117
+ /** Lock file content: pid + timestamp for stale detection. */
118
+ function lockJson() {
119
+ return JSON.stringify({ pid: process.pid, at: Date.now() });
120
+ }
121
+ /**
122
+ * Determine whether a lock is stale (exported for tests): mtime timeout or
123
+ * the pid inside the lock file is no longer alive (killed / power loss ->
124
+ * stale immediately without waiting for timeout).
125
+ * @param lockPath - lock file path
126
+ * @returns true when stale
127
+ */
128
+ export function isStaleLock(lockPath) {
129
+ try {
130
+ const info = statSync(lockPath);
131
+ try {
132
+ const owner = JSON.parse(readFileSync(lockPath, 'utf8'));
133
+ if (typeof owner.pid === 'number') {
134
+ try {
135
+ process.kill(owner.pid, 0);
136
+ return false; // owner alive -> valid
137
+ }
138
+ catch {
139
+ return true; // owner dead
140
+ }
141
+ }
142
+ }
143
+ catch {
144
+ // old-format / unparseable -> fall back to mtime
145
+ }
146
+ return Date.now() - info.mtimeMs > STALE_LOCK_MS;
147
+ }
148
+ catch {
149
+ return false;
150
+ }
151
+ }
152
+ /** Async variant of stale check for the async lock path. */
153
+ export async function isStaleLockAsync(lockPath) {
154
+ try {
155
+ const info = await stat(lockPath);
156
+ try {
157
+ const owner = JSON.parse(await readFile(lockPath, 'utf8'));
158
+ if (typeof owner.pid === 'number') {
159
+ try {
160
+ process.kill(owner.pid, 0);
161
+ return false;
162
+ }
163
+ catch {
164
+ return true;
165
+ }
166
+ }
167
+ }
168
+ catch {
169
+ // fall back to mtime
170
+ }
171
+ return Date.now() - info.mtimeMs > STALE_LOCK_MS;
172
+ }
173
+ catch {
174
+ return false;
175
+ }
176
+ }
177
+ function sleepSync(ms) {
178
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
179
+ }
180
+ function sleep(ms) {
181
+ return new Promise((resolve) => setTimeout(resolve, ms));
182
+ }
183
+ /**
184
+ * Acquire the directory lock synchronously, run `fn`, release.
185
+ * Reentrant within this process (outer section remains exclusive vs others).
186
+ * @param dir - directory whose lock to take
187
+ * @param fn - critical section
188
+ * @returns section result
189
+ */
190
+ export function withLockSync(dir, fn) {
191
+ if (heldLocks.has(dir))
192
+ return fn();
193
+ const lockPath = join(dir, LOCK_FILE);
194
+ mkdirSync(dir, { recursive: true });
195
+ const deadline = Date.now() + LOCK_TIMEOUT_MS;
196
+ for (;;) {
197
+ let acquired = false;
198
+ try {
199
+ const fd = openSync(lockPath, 'wx');
200
+ try {
201
+ writeFileSync(lockPath, lockJson());
202
+ }
203
+ finally {
204
+ closeSync(fd);
205
+ }
206
+ acquired = true;
207
+ }
208
+ catch (error) {
209
+ const code = error.code;
210
+ if (code !== 'EEXIST')
211
+ throw error;
212
+ }
213
+ if (acquired)
214
+ break;
215
+ if (isStaleLock(lockPath))
216
+ rmSync(lockPath, { force: true });
217
+ if (Date.now() >= deadline)
218
+ throw new Error('atomic-store: timed out waiting for directory lock');
219
+ sleepSync(LOCK_RETRY_MS);
220
+ }
221
+ heldLocks.add(dir);
222
+ try {
223
+ return fn();
224
+ }
225
+ finally {
226
+ heldLocks.delete(dir);
227
+ rmSync(lockPath, { force: true });
228
+ }
229
+ }
230
+ /**
231
+ * Acquire the directory lock asynchronously, run `fn`, release.
232
+ * Reentrant within this process.
233
+ * @param dir - directory whose lock to take
234
+ * @param fn - critical section (may be async)
235
+ * @returns section result
236
+ */
237
+ export async function withLock(dir, fn) {
238
+ if (heldLocks.has(dir))
239
+ return await fn();
240
+ const lockPath = join(dir, LOCK_FILE);
241
+ await mkdir(dir, { recursive: true });
242
+ const deadline = Date.now() + LOCK_TIMEOUT_MS;
243
+ for (;;) {
244
+ let acquired = false;
245
+ try {
246
+ const handle = await open(lockPath, 'wx', 0o600);
247
+ try {
248
+ await handle.writeFile(lockJson(), 'utf8');
249
+ }
250
+ finally {
251
+ await handle.close();
252
+ }
253
+ acquired = true;
254
+ }
255
+ catch (error) {
256
+ const code = error.code;
257
+ if (code !== 'EEXIST')
258
+ throw error;
259
+ }
260
+ if (acquired)
261
+ break;
262
+ if (await isStaleLockAsync(lockPath))
263
+ await rm(lockPath, { force: true });
264
+ if (Date.now() >= deadline)
265
+ throw new Error('atomic-store: timed out waiting for directory lock');
266
+ await sleep(LOCK_RETRY_MS);
267
+ }
268
+ heldLocks.add(dir);
269
+ try {
270
+ return await fn();
271
+ }
272
+ finally {
273
+ heldLocks.delete(dir);
274
+ await rm(lockPath, { force: true });
275
+ }
276
+ }
277
+ // ---------------------------------------------------------------------------
278
+ // Backups
279
+ // ---------------------------------------------------------------------------
280
+ /**
281
+ * Create a backup of `filePath` at `<file>.bak.<timestamp>`.
282
+ * Returns the backup path, or null when the source does not exist.
283
+ * @param filePath - file to back up
284
+ * @returns backup path or null
285
+ */
286
+ export function createBackupSync(filePath) {
287
+ if (!existsSync(filePath))
288
+ return null;
289
+ const backupPath = `${filePath}.bak.${Date.now()}`;
290
+ const data = readFileSync(filePath);
291
+ writeFileSync(backupPath, data);
292
+ return backupPath;
293
+ }
294
+ /**
295
+ * Async backup.
296
+ * @param filePath - file to back up
297
+ * @returns backup path or null
298
+ */
299
+ export async function createBackup(filePath) {
300
+ try {
301
+ const data = await readFile(filePath);
302
+ const backupPath = `${filePath}.bak.${Date.now()}`;
303
+ // Create backup atomically; if it exists, bump timestamp
304
+ await writeFile(backupPath, data, { flag: 'wx', mode: 0o600 });
305
+ return backupPath;
306
+ }
307
+ catch (error) {
308
+ const code = error.code;
309
+ if (code === 'ENOENT')
310
+ return null;
311
+ throw error;
312
+ }
313
+ }
314
+ // ---------------------------------------------------------------------------
315
+ // Atomic write: temp-write / rename / reread validation
316
+ // ---------------------------------------------------------------------------
317
+ /** fsync a POSIX directory so a just-renamed entry is crash-durable. */
318
+ /* v8 ignore start -- Windows rejects O_RDONLY directory opens */
319
+ async function fsyncDirectory(dir) {
320
+ if (process.platform === 'win32')
321
+ return;
322
+ const handle = await open(dir, 'r');
323
+ try {
324
+ await handle.sync();
325
+ }
326
+ finally {
327
+ await handle.close();
328
+ }
329
+ }
330
+ function fsyncDirectorySync(dir) {
331
+ if (process.platform === 'win32')
332
+ return;
333
+ const fd = openSync(dir, 'r');
334
+ try {
335
+ fsyncSync(fd);
336
+ }
337
+ finally {
338
+ closeSync(fd);
339
+ }
340
+ }
341
+ /* v8 ignore stop */
342
+ /**
343
+ * Durably replace `filePath` with `content` via same-directory temp file,
344
+ * fsync, atomic rename, directory fsync, and reread validation.
345
+ * @param filePath - absolute target file path
346
+ * @param content - full new file content
347
+ */
348
+ export async function writeAtomic(filePath, content) {
349
+ const dir = dirname(filePath);
350
+ await mkdir(dir, { recursive: true });
351
+ const tmp = join(dir, `.${randomUUID()}.tmp`);
352
+ try {
353
+ const handle = await open(tmp, 'wx', 0o600);
354
+ try {
355
+ await handle.writeFile(content, 'utf8');
356
+ await handle.sync();
357
+ }
358
+ finally {
359
+ await handle.close();
360
+ }
361
+ await rename(tmp, filePath);
362
+ await fsyncDirectory(dir);
363
+ // Reread validation: ensure the file now contains exactly what we wrote.
364
+ const reread = await readFile(filePath, 'utf8');
365
+ if (reread !== content) {
366
+ throw new Error(`atomic-store: reread validation failed for ${filePath} (expected ${content.length} chars, got ${reread.length})`);
367
+ }
368
+ }
369
+ catch (error) {
370
+ await rm(tmp, { force: true });
371
+ throw error;
372
+ }
373
+ }
374
+ /**
375
+ * Synchronous atomic write (same guarantees, blocking).
376
+ * @param filePath - absolute target file path
377
+ * @param content - full new file content
378
+ */
379
+ export function writeAtomicSync(filePath, content) {
380
+ const dir = dirname(filePath);
381
+ mkdirSync(dir, { recursive: true });
382
+ const tmp = join(dir, `.${randomUUID()}.tmp`);
383
+ try {
384
+ const fd = openSync(tmp, 'wx', 0o600);
385
+ try {
386
+ writeFileSync(tmp, content, 'utf8');
387
+ fsyncSync(fd);
388
+ }
389
+ finally {
390
+ closeSync(fd);
391
+ }
392
+ renameSync(tmp, filePath);
393
+ fsyncDirectorySync(dir);
394
+ // Reread validation
395
+ const reread = readFileSync(filePath, 'utf8');
396
+ if (reread !== content) {
397
+ throw new Error(`atomic-store: reread validation failed for ${filePath}`);
398
+ }
399
+ }
400
+ catch (error) {
401
+ rmSync(tmp, { force: true });
402
+ throw error;
403
+ }
404
+ }
405
+ // ---------------------------------------------------------------------------
406
+ // High-level entry helpers (lock + backup + duplicate detection + atomic write)
407
+ // ---------------------------------------------------------------------------
408
+ /**
409
+ * Read entries from `filePath`. Missing file yields [].
410
+ * @param filePath - file to read
411
+ * @returns parsed entries
412
+ */
413
+ export function readEntriesSync(filePath) {
414
+ try {
415
+ return parseEntries(readFileSync(filePath, 'utf8'));
416
+ }
417
+ catch (error) {
418
+ const code = error.code;
419
+ if (code === 'ENOENT')
420
+ return [];
421
+ throw error;
422
+ }
423
+ }
424
+ /**
425
+ * Async read entries.
426
+ * @param filePath - file to read
427
+ * @returns parsed entries
428
+ */
429
+ export async function readEntries(filePath) {
430
+ try {
431
+ return parseEntries(await readFile(filePath, 'utf8'));
432
+ }
433
+ catch (error) {
434
+ const code = error.code;
435
+ if (code === 'ENOENT')
436
+ return [];
437
+ throw error;
438
+ }
439
+ }
440
+ /**
441
+ * Append `entry` to `filePath` atomically, with duplicate detection and
442
+ * drift-guard backup.
443
+ *
444
+ * - Acquires per-directory lock.
445
+ * - Validates existing content is canonical; if not, backs up and throws.
446
+ * - Checks for duplicate (ID-stripped); returns `{duplicate:true}` without writing.
447
+ * - Writes via temp+rename+reread.
448
+ *
449
+ * @param filePath - target file
450
+ * @param entry - entry to append (already trimmed)
451
+ * @returns result
452
+ */
453
+ export async function appendEntryAtomic(filePath, entry) {
454
+ const dir = dirname(filePath);
455
+ return withLock(dir, async () => {
456
+ // Drift guard: read raw and validate
457
+ let raw = '';
458
+ try {
459
+ raw = await readFile(filePath, 'utf8');
460
+ }
461
+ catch (error) {
462
+ const code = error.code;
463
+ if (code !== 'ENOENT')
464
+ throw error;
465
+ raw = '';
466
+ }
467
+ if (raw !== '' && !isCanonical(raw)) {
468
+ const backup = await createBackup(filePath);
469
+ return { ok: false, error: `drift: non-canonical content backed up to ${backup}`, backup: backup ?? undefined };
470
+ }
471
+ const entries = parseEntries(raw);
472
+ if (isDuplicate(entries, entry)) {
473
+ return { ok: true, duplicate: true, entries };
474
+ }
475
+ const next = [...entries, entry];
476
+ const content = serializeEntries(next);
477
+ await writeAtomic(filePath, content);
478
+ return { ok: true, entries: next };
479
+ });
480
+ }
481
+ /**
482
+ * Synchronous variant of appendEntryAtomic.
483
+ * @param filePath - target file
484
+ * @param entry - entry to append
485
+ * @returns result
486
+ */
487
+ export function appendEntryAtomicSync(filePath, entry) {
488
+ const dir = dirname(filePath);
489
+ return withLockSync(dir, () => {
490
+ let raw = '';
491
+ try {
492
+ raw = readFileSync(filePath, 'utf8');
493
+ }
494
+ catch (error) {
495
+ const code = error.code;
496
+ if (code !== 'ENOENT')
497
+ throw error;
498
+ raw = '';
499
+ }
500
+ if (raw !== '' && !isCanonical(raw)) {
501
+ const backup = createBackupSync(filePath);
502
+ return { ok: false, error: `drift: non-canonical content backed up to ${backup}`, backup: backup ?? undefined };
503
+ }
504
+ const entries = parseEntries(raw);
505
+ if (isDuplicate(entries, entry)) {
506
+ return { ok: true, duplicate: true, entries };
507
+ }
508
+ const next = [...entries, entry];
509
+ const content = serializeEntries(next);
510
+ writeAtomicSync(filePath, content);
511
+ return { ok: true, entries: next };
512
+ });
513
+ }
514
+ /**
515
+ * Replace or remove via atomic write with drift guard and backup.
516
+ * Caller provides the next entries array; this validates, backs up on drift, and writes.
517
+ * @param filePath - target file
518
+ * @param nextEntries - complete new entries array
519
+ * @returns result
520
+ */
521
+ export async function writeEntriesAtomic(filePath, nextEntries) {
522
+ const dir = dirname(filePath);
523
+ return withLock(dir, async () => {
524
+ let raw = '';
525
+ try {
526
+ raw = await readFile(filePath, 'utf8');
527
+ }
528
+ catch (error) {
529
+ const code = error.code;
530
+ if (code !== 'ENOENT')
531
+ throw error;
532
+ raw = '';
533
+ }
534
+ if (raw !== '' && !isCanonical(raw)) {
535
+ const backup = await createBackup(filePath);
536
+ return { ok: false, error: `drift: non-canonical content backed up to ${backup}`, backup: backup ?? undefined };
537
+ }
538
+ const content = serializeEntries(nextEntries);
539
+ await writeAtomic(filePath, content);
540
+ return { ok: true, entries: nextEntries };
541
+ });
542
+ }
543
+ /**
544
+ * Synchronous variant.
545
+ * @param filePath - target file
546
+ * @param nextEntries - complete new entries array
547
+ * @returns result
548
+ */
549
+ export function writeEntriesAtomicSync(filePath, nextEntries) {
550
+ const dir = dirname(filePath);
551
+ return withLockSync(dir, () => {
552
+ let raw = '';
553
+ try {
554
+ raw = readFileSync(filePath, 'utf8');
555
+ }
556
+ catch (error) {
557
+ const code = error.code;
558
+ if (code !== 'ENOENT')
559
+ throw error;
560
+ raw = '';
561
+ }
562
+ if (raw !== '' && !isCanonical(raw)) {
563
+ const backup = createBackupSync(filePath);
564
+ return { ok: false, error: `drift: non-canonical content backed up to ${backup}`, backup: backup ?? undefined };
565
+ }
566
+ const content = serializeEntries(nextEntries);
567
+ writeAtomicSync(filePath, content);
568
+ return { ok: true, entries: nextEntries };
569
+ });
570
+ }
571
+ // ---------------------------------------------------------------------------
572
+ // Utility: project hash (re-export for layout consumers)
573
+ // ---------------------------------------------------------------------------
574
+ /**
575
+ * Stable 12-hex project hash for a cwd.
576
+ * @param cwd - working directory
577
+ * @returns 12-char hash
578
+ */
579
+ export function projectHash(cwd) {
580
+ return createHash('sha1').update(cwd).digest('hex').slice(0, 12);
581
+ }
582
+ //# sourceMappingURL=atomic-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atomic-store.js","sourceRoot":"","sources":["../../src/host/storage/atomic-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * storage/layout.ts — path resolution for five memory files, four todo files,
3
+ * archives, metadata, and legacy project hash.
4
+ * Pure, no Cordis import. Uses node:path + node:crypto.
5
+ */
6
+ /** Resolve the canonical memories root: config.memoryDir or ~/.dsh/memories */
7
+ export declare function resolveMemoryRoot(memoryDir: string | null | undefined): string;
8
+ /** 12-hex project hash for a cwd (sha1(cwd).slice(0,12)) */
9
+ export declare function projectHash(cwd: string): string;
10
+ /** Legacy alias — same as projectHash, explicit for spec compliance. */
11
+ export declare const legacyProjectHash: typeof projectHash;
12
+ export declare function globalMemoryPath(root: string): string;
13
+ export declare function userMemoryPath(root: string): string;
14
+ export declare function globalArchivePath(root: string): string;
15
+ export declare function userArchivePath(root: string): string;
16
+ export declare function suggestionsPath(root: string): string;
17
+ export declare function dailyDir(root: string): string;
18
+ export declare function dailyPath(root: string, date: string): string;
19
+ export declare function dailyTodoPath(root: string, date: string): string;
20
+ export declare function projectDir(root: string, cwd: string): string;
21
+ export declare function projectMemoryPath(root: string, cwd: string): string;
22
+ export declare function projectKeyPath(root: string, cwd: string): string;
23
+ export declare function projectKeyArchivePath(root: string, cwd: string): string;
24
+ export declare function projectTodoPath(root: string, cwd: string): string;
25
+ export declare function lifeTodoPath(root: string): string;
26
+ export declare function workTodoPath(root: string): string;
27
+ export declare function maestroMetaDir(root: string): string;
28
+ export declare function schemaPath(root: string): string;
29
+ export declare function journalPath(root: string): string;
30
+ export declare function backupsDir(root: string): string;
31
+ export declare function todoArchivePath(root: string): string;
32
+ export declare function backupManifestPath(root: string, runId: string): string;
33
+ export declare function backupFilesDirPath(root: string, runId: string): string;
34
+ /**
35
+ * Resolve all five memory files at once (pure).
36
+ */
37
+ export declare function allMemoryPaths(root: string, cwd: string, date: string): {
38
+ memory: string;
39
+ user: string;
40
+ daily: string;
41
+ project: string;
42
+ key: string;
43
+ };
44
+ /**
45
+ * Resolve all four todo files at once (pure).
46
+ */
47
+ export declare function allTodoPaths(root: string, cwd: string, date: string): {
48
+ life: string;
49
+ work: string;
50
+ project: string;
51
+ daily: string;
52
+ };
53
+ /**
54
+ * Resolve all archive files at once (pure).
55
+ */
56
+ export declare function allArchivePaths(root: string, cwd: string): {
57
+ memory: string;
58
+ user: string;
59
+ key: string;
60
+ todo: string;
61
+ };
62
+ /**
63
+ * Resolve metadata files at once (pure).
64
+ */
65
+ export declare function allMetadataPaths(root: string, runId?: string): {
66
+ metaDir: string;
67
+ schema: string;
68
+ journal: string;
69
+ backupsDir: string;
70
+ backupManifest: string | undefined;
71
+ backupFilesDir: string | undefined;
72
+ };
73
+ export declare const resolveMemoryFiles: typeof allMemoryPaths;
74
+ export declare const resolveTodoFiles: typeof allTodoPaths;
75
+ export declare const resolveArchiveFiles: typeof allArchivePaths;
76
+ export declare const resolveMetadataFiles: typeof allMetadataPaths;
77
+ export declare const getMemoryPaths: typeof allMemoryPaths;
78
+ export declare const getTodoPaths: typeof allTodoPaths;
79
+ export declare const getArchivePaths: typeof allArchivePaths;
80
+ export declare const getMetadataPaths: typeof allMetadataPaths;
81
+ //# sourceMappingURL=layout.d.ts.map