pi-weave 0.1.12 → 0.1.13

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 (71) hide show
  1. package/README.md +8 -37
  2. package/package.json +1 -2
  3. package/src/core/concurrency.ts +3 -6
  4. package/src/core/frontmatter.ts +0 -53
  5. package/src/core/graph/build.ts +6 -7
  6. package/src/core/graph/current.ts +2 -4
  7. package/src/core/graph/model.ts +1 -1
  8. package/src/core/graph/wikilinks.ts +3 -3
  9. package/src/core/index.ts +26 -27
  10. package/src/core/paths.ts +0 -7
  11. package/src/core/vault.ts +16 -681
  12. package/src/core/view/detail.ts +1 -1
  13. package/src/core/view/health.ts +1 -1
  14. package/src/core/view/tree.ts +1 -1
  15. package/src/pi/index.ts +6 -85
  16. package/src/pi/summarize.ts +2 -2
  17. package/src/pi/viewer/tui/bodyStore.ts +4 -7
  18. package/src/pi/viewer/tui/branding.ts +7 -148
  19. package/src/pi/viewer/tui/run.ts +3 -17
  20. package/src/pi/viewer/tui/surface/base.ts +24 -3
  21. package/src/pi/viewer/tui/surface/explore.ts +41 -6
  22. package/src/pi/viewer/tui/workspace.ts +23 -351
  23. package/src/pi/viewer/tui/workspaceRoot.ts +31 -172
  24. package/src/pi/viewer/web/run.ts +7 -117
  25. package/src/web/client/api.dom.ts +2 -2
  26. package/src/web/client/api.ts +14 -223
  27. package/src/web/client/bootstrap.ts +5 -14
  28. package/src/web/client/context/context.model.ts +9 -11
  29. package/src/web/client/dist/app.js +93 -219
  30. package/src/web/client/graph/dynamics.ts +5 -65
  31. package/src/web/client/graph/renderer.dom.ts +7 -8
  32. package/src/web/client/graph/renderer.ts +9 -35
  33. package/src/web/client/main.tsx +1 -1
  34. package/src/web/client/note/Note.tsx +21 -63
  35. package/src/web/client/search/SearchPalette.tsx +45 -36
  36. package/src/web/client/search/search.model.ts +33 -454
  37. package/src/web/client/shell/Columns.tsx +13 -83
  38. package/src/web/client/shell/Header.tsx +2 -10
  39. package/src/web/client/shell/Shell.tsx +50 -125
  40. package/src/web/client/shell/StatusBar.tsx +1 -4
  41. package/src/web/client/shell/icons.model.ts +4 -7
  42. package/src/web/client/shell/keys.model.ts +5 -42
  43. package/src/web/client/shell/keys.ts +2 -2
  44. package/src/web/client/shell/shell.model.ts +10 -133
  45. package/src/web/client/shell/theme.model.ts +2 -2
  46. package/src/web/client/shell/theme.ts +33 -157
  47. package/src/web/client/state.ts +9 -89
  48. package/src/web/client/tree/Tree.tsx +25 -575
  49. package/src/web/client/tree/tree.model.ts +8 -162
  50. package/src/web/client/workspace.ts +72 -242
  51. package/src/web/server/page.ts +8 -10
  52. package/src/web/server/routes.ts +30 -563
  53. package/src/web/server/server.ts +6 -145
  54. package/src/web/shared/layout.ts +72 -624
  55. package/src/web/shared/wire.ts +10 -196
  56. package/src/core/sessions.ts +0 -929
  57. package/src/pi/sessionScan.ts +0 -104
  58. package/src/pi/viewer/tui/explorer.ts +0 -586
  59. package/src/web/client/live.model.ts +0 -275
  60. package/src/web/client/live.ts +0 -151
  61. package/src/web/client/note/Editor.tsx +0 -109
  62. package/src/web/client/note/editor.controller.ts +0 -151
  63. package/src/web/client/note/editor.model.ts +0 -686
  64. package/src/web/client/search/search.ts +0 -107
  65. package/src/web/client/shell/Divider.tsx +0 -44
  66. package/src/web/client/shell/cssvars.ts +0 -70
  67. package/src/web/client/shell/drag.model.ts +0 -170
  68. package/src/web/client/shell/layout.model.ts +0 -500
  69. package/src/web/client/shell/viewport.ts +0 -29
  70. package/src/web/server/sse.ts +0 -321
  71. package/src/web/server/watcher.ts +0 -507
package/README.md CHANGED
@@ -27,7 +27,6 @@ It also understands the repository you are working in. Personal notes live in a
27
27
  while maintaining an organized summary above it.
28
28
  - **Knowledge retrieval.** Pi searches existing notes when answering questions about previous decisions, people, projects, or meetings.
29
29
  - **Unified visual workspace.** `/weave-view` presents notes, links, repository structure, and provenance in a live browser interface.
30
- - **Optional session memory.** `/weave-scan sessions` converts prior pi sessions into searchable notes and skips unchanged transcripts.
31
30
  - **Repository exploration.** A lightweight, git-aware index gives Pi a structural overview of the current codebase before it reads files.
32
31
 
33
32
  Nothing is captured silently. pi-weave creates or extends a personal note only when you ask it to.
@@ -98,42 +97,18 @@ Useful requests include:
98
97
 
99
98
  The browser workspace has four connected views:
100
99
 
101
- - **Tree** — notes, folders, session memories, and repository structure. Filter by text or provenance, create folders, drag notes between
102
- folders, and use the context menu to rename or delete.
103
- - **Note** — rendered Markdown with clickable `[[wikilinks]]`, link previews, tags, and authorship. Click the body or press `⌘E` / `Ctrl E`
104
- to edit; save with `⌘S` / `Ctrl S`.
100
+ - **Tree** — notes, folders, and repository structure, with text and provenance filters.
101
+ - **Note** — rendered Markdown with clickable `[[wikilinks]]`, link previews, tags, authorship, and an action to open the source in `$EDITOR`.
105
102
  - **Graph** — a navigable map of notes, links, mentions, modules, and repository relationships. Selecting something updates every view.
106
103
  - **Context** — links, backlinks, tags, and code mentions for the current selection.
107
104
 
108
105
  Search with `⌘K` / `Ctrl K`. Press `?` for all shortcuts. The workspace updates as notes change on disk, so a note written by Pi appears
109
106
  without a reload. It follows the system theme by default and can be switched between light and dark.
110
107
 
111
- The editor checks revisions before saving. If the note changed elsewhere, it asks whether to reload or overwrite instead of silently losing
112
- work. Unknown front-matter fields are preserved, so the same vault remains safe to edit with Obsidian or a text editor.
108
+ The browser is read-only. Edit with `$EDITOR`, Obsidian, or the `weave_note` tool; unknown front-matter fields remain preserved.
113
109
 
114
110
  `/weave-view tui` is the smaller, read-only terminal explorer: tree, focused neighborhood, details, and link health over the same graph.
115
111
 
116
- ## Remember past pi sessions
117
-
118
- ```bash
119
- /weave-scan sessions
120
- ```
121
-
122
- This reads pi’s local session transcripts and creates generated notes under:
123
-
124
- ```text
125
- ~/.okf/notes/sessions/
126
- ```
127
-
128
- Each note records what was asked, what happened, and any open threads. The scan is:
129
-
130
- - **opt-in** — it never runs automatically;
131
- - **incremental** — unchanged transcripts use no model calls;
132
- - **cancellable** — run `/weave-scan-cancel`;
133
- - **repo-independent** — it works from any directory.
134
-
135
- Session summaries use the active pi model and are marked `source: generated`.
136
-
137
112
  ## Repository knowledge
138
113
 
139
114
  Inside a Git repository, pi-weave detects whether `<repo>/.okf/` is missing, fresh, or stale.
@@ -159,8 +134,7 @@ Most people only need natural language and `/weave-view`.
159
134
  | Command | `/weave` | Show vault and repository status |
160
135
  | Command | `/weave-scan` | Build or refresh the repository index |
161
136
  | Command | `/weave-scan deep` | Add incremental model-written file summaries |
162
- | Command | `/weave-scan sessions` | Turn pi session history into vault notes |
163
- | Command | `/weave-scan-cancel` | Stop a deep or session scan |
137
+ | Command | `/weave-scan-cancel` | Stop a deep scan |
164
138
  | Tool | `weave_note` | List, read, add, append, finalize, and search notes |
165
139
  | Tool | `weave_repo` | Check, scan, and summarize the repository index |
166
140
 
@@ -174,8 +148,7 @@ Personal notes are ordinary Markdown files:
174
148
  ~/.okf/
175
149
  └── notes/
176
150
  ├── authentication-migration.md
177
- └── sessions/
178
- └── plan-the-release.md
151
+ └── release-plan.md
179
152
  ```
180
153
 
181
154
  A note has small YAML front matter followed by Markdown:
@@ -205,9 +178,8 @@ We probably want OIDC next quarter…
205
178
 
206
179
  Set `PI_WEAVE_VAULT` to use a different vault location.
207
180
 
208
- Reading, writing, searching, and viewing notes are local operations. Deep repository scans and session summaries send their bounded input to
209
- whichever model you configured in pi. The browser workspace binds only to loopback, uses a per-session token, and shuts down with the pi
210
- session.
181
+ Reading, writing, searching, and viewing notes are local operations. Deep repository scans send bounded input to whichever model you
182
+ configured in pi. The browser workspace binds only to loopback, uses a per-session token, and shuts down with the pi session.
211
183
 
212
184
  The vault format, repository index, and skills are intentionally harness-agnostic. `src/core` contains no pi-specific imports.
213
185
 
@@ -236,8 +208,7 @@ Read [AGENTS.md](AGENTS.md) before contributing. Work on a feature branch; do no
236
208
 
237
209
  - [Design](docs/design.md) — product and architecture
238
210
  - [Notepad skill](skills/weave-notepad/SKILL.md) — capture, narration, and provenance behavior
239
- - [Browser workspace](docs/weave-workspace.md) — UI architecture and security model
240
- - [Session scanning](docs/session-scan.md) — incremental session memory
211
+ - [Historical browser workspace notes](docs/weave-workspace.md) — superseded implementation record
241
212
  - [Repository exploration skill](skills/weave-explore/SKILL.md) — how Pi uses the index
242
213
 
243
214
  ## License
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-weave",
3
- "version": "0.1.12",
3
+ "version": "0.1.13",
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,
@@ -64,7 +64,6 @@
64
64
  "@earendil-works/pi-ai": "^0.84.2",
65
65
  "@earendil-works/pi-coding-agent": "^0.84.2",
66
66
  "@earendil-works/pi-tui": "^0.84.2",
67
- "@preact/signals": "^2.11.1",
68
67
  "@types/d3-force": "^3.0.10",
69
68
  "@types/node": "^24.0.0",
70
69
  "@vitest/coverage-v8": "^3.2.4",
@@ -1,10 +1,7 @@
1
1
  /**
2
- * Bounded-concurrency task runner shared by the deep scan (summaries.ts) and
3
- * the session scan (sessions.ts).
2
+ * Bounded-concurrency task runner shared by repository scans.
4
3
  *
5
- * Extracted from summaries.ts so the two scanners cannot drift: a second copy
6
- * of the scheduler is a second place for an off-by-one or a lost
7
- * cancellation check.
4
+ * Keeping the scheduler here gives every scan the same cancellation behavior.
8
5
  */
9
6
 
10
7
  /**
@@ -33,4 +30,4 @@ export async function mapWithConcurrency<T, R>(
33
30
  }
34
31
  await Promise.all(Array.from({ length: Math.max(1, concurrency) }, worker));
35
32
  return results;
36
- }
33
+ }
@@ -289,59 +289,6 @@ export function parseFrontMatter(text: string): ParsedFrontMatter | null {
289
289
  return { fields, body, lines };
290
290
  }
291
291
 
292
- /**
293
- * Upsert owned scalar fields into a front-matter block, preserving order.
294
- *
295
- * For each wanted key: the **first** line declaring it is replaced with a
296
- * fresh `key: value` line in place, and any later declarations are dropped —
297
- * mirroring how duplicate managed keys collapse in `replayBlock` (the
298
- * subset parser keeps the last occurrence, so collapsing to one line with
299
- * the fresh value is the consistent outcome). A key introducing a block
300
- * construct (`scalar: false`) is replaced too: the value this function writes
301
- * is a scalar, and leaving the old block head in place would orphan its
302
- * indented children under a duplicated key.
303
- *
304
- * Wanted keys the block never declared are appended at the end, in the order
305
- * given. Everything else — unknown keys, blank lines, junk — is carried
306
- * through byte-identically, the same round-trip contract `serializeNote`
307
- * honors for the note engine's writes.
308
- *
309
- * Keys arrive from pi-weave's own generated-note writers (`session_id`,
310
- * `session_hash`, …); there is no escaping for the *key* because a key is
311
- * caller-controlled code, not user input — `quoteField` guards the value.
312
- */
313
- export function upsertFrontMatterFields(
314
- lines: NoteFrontMatter,
315
- fields: Record<string, string>,
316
- ): NoteFrontMatter {
317
- const wanted = new Set(Object.keys(fields));
318
- const out: string[] = [];
319
- const written = new Set<string>();
320
- let inDroppedBlock = false;
321
- for (const line of scanFrontMatter(lines)) {
322
- // Continuation lines of a block construct whose head we replaced: their
323
- // parent key is gone, so carrying them would leave orphaned YAML children
324
- // under a scalar. The block ends at the first non-indented line.
325
- if (inDroppedBlock) {
326
- if (/^\s/.test(line.text)) continue;
327
- inDroppedBlock = false;
328
- }
329
- if (line.key !== null && wanted.has(line.key)) {
330
- if (!written.has(line.key)) {
331
- out.push(`${line.key}: ${quoteField(fields[line.key] ?? "")}`);
332
- written.add(line.key);
333
- if (!line.scalar) inDroppedBlock = true; // swallow the block body too
334
- }
335
- continue; // later duplicates collapse into the first occurrence
336
- }
337
- out.push(line.text);
338
- }
339
- for (const [key, value] of Object.entries(fields)) {
340
- if (!written.has(key)) out.push(`${key}: ${quoteField(value)}`);
341
- }
342
- return out;
343
- }
344
-
345
292
  /**
346
293
  * Parse a note file. Throws on missing/invalid front matter so callers can
347
294
  * treat the file as malformed rather than guessing.
@@ -2,7 +2,7 @@
2
2
  * Pure graph builder: knowledge workspace inputs → GraphModel.
3
3
  *
4
4
  * No I/O, no clock access, no harness imports (design §21,
5
- * docs/weave-view.md §3). Stability contract: identical inputs produce
5
+ * docs/design.md §11). Stability contract: identical inputs produce
6
6
  * byte-identical JSON (ids derive from slugs/paths only; `generatedAt` is
7
7
  * derived from input timestamps, never from the wall clock) — that is what
8
8
  * makes the page's refresh-polling cheap.
@@ -15,7 +15,7 @@ import type { EdgeKind, GraphEdge, GraphModel, GraphNode } from "./model";
15
15
  import { buildPathIndex, resolveMentions, type PathIndex } from "./mentions";
16
16
  import { extractWikilinks } from "./wikilinks";
17
17
 
18
- /** Hard cap on note nodes (docs/weave-view.md M3 guard). */
18
+ /** Hard cap on note nodes so a large vault cannot overwhelm the viewer. */
19
19
  export const DEFAULT_MAX_NOTES = 500;
20
20
 
21
21
  export interface BuildGraphInput {
@@ -133,10 +133,9 @@ function buildVaultSide(
133
133
 
134
134
  const keptSlugs = new Set(kept.map((n) => n.slug));
135
135
 
136
- // Nested notes (`sessions/foo` session memory, docs/session-scan.md) nest
137
- // under synthesized folder nodes so the vault tree groups them the way the
138
- // repository tree groups directories. Ids are prefixed `vfolder:` because a
139
- // repository module could legitimately share the path (`module:sessions`);
136
+ // Nested notes nest under synthesized folder nodes so the vault tree groups
137
+ // them the way the repository tree groups directories. Ids are prefixed
138
+ // `vfolder:` because a repository module could legitimately share the path;
140
139
  // the tree renders any `contains` chain, so the kind reuse needs no client
141
140
  // change. Deterministic: dirs sorted, parents before children.
142
141
  const folderIds = new Map<string, string>();
@@ -320,7 +319,7 @@ function buildRepositorySide(
320
319
 
321
320
  /**
322
321
  * Build the graph model for the viewer. Notes are capped at `maxNotes`
323
- * (docs/weave-view.md M3); the vault node carries a warning when truncated.
322
+ * (docs/design.md §11); the vault node carries a warning when truncated.
324
323
  */
325
324
  export function buildGraph(input: BuildGraphInput, options: { maxNotes?: number } = {}): GraphModel {
326
325
  const maxNotes = options.maxNotes ?? DEFAULT_MAX_NOTES;
@@ -1,6 +1,5 @@
1
1
  /**
2
- * Workspace assembly readers for the viewers (docs/weave-view.md §2,
3
- * weave-view-tui-design §3.1).
2
+ * Workspace assembly readers for the viewers (docs/design.md §11).
4
3
  *
5
4
  * These functions are *workspace assembly*, symmetric to `getWorkspaceStatus`
6
5
  * (already in core): pure fan-out over `core/vault`, `core/repoIndex`,
@@ -92,7 +91,7 @@ export async function readRepositorySide(
92
91
 
93
92
  /**
94
93
  * Assemble the fresh graph from disk. Called on every viewer fetch
95
- * (no caching — docs/weave-view.md §2). Reads the vault (capped at
94
+ * (no caching). Reads the vault (capped at
96
95
  * DEFAULT_MAX_NOTES) and, when cwd is an indexed git repository, the repo
97
96
  * index + deep-scan summary sidecars. Degrades to a vault-only graph when
98
97
  * the repo has no index or the index is corrupt.
@@ -117,4 +116,3 @@ export async function buildCurrentGraph(cwd: string, vaultRoot: string = resolve
117
116
  }
118
117
  return buildGraph(input);
119
118
  }
120
-
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Graph model shared between the builder (core) and the viewer (adapter).
3
- * Pure data — no harness imports (design §21). See docs/weave-view.md §3.
3
+ * Pure data — no harness imports (design §21).
4
4
  */
5
5
 
6
6
  import type { NoteSource, StalenessReport } from "../types";
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Obsidian-compatible [[wiki-link]] extraction from note bodies.
3
- * Pure module see docs/weave-view.md §3.
3
+ * Pure module used by graph construction and viewer view-models.
4
4
  */
5
5
 
6
6
  import { slugify } from "../slug";
@@ -14,8 +14,8 @@ const WIKILINK_RE = /\[\[([^\][|]+)(?:\|[^\]]*)?\]\]/g;
14
14
  * `release-plan`. Duplicates are removed, order of first appearance kept.
15
15
  *
16
16
  * Path separators survive: a nested note's slug is its path relative to
17
- * `notes/` (`sessions/foo`), so `[[sessions/foo]]` targets the session note,
18
- * not a flattened name. Each path segment is slugified independently.
17
+ * `notes/`, so links target the nested note rather than a flattened name.
18
+ * Each path segment is slugified independently.
19
19
  */
20
20
  export function extractWikilinks(body: string): string[] {
21
21
  const out: string[] = [];
package/src/core/index.ts CHANGED
@@ -3,24 +3,32 @@
3
3
  *
4
4
  * NO harness imports allowed in this tree (see docs/design.md §21).
5
5
  */
6
- export * from "./types";
7
- export * from "./slug";
8
- export * from "./frontmatter";
9
- export * from "./languages";
10
- export * from "./mutex";
11
- export * from "./paths";
12
- export * from "./git";
13
- export * from "./vault";
14
- export * from "./repoIndex";
15
- export * from "./summaries";
16
- export * from "./sessions";
17
- export * from "./concurrency";
18
- export * from "./workspace";
19
- export * from "./openInEditor";
20
- export * from "./graph/model";
21
- export * from "./graph/wikilinks";
22
- export * from "./view";
23
- export { buildGraph, dataTimestamp, DEFAULT_MAX_NOTES, type BuildGraphInput } from "./graph/build";
6
+ export type { WorkspaceStatus } from "./types";
7
+ export { NOTES_DIR, repoIndexDir, resolveVaultRoot } from "./paths";
8
+ export { findGitRoot } from "./git";
9
+ export {
10
+ assessStaleness,
11
+ buildRepoIndex,
12
+ readRepoIndex,
13
+ summarizeIndex,
14
+ writeRepoIndex,
15
+ } from "./repoIndex";
16
+ export { runDeepScan, type DeepScanOptions, type DeepScanResult, type SummarizeFn } from "./summaries";
17
+ export {
18
+ addNote,
19
+ appendToNote,
20
+ extractRawTail,
21
+ finalizeNote,
22
+ formatNote,
23
+ formatRawAppend,
24
+ getNote,
25
+ listNotes,
26
+ resolveNotePath,
27
+ searchNotes,
28
+ } from "./vault";
29
+ export { withMutationQueue } from "./mutex";
30
+ export { formatDashboard, formatStatusLine, getWorkspaceStatus } from "./workspace";
31
+ export { WorkspaceCache } from "./cache/workspace";
24
32
  export {
25
33
  buildCurrentGraph,
26
34
  readNoteForView,
@@ -28,12 +36,3 @@ export {
28
36
  readRepositorySide,
29
37
  type ViewNote,
30
38
  } from "./graph/current";
31
- export {
32
- classifyPath,
33
- DEFAULT_STALENESS_TTL_MS,
34
- WorkspaceCache,
35
- type CacheStats,
36
- type InvalidationScope,
37
- type WorkspaceCacheOptions,
38
- type WorkspaceSnapshot,
39
- } from "./cache/workspace";
package/src/core/paths.ts CHANGED
@@ -14,13 +14,6 @@ import { join } from "node:path";
14
14
  export const OKF_DIR = ".okf";
15
15
  export const OKF_MANIFEST = "okf.json";
16
16
  export const NOTES_DIR = "notes";
17
- /**
18
- * Vault collection for generated session memory (docs/session-scan.md).
19
- * A sibling of `notes/` rather than a subdirectory of it: session notes are
20
- * machine-derived memory, not hand-curated knowledge, so they stay out of
21
- * the note graph's flat listing — and out of its slug namespace.
22
- */
23
- export const SESSIONS_DIR = "sessions";
24
17
  export const REPOSITORY_DIR = "repository";
25
18
  export const VAULT_ENV_VAR = "PI_WEAVE_VAULT";
26
19