pi-weave 0.3.8 → 0.3.10

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 (80) hide show
  1. package/README.md +21 -17
  2. package/package.json +4 -3
  3. package/skills/weave-explore/SKILL.md +3 -5
  4. package/skills/weave-notepad/SKILL.md +22 -2
  5. package/skills/weave-notepad/references/second-thoughts.md +57 -0
  6. package/src/core/graph/build.ts +1 -1
  7. package/src/core/graph/current.ts +10 -8
  8. package/src/core/history.ts +417 -0
  9. package/src/core/index.ts +19 -0
  10. package/src/core/links/repair.ts +5 -68
  11. package/src/core/noteAction.ts +72 -2
  12. package/src/core/openInEditor.ts +3 -8
  13. package/src/core/repoAction.ts +20 -61
  14. package/src/core/repoIndex.ts +38 -0
  15. package/src/core/trail.ts +124 -0
  16. package/src/core/types.ts +1 -3
  17. package/src/core/vault.ts +116 -29
  18. package/src/core/view/focus.ts +2 -2
  19. package/src/core/view/index.ts +2 -3
  20. package/src/core/view/rawTail.ts +153 -0
  21. package/src/core/workspace.ts +20 -26
  22. package/src/core/workspaceCommands.ts +14 -14
  23. package/src/opencode/tools.ts +3 -3
  24. package/src/opencode/tui.ts +1 -7
  25. package/src/pi/index.ts +68 -80
  26. package/src/pi/tools/noteTool.ts +1 -1
  27. package/src/pi/tools/repoTool.ts +3 -6
  28. package/src/pi/viewer/web/run.ts +6 -8
  29. package/src/web/client/api.ts +103 -5
  30. package/src/web/client/context/context.model.ts +5 -0
  31. package/src/web/client/dist/app.js +499 -129
  32. package/src/web/client/graph/Graph.tsx +166 -14
  33. package/src/web/client/graph/column.model.ts +59 -2
  34. package/src/web/client/graph/graph.model.ts +183 -7
  35. package/src/web/client/graph/renderer.ts +39 -9
  36. package/src/web/client/graph/spotlight.model.ts +472 -0
  37. package/src/web/client/note/Note.tsx +370 -66
  38. package/src/web/client/note/Replay.tsx +139 -0
  39. package/src/web/client/note/drafts.ts +115 -11
  40. package/src/web/client/note/editbar.model.ts +153 -0
  41. package/src/web/client/note/note.model.ts +82 -1
  42. package/src/web/client/note/replay.model.ts +248 -0
  43. package/src/web/client/search/SearchPalette.tsx +103 -34
  44. package/src/web/client/search/search.model.ts +533 -26
  45. package/src/web/client/shell/ContextRail.tsx +7 -0
  46. package/src/web/client/shell/Settings.tsx +21 -1
  47. package/src/web/client/shell/Shell.tsx +93 -27
  48. package/src/web/client/shell/StatusBar.tsx +1 -0
  49. package/src/web/client/shell/icons.model.ts +68 -1
  50. package/src/web/client/shell/keys.model.ts +31 -8
  51. package/src/web/client/shell/shell.model.ts +48 -1
  52. package/src/web/client/shell/theme.model.ts +3 -0
  53. package/src/web/client/shell/theme.ts +354 -32
  54. package/src/web/client/shell/workspace.css.ts +40 -9
  55. package/src/web/client/trail/TrailStrip.tsx +55 -0
  56. package/src/web/client/trail/trail.model.ts +195 -0
  57. package/src/web/client/trail/trail.ts +45 -0
  58. package/src/web/client/tree/Tree.tsx +7 -3
  59. package/src/web/client/tree/tree.model.ts +2 -5
  60. package/src/web/server/page.ts +1 -1
  61. package/src/web/server/routes.ts +83 -3
  62. package/src/web/shared/logo.ts +1 -1
  63. package/src/web/shared/themes.ts +1 -1
  64. package/src/web/shared/view.ts +10 -4
  65. package/src/web/shared/wire.ts +74 -1
  66. package/src/web/shared/workspace.ts +10 -2
  67. package/src/core/view/health.ts +0 -160
  68. package/src/pi/viewer/tui/bodyStore.ts +0 -95
  69. package/src/pi/viewer/tui/branding.ts +0 -14
  70. package/src/pi/viewer/tui/model.ts +0 -390
  71. package/src/pi/viewer/tui/openNote.ts +0 -11
  72. package/src/pi/viewer/tui/run.ts +0 -71
  73. package/src/pi/viewer/tui/surface/base.ts +0 -203
  74. package/src/pi/viewer/tui/surface/detail.ts +0 -315
  75. package/src/pi/viewer/tui/surface/explore.ts +0 -241
  76. package/src/pi/viewer/tui/surface/focus.ts +0 -139
  77. package/src/pi/viewer/tui/surface/health.ts +0 -102
  78. package/src/pi/viewer/tui/theme.ts +0 -90
  79. package/src/pi/viewer/tui/workspace.ts +0 -53
  80. package/src/pi/viewer/tui/workspaceRoot.ts +0 -389
package/README.md CHANGED
@@ -113,7 +113,7 @@ You: Start a note for this interview. I’m going to narrate; keep my words
113
113
 
114
114
  For each chunk, the agent:
115
115
 
116
- 1. appends your words unchanged to the note’s `## Raw` tail;
116
+ 1. appends your words unchanged to the note’s `## Raw` tail, starting with the very first chunk;
117
117
  2. refreshes the structured summary above it;
118
118
  3. leaves the raw record untouched.
119
119
 
@@ -152,20 +152,25 @@ Renaming or moving a note rewrites its inbound links automatically, so its backl
152
152
 
153
153
  ```bash
154
154
  /weave-view # open the browser workspace
155
- /weave-view --no-open # start it and print the URL
156
- /weave-view tui # Pi only: terminal UI for SSH or browser-free use
155
+ /weave-view --no-open # start it and print the URL (handy over SSH)
157
156
  ```
158
157
 
159
158
  Browse and search your notes, follow links, or explore the graph. Use tabs and split panes to keep related notes side by side.
160
159
  Notes update within a couple of seconds as your agent writes them.
160
+ While your agent works, the notes it searches, reads, and writes light up in the tree and graph, and fade after about a minute.
161
+ The **This turn** list in the Context card shows those steps in order; click an earlier turn to replay it. Context floats over the page's
162
+ right margin: fold it with `]`, or pin it to dock it as a column.
163
+ In search, `Tab` switches between notes, code, and tags, and `⇧↵` lights every result up in the graph.
161
164
 
162
165
  Ask your agent to write, organize, and connect notes, or click **New note** on an empty tab to start one yourself.
163
- Click **Edit** to change Markdown and `⌘S` / `Ctrl S` to save. Your original words in `## Raw` stay intact.
166
+ Click **Edit** to change Markdown. Edits save themselves after a short pause (or `⌘S` / `Ctrl S` right away), and **Done** closes the
167
+ editor. Your original words in `## Raw` stay intact.
164
168
  Tabs, layout, and settings restore when you return and survive package updates for the same vault and repository;
165
169
  unsaved drafts last only while the browser stays open.
166
170
 
167
171
  Use **Settings** for themes, fonts, layout, and a manual vault ZIP backup. Press `?` for keyboard shortcuts.
168
- `/weave-view tui` opens a smaller, read-only terminal explorer in Pi.
172
+
173
+ Every change to a note is saved to `<vault>/history/`, whether you, your agent, or another editor made it. Press **History** on a note and drag the slider (or press play) to watch it being written, Raw tail included. Turn this off in **Settings → Editor → Note history**.
169
174
 
170
175
  ## Remember sessions
171
176
 
@@ -182,18 +187,18 @@ technical takeaways, works outside Git repositories, and can be stopped with `/w
182
187
 
183
188
  ## Repository knowledge
184
189
 
185
- Inside a Git repository, pi-weave detects whether `<repo>/.okf/` is missing, fresh, or stale.
190
+ Inside a Git repository, `/weave-scan` asks your session model for a short summary of each file, so you and your agent can find your way
191
+ around without reading everything. It only revisits files whose content changed since the last run.
186
192
 
187
193
  ```bash
188
- /weave-scan # fast structural index
189
- /weave-scan deep # also summarize changed files with the active model
194
+ /weave-scan # summarize changed files with the active model
190
195
  ```
191
196
 
192
- The light index covers languages, packages, modules, entry points, and Git state. A deep scan adds short per-file summaries and only
193
- revisits files whose content changed.
197
+ pi-weave also keeps a small structural index of the repository (languages, packages, modules, entry points, Git state). It feeds the graph
198
+ and links notes to the files they mention. You never build it by hand: it is refreshed automatically whenever something needs it.
194
199
 
195
- The repository index is a cache, not a source of truth. Delete `.okf`, scan again, and nothing important is lost. pi-weave excludes it
196
- locally from Git by default.
200
+ Everything under `<repo>/.okf/` is a cache, not a source of truth. Delete it and nothing important is lost. pi-weave excludes it locally
201
+ from Git by default.
197
202
 
198
203
  ## Commands and tools
199
204
 
@@ -201,14 +206,13 @@ Most people only need natural language and `/weave-view`.
201
206
 
202
207
  | Surface | Name | Purpose |
203
208
  |---|---|---|
204
- | Command | `/weave-view` | Open the browser workspace (`tui` is a Pi-only argument) |
209
+ | Command | `/weave-view` | Open the browser workspace |
205
210
  | Command | `/weave` | Show vault and repository status |
206
- | Command | `/weave-scan` | Build or refresh the repository index |
207
- | Command | `/weave-scan deep` | Add incremental model-written file summaries |
211
+ | Command | `/weave-scan` | Add incremental model-written file summaries |
208
212
  | Command | `/weave-scan sessions [path]` | Turn changed session-history files into durable memory notes |
209
- | Command | `/weave-scan-cancel` | Stop a deep or session scan |
213
+ | Command | `/weave-scan-cancel` | Stop a running scan |
210
214
  | Tool | `weave_note` | List, read, add, append, finalize, and search notes |
211
- | Tool | `weave_repo` | Check, scan, and summarize the repository index |
215
+ | Tool | `weave_repo` | Describe the repository structure for the agent |
212
216
 
213
217
  The included `weave-notepad` and `weave-explore` skills teach Pi and OpenCode when and how to use these tools. In OpenCode, `/weave` shows
214
218
  vault/repository status and scan progress appears in toasts; Pi keeps its persistent status line. V1 slash commands use its standard prompt
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-weave",
3
- "version": "0.3.8",
3
+ "version": "0.3.10",
4
4
  "description": "Local-first Markdown memory and knowledge graph for Pi and OpenCode: preserve decisions, recall sessions, and explore notes with code.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -59,6 +59,7 @@
59
59
  "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.web.json",
60
60
  "build:web": "node scripts/build-web.mjs",
61
61
  "build:web:check": "node scripts/build-web.mjs --check",
62
+ "e2e": "vitest run --config vitest.e2e.config.ts",
62
63
  "check": "npm run typecheck && npm run build:web:check && npm run coverage",
63
64
  "eval:notepad": "node scripts/eval-notepad-search.mjs",
64
65
  "rewrap:md": "node scripts/rewrap-md.mjs",
@@ -68,14 +69,13 @@
68
69
  "peerDependencies": {
69
70
  "@earendil-works/pi-ai": "*",
70
71
  "@earendil-works/pi-coding-agent": "*",
71
- "@earendil-works/pi-tui": "*",
72
72
  "typebox": "*"
73
73
  },
74
74
  "devDependencies": {
75
75
  "@earendil-works/pi-ai": "^0.84.2",
76
76
  "@earendil-works/pi-coding-agent": "^0.84.2",
77
- "@earendil-works/pi-tui": "^0.84.2",
78
77
  "@opencode-ai/plugin": "1.18.29",
78
+ "@opencode-ai/sdk": "1.18.29",
79
79
  "@opencode/plugin": "^2.0.19",
80
80
  "@types/d3-force": "^3.0.10",
81
81
  "@types/node": "^24.0.0",
@@ -86,6 +86,7 @@
86
86
  "graphology": "^0.26.0",
87
87
  "graphology-types": "^0.24.8",
88
88
  "marked": "^18.0.10",
89
+ "playwright-core": "1.56.1",
89
90
  "preact": "^10.29.8",
90
91
  "sigma": "^3.0.3",
91
92
  "typebox": "1.3.7",
@@ -9,17 +9,15 @@ Use `weave_repo`. Without the tool, read `<repo>/.okf/` directly. The index is a
9
9
 
10
10
  ## Workflow
11
11
 
12
- 1. Check `action=status`: if missing, offer a scan or scan when exploration was requested; if stale, rescan; if fresh, read
13
- `action=overview`.
12
+ 1. Read `action=overview`. It builds or refreshes the structure index itself, so there is nothing to scan first.
14
13
  2. Use the overview's languages, packages, modules, and entry points to select relevant paths instead of listing the whole repository.
15
14
  3. Read available `.okf/repository/summaries/` sidecars before opening full files.
16
15
  4. Inspect the relevant code and cite indexed paths in your answer.
17
16
 
18
17
  ## Commands
19
18
 
20
- - `/weave-scan` refreshes the index (`weave_repo` action=scan).
21
- - `/weave-scan deep` generates file summaries using the session model. Offer it when summaries are missing or stale; never run it
22
- implicitly. Only changed content is summarized again.
19
+ - `/weave-scan` generates file summaries using the session model. Offer it when summaries would help and the overview shows none;
20
+ never run it implicitly. Only changed content is summarized again.
23
21
  - `/weave-view` opens the browser workspace.
24
22
  - `/weave-scan sessions [path]` writes session memories to the vault, not the repository index; see `weave-notepad`.
25
23
 
@@ -26,7 +26,8 @@ Use `weave_note`. Without the tool, edit the files directly (`PI_WEAVE_VAULT` ov
26
26
 
27
27
  During live dictation or interviews, keep the organized note current after every append; do not wait until the session ends:
28
28
 
29
- 1. `append` with `raw: true` preserves the user's words verbatim under `## Raw`. Never silently reword dictation.
29
+ 1. Start the note with `add` and `raw: true`, so its first words land in `## Raw` too. Then `append` each chunk with `raw: true`.
30
+ Never silently reword dictation.
30
31
  2. Immediately `finalize` the body above the tail: TLDR, sections, decisions, questions, tasks, entities, and links reflecting
31
32
  everything said so far.
32
33
 
@@ -36,7 +37,7 @@ Outside dictation, finalize only on request.
36
37
 
37
38
  - Keep an append-only raw section at the bottom: `---`, then `## Raw`, then the notice shown below.
38
39
  - Fence verbatim input in code blocks. Prepend each subsequent block with `<!-- appended YYYY-MM-DD HH:MM -->`.
39
- - Use `append` with `raw: true` when available; it maintains this format and creates the tail if missing. Do not hand-format it.
40
+ - Use `add` or `append` with `raw: true` when available; they maintain this format and create the tail if missing. Do not hand-format it.
40
41
  - `finalize` changes only the body above the tail. Never rewrite, remove, or move words out of the raw tail. If no tail exists, preserve the
41
42
  entire previous body as a new raw tail before restructuring; the tool does this automatically.
42
43
 
@@ -58,6 +59,25 @@ When editing files directly, preserve the same format:
58
59
  ```
59
60
  ````
60
61
 
62
+ ## Second thoughts
63
+
64
+ When the user states a decision, check the `search` hits you already made and the target note's decisions for an earlier **human**
65
+ decision that it contradicts. Do not run extra searches or reads for this.
66
+
67
+ - Keep the bar high: an explicit decision against an explicit decision on the same subject. Overlapping facts, refinements, and
68
+ compatible choices are not conflicts. When unsure, stay silent.
69
+ - Capture first, then ask. Save the user's words as usual, then ask one short question quoting the earlier decision with its date and
70
+ slug: replace it, or are both true? At most one question per turn.
71
+ - `source` is per note, not per line, so judge each decision by where it came from. It is human when the user's words for it are in the
72
+ raw tail, or when it sits in a `source: human` note outside an "Agent addendum". Anything else counts as agent.
73
+ - Conflicting agent or `generated` decisions do not get a question; the human statement already wins. Mention them in one line.
74
+ - If the user says it replaces the old one, strike the old decision through instead of deleting it, and link the new one:
75
+ `~~OIDC next quarter~~ (superseded 2026-10-11 → [[auth-sso]])`, where the link names the note that holds the new decision. When the
76
+ new decision is in the same note, drop the link: `(superseded 2026-10-11)`. A note should not link to itself.
77
+ - Never edit the raw tail to supersede anything. `finalize` keeps superseded lines, struck through, in the body.
78
+
79
+ See [references/second-thoughts.md](references/second-thoughts.md) for example exchanges.
80
+
61
81
  ## TLDR on finalize
62
82
 
63
83
  Every finalized note opens with a TLDR directly under the title: a `**TLDR:**` block of 1–8 lines on what the note says and what
@@ -0,0 +1,57 @@
1
+ # Second thoughts
2
+
3
+ Catch a decision that contradicts an earlier human decision while the user is there to answer in one word. Silence is the default; a
4
+ false alarm costs more than a missed one.
5
+
6
+ ## Conflict: ask once, after saving
7
+
8
+ ```text
9
+ User: Add to the auth note that we're going with SAML.
10
+
11
+ Agent: (search "auth" → auth-migration; Decisions has "2026-09-03: OIDC next quarter, JWT compatibility window", source: human)
12
+ (append to auth-migration)
13
+
14
+ Agent: Added. One thing: on Sep 3 you decided "OIDC next quarter, JWT compatibility window" (auth-migration, Decisions).
15
+ Should SAML replace that decision, or are both true?
16
+
17
+ User: Replace it.
18
+
19
+ Agent: (edit the body above the raw tail)
20
+ Updated. The Sep 3 decision is now marked superseded.
21
+ ```
22
+
23
+ The Decisions section afterwards:
24
+
25
+ ```markdown
26
+ ## Decisions
27
+
28
+ - ~~2026-09-03: OIDC next quarter, JWT compatibility window~~ (superseded 2026-10-11)
29
+ - 2026-10-11: Go with SAML.
30
+ ```
31
+
32
+ The new decision is in the same note, so there is no link. When it lives in another note, link it:
33
+ `(superseded 2026-10-11 → [[auth-sso]])`.
34
+
35
+ If the user answers "both are true", leave both lines as they are and say so.
36
+
37
+ ## No question
38
+
39
+ | New statement | Earlier note | Why silent |
40
+ |---|---|---|
41
+ | "Use SAML for the admin portal" | human: "OIDC for the customer app" | Different subjects |
42
+ | "Use Okta as the OIDC provider" | human: "OIDC next quarter" | Compatible; it builds on the decision |
43
+ | "We're going with SAML" | generated session note: "team leaning OIDC" | Not a human decision; mention it in one line |
44
+ | "SAML might be simpler" | human: "OIDC next quarter" | Not a decision |
45
+ | "We're going with SAML" | "OIDC next quarter" in the Agent addendum of a `source: human` note | Agent-written line; mention it in one line |
46
+
47
+ ## Mixed authorship
48
+
49
+ `source` describes who created the note, not each line in it. A decision the user dictated into an agent note is human: its words are in
50
+ the raw tail. A decision in the Agent addendum of a human note is the agent's. Check the raw tail or the addendum before deciding whether to
51
+ ask.
52
+
53
+ ## Rules of thumb
54
+
55
+ - Use the hits from the one `search` the capture already made; no extra calls to look for conflicts.
56
+ - At most one question per turn, even if several decisions conflict. Name the strongest; mention others in the same question.
57
+ - Never delete the old decision and never touch the raw tail. Superseded lines survive `finalize`, struck through.
@@ -11,7 +11,7 @@
11
11
  import type { HtmlArtifact, Note, RepoIndex, StalenessReport, VaultStatus } from "../types";
12
12
  import { createHash } from "node:crypto";
13
13
  import type { SummaryRecord } from "../summaries";
14
- import type { EdgeKind, GraphEdge, GraphModel, GraphNode } from "./model";
14
+ import type { GraphEdge, GraphModel, GraphNode } from "./model";
15
15
  import { buildPathIndex, resolveMentions, type PathIndex } from "./mentions";
16
16
  import { extractWikilinks } from "./wikilinks";
17
17
 
@@ -15,7 +15,7 @@ import { join, resolve, sep } from "node:path";
15
15
  // that cache, so going through the barrel would close an import cycle.
16
16
  import { findGitRoot } from "../git";
17
17
  import { resolveVaultRoot } from "../paths";
18
- import { assessStaleness, readRepoIndex } from "../repoIndex";
18
+ import { ensureRepoIndex } from "../repoIndex";
19
19
  import { readSummaryMap } from "../summaries";
20
20
  import type { Note } from "../types";
21
21
  import { getNote, readVault, resolveNotePath } from "../vault";
@@ -72,7 +72,7 @@ export async function readOkfFileForView(cwd: string, rel: string): Promise<{ pa
72
72
 
73
73
  /**
74
74
  * Assemble the repository half of the graph input for `cwd`, or null when
75
- * cwd is not inside a git repository with a readable `.okf` index. Shared
75
+ * cwd is not inside a git repository with at least one commit. Shared
76
76
  * with `src/core/cache/workspace` so the cached and uncached paths cannot
77
77
  * drift in what they read.
78
78
  */
@@ -81,19 +81,21 @@ export async function readRepositorySide(
81
81
  ): Promise<Pick<BuildGraphInput, "repository" | "summaries"> | null> {
82
82
  const repoRoot = await findGitRoot(cwd);
83
83
  if (repoRoot === null) return null;
84
- const index = await readRepoIndex(repoRoot);
85
- if (index === null) return null;
84
+ // The structure index is plumbing: built or refreshed here when missing or
85
+ // stale, so the repository side of the graph never waits on a manual scan.
86
+ const repository = await ensureRepoIndex(repoRoot);
87
+ if (repository === null) return null;
86
88
  return {
87
- repository: { index, staleness: await assessStaleness(repoRoot) },
89
+ repository,
88
90
  summaries: await readSummaryMap(repoRoot), // deep-scan sidecars, read live
89
91
  };
90
92
  }
91
93
 
92
94
  /**
93
95
  * Assemble the fresh graph from disk. Called on every viewer fetch
94
- * (no caching). Reads the complete vault and, when cwd is an indexed git
95
- * repository, the repo index + deep-scan summary sidecars. Degrades to a vault-only graph when
96
- * the repo has no index or the index is corrupt.
96
+ * (no caching). Reads the complete vault and, when cwd is inside a git
97
+ * repository, the repo index (refreshed on demand) + deep-scan summary
98
+ * sidecars. Degrades to a vault-only graph when the repo has no commits.
97
99
  *
98
100
  * One read per note: `readVault` returns bodies *and* the file count, so the
99
101
  * old `listNotes` → `getNote`-per-slug → `noteCount` sequence (2N reads plus