@astrosheep/pi-context 0.17.0 → 0.18.0

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": "@astrosheep/pi-context",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "type": "module",
5
5
  "description": "Codex-style context windows for Pi: reset-style compaction, durable session history tools, and persistent notes.",
6
6
  "license": "MIT",
package/src/note-tools.ts CHANGED
@@ -2,7 +2,7 @@ import { Type } from "@earendil-works/pi-ai";
2
2
  import { defineTool, type ExtensionAPI } from "@earendil-works/pi-coding-agent";
3
3
  import { output, outputRaw, page, middleTruncate, prefixFit, earliestMatchOffsetChars, readCharacterWindow, characterWindowHeader, withinTextBudget } from "./tool-output.js";
4
4
  import { nullableString, positiveInteger, cursor, searchQuery, searchQueries } from "./tool-schema.js";
5
- import { notesFromSession, assertVirtualPath, assertVirtualPrefix, assertGlobPattern, globToRegExp, localIso, type NoteOperation } from "./notes.js";
5
+ import { notesFromSession, assertVirtualPath, assertGlobPattern, globToRegExp, localIso, type NoteOperation } from "./notes.js";
6
6
  import { NOTE_TYPE, MAX_NOTE_BYTES, MAX_NOTE_PATH_BYTES } from "./protocol.js";
7
7
 
8
8
  export function registerNoteTools(pi: ExtensionAPI) {
@@ -73,12 +73,13 @@ export function registerNoteTools(pi: ExtensionAPI) {
73
73
  pi.registerTool(defineTool({
74
74
  name: "notes_search_contents",
75
75
  label: "Notes search",
76
- description: "Case-sensitive literal substring search over note lines; query is one string or several (OR), each matched line appears once. No semantic search. Each file entry carries matches_total, its full match count before capping: matches_total minus matches.length is how many were dropped. Each match carries line and offset_chars (the file-absolute code-point offset of the earliest match): notes_read_file at offset_chars shows the query. An over-budget matched line comes back as a prefix with truncated and total_chars; read the rest at the same offset_chars.",
77
- parameters: Type.Object({ max_matches_per_file: positiveInteger(), cursor: cursor(), query: searchQuery(), recent_file_first: Type.Optional(Type.Boolean()), max_files: positiveInteger(), path_prefix: nullableString() }, { additionalProperties: false }),
76
+ description: "Case-sensitive literal substring search over note lines; query is one string or several (OR), each matched line appears once. No semantic search. Optionally filtered by a glob pattern (* within a path segment, ** across segments). Each file entry carries matches_total, its full match count before capping: matches_total minus matches.length is how many were dropped. Each match carries line and offset_chars (the file-absolute code-point offset of the earliest match): notes_read_file at offset_chars shows the query. An over-budget matched line comes back as a prefix with truncated and total_chars; read the rest at the same offset_chars.",
77
+ parameters: Type.Object({ max_matches_per_file: positiveInteger(), cursor: cursor(), query: searchQuery(), recent_file_first: Type.Optional(Type.Boolean()), max_files: positiveInteger(), pattern: nullableString() }, { additionalProperties: false }),
78
78
  async execute(_id, params, _signal, _update, ctx) {
79
79
  const queries = searchQueries(params.query);
80
- const prefix = assertVirtualPrefix(params.path_prefix);
81
- let files = [...notesFromSession(ctx)].filter(([path]) => !prefix || path.startsWith(prefix));
80
+ const pattern = assertGlobPattern(params.pattern);
81
+ const matcher = pattern ? globToRegExp(pattern) : undefined;
82
+ let files = [...notesFromSession(ctx)].filter(([path]) => !matcher || matcher.test(path));
82
83
  if (params.recent_file_first) files.sort((a, b) => b[1].createdAt - a[1].createdAt);
83
84
  const maxPerFile = params.max_matches_per_file ?? Number.POSITIVE_INFINITY;
84
85
  const result: Array<{ path: string; created_at: string; updated_at: string; matches_total: number; matches: Array<{ line: number; text: string; truncated: boolean; total_chars: number; offset_chars: number }>; path_truncated?: boolean }> = files
package/src/notes.ts CHANGED
@@ -20,11 +20,6 @@ export function assertVirtualPath(value: unknown): string {
20
20
  return value;
21
21
  }
22
22
 
23
- export function assertVirtualPrefix(value: unknown): string | undefined {
24
- if (value === undefined || value === null || value === "") return undefined;
25
- return assertVirtualPath(value);
26
- }
27
-
28
23
  /**
29
24
  * Minimal glob over virtual note paths: `*` matches any run within a segment (never
30
25
  * `/`), `**` matches any run across segments (a leading double-star followed by a
@@ -112,4 +107,3 @@ export function localIso(epochMs: number): string {
112
107
  const wallClock = `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())}T${pad2(date.getHours())}:${pad2(date.getMinutes())}:${pad2(date.getSeconds())}.${String(date.getMilliseconds()).padStart(3, "0")}`;
113
108
  return `${wallClock}${offset}`;
114
109
  }
115
-