@trim21/personal-pi-extensions 0.0.125 → 0.0.126

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/gh-readonly.ts +73 -68
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trim21/personal-pi-extensions",
3
- "version": "0.0.125",
3
+ "version": "0.0.126",
4
4
  "type": "module",
5
5
  "description": "Custom pi coding-agent extensions: bwrap sandbox, workspace guard, opencode edit, and more",
6
6
  "keywords": [
@@ -5,10 +5,10 @@
5
5
  *
6
6
  * Tools:
7
7
  * - read-github-issue: Get issue details
8
- * - list-github-issues: List issues
8
+ * - list-github-issues: List or search issues
9
9
  * - read-github-issue-comments: Get issue comments
10
10
  * - read-github-pr: Get PR details
11
- * - list-github-prs: List PRs
11
+ * - list-github-prs: List or search PRs
12
12
  * - read-github-pr-diff: Get PR diff
13
13
  * - read-github-pr-status: Get PR status checks
14
14
  * - read-github-pr-comments: Get PR comments
@@ -20,8 +20,6 @@
20
20
  * - read-github-release: Get release details
21
21
  * - wait-github-pr-checks: Watch PR CI checks
22
22
  * - watch-github-run: Watch a workflow run
23
- * - search-github-issues: Search GitHub issues
24
- * - search-github-prs: Search GitHub pull requests
25
23
  *
26
24
  * Install:
27
25
  * cp gh-readonly.ts ~/.pi/agent/extensions/
@@ -214,6 +212,55 @@ function toToolResult(stdout: string): {
214
212
  return { content: [{ type: "text", text }], details: { truncated } };
215
213
  }
216
214
 
215
+ interface ListFilters {
216
+ repo?: string;
217
+ keywords?: string;
218
+ state?: string;
219
+ label?: string;
220
+ author?: string;
221
+ assignee?: string;
222
+ milestone?: string;
223
+ limit?: number;
224
+ }
225
+
226
+ /**
227
+ * List or search issues/PRs with structured filters.
228
+ *
229
+ * `gh issue list` / `gh pr list` are used when a repo is available (repo param or
230
+ * current directory), with keywords passed via `--search`. When no repo is given
231
+ * and keywords are present, falls back to `gh search issues` / `gh search prs`
232
+ * with plain keywords — never embedding a `repo:` qualifier in the query string,
233
+ * because `gh` mis-parses `repo:` values followed by spaces.
234
+ */
235
+ async function listGithub(
236
+ kind: "issue" | "pr",
237
+ params: ListFilters,
238
+ ctx: { cwd?: string; signal?: AbortSignal; input?: unknown },
239
+ ): Promise<string> {
240
+ const { repo, keywords, state, label, author, assignee, milestone, limit } = params;
241
+
242
+ if (!repo && keywords) {
243
+ const args = ["search", kind === "issue" ? "issues" : "prs", keywords];
244
+ if (state && state !== "all") args.push("--state", state);
245
+ if (label) args.push("--label", label);
246
+ if (author) args.push("--author", author);
247
+ if (assignee) args.push("--assignee", assignee);
248
+ if (milestone) args.push("--milestone", milestone);
249
+ if (limit) args.push("--limit", String(limit));
250
+ return ghExec(args, ctx);
251
+ }
252
+
253
+ const args = [kind, "list", ...repoArgs(repo)];
254
+ if (state) args.push("--state", state);
255
+ if (keywords) args.push("--search", keywords);
256
+ if (label) args.push("--label", label);
257
+ if (author) args.push("--author", author);
258
+ if (assignee) args.push("--assignee", assignee);
259
+ if (milestone) args.push("--milestone", milestone);
260
+ if (limit) args.push("--limit", String(limit));
261
+ return ghExec(args, ctx);
262
+ }
263
+
217
264
  // ── CI helpers ───────────────────────────────────────────────────────────────
218
265
 
219
266
  export interface StepInfo {
@@ -444,19 +491,23 @@ export default function (pi: ExtensionAPI) {
444
491
  pi.registerTool({
445
492
  name: "list-github-issues",
446
493
  label: "GitHub Issues List",
447
- description: "List GitHub issues with optional filters.",
448
- promptSnippet: "List GitHub issues",
494
+ description:
495
+ "List GitHub issues with optional filters and keyword search. When repo is omitted, searches across GitHub using keywords.",
496
+ promptSnippet: "List or search GitHub issues",
449
497
  parameters: Type.Object({
450
- repo: Type.Optional(Type.String({ description: "OWNER/REPO" })),
498
+ repo: Type.Optional(Type.String({ description: "OWNER/REPO (defaults to current repo)" })),
499
+ keywords: Type.Optional(Type.String({ description: "Search keywords (free text)" })),
451
500
  state: Type.Optional(Type.String({ description: "open, closed, all (default: open)" })),
501
+ label: Type.Optional(Type.String({ description: "Filter by label" })),
502
+ author: Type.Optional(Type.String({ description: "Filter by author" })),
503
+ assignee: Type.Optional(Type.String({ description: "Filter by assignee" })),
504
+ milestone: Type.Optional(Type.String({ description: "Filter by milestone" })),
452
505
  limit: Type.Optional(Type.Number({ description: "Max results (default 30)" })),
453
506
  }),
454
507
  async execute(_id, params, signal, _onUpdate, ctx) {
455
- const { repo, state, limit } = params;
456
- const args = ["issue", "list", ...repoArgs(repo)];
457
- if (state) args.push("--state", state);
458
- if (limit) args.push("--limit", String(limit));
459
- return toToolResult(await ghExec(args, { cwd: ctx.cwd, signal, input: params }));
508
+ return toToolResult(
509
+ await listGithub("issue", params, { cwd: ctx.cwd, signal, input: params }),
510
+ );
460
511
  },
461
512
  });
462
513
 
@@ -492,21 +543,23 @@ export default function (pi: ExtensionAPI) {
492
543
  pi.registerTool({
493
544
  name: "list-github-prs",
494
545
  label: "GitHub PRs List",
495
- description: "List GitHub pull requests with optional filters.",
496
- promptSnippet: "List GitHub PRs",
546
+ description:
547
+ "List GitHub pull requests with optional filters and keyword search. When repo is omitted, searches across GitHub using keywords.",
548
+ promptSnippet: "List or search GitHub PRs",
497
549
  parameters: Type.Object({
498
- repo: Type.Optional(Type.String({ description: "OWNER/REPO" })),
550
+ repo: Type.Optional(Type.String({ description: "OWNER/REPO (defaults to current repo)" })),
551
+ keywords: Type.Optional(Type.String({ description: "Search keywords (free text)" })),
499
552
  state: Type.Optional(
500
553
  Type.String({ description: "open, closed, merged, all (default: open)" }),
501
554
  ),
555
+ label: Type.Optional(Type.String({ description: "Filter by label" })),
556
+ author: Type.Optional(Type.String({ description: "Filter by author" })),
557
+ assignee: Type.Optional(Type.String({ description: "Filter by assignee" })),
558
+ milestone: Type.Optional(Type.String({ description: "Filter by milestone" })),
502
559
  limit: Type.Optional(Type.Number({ description: "Max results (default 30)" })),
503
560
  }),
504
561
  async execute(_id, params, signal, _onUpdate, ctx) {
505
- const { repo, state, limit } = params;
506
- const args = ["pr", "list", ...repoArgs(repo)];
507
- if (state) args.push("--state", state);
508
- if (limit) args.push("--limit", String(limit));
509
- return toToolResult(await ghExec(args, { cwd: ctx.cwd, signal, input: params }));
562
+ return toToolResult(await listGithub("pr", params, { cwd: ctx.cwd, signal, input: params }));
510
563
  },
511
564
  });
512
565
 
@@ -1172,52 +1225,4 @@ export default function (pi: ExtensionAPI) {
1172
1225
  };
1173
1226
  },
1174
1227
  });
1175
-
1176
- // ── search-github-issues ───────────────────────────────────────────────────
1177
- pi.registerTool({
1178
- name: "search-github-issues",
1179
- label: "GitHub Issue Search",
1180
- description: "Search GitHub issues using GitHub search syntax.",
1181
- promptSnippet: "Search GitHub issues",
1182
- parameters: Type.Object({
1183
- query: Type.String({
1184
- description:
1185
- "GitHub search syntax (e.g. 'repo:owner/name keyword', 'is:open label:bug'). Do NOT include 'type:issue' or 'type:pr' qualifiers.",
1186
- }),
1187
- include_prs: Type.Optional(
1188
- Type.Boolean({
1189
- description: "Whether to include pull requests in results (default: false)",
1190
- }),
1191
- ),
1192
- limit: Type.Optional(Type.Number({ description: "Max results (default 20)" })),
1193
- }),
1194
- async execute(_id, params, signal, _onUpdate, ctx) {
1195
- const { query, include_prs, limit } = params;
1196
- const args = ["search", "issues", query];
1197
- if (include_prs) args.push("--include-prs");
1198
- if (limit) args.push("--limit", String(limit));
1199
- return toToolResult(await ghExec(args, { cwd: ctx.cwd, signal, input: params }));
1200
- },
1201
- });
1202
-
1203
- // ── search-github-prs ──────────────────────────────────────────────────────
1204
- pi.registerTool({
1205
- name: "search-github-prs",
1206
- label: "GitHub PR Search",
1207
- description: "Search GitHub pull requests using GitHub search syntax.",
1208
- promptSnippet: "Search GitHub PRs",
1209
- parameters: Type.Object({
1210
- query: Type.String({
1211
- description:
1212
- "GitHub search syntax (e.g. 'repo:owner/name keyword', 'is:open label:bug'). Do NOT include 'type:issue' or 'type:pr' qualifiers.",
1213
- }),
1214
- limit: Type.Optional(Type.Number({ description: "Max results (default 20)" })),
1215
- }),
1216
- async execute(_id, params, signal, _onUpdate, ctx) {
1217
- const { query, limit } = params;
1218
- const args = ["search", "prs", query];
1219
- if (limit) args.push("--limit", String(limit));
1220
- return toToolResult(await ghExec(args, { cwd: ctx.cwd, signal, input: params }));
1221
- },
1222
- });
1223
1228
  }