@henryqw/pi-add-dir 0.1.12 → 0.1.14

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/README.md CHANGED
@@ -1,11 +1,11 @@
1
1
  # `@henryqw/pi-add-dir`
2
2
 
3
- Add external directories to the current Pi session: inject context files, load skills, and search those trees.
3
+ Add external directories to the current Pi session. Pi can use their context files and skills, and search their files.
4
4
 
5
5
  ## Why
6
6
 
7
- - **Created for**: Extending Pi context and skill discovery beyond the current working directory.
8
- - **Advantage**: Inject files and load skills from external trees without copying them into the repo.
7
+ - **Created for**: Use context and skills outside the current working directory.
8
+ - **Advantage**: Use external trees without copying them into the repository.
9
9
 
10
10
  ## Install
11
11
 
@@ -23,10 +23,16 @@ pi install npm:@henryqw/pi-add-dir
23
23
  | `add_directory` | tool | Add a directory. |
24
24
  | `search_external_files` | tool | Glob-search added directories. |
25
25
 
26
- Added directories return root `AGENTS.md`, `CLAUDE.md`, `.pi/AGENTS.md`, and `.pi/CLAUDE.md`, and inject them into later prompts. Skills load from `.pi/skills`, `.agents/skills`, and `.claude/skills`. `/dir-add` reloads when it finds skills; `add_directory` reports when a reload is needed.
26
+ Added directories give Pi these resources:
27
27
 
28
- Search uses Node filesystem traversal, skips `.git` and `node_modules`, supports basename and relative-path globs, and caps results at 1,000 per call.
28
+ - Root `AGENTS.md`, `CLAUDE.md`, `.pi/AGENTS.md`, and `.pi/CLAUDE.md` files. Pi injects them into later prompts.
29
+ - Skills that Pi loads from `.pi/skills`, `.agents/skills`, and `.claude/skills`.
30
+ - Files in the editor's `@` autocomplete, with absolute paths.
31
+
32
+ `/dir-add` reloads when it finds skills. `add_directory` reports when a reload is needed.
33
+
34
+ Search supports basename and relative-path globs. It skips `.git` and `node_modules`. It uses Node filesystem traversal and returns at most 1,000 results per call.
29
35
 
30
36
  ## State
31
37
 
32
- Pi session entry `add-dir:state` stores added directories; package-managed, do not edit. Tree navigation restores the active branch's directories and reloads resources when that set changes.
38
+ Pi session entry `add-dir:state` stores added directories. It is package-managed; do not edit it. Tree navigation restores the active branch's directories and reloads resources when that set changes.
@@ -1,5 +1,11 @@
1
- import { basename, isAbsolute, relative, sep } from "node:path";
2
- import { Text, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
1
+ import { basename, isAbsolute, join, relative, sep } from "node:path";
2
+ import {
3
+ Text,
4
+ truncateToWidth,
5
+ visibleWidth,
6
+ type AutocompleteProvider,
7
+ type AutocompleteSuggestions,
8
+ } from "@earendil-works/pi-tui";
3
9
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
4
10
  import { Type } from "typebox";
5
11
  import {
@@ -85,6 +91,67 @@ function contextSummary(dirCtx: DirContext): string {
85
91
  return found.length > 0 ? ` Found: ${found.join(", ")}.` : " No context files found.";
86
92
  }
87
93
 
94
+ function extractAtToken(textBeforeCursor: string): string | undefined {
95
+ return textBeforeCursor.match(/(?:^|[ \t])(@(?:"[^"]*|[^\s"]*))$/)?.[1];
96
+ }
97
+
98
+ export function createExternalAutocompleteProvider(
99
+ current: AutocompleteProvider,
100
+ getAddedDirs: () => AddedDir[],
101
+ ): AutocompleteProvider {
102
+ return {
103
+ async getSuggestions(lines, cursorLine, cursorCol, options): Promise<AutocompleteSuggestions | null> {
104
+ const line = lines[cursorLine] ?? "";
105
+ const token = extractAtToken(line.slice(0, cursorCol));
106
+ if (!token) return current.getSuggestions(lines, cursorLine, cursorCol, options);
107
+
108
+ const quoted = token.startsWith('@"');
109
+ const query = token.slice(quoted ? 2 : 1);
110
+ if (isAbsolute(query) || query.startsWith("~/") || query.startsWith("./") || query.startsWith("../")) {
111
+ return current.getSuggestions(lines, cursorLine, cursorCol, options);
112
+ }
113
+
114
+ const tokenStart = cursorCol - token.length;
115
+ const sources = await Promise.all([
116
+ current.getSuggestions(lines, cursorLine, cursorCol, options),
117
+ ...getAddedDirs().map(async (dir) => {
118
+ const externalPath = join(dir.absolutePath, query);
119
+ const externalToken = `${quoted || externalPath.includes(" ") ? '@"' : "@"}${externalPath}`;
120
+ const externalLines = [...lines];
121
+ externalLines[cursorLine] = `${line.slice(0, tokenStart)}${externalToken}${line.slice(cursorCol)}`;
122
+ const suggestions = await current.getSuggestions(
123
+ externalLines,
124
+ cursorLine,
125
+ tokenStart + externalToken.length,
126
+ options,
127
+ );
128
+ return suggestions
129
+ ? {
130
+ ...suggestions,
131
+ items: suggestions.items.map((item) => ({
132
+ ...item,
133
+ description: `${dir.label}: ${item.description ?? item.value}`,
134
+ })),
135
+ }
136
+ : null;
137
+ }),
138
+ ]);
139
+
140
+ const itemCount = Math.max(...sources.map((source) => source?.items.length ?? 0));
141
+ const items = Array.from({ length: itemCount }, (_, index) =>
142
+ sources.flatMap((source) => (source?.items[index] ? [source.items[index]] : [])),
143
+ ).flat();
144
+ return items.length > 0 ? { prefix: token, items } : null;
145
+ },
146
+ applyCompletion(lines, cursorLine, cursorCol, item, prefix) {
147
+ return current.applyCompletion(lines, cursorLine, cursorCol, item, prefix);
148
+ },
149
+ shouldTriggerFileCompletion(lines, cursorLine, cursorCol) {
150
+ return current.shouldTriggerFileCompletion?.(lines, cursorLine, cursorCol) ?? true;
151
+ },
152
+ };
153
+ }
154
+
88
155
  export default function addDirExtension(pi: ExtensionAPI): void {
89
156
  let addedDirs: AddedDir[] = [];
90
157
  let currentCwd = "";
@@ -212,6 +279,7 @@ export default function addDirExtension(pi: ExtensionAPI): void {
212
279
 
213
280
  pi.on("session_start", async (_event, ctx) => {
214
281
  reconstructState(ctx);
282
+ ctx.ui.addAutocompleteProvider((current) => createExternalAutocompleteProvider(current, () => addedDirs));
215
283
  });
216
284
  pi.on("session_tree", async (_event, ctx) => {
217
285
  if (reconstructState(ctx)) pi.sendUserMessage("/dir-reload", { expandPromptTemplates: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@henryqw/pi-add-dir",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "description": "Add external directories to a Pi session with context, skills, and file search.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -15,9 +15,9 @@
15
15
  },
16
16
  "license": "MIT",
17
17
  "files": [
18
+ "LICENSE",
18
19
  "extensions",
19
- "README.md",
20
- "LICENSE"
20
+ "README.md"
21
21
  ],
22
22
  "scripts": {
23
23
  "test": "node --test test/*.test.ts",