@henryqw/pi-add-dir 3.1.2 → 3.1.3

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
@@ -50,7 +50,7 @@ Package-owned: `~/.pi/agent/config/pi-add-dir/config.json`
50
50
  | --- | --- | --- | --- |
51
51
  | `directories` | Directories loaded for every workspace. `/dir-add --global` updates this list. | Array of unique absolute paths without control characters. | `[]` |
52
52
 
53
- If the file is missing, the extension uses the default. Invalid config stops the load, and the extension does not overwrite the file. Reload Pi after you edit the file manually.
53
+ If the file is missing, the extension uses the default. At launch, the extension shows a warning for each unknown key and each invalid or duplicate directory entry. It ignores that key or entry and loads the other directories. If the file is malformed or `directories` is not an array, it shows a warning and uses the default. It does not change the file. `/dir-add --global` and global removal with `/dir-ls` fail until you fix the reported issues. Reload Pi after you edit the file manually.
54
54
 
55
55
  ## State and storage
56
56
 
@@ -1,40 +1,11 @@
1
- import { isAbsolute } from "node:path";
2
- import { createConfigStore } from "@henryqw/pi-config-store";
3
-
4
- export interface AddDirConfig {
5
- directories: string[];
6
- }
7
-
8
- function parseConfig(value: unknown): AddDirConfig {
9
- if (!value || typeof value !== "object" || Array.isArray(value)) {
10
- throw new Error("Invalid pi-add-dir config: expected an object.");
11
- }
12
- const config = value as Record<string, unknown>;
13
- if (Object.keys(config).length !== 1 || !Array.isArray(config.directories)) {
14
- throw new Error('Invalid pi-add-dir config: expected only a "directories" array.');
15
- }
16
- const directories = config.directories;
17
- if (
18
- directories.some(
19
- (directory) =>
20
- typeof directory !== "string" ||
21
- !isAbsolute(directory) ||
22
- /\p{C}/u.test(directory),
23
- )
24
- ) {
25
- throw new Error("Invalid pi-add-dir config: directories must be absolute paths without control characters.");
26
- }
27
- if (new Set(directories).size !== directories.length) {
28
- throw new Error("Invalid pi-add-dir config: directories must be unique.");
29
- }
30
- return { directories: [...directories] };
31
- }
1
+ import { createConfigStore, field, parsers } from "@henryqw/pi-config-store";
32
2
 
33
3
  export function createAddDirConfigStore(agentDir?: string) {
34
- return createConfigStore<AddDirConfig>({
4
+ return createConfigStore({
35
5
  extensionId: "pi-add-dir",
36
6
  agentDir,
37
- defaults: () => ({ directories: [] }),
38
- parse: parseConfig,
7
+ fields: {
8
+ directories: field(parsers.array(parsers.absolutePath(), { unique: (directory) => directory }), []),
9
+ },
39
10
  });
40
11
  }
@@ -9,6 +9,7 @@ import {
9
9
  type AutocompleteSuggestions,
10
10
  } from "@earendil-works/pi-tui";
11
11
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
12
+ import { registerConfigReporting } from "@henryqw/pi-config-store";
12
13
  import { Type } from "typebox";
13
14
  import {
14
15
  buildContextInjection,
@@ -227,6 +228,9 @@ export function createExternalAutocompleteProvider(
227
228
 
228
229
  export default function addDirExtension(pi: ExtensionAPI, options: ExtensionOptions = {}): void {
229
230
  const globalStore = createAddDirConfigStore(options.agentDir);
231
+ const configReporter = registerConfigReporting(pi, [globalStore]);
232
+ // Report the latest file before a global write, so issues found after launch use the same deduplicated report.
233
+ const reportGlobalConfig = (ctx: ExtensionContext) => configReporter.report(ctx, globalStore, globalStore.loadSync());
230
234
  let sessionDirs: SessionDir[] = [];
231
235
  let projectPaths: string[] = [];
232
236
  let globalPaths: string[] = [];
@@ -234,17 +238,10 @@ export default function addDirExtension(pi: ExtensionAPI, options: ExtensionOpti
234
238
  let addedDirs: ScopedDir[] = [];
235
239
  let currentCwd = "";
236
240
 
237
- function globalConfigError(error: unknown): Error {
238
- const message = error instanceof Error ? error.message : String(error);
239
- return new Error(`Cannot load pi-add-dir global config at ${globalStore.path}: ${message}`, { cause: error });
240
- }
241
-
241
+ // No field disables this config, so a load always has a value; bad entries are dropped and reported at launch.
242
242
  function loadGlobalPaths(): string[] {
243
- try {
244
- return globalStore.loadSync().value.directories;
245
- } catch (error) {
246
- throw globalConfigError(error);
247
- }
243
+ const loaded = globalStore.loadSync();
244
+ return loaded.status === "ok" ? loaded.value.directories : [];
248
245
  }
249
246
 
250
247
  function updateWidget(ctx: ExtensionContext): void {
@@ -391,17 +388,14 @@ export default function addDirExtension(pi: ExtensionAPI, options: ExtensionOpti
391
388
 
392
389
  async function writePersistentPath(source: "project" | "global", absolutePath: string, ctx: ExtensionContext): Promise<void> {
393
390
  if (source === "global") {
394
- try {
395
- const config = await globalStore.update((current) => ({
396
- directories: current.directories.includes(absolutePath)
397
- ? current.directories
398
- : [...current.directories, absolutePath],
399
- }));
400
- globalPaths = config.directories;
401
- return;
402
- } catch (error) {
403
- throw globalConfigError(error);
404
- }
391
+ reportGlobalConfig(ctx);
392
+ const config = await globalStore.update((current) => ({
393
+ directories: current.directories.includes(absolutePath)
394
+ ? current.directories
395
+ : [...current.directories, absolutePath],
396
+ }));
397
+ globalPaths = config.directories;
398
+ return;
405
399
  }
406
400
  projectPaths = await readProjectPaths(ctx.cwd, true);
407
401
  if (projectPaths.some((path) => resolveDir(path, ctx.cwd) === absolutePath)) return;
@@ -498,14 +492,11 @@ export default function addDirExtension(pi: ExtensionAPI, options: ExtensionOpti
498
492
  );
499
493
  persistState();
500
494
  } else if (dir.source === "global") {
501
- try {
502
- const config = await globalStore.update((current) => ({
503
- directories: current.directories.filter((path) => path !== dir.storedPath),
504
- }));
505
- globalPaths = config.directories;
506
- } catch (error) {
507
- throw globalConfigError(error);
508
- }
495
+ reportGlobalConfig(ctx);
496
+ const config = await globalStore.update((current) => ({
497
+ directories: current.directories.filter((path) => path !== dir.storedPath),
498
+ }));
499
+ globalPaths = config.directories;
509
500
  } else {
510
501
  await mutateProjectConfig(
511
502
  ["--unset-all", "--fixed-value", PROJECT_CONFIG_KEY, dir.storedPath],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@henryqw/pi-add-dir",
3
- "version": "3.1.2",
3
+ "version": "3.1.3",
4
4
  "description": "Add external directories to a Pi session with context, skills, and file search.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -47,7 +47,7 @@
47
47
  "image": "https://raw.githubusercontent.com/HenryQW/pi-harness/main/extensions/pi-add-dir/example.png"
48
48
  },
49
49
  "dependencies": {
50
- "@henryqw/pi-config-store": "^3.0.0"
50
+ "@henryqw/pi-config-store": "^4.0.0"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@earendil-works/pi-coding-agent": "1.1.0",