@hraness/wordcell 0.23.0 → 0.24.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 (45) hide show
  1. package/README.md +29 -16
  2. package/dist/authoring.js +2 -1
  3. package/dist/cli.js +10 -9
  4. package/dist/clip/jobs.js +5 -1
  5. package/dist/evaluation-builder.js +5 -4
  6. package/dist/evaluation-kb.js +5 -4
  7. package/dist/graph-authority.js +2 -1
  8. package/dist/graph-percolation.js +3 -2
  9. package/dist/{index-1er88ckw.js → index-3gc2yk4k.js} +10 -5
  10. package/dist/{index-mnq2wy34.js → index-59bcxqee.js} +1 -1
  11. package/dist/{index-p70t8j13.js → index-7drercgk.js} +1 -1
  12. package/dist/{index-tcaq1c7f.js → index-e26mdwxz.js} +1 -1
  13. package/dist/{index-r4qs6vq4.js → index-hz2wyggk.js} +1 -1
  14. package/dist/{index-vtwbapjr.js → index-jqkkb4fr.js} +72 -12
  15. package/dist/{index-tvd2sqrx.js → index-mcpppwbq.js} +439 -4
  16. package/dist/index-qkesh4c6.js +7 -0
  17. package/dist/{index-b0khj0h5.js → index-qt2zwza8.js} +6 -1
  18. package/dist/{index-0eacgvpv.js → index-t0fh8a1q.js} +7 -3
  19. package/dist/index.js +6 -5
  20. package/dist/portfolio.js +5 -4
  21. package/dist/sdk.js +4 -3
  22. package/dist/{support-c8hy7qfa.js → support-07jpt1z4.js} +9 -8
  23. package/dist/url-intelligence.js +2 -1
  24. package/dist/workflows/decision-context.js +5 -4
  25. package/dist/workflows/index.js +5 -4
  26. package/package.json +12 -8
  27. package/skills/wordcell/SKILL.md +1 -1
  28. package/skills/wordcell/references/publish.md +2 -1
  29. package/skills/wordcell/references/query.md +17 -3
  30. package/src/authoring-platform.ts +3 -0
  31. package/src/cli-help.ts +1 -0
  32. package/src/cli-program.ts +41 -3
  33. package/src/clip/jobs.ts +2 -1
  34. package/src/clip/metadata-search-tool/Cargo.lock +2 -0
  35. package/src/clip/metadata-search-tool/Cargo.toml +2 -0
  36. package/src/clip/metadata-search-tool/runner.ts +4 -1
  37. package/src/clip/metadata-search-tool/src/duckduckgo.rs +534 -0
  38. package/src/clip/metadata-search-tool/src/main.rs +31 -18
  39. package/src/clip/metadata-search.ts +4 -2
  40. package/src/clip/url-metadata.ts +2 -1
  41. package/src/directory-sync.ts +9 -0
  42. package/src/graph-authority.ts +5 -2
  43. package/src/mcp-tools.ts +17 -1
  44. package/src/sdk.ts +149 -1
  45. package/src/selected-passage.ts +409 -0
package/README.md CHANGED
@@ -3,9 +3,11 @@
3
3
 
4
4
  [![Agent Skill](https://raw.githubusercontent.com/hraness/wordcell/main/assets/agent-skill.svg)](https://github.com/hraness/wordcell/tree/main/skills/wordcell)
5
5
 
6
- Wordcell keeps decisions, plans, and sources as Markdown files beside your
7
- code. Coding agents find them by exact words, by meaning with an optional
8
- local model, or from the file they are about to change.
6
+ Wordcell is a Markdown knowledge base and local MCP memory server for coding
7
+ agents such as Claude Code, Codex, and Cursor. It keeps decisions, plans, and
8
+ sources as Markdown files beside your code, and agents find them by exact
9
+ words, by meaning with an optional local model, or from the file they are
10
+ about to change.
9
11
 
10
12
  A new coding-agent session can read your code, but not the decisions that
11
13
  stayed in the last session's chat. Wordcell keeps those decisions as Markdown
@@ -20,7 +22,7 @@ Web capture, optional hosted reranking, and your agent's provider reach other
20
22
  services; [Privacy and boundaries](#privacy-and-boundaries) says what each one
21
23
  sends.
22
24
 
23
- [Documentation](https://wordcell.io/docs) · [Comparisons](https://github.com/hraness/wordcell/blob/main/docs/comparisons.md) · [Measured evidence](https://github.com/hraness/wordcell/blob/main/docs/evidence.md) · [Changelog](https://github.com/hraness/wordcell/blob/main/CHANGELOG.md)
25
+ [Documentation](https://wordcell.io/docs) · [Comparisons](https://wordcell.io/docs/comparisons) · [Measured evidence](https://wordcell.io/docs/evidence) · [Changelog](https://github.com/hraness/wordcell/blob/main/CHANGELOG.md)
24
26
 
25
27
  ## Why Wordcell
26
28
 
@@ -42,7 +44,7 @@ sends.
42
44
  Plain Markdown may be enough for a small set of notes. QMD is a good fit for
43
45
  local document retrieval and also supplies Wordcell's optional semantic search.
44
46
  Wordcell adds a connected workflow for repository context, authored relationships,
45
- Git evidence, and selective publishing. [Compare the tradeoffs](https://github.com/hraness/wordcell/blob/main/docs/comparisons.md).
47
+ Git evidence, and selective publishing. [Compare the tradeoffs](https://wordcell.io/docs/comparisons).
46
48
 
47
49
  Wordcell keeps the record in Markdown files you own, rebuilds every index from those files, and lets the next session start from the notes for the file it is changing: the design every Hraness project shares. [The thread through hraness](https://hraness.com/writing/the-thread-through-hraness) follows that design across the projects, and the [ALGAL vision](https://algal.computer/docs/vision/) states the bet behind it.
48
50
 
@@ -52,14 +54,14 @@ Wordcell keeps the record in Markdown files you own, rebuilds every index from t
52
54
  The CLI and TypeScript SDK run with Bun. Install the versioned GitHub archive:
53
55
 
54
56
  ```sh
55
- bun add --global --ignore-scripts https://github.com/hraness/wordcell/releases/download/v0.23.0/hraness-wordcell-0.23.0.tgz
57
+ bun add --global --ignore-scripts https://github.com/hraness/wordcell/releases/download/v0.24.1/hraness-wordcell-0.24.1.tgz
56
58
  wordcell --help
57
59
  ```
58
60
 
59
61
  Prefer npm? The same release is [mirrored there](https://www.npmjs.com/package/@hraness/wordcell):
60
62
 
61
63
  ```sh
62
- npm install --global --ignore-scripts @hraness/wordcell@0.23.0
64
+ npm install --global --ignore-scripts @hraness/wordcell@0.24.1
63
65
  wordcell --help
64
66
  ```
65
67
 
@@ -101,7 +103,7 @@ After trying the CLI, install the public Agent Skill into a compatible agent,
101
103
  such as Claude Code, Codex, Cursor, or GitHub Copilot:
102
104
 
103
105
  ```sh
104
- bunx skills add hraness/wordcell#v0.23.0 --skill wordcell
106
+ bunx skills add hraness/wordcell#v0.24.1 --skill wordcell
105
107
  ```
106
108
 
107
109
  Then ask:
@@ -247,6 +249,14 @@ keeps one vault current on several machines.
247
249
 
248
250
  ## Evidence and comparisons
249
251
 
252
+ On eight questions sealed before the passage selector was finished,
253
+ `wordcell search --selected-passage` returned an excerpt holding the labeled
254
+ answer for **6 of 8**, versus **1 of 8** for the older search snippets, at the
255
+ same 512-byte limit and from the same retrieved notes. It runs locally with no
256
+ model. The corpus is small and consists of Wordcell's own notes; containing the
257
+ answer is not the same as an agent answering correctly.
258
+ [Method, raw results, and reproduction](https://github.com/hraness/wordcell/blob/main/docs/evidence.md#measure-whether-excerpts-contain-the-answer).
259
+
250
260
  In a four-query example over a seven-note public vault, packed search snippets
251
261
  used **80% fewer UTF-8 bytes** than passing the same matching notes in full:
252
262
  12,126 versus 60,584 bytes. This measures context payload size, not tokenizer
@@ -262,17 +272,20 @@ Wordcell's benefit is selecting relevant context and keeping its sources
262
272
  inspectable. Local ownership is also available in other tools, and Wordcell
263
273
  does not claim to beat QMD's retrieval quality or every Markdown workflow.
264
274
 
265
- [Measured evidence](https://github.com/hraness/wordcell/blob/main/docs/evidence.md)
275
+ [Measured evidence](https://wordcell.io/docs/evidence)
266
276
  shows a reproducible public-vault example, with the inputs, output sizes, and
267
- limits beside each result. [The comparison guide](https://github.com/hraness/wordcell/blob/main/docs/comparisons.md)
277
+ limits beside each result. [The comparison guide](https://wordcell.io/docs/comparisons)
268
278
  covers Markdown alone, QMD, Basic Memory, Obsidian, static publishing tools,
269
279
  Supermemory, Mem0, and Zep using their own documentation. Choose the smallest
270
280
  workflow that meets your needs.
271
281
 
272
- The [benchmarks page](https://wordcell.io/benchmarks) shows the payload and
273
- SciFact results beside the published results of Oh, the embedded memory
274
- framework, each with its source data and limits. Oh's scores measure its own
275
- memory-retrieval path, not a Wordcell vault.
282
+ The [benchmarks page](https://wordcell.io/benchmarks) shows the excerpt, payload,
283
+ and SciFact results beside the published results of Oh, the embedded memory
284
+ framework, each with its source data and limits. On all 500 LongMemEval-S
285
+ questions, Oh semantic retrieval scored 88.87% and BM25 86.13% with the same
286
+ reader and budget; on the measure Oh named before the run, its interval does
287
+ not rule out a tie. Oh's scores measure its own memory-retrieval path, not a
288
+ Wordcell vault.
276
289
 
277
290
  ## How the files fit together
278
291
 
@@ -321,7 +334,7 @@ exist. [Graph queries and proof limits](https://github.com/hraness/wordcell/blob
321
334
  Add the same immutable release to a Bun project:
322
335
 
323
336
  ```sh
324
- bun add --exact --ignore-scripts https://github.com/hraness/wordcell/releases/download/v0.23.0/hraness-wordcell-0.23.0.tgz
337
+ bun add --exact --ignore-scripts https://github.com/hraness/wordcell/releases/download/v0.24.1/hraness-wordcell-0.24.1.tgz
325
338
  ```
326
339
 
327
340
  The SDK provides read-only vault sessions, metadata queries, search, graph
@@ -358,7 +371,7 @@ the design. Browse it on the [documentation index](https://wordcell.io/docs).
358
371
  | [Web capture](https://github.com/hraness/wordcell/blob/main/docs/capture.md) and [PDF capture](https://github.com/hraness/wordcell/blob/main/docs/pdf.md) | Save sources with provenance, assets, and explicit completeness limits. |
359
372
  | [Publish selected notes](https://github.com/hraness/wordcell/blob/main/docs/publish.md) | Preview a slice, build a static site, and choose how to host it. |
360
373
  | [Graph guide](https://github.com/hraness/wordcell/blob/main/docs/graph-authority.md) | Named queries, proofs, revisions, resource limits, and cache recovery. |
361
- | [Portfolio federation](https://github.com/hraness/wordcell/blob/main/docs/portfolio.md) | Search only selected, authorized vaults. |
374
+ | [Search several vaults together](https://github.com/hraness/wordcell/blob/main/docs/portfolio.md) | Search only selected, authorized vaults. |
362
375
  | [Sync with Git](https://github.com/hraness/wordcell/blob/main/docs/sync.md) | Keep one vault current on several machines with a private repository. |
363
376
  | [Migrate from Supermemory](https://github.com/hraness/wordcell/blob/main/docs/migration-from-supermemory.md) | Export documents and memory entries, import them as notes, and replace connectors. |
364
377
  | [Design](https://github.com/hraness/wordcell/blob/main/docs/design.md) and [memory rationale](https://github.com/hraness/wordcell/blob/main/docs/agent-memory.md) | File contracts, design choices, and evaluation context. |
package/dist/authoring.js CHANGED
@@ -14,8 +14,9 @@ import {
14
14
  noteRevision,
15
15
  removeNoteRelation,
16
16
  updateNoteBody
17
- } from "./index-b0khj0h5.js";
17
+ } from "./index-qt2zwza8.js";
18
18
  import"./index-3rm7cz6h.js";
19
+ import"./index-qkesh4c6.js";
19
20
  import"./index-jvb7w0gg.js";
20
21
  import"./index-z1w83f81.js";
21
22
  export {
package/dist/cli.js CHANGED
@@ -6,10 +6,10 @@ import {
6
6
  parseArguments,
7
7
  runExecutable,
8
8
  usage
9
- } from "./index-vtwbapjr.js";
10
- import"./index-0eacgvpv.js";
9
+ } from "./index-jqkkb4fr.js";
10
+ import"./index-t0fh8a1q.js";
11
11
  import"./index-qfqjfxaa.js";
12
- import"./index-r4qs6vq4.js";
12
+ import"./index-hz2wyggk.js";
13
13
  import"./index-j4zgmzjr.js";
14
14
  import"./index-2fmnttca.js";
15
15
  import"./index-6jcz0m1c.js";
@@ -28,16 +28,16 @@ import"./index-mxxxytys.js";
28
28
  import"./index-23z4zxgg.js";
29
29
  import"./index-pj501bh1.js";
30
30
  import"./index-9rf81m0p.js";
31
- import"./index-mnq2wy34.js";
32
- import"./index-tvd2sqrx.js";
31
+ import"./index-59bcxqee.js";
32
+ import"./index-mcpppwbq.js";
33
33
  import"./index-adx6khj5.js";
34
34
  import"./index-fp732bgg.js";
35
35
  import"./index-4j3tt0c3.js";
36
36
  import"./index-j70m75wd.js";
37
37
  import"./index-b88v3vtm.js";
38
- import"./index-tcaq1c7f.js";
38
+ import"./index-e26mdwxz.js";
39
39
  import"./index-bdwcjvr4.js";
40
- import"./index-1er88ckw.js";
40
+ import"./index-3gc2yk4k.js";
41
41
  import"./index-pgtm2nhf.js";
42
42
  import"./index-1gwbassd.js";
43
43
  import"./index-ahyhryb8.js";
@@ -53,8 +53,9 @@ import"./index-23m6bbjt.js";
53
53
  import"./index-hya40gb2.js";
54
54
  import"./index-5vwpzb5a.js";
55
55
  import"./index-x3fthpsc.js";
56
- import"./index-b0khj0h5.js";
56
+ import"./index-qt2zwza8.js";
57
57
  import"./index-3rm7cz6h.js";
58
+ import"./index-qkesh4c6.js";
58
59
  import"./index-jvb7w0gg.js";
59
60
  import {
60
61
  __require
@@ -62,7 +63,7 @@ import {
62
63
 
63
64
  // src/cli.ts
64
65
  if (import.meta.main) {
65
- const { standaloneSupportEnvironment, isUsefulSupportResult, runProductSupportCommand, showProductSupportInvitation } = await import("./support-c8hy7qfa.js");
66
+ const { standaloneSupportEnvironment, isUsefulSupportResult, runProductSupportCommand, showProductSupportInvitation } = await import("./support-07jpt1z4.js");
66
67
  const env = standaloneSupportEnvironment();
67
68
  const args = process.argv.slice(2);
68
69
  if (args[0] === "support") {
package/dist/clip/jobs.js CHANGED
@@ -9,6 +9,9 @@ import {
9
9
  import {
10
10
  acquireFileLease
11
11
  } from "../index-3rm7cz6h.js";
12
+ import {
13
+ syncsDirectories
14
+ } from "../index-qkesh4c6.js";
12
15
  import"../index-z1w83f81.js";
13
16
 
14
17
  // src/clip/jobs.ts
@@ -360,7 +363,8 @@ async function fsyncStore(store) {
360
363
  if (!stat.isDirectory() || !sameIdentity(stat, expected)) {
361
364
  throw new CaptureJobSafetyError("Capture job store changed before synchronization.");
362
365
  }
363
- await handle.sync();
366
+ if (syncsDirectories())
367
+ await handle.sync();
364
368
  } finally {
365
369
  await handle.close();
366
370
  }
@@ -4,8 +4,8 @@ import {
4
4
  knowledgeBaseEvaluationRetrieverIds,
5
5
  openKnowledgeBaseEvaluation,
6
6
  verifyFrozenEvaluationSnapshot
7
- } from "./index-mnq2wy34.js";
8
- import"./index-tvd2sqrx.js";
7
+ } from "./index-59bcxqee.js";
8
+ import"./index-mcpppwbq.js";
9
9
  import"./index-adx6khj5.js";
10
10
  import {
11
11
  indexSemanticVault,
@@ -19,9 +19,9 @@ import {
19
19
  MAX_EVALUATION_EVIDENCE_BYTES,
20
20
  MAX_EVALUATION_RESULTS_PER_QUERY
21
21
  } from "./index-b88v3vtm.js";
22
- import"./index-tcaq1c7f.js";
22
+ import"./index-e26mdwxz.js";
23
23
  import"./index-bdwcjvr4.js";
24
- import"./index-1er88ckw.js";
24
+ import"./index-3gc2yk4k.js";
25
25
  import"./index-pgtm2nhf.js";
26
26
  import {
27
27
  runGitCommand
@@ -36,6 +36,7 @@ import"./index-48pz4jpc.js";
36
36
  import"./index-06c9ctr6.js";
37
37
  import"./index-5vwpzb5a.js";
38
38
  import"./index-3rm7cz6h.js";
39
+ import"./index-qkesh4c6.js";
39
40
  import"./index-jvb7w0gg.js";
40
41
  import"./index-z1w83f81.js";
41
42
 
@@ -4,16 +4,16 @@ import {
4
4
  knowledgeBaseEvaluationRetrieverIds,
5
5
  openKnowledgeBaseEvaluation,
6
6
  verifyFrozenEvaluationSnapshot
7
- } from "./index-mnq2wy34.js";
8
- import"./index-tvd2sqrx.js";
7
+ } from "./index-59bcxqee.js";
8
+ import"./index-mcpppwbq.js";
9
9
  import"./index-adx6khj5.js";
10
10
  import"./index-fp732bgg.js";
11
11
  import"./index-4j3tt0c3.js";
12
12
  import"./index-j70m75wd.js";
13
13
  import"./index-b88v3vtm.js";
14
- import"./index-tcaq1c7f.js";
14
+ import"./index-e26mdwxz.js";
15
15
  import"./index-bdwcjvr4.js";
16
- import"./index-1er88ckw.js";
16
+ import"./index-3gc2yk4k.js";
17
17
  import"./index-pgtm2nhf.js";
18
18
  import"./index-1gwbassd.js";
19
19
  import"./index-ahyhryb8.js";
@@ -24,6 +24,7 @@ import"./index-48pz4jpc.js";
24
24
  import"./index-06c9ctr6.js";
25
25
  import"./index-5vwpzb5a.js";
26
26
  import"./index-3rm7cz6h.js";
27
+ import"./index-qkesh4c6.js";
27
28
  import"./index-jvb7w0gg.js";
28
29
  import"./index-z1w83f81.js";
29
30
  export {
@@ -5,7 +5,7 @@ import {
5
5
  queryGraph,
6
6
  rebuildGraph,
7
7
  verifyGraph
8
- } from "./index-1er88ckw.js";
8
+ } from "./index-3gc2yk4k.js";
9
9
  import {
10
10
  GRAPH_LIMITS,
11
11
  GraphAuthorityError,
@@ -15,6 +15,7 @@ import"./index-1s8mc4hz.js";
15
15
  import"./index-06c9ctr6.js";
16
16
  import"./index-5vwpzb5a.js";
17
17
  import"./index-3rm7cz6h.js";
18
+ import"./index-qkesh4c6.js";
18
19
  import"./index-jvb7w0gg.js";
19
20
  import"./index-z1w83f81.js";
20
21
  export {
@@ -1,14 +1,15 @@
1
1
  // @bun
2
2
  import {
3
3
  percolateWithGraph
4
- } from "./index-tcaq1c7f.js";
4
+ } from "./index-e26mdwxz.js";
5
5
  import"./index-bdwcjvr4.js";
6
- import"./index-1er88ckw.js";
6
+ import"./index-3gc2yk4k.js";
7
7
  import"./index-pgtm2nhf.js";
8
8
  import"./index-1s8mc4hz.js";
9
9
  import"./index-06c9ctr6.js";
10
10
  import"./index-5vwpzb5a.js";
11
11
  import"./index-3rm7cz6h.js";
12
+ import"./index-qkesh4c6.js";
12
13
  import"./index-jvb7w0gg.js";
13
14
  import"./index-z1w83f81.js";
14
15
  export {
@@ -13,6 +13,9 @@ import {
13
13
  import {
14
14
  acquireFileLease
15
15
  } from "./index-3rm7cz6h.js";
16
+ import {
17
+ syncsDirectories
18
+ } from "./index-qkesh4c6.js";
16
19
  import {
17
20
  VaultAnalysisBudgetError,
18
21
  analyzeVault,
@@ -443,11 +446,13 @@ async function rebuildGraph(rootInput, options = {}) {
443
446
  await handle.close();
444
447
  }
445
448
  await rename(stagedPath, path);
446
- const directory = await graphCacheFileSystem.open(cache, constants.O_RDONLY);
447
- try {
448
- await directory.sync();
449
- } finally {
450
- await directory.close();
449
+ if (syncsDirectories()) {
450
+ const directory = await graphCacheFileSystem.open(cache, constants.O_RDONLY);
451
+ try {
452
+ await directory.sync();
453
+ } finally {
454
+ await directory.close();
455
+ }
451
456
  }
452
457
  return result;
453
458
  } finally {
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  MAX_SEARCH_RESULTS,
4
4
  openKnowledgeBase
5
- } from "./index-tvd2sqrx.js";
5
+ } from "./index-mcpppwbq.js";
6
6
  import {
7
7
  MAX_EVALUATION_EVIDENCE_BYTES,
8
8
  redactEvaluationMachinePaths
@@ -4,7 +4,7 @@ import {
4
4
  } from "./index-h170byqw.js";
5
5
  import {
6
6
  packUntrustedSearchContext
7
- } from "./index-tvd2sqrx.js";
7
+ } from "./index-mcpppwbq.js";
8
8
 
9
9
  // src/workflows/decision-context.ts
10
10
  var decisionContextWorkflow = defineWorkflow({
@@ -5,7 +5,7 @@ import {
5
5
  import {
6
6
  createGraphSnapshot,
7
7
  openGraphAuthority
8
- } from "./index-1er88ckw.js";
8
+ } from "./index-3gc2yk4k.js";
9
9
  import {
10
10
  GraphAuthorityError,
11
11
  validateGraphPercolationOptions
@@ -5,7 +5,7 @@ import {
5
5
  MAX_SEARCH_RESULTS,
6
6
  openKnowledgeBase,
7
7
  validateKnowledgeBaseSearchHistory
8
- } from "./index-tvd2sqrx.js";
8
+ } from "./index-mcpppwbq.js";
9
9
  import {
10
10
  expandSearchRequest,
11
11
  parseSearchRules
@@ -3,7 +3,7 @@ import {
3
3
  backfillSavedUrlMetadata,
4
4
  createRustMetadataSearchProvider,
5
5
  savedSourceProblem
6
- } from "./index-0eacgvpv.js";
6
+ } from "./index-t0fh8a1q.js";
7
7
  import {
8
8
  main as main2
9
9
  } from "./index-qfqjfxaa.js";
@@ -13,7 +13,7 @@ import {
13
13
  loadPortfolioRegistry,
14
14
  openKnowledgePortfolio,
15
15
  snapshotPortfolioRegistry
16
- } from "./index-r4qs6vq4.js";
16
+ } from "./index-hz2wyggk.js";
17
17
  import {
18
18
  diffCaptureBundle
19
19
  } from "./index-j4zgmzjr.js";
@@ -51,7 +51,7 @@ import {
51
51
  import {
52
52
  knowledgeBaseEvaluationRetrieverIds,
53
53
  openKnowledgeBaseEvaluation
54
- } from "./index-mnq2wy34.js";
54
+ } from "./index-59bcxqee.js";
55
55
  import {
56
56
  DEFAULT_SEARCH_RESULTS,
57
57
  MAX_SEARCH_CANDIDATES,
@@ -60,7 +60,7 @@ import {
60
60
  MAX_SEARCH_RESULTS,
61
61
  openKnowledgeBase,
62
62
  searchEvidenceRank
63
- } from "./index-tvd2sqrx.js";
63
+ } from "./index-mcpppwbq.js";
64
64
  import {
65
65
  MAX_SEARCH_RULE_CONFIG_BYTES,
66
66
  parseSearchRules
@@ -85,7 +85,7 @@ import {
85
85
  } from "./index-b88v3vtm.js";
86
86
  import {
87
87
  percolateWithGraph
88
- } from "./index-tcaq1c7f.js";
88
+ } from "./index-e26mdwxz.js";
89
89
  import {
90
90
  MAX_PERCOLATION_MENTIONS,
91
91
  MAX_PERCOLATION_MENTION_PAIRS,
@@ -97,7 +97,7 @@ import {
97
97
  queryGraph,
98
98
  rebuildGraph,
99
99
  verifyGraph
100
- } from "./index-1er88ckw.js";
100
+ } from "./index-3gc2yk4k.js";
101
101
  import {
102
102
  validateGraphQueryRequest
103
103
  } from "./index-pgtm2nhf.js";
@@ -184,7 +184,10 @@ import {
184
184
  sha256,
185
185
  updateNoteBody,
186
186
  updateNoteBodyProgram
187
- } from "./index-b0khj0h5.js";
187
+ } from "./index-qt2zwza8.js";
188
+ import {
189
+ syncsDirectories
190
+ } from "./index-qkesh4c6.js";
188
191
  import {
189
192
  isMetadataNumber,
190
193
  lookupNote,
@@ -440,7 +443,10 @@ function installMetadataSearchExecutable(input) {
440
443
  const backupName = `.${executableName}.${process.pid}.${randomUUID()}.backup`;
441
444
  const stagingPath = join(toolDirectory, stagingName);
442
445
  const backupPath = join(toolDirectory, backupName);
443
- const syncDirectory = input.syncDirectory ?? ((descriptor) => fsyncSync(descriptor));
446
+ const syncDirectory = input.syncDirectory ?? ((descriptor) => {
447
+ if (syncsDirectories())
448
+ fsyncSync(descriptor);
449
+ });
444
450
  const toolBinding = openBoundDirectory(toolDirectory, "metadata-search tool directory");
445
451
  let targetBinding = null;
446
452
  let releaseBinding = null;
@@ -932,6 +938,7 @@ var commandHelp = [
932
938
  ["--min-score <score>", "Drop results scoring below this (0 to 1)"],
933
939
  ["--rerank <typesafe>", "Rerank results with the hosted TypeSafe service"],
934
940
  ["--rerank-limit <2..25>", "How many results the reranker sees"],
941
+ ["--selected-passage", "Add a local source excerpt to each result"],
935
942
  JSON_OPTION
936
943
  ],
937
944
  examples: ['wordcell search "retries" --root kb --mode exact', 'wordcell search "retry policy" --root kb --limit 5']
@@ -3624,6 +3631,15 @@ function requiredText(arguments_, key, maxBytes) {
3624
3631
  function optionalText(arguments_, key, maxBytes) {
3625
3632
  return has(arguments_, key) ? checkedText(arguments_[key], key, maxBytes) : undefined;
3626
3633
  }
3634
+ function optionalBoolean(arguments_, key) {
3635
+ if (!has(arguments_, key))
3636
+ return;
3637
+ const value = arguments_[key];
3638
+ if (typeof value !== "boolean") {
3639
+ throw new ToolArgumentError(`Invalid argument "${key}": expected a boolean.`);
3640
+ }
3641
+ return value;
3642
+ }
3627
3643
  function optionalInteger(arguments_, key, minimum, maximum) {
3628
3644
  if (!has(arguments_, key))
3629
3645
  return;
@@ -3923,6 +3939,11 @@ function readTools(root, sessions) {
3923
3939
  properties: {
3924
3940
  query: { type: "string", minLength: 1, maxLength: MAX_QUERY_TEXT_UTF8_BYTES },
3925
3941
  mode: { type: "string", enum: SEARCH_MODES, default: "hybrid" },
3942
+ selectedPassage: {
3943
+ type: "boolean",
3944
+ default: false,
3945
+ description: "Include a local source excerpt with line references and section headings. Ranking is unchanged."
3946
+ },
3926
3947
  limit: { type: "integer", minimum: 1, maximum: MAX_SEARCH_RESULTS, default: DEFAULT_SEARCH_RESULTS },
3927
3948
  ...filterProperties
3928
3949
  }
@@ -3930,10 +3951,11 @@ function readTools(root, sessions) {
3930
3951
  annotations: { title: "Search notes", ...READ_ONLY }
3931
3952
  },
3932
3953
  handler: async (arguments_) => {
3933
- allowOnly(arguments_, ["query", "mode", "limit", "tags", "where", "has", "scope"]);
3954
+ allowOnly(arguments_, ["query", "mode", "limit", "tags", "where", "has", "scope", "selectedPassage"]);
3934
3955
  const query = requiredText(arguments_, "query", MAX_QUERY_TEXT_UTF8_BYTES);
3935
3956
  const mode = optionalChoice(arguments_, "mode", SEARCH_MODES) ?? "hybrid";
3936
3957
  const limit = optionalInteger(arguments_, "limit", 1, MAX_SEARCH_RESULTS) ?? DEFAULT_SEARCH_RESULTS;
3958
+ const selectedPassage = optionalBoolean(arguments_, "selectedPassage");
3937
3959
  const filters = metadataFilters(arguments_);
3938
3960
  const tags = optionalTextList(arguments_, "tags", MAX_QUERY_TAGS, MAX_QUERY_TEXT_UTF8_BYTES);
3939
3961
  const scopes = optionalTextList(arguments_, "scope", MAX_REPOSITORY_SCOPES, MAX_QUERY_TEXT_UTF8_BYTES);
@@ -3942,6 +3964,7 @@ function readTools(root, sessions) {
3942
3964
  query,
3943
3965
  mode,
3944
3966
  limit,
3967
+ ...selectedPassage === undefined ? {} : { selectedPassage },
3945
3968
  ...filters.length === 0 ? {} : { filters },
3946
3969
  ...tags === undefined ? {} : { tags },
3947
3970
  ...scopes === undefined ? {} : { repositoryScopes: scopes }
@@ -5151,6 +5174,7 @@ function parseSemanticCommand(command, arguments_) {
5151
5174
  let minScore;
5152
5175
  let rerank;
5153
5176
  let rerankLimit;
5177
+ let selectedPassage = false;
5154
5178
  let graphDepth;
5155
5179
  let noGraph = false;
5156
5180
  let history = false;
@@ -5177,6 +5201,10 @@ function parseSemanticCommand(command, arguments_) {
5177
5201
  noGraph = true;
5178
5202
  continue;
5179
5203
  }
5204
+ if (argument === "--selected-passage" && command === "search") {
5205
+ selectedPassage = true;
5206
+ continue;
5207
+ }
5180
5208
  if (argument === "--no-history" && command === "search") {
5181
5209
  noHistory = true;
5182
5210
  continue;
@@ -5402,6 +5430,7 @@ function parseSemanticCommand(command, arguments_) {
5402
5430
  ...minScore === undefined ? {} : { minScore },
5403
5431
  ...rerank === undefined ? {} : { rerank },
5404
5432
  ...rerankLimit === undefined ? {} : { rerankLimit },
5433
+ ...selectedPassage ? { selectedPassage: true } : {},
5405
5434
  query,
5406
5435
  json
5407
5436
  }
@@ -6293,6 +6322,22 @@ function renderSemanticIndex(result) {
6293
6322
  ].join(`
6294
6323
  `);
6295
6324
  }
6325
+ function searchForDisplay(result) {
6326
+ return {
6327
+ ...result,
6328
+ results: result.results.map((hit) => {
6329
+ const passage = hit.selectedPassage;
6330
+ if (passage?.status !== "selected")
6331
+ return hit;
6332
+ const texts = [passage.text, ...passage.headings.map(({ text }) => text)];
6333
+ if (texts.every((text) => sanitizeTerminalText(redactSensitiveText(text)) === text.replaceAll(`\r
6334
+ `, `
6335
+ `).replaceAll("\t", " ")))
6336
+ return hit;
6337
+ return { ...hit, selectedPassage: { status: "unavailable", reason: "display-sanitized" } };
6338
+ })
6339
+ };
6340
+ }
6296
6341
  function renderKnowledgeBaseSearch(result) {
6297
6342
  const lines = [
6298
6343
  `${result.mode[0]?.toLocaleUpperCase("en-US") ?? ""}${result.mode.slice(1)} results for \u201C${safe(result.query)}\u201D (${result.results.length})${result.partial ? " [partial]" : ""}`
@@ -6315,8 +6360,21 @@ function renderKnowledgeBaseSearch(result) {
6315
6360
  const location = `${safe(hit.path)}${hit.line === undefined ? "" : `:${hit.line}`}`;
6316
6361
  const evidence = hit.evidence.map((item) => `${item.kind}#${searchEvidenceRank(item)}`).join(", ");
6317
6362
  lines.push(` ${hit.rank}. ${hit.score.toFixed(3)} ${location} \u2014 ${safe(hit.title)} [${safe(evidence)}]`);
6318
- if (hit.snippet !== "")
6319
- lines.push(` ${safe(hit.snippet)}`);
6363
+ const passage = hit.selectedPassage;
6364
+ if (passage?.status === "selected") {
6365
+ lines.push(` Selected passage: ${safe(hit.path)}:${passage.startLine}-${passage.endLine}` + (passage.clippedStart || passage.clippedEnd ? " (clipped excerpt)" : ""));
6366
+ for (const heading of passage.headings) {
6367
+ lines.push(` Section: ${safe(heading.text)}${heading.truncated ? " (clipped)" : ""}`);
6368
+ }
6369
+ for (const line of passage.text.split(/\r?\n/u))
6370
+ lines.push(` ${safe(line)}`);
6371
+ } else {
6372
+ if (hit.snippet !== "")
6373
+ lines.push(` ${safe(hit.snippet)}`);
6374
+ if (passage !== undefined) {
6375
+ lines.push(` Selected passage: ${passage.status} (${safe(passage.reason)}); showing the search snippet.`);
6376
+ }
6377
+ }
6320
6378
  }
6321
6379
  if ((result.graph?.related.length ?? 0) > 0) {
6322
6380
  lines.push(` Related graph context: ${result.graph?.related.length ?? 0}`);
@@ -6369,6 +6427,7 @@ async function runSemantic(command, output, dependencies) {
6369
6427
  ...command.limit === undefined ? {} : { limit: command.limit },
6370
6428
  ...command.candidateLimit === undefined ? {} : { candidateLimit: command.candidateLimit },
6371
6429
  ...command.minScore === undefined ? {} : { minScore: command.minScore },
6430
+ ...command.selectedPassage === undefined ? {} : { selectedPassage: command.selectedPassage },
6372
6431
  ...command.rerank === undefined ? {} : {
6373
6432
  rerank: {
6374
6433
  engine: command.rerank,
@@ -6376,7 +6435,8 @@ async function runSemantic(command, output, dependencies) {
6376
6435
  }
6377
6436
  }
6378
6437
  });
6379
- output.stdout(command.json ? terminalSafeJson(result) : sanitizeTerminalText(renderKnowledgeBaseSearch(result)));
6438
+ const display = searchForDisplay(result);
6439
+ output.stdout(command.json ? terminalSafeJson(display) : sanitizeTerminalText(renderKnowledgeBaseSearch(display)));
6380
6440
  if (withoutIndex && person) {
6381
6441
  output.stderr("Searched without an index, so only exact words matched. " + `For meaning-based search, run wordcell index --root ${safe(shellWord(command.root))} ` + `(downloads about 300 MB once).
6382
6442
  `);