@diegopetrucci/pi-extensions 0.1.22 → 0.1.24

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
@@ -5,6 +5,7 @@ A collection of [pi](https://github.com/earendil-works/pi-mono) agent extensions
5
5
  - [`confirm-destructive`](./extensions/confirm-destructive): Confirms before destructive session actions like clear, switch, and fork.
6
6
  - [`context-cap`](./extensions/context-cap): Caps effective model context windows at 200k tokens by default so pi avoids the `dumb zone`; toggle temporarily with `/context-cap`.
7
7
  - [`context-inspector`](./extensions/context-inspector): Adds `/context`, a local self-contained HTML dashboard that breaks down where the current session context is going, with category overview, top offenders, and drilldown search.
8
+ - [`dirty-repo-guard`](./extensions/dirty-repo-guard): Prompts before new sessions, session switches, or forks when the current git repo has uncommitted changes.
8
9
  - [`librarian`](./extensions/librarian): Adds a GitHub research scout with a local repo checkout cache enabled by default under the OS user cache directory, toggleable with `/librarian-cache`, with cached repos expiring after 30 days of non-use.
9
10
  - [`minimal-footer`](./extensions/minimal-footer): Replaces pi's built-in footer with a minimal configurable two-line layout: branch/repo on the first line, context/model on the second, optional `DUMB ZONE`, plus OpenAI Codex 5-hour and 7-day usage when available.
10
11
  - [`notify`](./extensions/notify): Sends configurable terminal, desktop, bell, and sound notifications when pi finishes and is ready for input.
@@ -26,7 +27,7 @@ pi install npm:@diegopetrucci/pi-extensions
26
27
  Or pin the GitHub package to this release:
27
28
 
28
29
  ```bash
29
- pi install git:github.com/diegopetrucci/pi-extensions@v0.1.22
30
+ pi install git:github.com/diegopetrucci/pi-extensions@v0.1.24
30
31
  ```
31
32
 
32
33
  Or a specific extension:
@@ -0,0 +1,47 @@
1
+ # dirty-repo-guard
2
+
3
+ A small pi extension that prompts before session changes when the current git repo has uncommitted changes.
4
+
5
+ This is copied from the original `dirty-repo-guard.ts` example in [`earendil-works/pi`](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/examples/extensions/dirty-repo-guard.ts) and kept basically the same.
6
+
7
+ ## What it checks
8
+
9
+ Before creating a new session, switching sessions, or forking, the extension runs:
10
+
11
+ ```bash
12
+ git status --porcelain
13
+ ```
14
+
15
+ If the command reports changed files, pi asks whether to proceed anyway. If pi is running without an interactive UI, matching actions are cancelled by default.
16
+
17
+ ## Install
18
+
19
+ ### Standalone npm package
20
+
21
+ ```bash
22
+ pi install npm:@diegopetrucci/pi-dirty-repo-guard
23
+ ```
24
+
25
+ ### Collection package
26
+
27
+ ```bash
28
+ pi install npm:@diegopetrucci/pi-extensions
29
+ ```
30
+
31
+ ### GitHub package
32
+
33
+ ```bash
34
+ pi install git:github.com/diegopetrucci/pi-extensions
35
+ ```
36
+
37
+ Then reload pi:
38
+
39
+ ```text
40
+ /reload
41
+ ```
42
+
43
+ ## Notes
44
+
45
+ - Hooks `session_before_switch` and `session_before_fork`.
46
+ - Allows session changes outside git repos.
47
+ - Cancels the action when the user declines.
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Dirty Repo Guard Extension
3
+ *
4
+ * Prevents session changes when there are uncommitted git changes.
5
+ * Useful to ensure work is committed before switching context.
6
+ */
7
+
8
+ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
9
+
10
+ async function checkDirtyRepo(
11
+ pi: ExtensionAPI,
12
+ ctx: ExtensionContext,
13
+ action: string,
14
+ ): Promise<{ cancel: boolean } | undefined> {
15
+ // Check for uncommitted changes
16
+ const { stdout, code } = await pi.exec("git", ["status", "--porcelain"]);
17
+
18
+ if (code !== 0) {
19
+ // Not a git repo, allow the action
20
+ return;
21
+ }
22
+
23
+ const hasChanges = stdout.trim().length > 0;
24
+ if (!hasChanges) {
25
+ return;
26
+ }
27
+
28
+ if (!ctx.hasUI) {
29
+ // In non-interactive mode, block by default
30
+ return { cancel: true };
31
+ }
32
+
33
+ // Count changed files
34
+ const changedFiles = stdout.trim().split("\n").filter(Boolean).length;
35
+
36
+ const choice = await ctx.ui.select(`You have ${changedFiles} uncommitted file(s). ${action} anyway?`, [
37
+ "Yes, proceed anyway",
38
+ "No, let me commit first",
39
+ ]);
40
+
41
+ if (choice !== "Yes, proceed anyway") {
42
+ ctx.ui.notify("Commit your changes first", "warning");
43
+ return { cancel: true };
44
+ }
45
+ }
46
+
47
+ export default function (pi: ExtensionAPI) {
48
+ pi.on("session_before_switch", async (event, ctx) => {
49
+ const action = event.reason === "new" ? "new session" : "switch session";
50
+ return checkDirtyRepo(pi, ctx, action);
51
+ });
52
+
53
+ pi.on("session_before_fork", async (_event, ctx) => {
54
+ return checkDirtyRepo(pi, ctx, "fork");
55
+ });
56
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@diegopetrucci/pi-dirty-repo-guard",
3
+ "version": "0.1.0",
4
+ "description": "A pi extension that prompts before session changes when the current git repo has uncommitted changes.",
5
+ "keywords": ["pi-package", "pi", "git", "safety"],
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/diegopetrucci/pi-extensions.git",
10
+ "directory": "extensions/dirty-repo-guard"
11
+ },
12
+ "files": [
13
+ "index.ts",
14
+ "README.md"
15
+ ],
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "pi": {
20
+ "extensions": [
21
+ "index.ts"
22
+ ]
23
+ },
24
+ "peerDependencies": {
25
+ "@earendil-works/pi-coding-agent": "*"
26
+ }
27
+ }
@@ -260,8 +260,13 @@ function getCacheConfigPath(): string {
260
260
  }
261
261
 
262
262
  function parseCacheMode(value: unknown): CacheMode | undefined {
263
- if (value === "enabled" || value === "on" || value === true) return "enabled";
264
- if (value === "disabled" || value === "off" || value === false) return "disabled";
263
+ if (typeof value === "string") {
264
+ const normalized = value.trim().toLowerCase();
265
+ if (normalized === "enabled" || normalized === "on" || normalized === "true") return "enabled";
266
+ if (normalized === "disabled" || normalized === "off" || normalized === "false") return "disabled";
267
+ }
268
+ if (value === true) return "enabled";
269
+ if (value === false) return "disabled";
265
270
  return undefined;
266
271
  }
267
272
 
@@ -280,8 +285,8 @@ async function readCachePreference(): Promise<CacheMode> {
280
285
  parseCacheMode(parsed.cache?.enabled) ??
281
286
  DEFAULT_CACHE_MODE
282
287
  );
283
- } catch {
284
- return DEFAULT_CACHE_MODE;
288
+ } catch (error) {
289
+ return (error as NodeJS.ErrnoException).code === "ENOENT" ? DEFAULT_CACHE_MODE : "disabled";
285
290
  }
286
291
  }
287
292
 
@@ -324,7 +329,12 @@ function resolveToolPath(cwd: string, rawPath: string): string {
324
329
  }
325
330
 
326
331
  function getBlockedBashReason(command: string, options: { workspace: string; cacheRoot: string; cacheEnabled: boolean }): string | undefined {
327
- const scan = command.split(options.workspace).join("<WORKSPACE>").split(options.cacheRoot).join("<CACHE>");
332
+ if (!options.cacheEnabled && command.includes(options.cacheRoot)) {
333
+ return "Local repo checkout cache is disabled for this Librarian call.";
334
+ }
335
+
336
+ let scan = command.split(options.workspace).join("<WORKSPACE>");
337
+ if (options.cacheEnabled) scan = scan.split(options.cacheRoot).join("<CACHE>");
328
338
  if (/(^|[\n;&|()])\s*\//.test(scan)) return "Librarian bash blocks absolute-path executables.";
329
339
 
330
340
  const destructiveLocal = /(^|[\n;&|()])\s*(?:sudo|su|rm|rmdir|mv|cp|chmod|chown|dd|truncate|killall|pkill|launchctl|osascript|pbcopy|pbpaste|eval|exec|xargs)(?=$|[\s;&|()])/;
@@ -580,9 +590,21 @@ export default function librarianExtension(pi: ExtensionAPI) {
580
590
  await fs.mkdir(path.join(workspace, "repos"), { recursive: true });
581
591
 
582
592
  const cacheRoot = getUserCacheReposRoot();
583
- const cacheDecision = resolveCacheDecision(cachePreference);
584
- if (cacheDecision.enabled) await fs.mkdir(cacheRoot, { recursive: true });
585
- const cleanup = cacheDecision.enabled ? await cleanupExpiredCache(cacheRoot) : { deleted: 0, errors: [] };
593
+ let cacheDecision = resolveCacheDecision(cachePreference);
594
+ let cleanup: { deleted: number; errors: string[] } = { deleted: 0, errors: [] };
595
+ if (cacheDecision.enabled) {
596
+ try {
597
+ await fs.mkdir(cacheRoot, { recursive: true });
598
+ cleanup = await cleanupExpiredCache(cacheRoot);
599
+ } catch (error) {
600
+ const message = error instanceof Error ? error.message : String(error);
601
+ cacheDecision = {
602
+ enabled: false,
603
+ reason: `cache setup failed (${message}); using GitHub API/temp files only`,
604
+ };
605
+ cleanup = { deleted: 0, errors: [`cache setup: ${message}`] };
606
+ }
607
+ }
586
608
 
587
609
  const details: LibrarianDetails = {
588
610
  status: "running",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-librarian",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "A pi GitHub research scout with a toggleable local repo checkout cache under the user's OS cache directory.",
5
5
  "keywords": ["pi-package", "pi", "github", "research", "subagent", "cache"],
6
6
  "license": "MIT",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-extensions",
3
- "version": "0.1.22",
4
- "description": "A collection of pi extensions, including a GitHub librarian with toggleable local repo checkout caching, a minimal custom footer, an Amp-style oracle, a 200k context cap for auto-compaction, a local HTML context inspector, OpenAI Codex Fast mode controls, quiet one-line collapsed invocation previews, a permission gate for dangerous bash commands, confirm-before-destructive session actions, and terminal notifications when pi is ready for input.",
3
+ "version": "0.1.24",
4
+ "description": "A collection of pi extensions, including a GitHub librarian with toggleable local repo checkout caching, a minimal custom footer, an Amp-style oracle, a 200k context cap for auto-compaction, a local HTML context inspector, a dirty repository guard for session changes, OpenAI Codex Fast mode controls, quiet one-line collapsed invocation previews, a permission gate for dangerous bash commands, confirm-before-destructive session actions, and terminal notifications when pi is ready for input.",
5
5
  "keywords": ["pi-package", "pi", "terminal", "agent"],
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -32,6 +32,7 @@
32
32
  "./extensions/oracle/index.ts",
33
33
  "./extensions/context-cap/index.ts",
34
34
  "./extensions/context-inspector/index.ts",
35
+ "./extensions/dirty-repo-guard/index.ts",
35
36
  "./extensions/librarian/index.ts",
36
37
  "./extensions/quiet-tools/index.ts",
37
38
  "./extensions/permission-gate/index.ts",