pi-weave 0.1.14 → 0.1.16

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-weave",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "description": "An agent-native knowledge workspace for your life and your code. Smart notepad + repository exploration, readable by humans and agents alike.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -53,8 +53,6 @@ export interface WorkspaceSnapshot {
53
53
  * caller mutating it would corrupt the next build.
54
54
  */
55
55
  notes: readonly Note[];
56
- /** Exactly the HTML artifacts represented by the graph. */
57
- artifacts: readonly HtmlArtifact[];
58
56
  }
59
57
 
60
58
  /** Cumulative counters, from construction. Callers take deltas. */
@@ -408,7 +406,6 @@ export class WorkspaceCache {
408
406
  const snapshot: WorkspaceSnapshot = {
409
407
  model: buildGraph(input),
410
408
  notes: Object.freeze(kept),
411
- artifacts: Object.freeze(input.artifacts ?? []),
412
409
  };
413
410
  this.lastSnapshot = snapshot;
414
411
  return snapshot;
package/src/core/vault.ts CHANGED
@@ -347,26 +347,51 @@ export async function finalizeNote(
347
347
  });
348
348
  }
349
349
 
350
- async function listNoteFiles(root: string): Promise<string[]> {
350
+ async function listVaultEntries(root: string): Promise<{ files: string[]; folders: string[] }> {
351
351
  const dir = join(root, NOTES_DIR);
352
- const out: string[] = [];
353
- async function walk(prefix: string): Promise<void> {
352
+ const files: string[] = [];
353
+ const folders: string[] = [];
354
+ const seen = new Set<string>();
355
+ async function walk(prefix: string, includeFolders = true): Promise<boolean> {
356
+ const path = prefix.length > 0 ? join(dir, prefix) : dir;
357
+ let realPath: string;
354
358
  let entries;
355
359
  try {
356
- entries = await fs.readdir(prefix.length > 0 ? join(dir, prefix) : dir, { withFileTypes: true });
360
+ realPath = await fs.realpath(path);
361
+ if (seen.has(realPath)) return false;
362
+ seen.add(realPath);
363
+ entries = await fs.readdir(path, { withFileTypes: true });
357
364
  } catch {
358
- return;
365
+ return false;
359
366
  }
360
367
  for (const entry of entries) {
361
- if (entry.isDirectory()) {
362
- await walk(prefix.length > 0 ? `${prefix}/${entry.name}` : entry.name);
363
- } else if (entry.isFile() && isVaultArtifact(entry.name)) {
364
- out.push(prefix.length > 0 ? `${prefix}/${entry.name}` : entry.name);
368
+ const child = prefix.length > 0 ? `${prefix}/${entry.name}` : entry.name;
369
+ let isDirectory = entry.isDirectory();
370
+ let isFile = entry.isFile();
371
+ if (entry.isSymbolicLink()) {
372
+ try {
373
+ const stat = await fs.stat(join(dir, child));
374
+ isDirectory = stat.isDirectory();
375
+ isFile = stat.isFile();
376
+ } catch {
377
+ continue;
378
+ }
379
+ }
380
+ if (isDirectory) {
381
+ const visible = includeFolders && !entry.name.startsWith(".");
382
+ if ((await walk(child, visible)) && visible) folders.push(child);
383
+ } else if (isFile && isVaultArtifact(entry.name)) {
384
+ files.push(child);
365
385
  }
366
386
  }
387
+ return true;
367
388
  }
368
389
  await walk("");
369
- return out.sort();
390
+ return { files: files.sort(), folders: folders.sort() };
391
+ }
392
+
393
+ async function listNoteFiles(root: string): Promise<string[]> {
394
+ return (await listVaultEntries(root)).files;
370
395
  }
371
396
 
372
397
  function isMarkdown(path: string): boolean {
@@ -461,25 +486,7 @@ function byUpdatedDesc(a: { updated: string }, b: { updated: string }): number {
461
486
  }
462
487
 
463
488
  export async function listNoteFolders(root: string): Promise<string[]> {
464
- const dir = join(root, NOTES_DIR);
465
- const out: string[] = [];
466
- async function walk(prefix: string): Promise<void> {
467
- let entries;
468
- try {
469
- entries = await fs.readdir(prefix.length > 0 ? join(dir, prefix) : dir, { withFileTypes: true });
470
- } catch {
471
- return;
472
- }
473
- for (const entry of entries) {
474
- if (entry.isDirectory() && !entry.name.startsWith(".")) {
475
- const folder = prefix.length > 0 ? `${prefix}/${entry.name}` : entry.name;
476
- out.push(folder);
477
- await walk(folder);
478
- }
479
- }
480
- }
481
- await walk("");
482
- return out.sort();
489
+ return (await listVaultEntries(root)).folders;
483
490
  }
484
491
 
485
492
  /**
@@ -508,7 +515,7 @@ export interface VaultSnapshot {
508
515
 
509
516
  /** Read the whole vault in one pass: one readdir, one read per note. */
510
517
  export async function readVault(root: string): Promise<VaultSnapshot> {
511
- const [files, folders] = await Promise.all([listNoteFiles(root), listNoteFolders(root)]);
518
+ const { files, folders } = await listVaultEntries(root);
512
519
  const notes: Note[] = [];
513
520
  const artifacts: HtmlArtifact[] = [];
514
521
  for (const file of files) {