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 +34 -8
- package/fileChangeReminderExtension.ts +57 -9
- package/package.json +6 -1
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
|
-
|
|
48
|
+
Preferred configuration uses Pi settings via the package-scoped `pi-file-change-reminder` block.
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
- `.pi`
|
|
50
|
+
Global `~/.pi/agent/settings.json`:
|
|
52
51
|
|
|
53
|
-
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"pi-file-change-reminder": {
|
|
55
|
+
"rulesFile": ".pi/reminders.json"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
54
59
|
|
|
55
|
-
|
|
60
|
+
Project `<cwd>/.pi/settings.json`:
|
|
56
61
|
|
|
57
|
-
```
|
|
58
|
-
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"pi-file-change-reminder": {
|
|
65
|
+
"rulesFile": "config/reminders.json"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
59
68
|
```
|
|
60
69
|
|
|
61
|
-
|
|
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
|
|
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
|
|
75
|
-
const
|
|
76
|
-
|
|
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 (
|
|
85
|
-
const projectDirectory = await resolveProjectDirectory(cwd);
|
|
86
|
-
|
|
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
|
|
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 =
|
|
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": "
|
|
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
|
}
|