pi-file-change-reminder 1.0.0 → 2.0.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/README.md CHANGED
@@ -45,20 +45,38 @@ You can also include a specific change request:
45
45
 
46
46
  ## Rules file resolution
47
47
 
48
- Default rules path is `.pi/reminders.json` resolved from the **nearest ancestor directory** containing either:
48
+ Preferred configuration uses Pi settings via the package-scoped `pi-file-change-reminder` block.
49
49
 
50
- - `.git`, or
51
- - `.pi`
50
+ Global `~/.pi/agent/settings.json`:
52
51
 
53
- If no ancestor contains either marker, resolution falls back to Pi's current working directory.
52
+ ```json
53
+ {
54
+ "pi-file-change-reminder": {
55
+ "rulesFile": ".pi/reminders.json"
56
+ }
57
+ }
58
+ ```
54
59
 
55
- You can override with `PI_REMINDERS_FILE`:
60
+ Project `<cwd>/.pi/settings.json`:
56
61
 
57
- ```bash
58
- export PI_REMINDERS_FILE=/absolute/path/to/reminders.json
62
+ ```json
63
+ {
64
+ "pi-file-change-reminder": {
65
+ "rulesFile": "config/reminders.json"
66
+ }
67
+ }
59
68
  ```
60
69
 
61
- If `PI_REMINDERS_FILE` is relative, it is resolved from the detected project directory (same logic as above).
70
+ The extension reads both scopes through Pi's `SettingsManager`, with project settings overriding global settings.
71
+
72
+ If no setting is configured, the default rules path is `.pi/reminders.json` resolved from the **nearest ancestor directory** containing either:
73
+
74
+ - `.git`, or
75
+ - `.pi`
76
+
77
+ If no ancestor contains either marker, resolution falls back to Pi's current working directory.
78
+
79
+ Relative `rulesFile` values are resolved from that detected project directory. Absolute `rulesFile` values are used as-is.
62
80
 
63
81
  ## Glob behavior
64
82
 
@@ -73,8 +91,16 @@ If `PI_REMINDERS_FILE` is relative, it is resolved from the detected project dir
73
91
 
74
92
  ## Development
75
93
 
94
+ This package uses npm.
95
+
76
96
  ```bash
77
97
  npm install
98
+ npm run check
99
+ ```
100
+
101
+ Or run the individual checks:
102
+
103
+ ```bash
78
104
  npm run typecheck
79
105
  npm run verify:pi-load
80
106
  ```
@@ -1,7 +1,7 @@
1
1
  import { readFile, stat } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import picomatch from "picomatch";
4
- import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
4
+ import { SettingsManager, type ExtensionAPI, type ExtensionContext } from "@mariozechner/pi-coding-agent";
5
5
 
6
6
  interface IReminderRule {
7
7
  glob: string;
@@ -13,6 +13,7 @@ interface IToolInputWithPath {
13
13
  }
14
14
 
15
15
  const EXTENSION_ID = "file-change-reminder";
16
+ const SETTINGS_NAMESPACE = "pi-file-change-reminder";
16
17
  const DEFAULT_RULES_FILE = ".pi/reminders.json";
17
18
  const COMMAND_NAME = "pi-file-change-reminder";
18
19
  const INJECTED_ENTRY_TYPE = `${EXTENSION_ID}.injected`;
@@ -22,6 +23,7 @@ export default function fileChangeReminderExtension(pi: ExtensionAPI): void {
22
23
  let rulesMtimeMs = -1;
23
24
  let hasLoadedRules = false;
24
25
  let lastLoadError = "";
26
+ let lastSettingsError = "";
25
27
  let injectedReminderKeys = new Set<string>();
26
28
  const globMatcherCache = new Map<string, (candidatePath: string) => boolean>();
27
29
  const projectDirectoryCache = new Map<string, string>();
@@ -71,9 +73,41 @@ export default function fileChangeReminderExtension(pi: ExtensionAPI): void {
71
73
  }
72
74
  };
73
75
 
74
- const resolveRulesFilePathFromProjectDirectory = (projectDirectory: string): string => {
75
- const envValue = process.env.PI_REMINDERS_FILE;
76
- const configuredPath = readNonEmptyString(envValue) ?? DEFAULT_RULES_FILE;
76
+ const reportSettingsErrors = (ctx: ExtensionContext, settingsManager: SettingsManager): void => {
77
+ const settingsErrors = settingsManager.drainErrors();
78
+ if (settingsErrors.length === 0) {
79
+ lastSettingsError = "";
80
+ return;
81
+ }
82
+
83
+ const settingsErrorMessage = settingsErrors
84
+ .map(({ scope, error }) => `${scope}: ${getErrorMessage(error)}`)
85
+ .join("; ");
86
+ if (settingsErrorMessage !== lastSettingsError && ctx.hasUI) {
87
+ ctx.ui.notify(`Failed to load reminder settings: ${settingsErrorMessage}`, "warning");
88
+ }
89
+
90
+ lastSettingsError = settingsErrorMessage;
91
+ };
92
+
93
+ const getConfiguredRulesFilePath = (ctx: ExtensionContext): string | null => {
94
+ const settingsManager = SettingsManager.create(ctx.cwd);
95
+ reportSettingsErrors(ctx, settingsManager);
96
+
97
+ const projectConfiguredPath = readRulesFilePathFromSettings(settingsManager.getProjectSettings());
98
+ if (projectConfiguredPath !== null) {
99
+ return projectConfiguredPath;
100
+ }
101
+
102
+ const globalConfiguredPath = readRulesFilePathFromSettings(settingsManager.getGlobalSettings());
103
+ if (globalConfiguredPath !== null) {
104
+ return globalConfiguredPath;
105
+ }
106
+
107
+ return null;
108
+ };
109
+
110
+ const resolveRulesFilePathFromProjectDirectory = (projectDirectory: string, configuredPath: string): string => {
77
111
  if (path.isAbsolute(configuredPath)) {
78
112
  return configuredPath;
79
113
  }
@@ -81,13 +115,14 @@ export default function fileChangeReminderExtension(pi: ExtensionAPI): void {
81
115
  return path.resolve(projectDirectory, configuredPath);
82
116
  };
83
117
 
84
- const resolveRulesFilePath = async (cwd: string): Promise<string> => {
85
- const projectDirectory = await resolveProjectDirectory(cwd);
86
- return resolveRulesFilePathFromProjectDirectory(projectDirectory);
118
+ const resolveRulesFilePath = async (ctx: ExtensionContext): Promise<string> => {
119
+ const projectDirectory = await resolveProjectDirectory(ctx.cwd);
120
+ const configuredPath = getConfiguredRulesFilePath(ctx) ?? DEFAULT_RULES_FILE;
121
+ return resolveRulesFilePathFromProjectDirectory(projectDirectory, configuredPath);
87
122
  };
88
123
 
89
124
  const loadRules = async (ctx: ExtensionContext, force: boolean): Promise<IReminderRule[]> => {
90
- const rulesFilePath = await resolveRulesFilePath(ctx.cwd);
125
+ const rulesFilePath = await resolveRulesFilePath(ctx);
91
126
 
92
127
  let mtimeMs = -1;
93
128
  try {
@@ -181,7 +216,7 @@ export default function fileChangeReminderExtension(pi: ExtensionAPI): void {
181
216
  description: "Inject a prompt that explains how to update reminder rules",
182
217
  handler: async (args, ctx) => {
183
218
  const projectDirectory = await resolveProjectDirectory(ctx.cwd);
184
- const rulesFilePath = resolveRulesFilePathFromProjectDirectory(projectDirectory);
219
+ const rulesFilePath = await resolveRulesFilePath(ctx);
185
220
  const normalizedRulesFilePath = normalizePath(rulesFilePath);
186
221
  const normalizedProjectDirectory = normalizePath(projectDirectory);
187
222
  const prompt = buildReminderEditPrompt(normalizedRulesFilePath, normalizedProjectDirectory, args);
@@ -366,6 +401,19 @@ function buildReminderEditPrompt(rulesFilePath: string, projectDirectory: string
366
401
  ].join("\n");
367
402
  }
368
403
 
404
+ function readRulesFilePathFromSettings(settings: unknown): string | null {
405
+ if (!isRecord(settings)) {
406
+ return null;
407
+ }
408
+
409
+ const extensionSettings = settings[SETTINGS_NAMESPACE];
410
+ if (!isRecord(extensionSettings)) {
411
+ return null;
412
+ }
413
+
414
+ return readNonEmptyString(extensionSettings.rulesFile);
415
+ }
416
+
369
417
  async function pathExists(filePath: string): Promise<boolean> {
370
418
  try {
371
419
  await stat(filePath);
package/package.json CHANGED
@@ -1,9 +1,13 @@
1
1
  {
2
2
  "name": "pi-file-change-reminder",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "description": "Pi extension that injects reminder messages when specific files are modified.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/alexgorbatchev/pi-file-change-reminder.git"
10
+ },
7
11
  "keywords": [
8
12
  "pi-package",
9
13
  "pi-extension",
@@ -30,6 +34,7 @@
30
34
  "typescript": "^5.9.3"
31
35
  },
32
36
  "scripts": {
37
+ "check": "npm run typecheck && npm run verify:pi-load",
33
38
  "typecheck": "tsc --noEmit -p tsconfig.json",
34
39
  "verify:pi-load": "PI_OFFLINE=1 pi --no-extensions -e ./fileChangeReminderExtension.ts --list-models > /dev/null"
35
40
  }