@trim21/personal-pi-extensions 0.1.494 → 0.1.495

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 +49 -26
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trim21/personal-pi-extensions",
3
- "version": "0.1.494",
3
+ "version": "0.1.495",
4
4
  "type": "module",
5
5
  "description": "Custom pi coding-agent extensions: bwrap sandbox, workspace guard, opencode edit, and more",
6
6
  "keywords": [
@@ -336,41 +336,57 @@ interface ListFilters {
336
336
  }
337
337
 
338
338
  /**
339
- * List or search issues/PRs with structured filters.
339
+ * Build the `gh` argv for listing or keyword-searching issues/PRs.
340
340
  *
341
- * `gh issue list` / `gh pr list` are used when a repo is available (repo param or
342
- * current directory), with keywords passed via `--search`. When no repo is given
343
- * and keywords are present, falls back to `gh search issues` / `gh search prs`
344
- * with plain keywords never embedding a `repo:` qualifier in the query string,
345
- * because `gh` mis-parses `repo:` values followed by spaces.
341
+ * Keyword searches route through `gh search issues` / `gh search prs`, which rank
342
+ * results by relevance and are the only path that can include closed items.
343
+ * `gh issue list --search` would silently stay on the list's state filter (open
344
+ * by default), so keyword lookups must not use it. Browse calls (no keywords)
345
+ * keep `gh issue list` / `gh pr list` semantics.
346
+ *
347
+ * The state value sets differ between the two command families: `gh issue list`
348
+ * accepts `--state all` and `gh pr list` `--state merged`, while `gh search`
349
+ * only knows `--state open|closed` plus `--merged` for PRs. The shared `state`
350
+ * param is translated here: search defaults to open, `all` means open+closed
351
+ * (no state filter), and `merged` maps to `--merged` for PRs.
346
352
  */
347
- async function listGithub(
348
- kind: "issue" | "pr",
349
- params: ListFilters,
350
- ctx: { cwd?: string; signal?: AbortSignal; input?: unknown },
351
- ): Promise<string> {
353
+ export function listGithubArgs(kind: "issue" | "pr", params: ListFilters): string[] {
352
354
  const { repo, keywords, state, label, author, assignee, milestone, limit } = params;
353
355
 
354
- if (!repo && keywords) {
355
- const args = ["search", kind === "issue" ? "issues" : "prs", keywords];
356
- if (state && state !== "all") args.push("--state", state);
356
+ const addFilters = (args: string[]) => {
357
357
  if (label) args.push("--label", label);
358
358
  if (author) args.push("--author", author);
359
359
  if (assignee) args.push("--assignee", assignee);
360
360
  if (milestone) args.push("--milestone", milestone);
361
361
  if (limit) args.push("--limit", String(limit));
362
- return ghExec(args, ctx);
362
+ return args;
363
+ };
364
+
365
+ if (keywords) {
366
+ const args = ["search", kind === "issue" ? "issues" : "prs", ...repoArgs(repo), keywords];
367
+ if (kind === "pr" && state === "merged") {
368
+ args.push("--merged");
369
+ } else if (state && state !== "all") {
370
+ args.push("--state", state);
371
+ } else if (!state) {
372
+ // keep the browse tools' default rather than gh search's implicit
373
+ // open+closed; pass state="all" to cover closed items
374
+ args.push("--state", "open");
375
+ }
376
+ return addFilters(args);
363
377
  }
364
378
 
365
379
  const args = [kind, "list", ...repoArgs(repo)];
366
380
  if (state) args.push("--state", state);
367
- if (keywords) args.push("--search", keywords);
368
- if (label) args.push("--label", label);
369
- if (author) args.push("--author", author);
370
- if (assignee) args.push("--assignee", assignee);
371
- if (milestone) args.push("--milestone", milestone);
372
- if (limit) args.push("--limit", String(limit));
373
- return ghExec(args, ctx);
381
+ return addFilters(args);
382
+ }
383
+
384
+ async function listGithub(
385
+ kind: "issue" | "pr",
386
+ params: ListFilters,
387
+ ctx: { cwd?: string; signal?: AbortSignal; input?: unknown },
388
+ ): Promise<string> {
389
+ return ghExec(listGithubArgs(kind, params), ctx);
374
390
  }
375
391
 
376
392
  // ── CI helpers ───────────────────────────────────────────────────────────────
@@ -1164,12 +1180,16 @@ export default function ghReadonlyTools(pi: ExtensionAPI) {
1164
1180
  name: "list-github-issues",
1165
1181
  label: "GitHub Issues List",
1166
1182
  description:
1167
- "List GitHub issues with optional filters and keyword search. When repo is omitted, searches across GitHub using keywords.",
1183
+ 'List GitHub issues with optional filters and keyword search. When repo is omitted, keyword search runs across GitHub. Keyword results default to open issues — pass state="all" to include closed ones.',
1168
1184
  promptSnippet: "List or search GitHub issues",
1169
1185
  parameters: Type.Object({
1170
1186
  repo: Type.Optional(Type.String({ description: "OWNER/REPO (defaults to current repo)" })),
1171
1187
  keywords: Type.Optional(Type.String({ description: "Search keywords (free text)" })),
1172
- state: Type.Optional(Type.String({ description: "open, closed, all (default: open)" })),
1188
+ state: Type.Optional(
1189
+ Type.String({
1190
+ description: "open, closed, all (default: open; with keywords, all covers closed too)",
1191
+ }),
1192
+ ),
1173
1193
  label: Type.Optional(Type.String({ description: "Filter by label" })),
1174
1194
  author: Type.Optional(Type.String({ description: "Filter by author" })),
1175
1195
  assignee: Type.Optional(Type.String({ description: "Filter by assignee" })),
@@ -1222,13 +1242,16 @@ export default function ghReadonlyTools(pi: ExtensionAPI) {
1222
1242
  name: "list-github-prs",
1223
1243
  label: "GitHub PRs List",
1224
1244
  description:
1225
- "List GitHub pull requests with optional filters and keyword search. When repo is omitted, searches across GitHub using keywords.",
1245
+ 'List GitHub pull requests with optional filters and keyword search. When repo is omitted, keyword search runs across GitHub. Keyword results default to open PRs — pass state="all" (open + closed) or state="merged" to broaden.',
1226
1246
  promptSnippet: "List or search GitHub PRs",
1227
1247
  parameters: Type.Object({
1228
1248
  repo: Type.Optional(Type.String({ description: "OWNER/REPO (defaults to current repo)" })),
1229
1249
  keywords: Type.Optional(Type.String({ description: "Search keywords (free text)" })),
1230
1250
  state: Type.Optional(
1231
- Type.String({ description: "open, closed, merged, all (default: open)" }),
1251
+ Type.String({
1252
+ description:
1253
+ "open, closed, merged, all (default: open; with keywords, merged and all broaden the search)",
1254
+ }),
1232
1255
  ),
1233
1256
  label: Type.Optional(Type.String({ description: "Filter by label" })),
1234
1257
  author: Type.Optional(Type.String({ description: "Filter by author" })),