@paretools/github 0.8.5 → 0.9.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.
Files changed (82) hide show
  1. package/dist/lib/formatters.d.ts.map +1 -1
  2. package/dist/lib/formatters.js +112 -16
  3. package/dist/lib/formatters.js.map +1 -1
  4. package/dist/lib/parsers.d.ts +33 -11
  5. package/dist/lib/parsers.d.ts.map +1 -1
  6. package/dist/lib/parsers.js +309 -24
  7. package/dist/lib/parsers.js.map +1 -1
  8. package/dist/lib/path-validation.d.ts +13 -0
  9. package/dist/lib/path-validation.d.ts.map +1 -0
  10. package/dist/lib/path-validation.js +54 -0
  11. package/dist/lib/path-validation.js.map +1 -0
  12. package/dist/schemas/index.d.ts +126 -3
  13. package/dist/schemas/index.d.ts.map +1 -1
  14. package/dist/schemas/index.js +116 -3
  15. package/dist/schemas/index.js.map +1 -1
  16. package/dist/tools/api.d.ts.map +1 -1
  17. package/dist/tools/api.js +135 -5
  18. package/dist/tools/api.js.map +1 -1
  19. package/dist/tools/gist-create.d.ts.map +1 -1
  20. package/dist/tools/gist-create.js +70 -16
  21. package/dist/tools/gist-create.js.map +1 -1
  22. package/dist/tools/issue-close.d.ts.map +1 -1
  23. package/dist/tools/issue-close.js +31 -5
  24. package/dist/tools/issue-close.js.map +1 -1
  25. package/dist/tools/issue-comment.d.ts.map +1 -1
  26. package/dist/tools/issue-comment.js +43 -6
  27. package/dist/tools/issue-comment.js.map +1 -1
  28. package/dist/tools/issue-create.d.ts.map +1 -1
  29. package/dist/tools/issue-create.js +69 -5
  30. package/dist/tools/issue-create.js.map +1 -1
  31. package/dist/tools/issue-list.d.ts.map +1 -1
  32. package/dist/tools/issue-list.js +79 -3
  33. package/dist/tools/issue-list.js.map +1 -1
  34. package/dist/tools/issue-update.d.ts.map +1 -1
  35. package/dist/tools/issue-update.js +102 -11
  36. package/dist/tools/issue-update.js.map +1 -1
  37. package/dist/tools/issue-view.d.ts.map +1 -1
  38. package/dist/tools/issue-view.js +26 -6
  39. package/dist/tools/issue-view.js.map +1 -1
  40. package/dist/tools/pr-checks.d.ts.map +1 -1
  41. package/dist/tools/pr-checks.js +27 -7
  42. package/dist/tools/pr-checks.js.map +1 -1
  43. package/dist/tools/pr-comment.d.ts.map +1 -1
  44. package/dist/tools/pr-comment.js +43 -6
  45. package/dist/tools/pr-comment.js.map +1 -1
  46. package/dist/tools/pr-create.d.ts.map +1 -1
  47. package/dist/tools/pr-create.js +125 -3
  48. package/dist/tools/pr-create.js.map +1 -1
  49. package/dist/tools/pr-diff.d.ts.map +1 -1
  50. package/dist/tools/pr-diff.js +36 -4
  51. package/dist/tools/pr-diff.js.map +1 -1
  52. package/dist/tools/pr-list.d.ts.map +1 -1
  53. package/dist/tools/pr-list.js +83 -3
  54. package/dist/tools/pr-list.js.map +1 -1
  55. package/dist/tools/pr-merge.d.ts.map +1 -1
  56. package/dist/tools/pr-merge.js +77 -6
  57. package/dist/tools/pr-merge.js.map +1 -1
  58. package/dist/tools/pr-review.d.ts.map +1 -1
  59. package/dist/tools/pr-review.js +43 -6
  60. package/dist/tools/pr-review.js.map +1 -1
  61. package/dist/tools/pr-update.d.ts.map +1 -1
  62. package/dist/tools/pr-update.js +110 -5
  63. package/dist/tools/pr-update.js.map +1 -1
  64. package/dist/tools/pr-view.d.ts.map +1 -1
  65. package/dist/tools/pr-view.js +27 -6
  66. package/dist/tools/pr-view.js.map +1 -1
  67. package/dist/tools/release-create.d.ts.map +1 -1
  68. package/dist/tools/release-create.js +79 -3
  69. package/dist/tools/release-create.js.map +1 -1
  70. package/dist/tools/release-list.d.ts.map +1 -1
  71. package/dist/tools/release-list.js +23 -5
  72. package/dist/tools/release-list.js.map +1 -1
  73. package/dist/tools/run-list.d.ts.map +1 -1
  74. package/dist/tools/run-list.js +84 -3
  75. package/dist/tools/run-list.js.map +1 -1
  76. package/dist/tools/run-rerun.d.ts.map +1 -1
  77. package/dist/tools/run-rerun.js +20 -4
  78. package/dist/tools/run-rerun.js.map +1 -1
  79. package/dist/tools/run-view.d.ts.map +1 -1
  80. package/dist/tools/run-view.js +44 -4
  81. package/dist/tools/run-view.js.map +1 -1
  82. package/package.json +2 -2
package/dist/tools/api.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { dualOutput, INPUT_LIMITS } from "@paretools/shared";
2
+ import { dualOutput, assertNoFlagInjection, INPUT_LIMITS } from "@paretools/shared";
3
3
  import { ghCmd } from "../lib/gh-runner.js";
4
4
  import { parseApi } from "../lib/parsers.js";
5
5
  import { formatApi } from "../lib/formatters.js";
@@ -27,6 +27,11 @@ export function registerApiTool(server) {
27
27
  .record(z.string(), z.string())
28
28
  .optional()
29
29
  .describe("Key-value pairs sent as --raw-field parameters"),
30
+ // S-gap P1: Add typedFields for --field (typed field input)
31
+ typedFields: z
32
+ .record(z.string(), z.string())
33
+ .optional()
34
+ .describe("Key-value pairs sent as --field parameters (typed: booleans, numbers, null parsed by gh)"),
30
35
  paginate: z
31
36
  .boolean()
32
37
  .optional()
@@ -37,6 +42,55 @@ export function registerApiTool(server) {
37
42
  .max(INPUT_LIMITS.STRING_MAX)
38
43
  .optional()
39
44
  .describe("jq filter expression to apply to the response"),
45
+ slurp: z
46
+ .boolean()
47
+ .optional()
48
+ .describe("Combine paginated JSON arrays into a single array (--slurp)"),
49
+ include: z
50
+ .boolean()
51
+ .optional()
52
+ .describe("Include response headers in output (-i/--include)"),
53
+ silent: z.boolean().optional().describe("Suppress response body output (--silent)"),
54
+ verbose: z.boolean().optional().describe("Show verbose debug output (--verbose)"),
55
+ // S-gap P0: Add headers param
56
+ headers: z
57
+ .record(z.string(), z.string())
58
+ .optional()
59
+ .describe("Custom HTTP headers as key-value pairs (each maps to -H/--header)"),
60
+ // S-gap P1: Add hostname for GitHub Enterprise
61
+ hostname: z
62
+ .string()
63
+ .max(INPUT_LIMITS.SHORT_STRING_MAX)
64
+ .optional()
65
+ .describe("GitHub Enterprise hostname (--hostname)"),
66
+ // S-gap P1: Add cache TTL
67
+ cache: z
68
+ .string()
69
+ .max(INPUT_LIMITS.SHORT_STRING_MAX)
70
+ .optional()
71
+ .describe("Cache TTL for responses, e.g. '5m', '1h' (--cache)"),
72
+ // S-gap P2: Add preview for API preview features
73
+ preview: z
74
+ .string()
75
+ .max(INPUT_LIMITS.SHORT_STRING_MAX)
76
+ .optional()
77
+ .describe("API preview feature name (--preview)"),
78
+ // S-gap P2: Add inputFile for reading body from file
79
+ inputFile: z
80
+ .string()
81
+ .max(INPUT_LIMITS.PATH_MAX)
82
+ .optional()
83
+ .describe("Read request body from file (--input). Mutually exclusive with body."),
84
+ // P1-gap #142: GraphQL support
85
+ query: z
86
+ .string()
87
+ .max(INPUT_LIMITS.STRING_MAX)
88
+ .optional()
89
+ .describe("GraphQL query string. When provided, makes a GraphQL request via `gh api graphql`."),
90
+ variables: z
91
+ .record(z.string(), z.unknown())
92
+ .optional()
93
+ .describe("GraphQL variables as key-value pairs. Only used with `query` parameter."),
40
94
  path: z
41
95
  .string()
42
96
  .max(INPUT_LIMITS.PATH_MAX)
@@ -44,8 +98,49 @@ export function registerApiTool(server) {
44
98
  .describe("Repository path (default: cwd)"),
45
99
  },
46
100
  outputSchema: ApiResultSchema,
47
- }, async ({ endpoint, method, body, fields, paginate, jq, path }) => {
101
+ }, async ({ endpoint, method, body, fields, typedFields, paginate, jq, slurp, include: _include, silent, verbose, headers, hostname, cache, preview, inputFile, query, variables, path, }) => {
48
102
  const cwd = path || process.cwd();
103
+ assertNoFlagInjection(endpoint, "endpoint");
104
+ if (jq)
105
+ assertNoFlagInjection(jq, "jq");
106
+ if (hostname)
107
+ assertNoFlagInjection(hostname, "hostname");
108
+ if (cache)
109
+ assertNoFlagInjection(cache, "cache");
110
+ if (preview)
111
+ assertNoFlagInjection(preview, "preview");
112
+ if (inputFile)
113
+ assertNoFlagInjection(inputFile, "inputFile");
114
+ // P1-gap #142: Handle GraphQL queries
115
+ if (query) {
116
+ const gqlArgs = ["api", "graphql", "-f", `query=${query}`];
117
+ if (variables) {
118
+ for (const [key, value] of Object.entries(variables)) {
119
+ gqlArgs.push("-f", `${key}=${typeof value === "string" ? value : JSON.stringify(value)}`);
120
+ }
121
+ }
122
+ if (hostname)
123
+ gqlArgs.push("--hostname", hostname);
124
+ if (headers) {
125
+ for (const [key, value] of Object.entries(headers)) {
126
+ gqlArgs.push("-H", `${key}:${value}`);
127
+ }
128
+ }
129
+ if (jq)
130
+ gqlArgs.push("--jq", jq);
131
+ if (paginate)
132
+ gqlArgs.push("--paginate");
133
+ if (slurp)
134
+ gqlArgs.push("--slurp");
135
+ gqlArgs.push("--include");
136
+ const gqlResult = await ghCmd(gqlArgs, { cwd });
137
+ if (gqlResult.exitCode !== 0 && !gqlResult.stdout && gqlResult.stderr) {
138
+ const data = parseApi("", gqlResult.exitCode, "graphql", "POST", gqlResult.stderr);
139
+ return dualOutput(data, formatApi);
140
+ }
141
+ const data = parseApi(gqlResult.stdout, gqlResult.exitCode, "graphql", "POST", gqlResult.stderr);
142
+ return dualOutput(data, formatApi);
143
+ }
49
144
  const args = ["api", endpoint, "--method", method];
50
145
  if (paginate) {
51
146
  args.push("--paginate");
@@ -53,23 +148,58 @@ export function registerApiTool(server) {
53
148
  if (jq) {
54
149
  args.push("--jq", jq);
55
150
  }
151
+ if (slurp)
152
+ args.push("--slurp");
153
+ // Always include response headers so we can parse the real HTTP status code.
154
+ // The --include flag is always set; the user's `include` param is redundant but harmless.
155
+ args.push("--include");
156
+ if (silent)
157
+ args.push("--silent");
158
+ if (verbose)
159
+ args.push("--verbose");
160
+ if (hostname)
161
+ args.push("--hostname", hostname);
162
+ if (cache)
163
+ args.push("--cache", cache);
164
+ if (preview)
165
+ args.push("--preview", preview);
166
+ // S-gap P0: Add custom headers
167
+ if (headers) {
168
+ for (const [key, value] of Object.entries(headers)) {
169
+ args.push("-H", `${key}:${value}`);
170
+ }
171
+ }
56
172
  // Add --raw-field for each field entry
57
173
  if (fields) {
58
174
  for (const [key, value] of Object.entries(fields)) {
59
175
  args.push("--raw-field", `${key}=${value}`);
60
176
  }
61
177
  }
178
+ // S-gap P1: Add --field for each typed field entry
179
+ if (typedFields) {
180
+ for (const [key, value] of Object.entries(typedFields)) {
181
+ args.push("--field", `${key}=${value}`);
182
+ }
183
+ }
62
184
  // Pass JSON body via stdin using --input -
63
185
  let stdin;
64
186
  if (body) {
65
187
  args.push("--input", "-");
66
188
  stdin = JSON.stringify(body);
67
189
  }
190
+ else if (inputFile) {
191
+ // S-gap P2: Read body from file
192
+ args.push("--input", inputFile);
193
+ }
68
194
  const result = await ghCmd(args, { cwd, stdin });
69
- if (result.exitCode !== 0 && result.stderr) {
70
- throw new Error(`gh api failed: ${result.stderr}`);
195
+ // P1-gap #141: Pass stderr to parser for error body preservation
196
+ // Only throw if there's no stdout at all (completely failed)
197
+ if (result.exitCode !== 0 && !result.stdout && result.stderr) {
198
+ // Still try to parse — pass stderr for error body
199
+ const data = parseApi("", result.exitCode, endpoint, method, result.stderr);
200
+ return dualOutput(data, formatApi);
71
201
  }
72
- const data = parseApi(result.stdout, result.exitCode, endpoint, method);
202
+ const data = parseApi(result.stdout, result.exitCode, endpoint, method, result.stderr);
73
203
  return dualOutput(data, formatApi);
74
204
  });
75
205
  }
@@ -1 +1 @@
1
- {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/tools/api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/tools/api.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"gist-create.d.ts","sourceRoot":"","sources":["../../src/tools/gist-create.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAOzE,gEAAgE;AAChE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,QAqDvD"}
1
+ {"version":3,"file":"gist-create.d.ts","sourceRoot":"","sources":["../../src/tools/gist-create.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAWzE,gEAAgE;AAChE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,QA4GvD"}
@@ -1,20 +1,30 @@
1
1
  import { z } from "zod";
2
- import { dualOutput, INPUT_LIMITS } from "@paretools/shared";
2
+ import { dualOutput, assertNoFlagInjection, INPUT_LIMITS } from "@paretools/shared";
3
3
  import { ghCmd } from "../lib/gh-runner.js";
4
4
  import { parseGistCreate } from "../lib/parsers.js";
5
5
  import { formatGistCreate } from "../lib/formatters.js";
6
6
  import { GistCreateResultSchema } from "../schemas/index.js";
7
+ import { assertSafeFilePath } from "../lib/path-validation.js";
8
+ import { mkdtempSync, writeFileSync, rmSync } from "node:fs";
9
+ import { tmpdir } from "node:os";
10
+ import { join } from "node:path";
7
11
  /** Registers the `gist-create` tool on the given MCP server. */
8
12
  export function registerGistCreateTool(server) {
9
13
  server.registerTool("gist-create", {
10
14
  title: "Gist Create",
11
- description: "Creates a new GitHub gist from one or more files. Returns structured data with gist ID, URL, and visibility. Use instead of running `gh gist create` in the terminal.",
15
+ description: "Creates a new GitHub gist from one or more files. Returns structured data with gist ID, URL, visibility, file names, description, and file count. Use instead of running `gh gist create` in the terminal.",
12
16
  inputSchema: {
13
17
  files: z
14
18
  .array(z.string().max(INPUT_LIMITS.PATH_MAX))
15
- .min(1)
16
19
  .max(INPUT_LIMITS.ARRAY_MAX)
17
- .describe("File paths to include in the gist"),
20
+ .optional()
21
+ .describe("File paths to include in the gist. Either files or content must be provided."),
22
+ // P1-gap #143: Add content-based gist creation from inline content
23
+ content: z
24
+ .record(z.string(), z.string())
25
+ .optional()
26
+ .describe("Inline content as filename-to-content map (e.g., {'script.py': 'print(1)'}). " +
27
+ "Creates gist from inline content instead of file paths. Either files or content must be provided."),
18
28
  description: z
19
29
  .string()
20
30
  .max(INPUT_LIMITS.STRING_MAX)
@@ -32,22 +42,66 @@ export function registerGistCreateTool(server) {
32
42
  .describe("Working directory (default: cwd)"),
33
43
  },
34
44
  outputSchema: GistCreateResultSchema,
35
- }, async ({ files, description, public: isPublic, path }) => {
45
+ }, async ({ files, content: contentMap, description, public: isPublic, path }) => {
36
46
  const cwd = path || process.cwd();
37
- const args = ["gist", "create"];
38
- if (description) {
39
- args.push("--desc", description);
47
+ if (description)
48
+ assertNoFlagInjection(description, "description");
49
+ // P1-gap #143: Validate that at least one of files or content is provided
50
+ const hasFiles = files && files.length > 0;
51
+ const hasContent = contentMap && Object.keys(contentMap).length > 0;
52
+ if (!hasFiles && !hasContent) {
53
+ throw new Error("Either `files` or `content` must be provided.");
40
54
  }
41
- if (isPublic) {
42
- args.push("--public");
55
+ // Track temp dir for cleanup
56
+ let tempDir;
57
+ let resolvedFiles = [];
58
+ try {
59
+ if (hasFiles) {
60
+ // Validate all file paths are safe before passing to gh CLI
61
+ for (const file of files) {
62
+ assertNoFlagInjection(file, "files");
63
+ assertSafeFilePath(file, cwd);
64
+ }
65
+ resolvedFiles = files;
66
+ }
67
+ if (hasContent) {
68
+ // P1-gap #143: Write inline content to temp files
69
+ tempDir = mkdtempSync(join(tmpdir(), "pare-gist-"));
70
+ for (const [filename, fileContent] of Object.entries(contentMap)) {
71
+ assertNoFlagInjection(filename, "content filename");
72
+ const tempPath = join(tempDir, filename);
73
+ writeFileSync(tempPath, fileContent);
74
+ resolvedFiles.push(tempPath);
75
+ }
76
+ }
77
+ const args = ["gist", "create"];
78
+ if (description) {
79
+ args.push("--desc", description);
80
+ }
81
+ if (isPublic) {
82
+ args.push("--public");
83
+ }
84
+ args.push(...resolvedFiles);
85
+ const result = await ghCmd(args, cwd);
86
+ if (result.exitCode !== 0) {
87
+ throw new Error(`gh gist create failed: ${result.stderr}`);
88
+ }
89
+ // Build file list for output (use original filenames for content-based gists)
90
+ const outputFiles = hasContent ? Object.keys(contentMap) : (files ?? []);
91
+ const data = parseGistCreate(result.stdout, !!isPublic, outputFiles, description);
92
+ return dualOutput(data, formatGistCreate);
43
93
  }
44
- args.push(...files);
45
- const result = await ghCmd(args, cwd);
46
- if (result.exitCode !== 0) {
47
- throw new Error(`gh gist create failed: ${result.stderr}`);
94
+ finally {
95
+ // P1-gap #143: Clean up temp files
96
+ if (tempDir) {
97
+ try {
98
+ rmSync(tempDir, { recursive: true, force: true });
99
+ }
100
+ catch {
101
+ // Best-effort cleanup
102
+ }
103
+ }
48
104
  }
49
- const data = parseGistCreate(result.stdout, !!isPublic);
50
- return dualOutput(data, formatGistCreate);
51
105
  });
52
106
  }
53
107
  //# sourceMappingURL=gist-create.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"gist-create.js","sourceRoot":"","sources":["../../src/tools/gist-create.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAE7D,gEAAgE;AAChE,MAAM,UAAU,sBAAsB,CAAC,MAAiB;IACtD,MAAM,CAAC,YAAY,CACjB,aAAa,EACb;QACE,KAAK,EAAE,aAAa;QACpB,WAAW,EACT,uKAAuK;QACzK,WAAW,EAAE;YACX,KAAK,EAAE,CAAC;iBACL,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;iBAC5C,GAAG,CAAC,CAAC,CAAC;iBACN,GAAG,CAAC,YAAY,CAAC,SAAS,CAAC;iBAC3B,QAAQ,CAAC,mCAAmC,CAAC;YAChD,WAAW,EAAE,CAAC;iBACX,MAAM,EAAE;iBACR,GAAG,CAAC,YAAY,CAAC,UAAU,CAAC;iBAC5B,QAAQ,EAAE;iBACV,QAAQ,CAAC,kBAAkB,CAAC;YAC/B,MAAM,EAAE,CAAC;iBACN,OAAO,EAAE;iBACT,QAAQ,EAAE;iBACV,OAAO,CAAC,KAAK,CAAC;iBACd,QAAQ,CAAC,yCAAyC,CAAC;YACtD,IAAI,EAAE,CAAC;iBACJ,MAAM,EAAE;iBACR,GAAG,CAAC,YAAY,CAAC,QAAQ,CAAC;iBAC1B,QAAQ,EAAE;iBACV,QAAQ,CAAC,kCAAkC,CAAC;SAChD;QACD,YAAY,EAAE,sBAAsB;KACrC,EACD,KAAK,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE;QACvD,MAAM,GAAG,GAAG,IAAI,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QAElC,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAChC,IAAI,WAAW,EAAE,CAAC;YAChB,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;QACnC,CAAC;QACD,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACxB,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC;QAEpB,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAEtC,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,0BAA0B,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QAC7D,CAAC;QAED,MAAM,IAAI,GAAG,eAAe,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC;QACxD,OAAO,UAAU,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;IAC5C,CAAC,CACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"gist-create.js","sourceRoot":"","sources":["../../src/tools/gist-create.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"issue-close.d.ts","sourceRoot":"","sources":["../../src/tools/issue-close.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAOzE,gEAAgE;AAChE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,QA+CvD"}
1
+ {"version":3,"file":"issue-close.d.ts","sourceRoot":"","sources":["../../src/tools/issue-close.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAOzE,gEAAgE;AAChE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,QA0EvD"}
@@ -8,14 +8,20 @@ import { IssueCloseResultSchema } from "../schemas/index.js";
8
8
  export function registerIssueCloseTool(server) {
9
9
  server.registerTool("issue-close", {
10
10
  title: "Issue Close",
11
- description: "Closes an issue with an optional comment and reason. Returns structured data with issue number, state, and URL. Use instead of running `gh issue close` in the terminal.",
11
+ description: "Closes an issue with an optional comment and reason. Returns structured data with issue number, state, URL, reason, and comment URL. Use instead of running `gh issue close` in the terminal.",
12
12
  inputSchema: {
13
- number: z.number().describe("Issue number"),
13
+ number: z.string().max(INPUT_LIMITS.STRING_MAX).describe("Issue number or URL"),
14
14
  comment: z.string().max(INPUT_LIMITS.STRING_MAX).optional().describe("Closing comment"),
15
15
  reason: z
16
16
  .enum(["completed", "not planned"])
17
17
  .optional()
18
18
  .describe('Close reason: "completed" or "not planned"'),
19
+ // S-gap P1: Add repo for cross-repo close
20
+ repo: z
21
+ .string()
22
+ .max(INPUT_LIMITS.SHORT_STRING_MAX)
23
+ .optional()
24
+ .describe("Repository in OWNER/REPO format (default: current repo)"),
19
25
  path: z
20
26
  .string()
21
27
  .max(INPUT_LIMITS.PATH_MAX)
@@ -23,23 +29,43 @@ export function registerIssueCloseTool(server) {
23
29
  .describe("Repository path (default: cwd)"),
24
30
  },
25
31
  outputSchema: IssueCloseResultSchema,
26
- }, async ({ number, comment, reason, path }) => {
32
+ }, async ({ number, comment, reason, repo, path }) => {
27
33
  const cwd = path || process.cwd();
28
34
  if (comment) {
29
35
  assertNoFlagInjection(comment, "comment");
30
36
  }
31
- const args = ["issue", "close", String(number)];
37
+ if (repo)
38
+ assertNoFlagInjection(repo, "repo");
39
+ if (typeof number === "string")
40
+ assertNoFlagInjection(number, "number");
41
+ const selector = String(number);
42
+ const issueNum = typeof number === "number" ? number : 0;
43
+ const args = ["issue", "close", selector];
32
44
  if (comment) {
33
45
  args.push("--comment", comment);
34
46
  }
35
47
  if (reason) {
36
48
  args.push("--reason", reason);
37
49
  }
50
+ if (repo) {
51
+ args.push("--repo", repo);
52
+ }
38
53
  const result = await ghCmd(args, cwd);
54
+ // P1-gap #144: Detect already-closed issues instead of throwing
39
55
  if (result.exitCode !== 0) {
56
+ const combined = `${result.stdout}\n${result.stderr}`;
57
+ const isAlreadyClosed = /already closed/i.test(combined) ||
58
+ /issue .* is already closed/i.test(combined) ||
59
+ /already been closed/i.test(combined);
60
+ if (isAlreadyClosed) {
61
+ // Return structured output with alreadyClosed flag
62
+ const data = parseIssueClose(result.stdout, issueNum, reason, comment, result.stderr);
63
+ return dualOutput(data, formatIssueClose);
64
+ }
40
65
  throw new Error(`gh issue close failed: ${result.stderr}`);
41
66
  }
42
- const data = parseIssueClose(result.stdout, number);
67
+ // S-gap: Pass reason, comment, and stderr for echo in output
68
+ const data = parseIssueClose(result.stdout, issueNum, reason, comment, result.stderr);
43
69
  return dualOutput(data, formatIssueClose);
44
70
  });
45
71
  }
@@ -1 +1 @@
1
- {"version":3,"file":"issue-close.js","sourceRoot":"","sources":["../../src/tools/issue-close.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACpF,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAE7D,gEAAgE;AAChE,MAAM,UAAU,sBAAsB,CAAC,MAAiB;IACtD,MAAM,CAAC,YAAY,CACjB,aAAa,EACb;QACE,KAAK,EAAE,aAAa;QACpB,WAAW,EACT,0KAA0K;QAC5K,WAAW,EAAE;YACX,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,cAAc,CAAC;YAC3C,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,iBAAiB,CAAC;YACvF,MAAM,EAAE,CAAC;iBACN,IAAI,CAAC,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;iBAClC,QAAQ,EAAE;iBACV,QAAQ,CAAC,4CAA4C,CAAC;YACzD,IAAI,EAAE,CAAC;iBACJ,MAAM,EAAE;iBACR,GAAG,CAAC,YAAY,CAAC,QAAQ,CAAC;iBAC1B,QAAQ,EAAE;iBACV,QAAQ,CAAC,gCAAgC,CAAC;SAC9C;QACD,YAAY,EAAE,sBAAsB;KACrC,EACD,KAAK,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE;QAC1C,MAAM,GAAG,GAAG,IAAI,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QAElC,IAAI,OAAO,EAAE,CAAC;YACZ,qBAAqB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAC5C,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;QAChD,IAAI,OAAO,EAAE,CAAC;YACZ,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QAClC,CAAC;QACD,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAChC,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAEtC,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,0BAA0B,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QAC7D,CAAC;QAED,MAAM,IAAI,GAAG,eAAe,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACpD,OAAO,UAAU,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;IAC5C,CAAC,CACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"issue-close.js","sourceRoot":"","sources":["../../src/tools/issue-close.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"issue-comment.d.ts","sourceRoot":"","sources":["../../src/tools/issue-comment.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAOzE,kEAAkE;AAClE,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,QAmCzD"}
1
+ {"version":3,"file":"issue-comment.d.ts","sourceRoot":"","sources":["../../src/tools/issue-comment.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAOzE,kEAAkE;AAClE,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,QAqEzD"}
@@ -8,10 +8,28 @@ import { CommentResultSchema } from "../schemas/index.js";
8
8
  export function registerIssueCommentTool(server) {
9
9
  server.registerTool("issue-comment", {
10
10
  title: "Issue Comment",
11
- description: "Adds a comment to an issue. Returns structured data with the comment URL. Use instead of running `gh issue comment` in the terminal.",
11
+ description: "Adds, edits, or deletes a comment on an issue. Returns structured data with the comment URL, operation type, comment ID, issue number, and body echo. Use instead of running `gh issue comment` in the terminal.",
12
12
  inputSchema: {
13
- number: z.number().describe("Issue number"),
13
+ number: z.string().max(INPUT_LIMITS.STRING_MAX).describe("Issue number or URL"),
14
14
  body: z.string().max(INPUT_LIMITS.STRING_MAX).describe("Comment text"),
15
+ editLast: z
16
+ .boolean()
17
+ .optional()
18
+ .describe("Edit the last comment instead of creating a new one (--edit-last)"),
19
+ deleteLast: z
20
+ .boolean()
21
+ .optional()
22
+ .describe("Delete the last comment (uses gh issue comment --delete-last)"),
23
+ createIfNone: z
24
+ .boolean()
25
+ .optional()
26
+ .describe("When used with editLast, create a new comment if no existing comment exists (--create-if-none)"),
27
+ // S-gap P1: Add repo for cross-repo commenting
28
+ repo: z
29
+ .string()
30
+ .max(INPUT_LIMITS.SHORT_STRING_MAX)
31
+ .optional()
32
+ .describe("Repository in OWNER/REPO format (default: current repo)"),
15
33
  path: z
16
34
  .string()
17
35
  .max(INPUT_LIMITS.PATH_MAX)
@@ -19,15 +37,34 @@ export function registerIssueCommentTool(server) {
19
37
  .describe("Repository path (default: cwd)"),
20
38
  },
21
39
  outputSchema: CommentResultSchema,
22
- }, async ({ number, body, path }) => {
40
+ }, async ({ number, body, editLast, deleteLast, createIfNone, repo, path }) => {
23
41
  const cwd = path || process.cwd();
24
- assertNoFlagInjection(body, "body");
25
- const args = ["issue", "comment", String(number), "--body-file", "-"];
42
+ if (repo)
43
+ assertNoFlagInjection(repo, "repo");
44
+ if (typeof number === "string")
45
+ assertNoFlagInjection(number, "number");
46
+ const selector = String(number);
47
+ const issueNum = typeof number === "number" ? number : 0;
48
+ const args = ["issue", "comment", selector, "--body-file", "-"];
49
+ if (editLast)
50
+ args.push("--edit-last");
51
+ if (deleteLast)
52
+ args.push("--delete-last");
53
+ if (createIfNone)
54
+ args.push("--create-if-none");
55
+ if (repo)
56
+ args.push("--repo", repo);
26
57
  const result = await ghCmd(args, { cwd, stdin: body });
27
58
  if (result.exitCode !== 0) {
28
59
  throw new Error(`gh issue comment failed: ${result.stderr}`);
29
60
  }
30
- const data = parseComment(result.stdout);
61
+ // S-gap: Determine operation type and pass context
62
+ const operation = deleteLast ? "delete" : editLast ? "edit" : "create";
63
+ const data = parseComment(result.stdout, {
64
+ operation: operation,
65
+ issueNumber: issueNum,
66
+ body,
67
+ });
31
68
  return dualOutput(data, formatComment);
32
69
  });
33
70
  }
@@ -1 +1 @@
1
- {"version":3,"file":"issue-comment.js","sourceRoot":"","sources":["../../src/tools/issue-comment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACpF,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE1D,kEAAkE;AAClE,MAAM,UAAU,wBAAwB,CAAC,MAAiB;IACxD,MAAM,CAAC,YAAY,CACjB,eAAe,EACf;QACE,KAAK,EAAE,eAAe;QACtB,WAAW,EACT,sIAAsI;QACxI,WAAW,EAAE;YACX,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,cAAc,CAAC;YAC3C,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,cAAc,CAAC;YACtE,IAAI,EAAE,CAAC;iBACJ,MAAM,EAAE;iBACR,GAAG,CAAC,YAAY,CAAC,QAAQ,CAAC;iBAC1B,QAAQ,EAAE;iBACV,QAAQ,CAAC,gCAAgC,CAAC;SAC9C;QACD,YAAY,EAAE,mBAAmB;KAClC,EACD,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE;QAC/B,MAAM,GAAG,GAAG,IAAI,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QAElC,qBAAqB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAEpC,MAAM,IAAI,GAAG,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,aAAa,EAAE,GAAG,CAAC,CAAC;QAEtE,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAEvD,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,4BAA4B,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QAC/D,CAAC;QAED,MAAM,IAAI,GAAG,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACzC,OAAO,UAAU,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;IACzC,CAAC,CACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"issue-comment.js","sourceRoot":"","sources":["../../src/tools/issue-comment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACpF,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE1D,kEAAkE;AAClE,MAAM,UAAU,wBAAwB,CAAC,MAAiB;IACxD,MAAM,CAAC,YAAY,CACjB,eAAe,EACf;QACE,KAAK,EAAE,eAAe;QACtB,WAAW,EACT,kNAAkN;QACpN,WAAW,EAAE;YACX,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,qBAAqB,CAAC;YAC/E,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,cAAc,CAAC;YACtE,QAAQ,EAAE,CAAC;iBACR,OAAO,EAAE;iBACT,QAAQ,EAAE;iBACV,QAAQ,CAAC,mEAAmE,CAAC;YAChF,UAAU,EAAE,CAAC;iBACV,OAAO,EAAE;iBACT,QAAQ,EAAE;iBACV,QAAQ,CAAC,+DAA+D,CAAC;YAC5E,YAAY,EAAE,CAAC;iBACZ,OAAO,EAAE;iBACT,QAAQ,EAAE;iBACV,QAAQ,CACP,gGAAgG,CACjG;YACH,+CAA+C;YAC/C,IAAI,EAAE,CAAC;iBACJ,MAAM,EAAE;iBACR,GAAG,CAAC,YAAY,CAAC,gBAAgB,CAAC;iBAClC,QAAQ,EAAE;iBACV,QAAQ,CAAC,yDAAyD,CAAC;YACtE,IAAI,EAAE,CAAC;iBACJ,MAAM,EAAE;iBACR,GAAG,CAAC,YAAY,CAAC,QAAQ,CAAC;iBAC1B,QAAQ,EAAE;iBACV,QAAQ,CAAC,gCAAgC,CAAC;SAC9C;QACD,YAAY,EAAE,mBAAmB;KAClC,EACD,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE;QACzE,MAAM,GAAG,GAAG,IAAI,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QAElC,IAAI,IAAI;YAAE,qBAAqB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC9C,IAAI,OAAO,MAAM,KAAK,QAAQ;YAAE,qBAAqB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAExE,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;QAChC,MAAM,QAAQ,GAAG,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAEzD,MAAM,IAAI,GAAG,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,aAAa,EAAE,GAAG,CAAC,CAAC;QAChE,IAAI,QAAQ;YAAE,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACvC,IAAI,UAAU;YAAE,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAC3C,IAAI,YAAY;YAAE,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QAChD,IAAI,IAAI;YAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAEpC,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAEvD,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,4BAA4B,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QAC/D,CAAC;QAED,mDAAmD;QACnD,MAAM,SAAS,GAAG,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;QACvE,MAAM,IAAI,GAAG,YAAY,CAAC,MAAM,CAAC,MAAM,EAAE;YACvC,SAAS,EAAE,SAAyC;YACpD,WAAW,EAAE,QAAQ;YACrB,IAAI;SACL,CAAC,CAAC;QACH,OAAO,UAAU,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;IACzC,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"issue-create.d.ts","sourceRoot":"","sources":["../../src/tools/issue-create.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAOzE,iEAAiE;AACjE,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,QAkDxD"}
1
+ {"version":3,"file":"issue-create.d.ts","sourceRoot":"","sources":["../../src/tools/issue-create.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAOzE,iEAAiE;AACjE,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,QA0GxD"}
@@ -8,7 +8,7 @@ import { IssueCreateResultSchema } from "../schemas/index.js";
8
8
  export function registerIssueCreateTool(server) {
9
9
  server.registerTool("issue-create", {
10
10
  title: "Issue Create",
11
- description: "Creates a new issue. Returns structured data with issue number and URL. Use instead of running `gh issue create` in the terminal.",
11
+ description: "Creates a new issue. Returns structured data with issue number, URL, and labels applied. Use instead of running `gh issue create` in the terminal.",
12
12
  inputSchema: {
13
13
  title: z.string().max(INPUT_LIMITS.SHORT_STRING_MAX).describe("Issue title"),
14
14
  body: z.string().max(INPUT_LIMITS.STRING_MAX).describe("Issue body/description"),
@@ -17,6 +17,36 @@ export function registerIssueCreateTool(server) {
17
17
  .max(INPUT_LIMITS.ARRAY_MAX)
18
18
  .optional()
19
19
  .describe("Labels to apply"),
20
+ // S-gap P0: Add assignees
21
+ assignees: z
22
+ .array(z.string().max(INPUT_LIMITS.SHORT_STRING_MAX))
23
+ .max(INPUT_LIMITS.ARRAY_MAX)
24
+ .optional()
25
+ .describe("Assignee usernames (-a/--assignee)"),
26
+ // S-gap P0: Add milestone
27
+ milestone: z
28
+ .string()
29
+ .max(INPUT_LIMITS.SHORT_STRING_MAX)
30
+ .optional()
31
+ .describe("Milestone name or number (-m/--milestone)"),
32
+ // S-gap P1: Add project
33
+ project: z
34
+ .string()
35
+ .max(INPUT_LIMITS.SHORT_STRING_MAX)
36
+ .optional()
37
+ .describe("Project board name (-p/--project)"),
38
+ // S-gap P1: Add template
39
+ template: z
40
+ .string()
41
+ .max(INPUT_LIMITS.SHORT_STRING_MAX)
42
+ .optional()
43
+ .describe("Issue template file name (--template)"),
44
+ // S-gap P1: Add repo for cross-repo creation
45
+ repo: z
46
+ .string()
47
+ .max(INPUT_LIMITS.SHORT_STRING_MAX)
48
+ .optional()
49
+ .describe("Repository in OWNER/REPO format (--repo). Default: current repo."),
20
50
  path: z
21
51
  .string()
22
52
  .max(INPUT_LIMITS.PATH_MAX)
@@ -24,7 +54,7 @@ export function registerIssueCreateTool(server) {
24
54
  .describe("Repository path (default: cwd)"),
25
55
  },
26
56
  outputSchema: IssueCreateResultSchema,
27
- }, async ({ title, body, labels, path }) => {
57
+ }, async ({ title, body, labels, assignees, milestone, project, template, repo, path }) => {
28
58
  const cwd = path || process.cwd();
29
59
  assertNoFlagInjection(title, "title");
30
60
  if (labels) {
@@ -32,17 +62,51 @@ export function registerIssueCreateTool(server) {
32
62
  assertNoFlagInjection(label, "labels");
33
63
  }
34
64
  }
35
- const args = ["issue", "create", "--title", title, "--body", body];
65
+ if (assignees) {
66
+ for (const assignee of assignees) {
67
+ assertNoFlagInjection(assignee, "assignees");
68
+ }
69
+ }
70
+ if (milestone)
71
+ assertNoFlagInjection(milestone, "milestone");
72
+ if (project)
73
+ assertNoFlagInjection(project, "project");
74
+ if (template)
75
+ assertNoFlagInjection(template, "template");
76
+ if (repo)
77
+ assertNoFlagInjection(repo, "repo");
78
+ // Use --body-file - to pass body via stdin, avoiding shell escaping issues
79
+ // for long bodies with special characters
80
+ const args = ["issue", "create", "--title", title, "--body-file", "-"];
36
81
  if (labels && labels.length > 0) {
37
82
  for (const label of labels) {
38
83
  args.push("--label", label);
39
84
  }
40
85
  }
41
- const result = await ghCmd(args, cwd);
86
+ // S-gap P0: Map assignees to --assignee flags
87
+ if (assignees && assignees.length > 0) {
88
+ for (const assignee of assignees) {
89
+ args.push("--assignee", assignee);
90
+ }
91
+ }
92
+ // S-gap P0: Map milestone to --milestone
93
+ if (milestone)
94
+ args.push("--milestone", milestone);
95
+ // S-gap P1: Map project to --project
96
+ if (project)
97
+ args.push("--project", project);
98
+ // S-gap P1: Map template to --template
99
+ if (template)
100
+ args.push("--template", template);
101
+ // S-gap P1: Map repo to --repo
102
+ if (repo)
103
+ args.push("--repo", repo);
104
+ const result = await ghCmd(args, { cwd, stdin: body });
42
105
  if (result.exitCode !== 0) {
43
106
  throw new Error(`gh issue create failed: ${result.stderr}`);
44
107
  }
45
- const data = parseIssueCreate(result.stdout);
108
+ // S-gap: Pass labels for echo in output
109
+ const data = parseIssueCreate(result.stdout, labels);
46
110
  return dualOutput(data, formatIssueCreate);
47
111
  });
48
112
  }