@diegopetrucci/pi-extensions 0.1.80 → 0.1.82

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.
Files changed (53) hide show
  1. package/README.md +6 -1
  2. package/extensions/agent-workflow-audit/index.ts +47 -25
  3. package/extensions/agent-workflow-audit/package.json +1 -1
  4. package/extensions/brrr/index.ts +21 -15
  5. package/extensions/brrr/package.json +1 -1
  6. package/extensions/code-reviewer/index.ts +52 -19
  7. package/extensions/code-reviewer/package.json +1 -1
  8. package/extensions/contrarian/index.ts +17 -17
  9. package/extensions/contrarian/package.json +1 -1
  10. package/extensions/developer/.pi-fleet-tested-version +1 -0
  11. package/extensions/developer/README.md +70 -0
  12. package/extensions/developer/index.ts +906 -0
  13. package/extensions/developer/package.json +40 -0
  14. package/extensions/diff-summarizer/.pi-fleet-tested-version +1 -0
  15. package/extensions/diff-summarizer/README.md +75 -0
  16. package/extensions/diff-summarizer/index.ts +1585 -0
  17. package/extensions/diff-summarizer/package.json +40 -0
  18. package/extensions/dirty-repo-guard/index.ts +5 -2
  19. package/extensions/dirty-repo-guard/package.json +1 -1
  20. package/extensions/fast/README.md +1 -5
  21. package/extensions/fast/index.ts +13 -76
  22. package/extensions/fast/package.json +1 -1
  23. package/extensions/inline-bash/index.ts +1 -0
  24. package/extensions/inline-bash/package.json +1 -1
  25. package/extensions/librarian/index.ts +10 -0
  26. package/extensions/librarian/package.json +1 -1
  27. package/extensions/minimal-footer/index.ts +5 -1
  28. package/extensions/minimal-footer/package.json +1 -1
  29. package/extensions/notify/index.ts +7 -2
  30. package/extensions/notify/package.json +1 -1
  31. package/extensions/oracle/index.ts +17 -17
  32. package/extensions/oracle/package.json +1 -1
  33. package/extensions/permission-gate/README.md +6 -11
  34. package/extensions/permission-gate/index.ts +11 -54
  35. package/extensions/permission-gate/package.json +1 -3
  36. package/extensions/project-mcp-json/README.md +4 -3
  37. package/extensions/project-mcp-json/package.json +1 -1
  38. package/extensions/repo-scout/.pi-fleet-tested-version +1 -0
  39. package/extensions/repo-scout/README.md +74 -0
  40. package/extensions/repo-scout/index.ts +1575 -0
  41. package/extensions/repo-scout/package.json +40 -0
  42. package/extensions/test-runner/.pi-fleet-tested-version +1 -0
  43. package/extensions/test-runner/README.md +70 -0
  44. package/extensions/test-runner/index.ts +798 -0
  45. package/extensions/test-runner/package.json +40 -0
  46. package/extensions/triage-comments/index.ts +66 -16
  47. package/extensions/triage-comments/package.json +1 -1
  48. package/extensions/web-scout/.pi-fleet-tested-version +1 -0
  49. package/extensions/web-scout/README.md +67 -0
  50. package/extensions/web-scout/index.ts +1204 -0
  51. package/extensions/web-scout/package.json +41 -0
  52. package/package.json +7 -2
  53. package/extensions/permission-gate/powershell-safety.ts +0 -1053
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@diegopetrucci/pi-test-runner",
3
+ "version": "0.1.0",
4
+ "description": "A standalone pi extension that adds a test_runner subagent tool that runs exact validation steps in an isolated child agent.",
5
+ "keywords": [
6
+ "pi-package",
7
+ "pi",
8
+ "testing",
9
+ "validation",
10
+ "subagent"
11
+ ],
12
+ "license": "MIT",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/diegopetrucci/pi-extensions.git",
16
+ "directory": "extensions/test-runner"
17
+ },
18
+ "files": [
19
+ "index.ts",
20
+ "README.md",
21
+ ".pi-fleet-tested-version"
22
+ ],
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "pi": {
27
+ "extensions": [
28
+ "index.ts"
29
+ ]
30
+ },
31
+ "peerDependencies": {
32
+ "@earendil-works/pi-ai": "*",
33
+ "@earendil-works/pi-coding-agent": "*",
34
+ "typebox": "*"
35
+ },
36
+ "type": "module",
37
+ "engines": {
38
+ "node": ">=22.19.0"
39
+ }
40
+ }
@@ -175,11 +175,39 @@ const SAFE_GIT_BRANCH_FLAGS = new Set([
175
175
  "--remotes",
176
176
  "-v",
177
177
  "-vv",
178
+ "--verbose",
179
+ "-l",
180
+ "--list",
178
181
  "--show-current",
182
+ "--column",
183
+ "--no-column",
184
+ "--color",
185
+ "--no-color",
186
+ "-i",
187
+ "--ignore-case",
188
+ ]);
189
+ const SAFE_GIT_BRANCH_VALUE_FLAGS = new Set([
190
+ "--contains",
191
+ "--no-contains",
192
+ "--merged",
193
+ "--no-merged",
194
+ "--points-at",
195
+ "--sort",
196
+ "--format",
197
+ ]);
198
+ // Flags that make `git branch` list instead of create a branch from positional args.
199
+ const GIT_BRANCH_LISTING_FLAGS = new Set([
200
+ "-a",
201
+ "--all",
202
+ "-r",
203
+ "--remotes",
204
+ "-l",
179
205
  "--list",
180
206
  "--contains",
207
+ "--no-contains",
181
208
  "--merged",
182
209
  "--no-merged",
210
+ "--points-at",
183
211
  ]);
184
212
  const SAFE_GIT_GLOBAL_FLAGS = new Set(["--no-pager", "--no-optional-locks"]);
185
213
  const SAFE_GIT_CONFIG_OVERRIDES = [
@@ -1015,22 +1043,27 @@ function getGitSubcommand(tokens: string[]): {
1015
1043
  function isSafeGitBranchCommand(tokens: string[], subcommandIndex: number): boolean {
1016
1044
  const args = tokens.slice(subcommandIndex + 1);
1017
1045
  if (args.length === 0) return true;
1018
- if (
1019
- args.some(
1020
- (arg) =>
1021
- /^-(?:d|D|m|M|c|C|f)$/.test(arg) ||
1022
- /^--(?:delete|move|copy|force|set-upstream-to|unset-upstream)$/.test(arg),
1023
- )
1024
- ) {
1046
+ let hasListingMode = false;
1047
+ let hasPositional = false;
1048
+ for (const arg of args) {
1049
+ if (!arg.startsWith("-")) {
1050
+ hasPositional = true;
1051
+ continue;
1052
+ }
1053
+ const name = arg.split("=", 1)[0] ?? "";
1054
+ const hasValue = arg.includes("=");
1055
+ if (!hasValue && SAFE_GIT_BRANCH_FLAGS.has(arg)) {
1056
+ if (GIT_BRANCH_LISTING_FLAGS.has(arg)) hasListingMode = true;
1057
+ continue;
1058
+ }
1059
+ if (SAFE_GIT_BRANCH_VALUE_FLAGS.has(name)) {
1060
+ if (GIT_BRANCH_LISTING_FLAGS.has(name)) hasListingMode = true;
1061
+ continue;
1062
+ }
1025
1063
  return false;
1026
1064
  }
1027
- return args.some(
1028
- (arg) =>
1029
- SAFE_GIT_BRANCH_FLAGS.has(arg) ||
1030
- arg.startsWith("--contains=") ||
1031
- arg.startsWith("--merged=") ||
1032
- arg.startsWith("--no-merged="),
1033
- );
1065
+ if (args.includes("--show-current")) return !hasPositional;
1066
+ return !hasPositional || hasListingMode;
1034
1067
  }
1035
1068
 
1036
1069
  function isSafeGitRemoteCommand(tokens: string[], subcommandIndex: number): boolean {
@@ -1038,7 +1071,7 @@ function isSafeGitRemoteCommand(tokens: string[], subcommandIndex: number): bool
1038
1071
  if (args.length === 0) return true;
1039
1072
  if (args.length === 1 && (args[0] === "-v" || args[0] === "--verbose")) return true;
1040
1073
  const remoteAction = args.find((arg) => !arg.startsWith("-"));
1041
- return remoteAction === "show" || remoteAction === "get-url";
1074
+ return remoteAction === "get-url";
1042
1075
  }
1043
1076
 
1044
1077
  function isBlockedGitLocalFileOption(
@@ -1143,7 +1176,7 @@ function getBlockedGitReason(command: string): string | undefined {
1143
1176
  }
1144
1177
  if (parsed.subcommand === "remote") {
1145
1178
  if (!isSafeGitRemoteCommand(tokens, parsed.index)) {
1146
- return "Triage bash allows git remote only for read-only list/show/get-url queries.";
1179
+ return "Triage bash allows git remote only for read-only list or get-url queries.";
1147
1180
  }
1148
1181
  return undefined;
1149
1182
  }
@@ -1193,6 +1226,9 @@ function getBlockedGhApiReason(tokens: string[]): string | undefined {
1193
1226
  ) {
1194
1227
  return "Triage bash allows read-only gh api calls only; request fields and input files are blocked.";
1195
1228
  }
1229
+ if (token === "--cache" || lowerToken.startsWith("--cache=")) {
1230
+ return "Triage bash blocks gh api --cache because it writes local cache files.";
1231
+ }
1196
1232
 
1197
1233
  let method: string | undefined;
1198
1234
  if (lowerToken === "-x" || lowerToken === "--method") {
@@ -1238,6 +1274,20 @@ function getBlockedGhReason(command: string): string | undefined {
1238
1274
  if (/\bgh\s+auth\s+(?:login|logout|refresh|setup-git|token)\b/i.test(command)) {
1239
1275
  return "Triage bash blocks gh auth commands and token inspection.";
1240
1276
  }
1277
+ if (
1278
+ tokens.some((token) => {
1279
+ const lowerToken = token.toLowerCase();
1280
+ return (
1281
+ lowerToken === "--web" ||
1282
+ lowerToken === "-w" ||
1283
+ (/^-[a-z]{2,}$/i.test(token) && lowerToken.includes("w")) ||
1284
+ lowerToken.startsWith("--web=") ||
1285
+ lowerToken.startsWith("-w=")
1286
+ );
1287
+ })
1288
+ ) {
1289
+ return "Triage bash blocks gh --web because it can launch external browser helpers.";
1290
+ }
1241
1291
  const apiReason = getBlockedGhApiReason(tokens);
1242
1292
  if (apiReason) return apiReason;
1243
1293
  if (
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-triage-comments",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "description": "A pi extension that adds /triage-comments and a read-only triage_comments subagent tool for review-comment triage.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -0,0 +1,67 @@
1
+ # web-scout
2
+
3
+ Adds a `web_scout` subagent tool that adapts The Last Harness [`web-scout`](https://github.com/diegopetrucci/the-last-harness/blob/main/agents/subagents/web-scout.md) prompt into a standalone pi extension.
4
+
5
+ Use it for focused, read-only web research that needs cited sources. The subagent searches and fetches pages through the hosted [Exa](https://exa.ai) MCP endpoint and reports findings with URLs, retrieval timestamps, and short verbatim quotes. Blockers are reported in the final result; the subagent cannot ask follow-up questions.
6
+
7
+ ## Install
8
+
9
+ ### Standalone npm package
10
+
11
+ ```bash
12
+ pi install npm:@diegopetrucci/pi-web-scout
13
+ ```
14
+
15
+ ### Collection package
16
+
17
+ ```bash
18
+ pi install npm:@diegopetrucci/pi-extensions
19
+ ```
20
+
21
+ ### GitHub package
22
+
23
+ ```bash
24
+ pi install git:github.com/diegopetrucci/pi-extensions
25
+ ```
26
+
27
+ Then reload pi:
28
+
29
+ ```text
30
+ /reload
31
+ ```
32
+
33
+ ## `web_scout` tool behavior
34
+
35
+ The tool accepts:
36
+
37
+ - `task` — required research question
38
+ - `cwd` — optional working directory used for local read-only context; defaults to the current session directory
39
+
40
+ The subagent runs in-process in an isolated child session with:
41
+
42
+ - the current session model (no automatic model selection and no persisted `/config` commands);
43
+ - `medium` thinking, clamped to the model's supported levels;
44
+ - `web_search` and `fetch_url`, implemented in this package by calling `https://mcp.exa.ai/mcp` with plain JSON-RPC over HTTP (`web_search_exa` / `web_fetch_exa`); no SDK dependency;
45
+ - read-only built-in tools: `read`, `grep`, `find`, `ls`;
46
+ - configured MCP tools (run without confirmation in the subagent; may have side effects; requires host Pi >=1.0.4; older hosts degrade to no MCP, no error); the global `mcp.json` always applies; a project `.pi/mcp.json` is loaded only when the host session has already trusted the project;
47
+ - a local-checkout path guard for file inspection.
48
+
49
+ The output is a markdown report: research target, key findings with citations, limitations, and next steps. The tool appends a run-details suffix with the model, effective thinking level, turn count, tool calls, duration, working directory, and tools used.
50
+
51
+ ## API key
52
+
53
+ No API key is required. If `EXA_API_KEY` is set in the environment it is passed to Exa (`exaApiKey` query parameter) for higher rate limits. The key is never logged or echoed: it is redacted from error messages and is not shown to the model.
54
+
55
+ ## Guarantees
56
+
57
+ - `fetch_url` accepts only `http://` and `https://` URLs; other schemes are rejected before any request.
58
+ - Each Exa request has a 30 second timeout; tool output is truncated (20,000 characters), and `fetch_url` page size is capped.
59
+ - Rate limits (HTTP 429 or Exa rate-limit errors) and network failures produce clear tool errors rather than silent empty results.
60
+ - Fetched content is treated as untrusted data; the prompt requires citations and forbids fabrication.
61
+ - Runtime guards block write/edit/bash tools and path traversal outside the checkout. Guards apply to built-in tools only; configured MCP tools pass the guard and are limited only by prompt guidance.
62
+
63
+ ## Limitations
64
+
65
+ - The subagent has an 8-turn and 8-minute budget; tool use is disabled on the final turn so it can answer.
66
+ - Web access depends on the Exa hosted endpoint's availability and unauthenticated rate limits.
67
+ - Search and fetch contents are sent to Exa; do not include secrets in the research task.