postgresai 0.14.0-dev.56 → 0.14.0-dev.58

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.
@@ -13064,7 +13064,7 @@ var {
13064
13064
  // package.json
13065
13065
  var package_default = {
13066
13066
  name: "postgresai",
13067
- version: "0.14.0-dev.56",
13067
+ version: "0.14.0-dev.58",
13068
13068
  description: "postgres_ai CLI",
13069
13069
  license: "Apache-2.0",
13070
13070
  private: false,
@@ -13145,10 +13145,10 @@ function readConfig() {
13145
13145
  try {
13146
13146
  const content = fs.readFileSync(userConfigPath, "utf8");
13147
13147
  const parsed = JSON.parse(content);
13148
- config.apiKey = parsed.apiKey || null;
13149
- config.baseUrl = parsed.baseUrl || null;
13150
- config.orgId = parsed.orgId || null;
13151
- config.defaultProject = parsed.defaultProject || null;
13148
+ config.apiKey = parsed.apiKey ?? null;
13149
+ config.baseUrl = parsed.baseUrl ?? null;
13150
+ config.orgId = parsed.orgId ?? null;
13151
+ config.defaultProject = parsed.defaultProject ?? null;
13152
13152
  return config;
13153
13153
  } catch (err) {
13154
13154
  const message = err instanceof Error ? err.message : String(err);
@@ -15887,7 +15887,7 @@ var Result = import_lib.default.Result;
15887
15887
  var TypeOverrides = import_lib.default.TypeOverrides;
15888
15888
  var defaults = import_lib.default.defaults;
15889
15889
  // package.json
15890
- var version = "0.14.0-dev.56";
15890
+ var version = "0.14.0-dev.58";
15891
15891
  var package_default2 = {
15892
15892
  name: "postgresai",
15893
15893
  version,
@@ -15971,10 +15971,10 @@ function readConfig2() {
15971
15971
  try {
15972
15972
  const content = fs2.readFileSync(userConfigPath, "utf8");
15973
15973
  const parsed = JSON.parse(content);
15974
- config.apiKey = parsed.apiKey || null;
15975
- config.baseUrl = parsed.baseUrl || null;
15976
- config.orgId = parsed.orgId || null;
15977
- config.defaultProject = parsed.defaultProject || null;
15974
+ config.apiKey = parsed.apiKey ?? null;
15975
+ config.baseUrl = parsed.baseUrl ?? null;
15976
+ config.orgId = parsed.orgId ?? null;
15977
+ config.defaultProject = parsed.defaultProject ?? null;
15978
15978
  return config;
15979
15979
  } catch (err) {
15980
15980
  const message = err instanceof Error ? err.message : String(err);
@@ -16089,7 +16089,8 @@ async function fetchIssues(params) {
16089
16089
  const headers = {
16090
16090
  "access-token": apiKey,
16091
16091
  Prefer: "return=representation",
16092
- "Content-Type": "application/json"
16092
+ "Content-Type": "application/json",
16093
+ Connection: "close"
16093
16094
  };
16094
16095
  if (debug) {
16095
16096
  const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
@@ -16130,7 +16131,8 @@ async function fetchIssueComments(params) {
16130
16131
  const headers = {
16131
16132
  "access-token": apiKey,
16132
16133
  Prefer: "return=representation",
16133
- "Content-Type": "application/json"
16134
+ "Content-Type": "application/json",
16135
+ Connection: "close"
16134
16136
  };
16135
16137
  if (debug) {
16136
16138
  const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
@@ -16174,7 +16176,8 @@ async function fetchIssue(params) {
16174
16176
  const headers = {
16175
16177
  "access-token": apiKey,
16176
16178
  Prefer: "return=representation",
16177
- "Content-Type": "application/json"
16179
+ "Content-Type": "application/json",
16180
+ Connection: "close"
16178
16181
  };
16179
16182
  if (debug) {
16180
16183
  const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
@@ -16207,6 +16210,67 @@ async function fetchIssue(params) {
16207
16210
  throw new Error(formatHttpError("Failed to fetch issue", response.status, data));
16208
16211
  }
16209
16212
  }
16213
+ async function createIssue(params) {
16214
+ const { apiKey, apiBaseUrl, title, orgId, description, projectId, labels, debug } = params;
16215
+ if (!apiKey) {
16216
+ throw new Error("API key is required");
16217
+ }
16218
+ if (!title) {
16219
+ throw new Error("title is required");
16220
+ }
16221
+ if (typeof orgId !== "number") {
16222
+ throw new Error("orgId is required");
16223
+ }
16224
+ const base = normalizeBaseUrl(apiBaseUrl);
16225
+ const url = new URL(`${base}/rpc/issue_create`);
16226
+ const bodyObj = {
16227
+ title,
16228
+ org_id: orgId
16229
+ };
16230
+ if (description !== undefined) {
16231
+ bodyObj.description = description;
16232
+ }
16233
+ if (projectId !== undefined) {
16234
+ bodyObj.project_id = projectId;
16235
+ }
16236
+ if (labels && labels.length > 0) {
16237
+ bodyObj.labels = labels;
16238
+ }
16239
+ const body = JSON.stringify(bodyObj);
16240
+ const headers = {
16241
+ "access-token": apiKey,
16242
+ Prefer: "return=representation",
16243
+ "Content-Type": "application/json",
16244
+ Connection: "close"
16245
+ };
16246
+ if (debug) {
16247
+ const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
16248
+ console.log(`Debug: Resolved API base URL: ${base}`);
16249
+ console.log(`Debug: POST URL: ${url.toString()}`);
16250
+ console.log(`Debug: Auth scheme: access-token`);
16251
+ console.log(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
16252
+ console.log(`Debug: Request body: ${body}`);
16253
+ }
16254
+ const response = await fetch(url.toString(), {
16255
+ method: "POST",
16256
+ headers,
16257
+ body
16258
+ });
16259
+ if (debug) {
16260
+ console.log(`Debug: Response status: ${response.status}`);
16261
+ console.log(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
16262
+ }
16263
+ const data = await response.text();
16264
+ if (response.ok) {
16265
+ try {
16266
+ return JSON.parse(data);
16267
+ } catch {
16268
+ throw new Error(`Failed to parse create issue response: ${data}`);
16269
+ }
16270
+ } else {
16271
+ throw new Error(formatHttpError("Failed to create issue", response.status, data));
16272
+ }
16273
+ }
16210
16274
  async function createIssueComment(params) {
16211
16275
  const { apiKey, apiBaseUrl, issueId, content, parentCommentId, debug } = params;
16212
16276
  if (!apiKey) {
@@ -16231,7 +16295,8 @@ async function createIssueComment(params) {
16231
16295
  const headers = {
16232
16296
  "access-token": apiKey,
16233
16297
  Prefer: "return=representation",
16234
- "Content-Type": "application/json"
16298
+ "Content-Type": "application/json",
16299
+ Connection: "close"
16235
16300
  };
16236
16301
  if (debug) {
16237
16302
  const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
@@ -16261,6 +16326,121 @@ async function createIssueComment(params) {
16261
16326
  throw new Error(formatHttpError("Failed to create issue comment", response.status, data));
16262
16327
  }
16263
16328
  }
16329
+ async function updateIssue(params) {
16330
+ const { apiKey, apiBaseUrl, issueId, title, description, status, labels, debug } = params;
16331
+ if (!apiKey) {
16332
+ throw new Error("API key is required");
16333
+ }
16334
+ if (!issueId) {
16335
+ throw new Error("issueId is required");
16336
+ }
16337
+ if (title === undefined && description === undefined && status === undefined && labels === undefined) {
16338
+ throw new Error("At least one field to update is required (title, description, status, or labels)");
16339
+ }
16340
+ const base = normalizeBaseUrl(apiBaseUrl);
16341
+ const url = new URL(`${base}/rpc/issue_update`);
16342
+ const bodyObj = {
16343
+ p_id: issueId
16344
+ };
16345
+ if (title !== undefined) {
16346
+ bodyObj.p_title = title;
16347
+ }
16348
+ if (description !== undefined) {
16349
+ bodyObj.p_description = description;
16350
+ }
16351
+ if (status !== undefined) {
16352
+ bodyObj.p_status = status;
16353
+ }
16354
+ if (labels !== undefined) {
16355
+ bodyObj.p_labels = labels;
16356
+ }
16357
+ const body = JSON.stringify(bodyObj);
16358
+ const headers = {
16359
+ "access-token": apiKey,
16360
+ Prefer: "return=representation",
16361
+ "Content-Type": "application/json",
16362
+ Connection: "close"
16363
+ };
16364
+ if (debug) {
16365
+ const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
16366
+ console.log(`Debug: Resolved API base URL: ${base}`);
16367
+ console.log(`Debug: POST URL: ${url.toString()}`);
16368
+ console.log(`Debug: Auth scheme: access-token`);
16369
+ console.log(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
16370
+ console.log(`Debug: Request body: ${body}`);
16371
+ }
16372
+ const response = await fetch(url.toString(), {
16373
+ method: "POST",
16374
+ headers,
16375
+ body
16376
+ });
16377
+ if (debug) {
16378
+ console.log(`Debug: Response status: ${response.status}`);
16379
+ console.log(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
16380
+ }
16381
+ const data = await response.text();
16382
+ if (response.ok) {
16383
+ try {
16384
+ return JSON.parse(data);
16385
+ } catch {
16386
+ throw new Error(`Failed to parse update issue response: ${data}`);
16387
+ }
16388
+ } else {
16389
+ throw new Error(formatHttpError("Failed to update issue", response.status, data));
16390
+ }
16391
+ }
16392
+ async function updateIssueComment(params) {
16393
+ const { apiKey, apiBaseUrl, commentId, content, debug } = params;
16394
+ if (!apiKey) {
16395
+ throw new Error("API key is required");
16396
+ }
16397
+ if (!commentId) {
16398
+ throw new Error("commentId is required");
16399
+ }
16400
+ if (!content) {
16401
+ throw new Error("content is required");
16402
+ }
16403
+ const base = normalizeBaseUrl(apiBaseUrl);
16404
+ const url = new URL(`${base}/rpc/issue_comment_update`);
16405
+ const bodyObj = {
16406
+ p_id: commentId,
16407
+ p_content: content
16408
+ };
16409
+ const body = JSON.stringify(bodyObj);
16410
+ const headers = {
16411
+ "access-token": apiKey,
16412
+ Prefer: "return=representation",
16413
+ "Content-Type": "application/json",
16414
+ Connection: "close"
16415
+ };
16416
+ if (debug) {
16417
+ const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
16418
+ console.log(`Debug: Resolved API base URL: ${base}`);
16419
+ console.log(`Debug: POST URL: ${url.toString()}`);
16420
+ console.log(`Debug: Auth scheme: access-token`);
16421
+ console.log(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
16422
+ console.log(`Debug: Request body: ${body}`);
16423
+ }
16424
+ const response = await fetch(url.toString(), {
16425
+ method: "POST",
16426
+ headers,
16427
+ body
16428
+ });
16429
+ if (debug) {
16430
+ console.log(`Debug: Response status: ${response.status}`);
16431
+ console.log(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
16432
+ }
16433
+ const data = await response.text();
16434
+ if (response.ok) {
16435
+ try {
16436
+ return JSON.parse(data);
16437
+ } catch {
16438
+ throw new Error(`Failed to parse update comment response: ${data}`);
16439
+ }
16440
+ } else {
16441
+ throw new Error(formatHttpError("Failed to update issue comment", response.status, data));
16442
+ }
16443
+ }
16264
16444
 
16265
16445
  // node_modules/zod/v4/core/core.js
16266
16446
  var NEVER = Object.freeze({
@@ -23148,10 +23328,116 @@ class StdioServerTransport {
23148
23328
  }
23149
23329
 
23150
23330
  // lib/mcp-server.ts
23331
+ var interpretEscapes = (str2) => (str2 || "").replace(/\\n/g, `
23332
+ `).replace(/\\t/g, "\t").replace(/\\r/g, "\r").replace(/\\"/g, '"').replace(/\\'/g, "'");
23333
+ async function handleToolCall(req, rootOpts, extra) {
23334
+ const toolName = req.params.name;
23335
+ const args = req.params.arguments || {};
23336
+ const cfg = readConfig2();
23337
+ const apiKey = (rootOpts?.apiKey || process.env.PGAI_API_KEY || cfg.apiKey || "").toString();
23338
+ const { apiBaseUrl } = resolveBaseUrls(rootOpts, cfg);
23339
+ const debug = Boolean(args.debug ?? extra?.debug);
23340
+ if (!apiKey) {
23341
+ return {
23342
+ content: [
23343
+ {
23344
+ type: "text",
23345
+ text: "API key is required. Run 'pgai auth' or set PGAI_API_KEY."
23346
+ }
23347
+ ],
23348
+ isError: true
23349
+ };
23350
+ }
23351
+ try {
23352
+ if (toolName === "list_issues") {
23353
+ const issues = await fetchIssues({ apiKey, apiBaseUrl, debug });
23354
+ return { content: [{ type: "text", text: JSON.stringify(issues, null, 2) }] };
23355
+ }
23356
+ if (toolName === "view_issue") {
23357
+ const issueId = String(args.issue_id || "").trim();
23358
+ if (!issueId) {
23359
+ return { content: [{ type: "text", text: "issue_id is required" }], isError: true };
23360
+ }
23361
+ const issue2 = await fetchIssue({ apiKey, apiBaseUrl, issueId, debug });
23362
+ if (!issue2) {
23363
+ return { content: [{ type: "text", text: "Issue not found" }], isError: true };
23364
+ }
23365
+ const comments = await fetchIssueComments({ apiKey, apiBaseUrl, issueId, debug });
23366
+ const combined = { issue: issue2, comments };
23367
+ return { content: [{ type: "text", text: JSON.stringify(combined, null, 2) }] };
23368
+ }
23369
+ if (toolName === "post_issue_comment") {
23370
+ const issueId = String(args.issue_id || "").trim();
23371
+ const rawContent = String(args.content || "");
23372
+ const parentCommentId = args.parent_comment_id ? String(args.parent_comment_id) : undefined;
23373
+ if (!issueId) {
23374
+ return { content: [{ type: "text", text: "issue_id is required" }], isError: true };
23375
+ }
23376
+ if (!rawContent) {
23377
+ return { content: [{ type: "text", text: "content is required" }], isError: true };
23378
+ }
23379
+ const content = interpretEscapes(rawContent);
23380
+ const result = await createIssueComment({ apiKey, apiBaseUrl, issueId, content, parentCommentId, debug });
23381
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
23382
+ }
23383
+ if (toolName === "create_issue") {
23384
+ const rawTitle = String(args.title || "").trim();
23385
+ if (!rawTitle) {
23386
+ return { content: [{ type: "text", text: "title is required" }], isError: true };
23387
+ }
23388
+ const title = interpretEscapes(rawTitle);
23389
+ const rawDescription = args.description ? String(args.description) : undefined;
23390
+ const description = rawDescription ? interpretEscapes(rawDescription) : undefined;
23391
+ const projectId = args.project_id !== undefined ? Number(args.project_id) : undefined;
23392
+ const labels = Array.isArray(args.labels) ? args.labels.map(String) : undefined;
23393
+ const orgId = args.org_id !== undefined ? Number(args.org_id) : cfg.orgId;
23394
+ if (orgId === undefined || orgId === null || Number.isNaN(orgId)) {
23395
+ return { content: [{ type: "text", text: "org_id is required. Either provide it as a parameter or run 'pgai auth' to set it in config." }], isError: true };
23396
+ }
23397
+ const result = await createIssue({ apiKey, apiBaseUrl, title, orgId, description, projectId, labels, debug });
23398
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
23399
+ }
23400
+ if (toolName === "update_issue") {
23401
+ const issueId = String(args.issue_id || "").trim();
23402
+ if (!issueId) {
23403
+ return { content: [{ type: "text", text: "issue_id is required" }], isError: true };
23404
+ }
23405
+ const rawTitle = args.title !== undefined ? String(args.title) : undefined;
23406
+ const title = rawTitle !== undefined ? interpretEscapes(rawTitle) : undefined;
23407
+ const rawDescription = args.description !== undefined ? String(args.description) : undefined;
23408
+ const description = rawDescription !== undefined ? interpretEscapes(rawDescription) : undefined;
23409
+ const status = args.status !== undefined ? Number(args.status) : undefined;
23410
+ const labels = Array.isArray(args.labels) ? args.labels.map(String) : undefined;
23411
+ if (title === undefined && description === undefined && status === undefined && labels === undefined) {
23412
+ return { content: [{ type: "text", text: "At least one field to update is required (title, description, status, or labels)" }], isError: true };
23413
+ }
23414
+ if (status !== undefined && (Number.isNaN(status) || status !== 0 && status !== 1)) {
23415
+ return { content: [{ type: "text", text: "status must be 0 (open) or 1 (closed)" }], isError: true };
23416
+ }
23417
+ const result = await updateIssue({ apiKey, apiBaseUrl, issueId, title, description, status, labels, debug });
23418
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
23419
+ }
23420
+ if (toolName === "update_issue_comment") {
23421
+ const commentId = String(args.comment_id || "").trim();
23422
+ const rawContent = String(args.content || "");
23423
+ if (!commentId) {
23424
+ return { content: [{ type: "text", text: "comment_id is required" }], isError: true };
23425
+ }
23426
+ if (!rawContent.trim()) {
23427
+ return { content: [{ type: "text", text: "content is required" }], isError: true };
23428
+ }
23429
+ const content = interpretEscapes(rawContent);
23430
+ const result = await updateIssueComment({ apiKey, apiBaseUrl, commentId, content, debug });
23431
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
23432
+ }
23433
+ throw new Error(`Unknown tool: ${toolName}`);
23434
+ } catch (err) {
23435
+ const message = err instanceof Error ? err.message : String(err);
23436
+ return { content: [{ type: "text", text: message }], isError: true };
23437
+ }
23438
+ }
23151
23439
  async function startMcpServer(rootOpts, extra) {
23152
23440
  const server = new Server({ name: "postgresai-mcp", version: package_default2.version }, { capabilities: { tools: {} } });
23153
- const interpretEscapes = (str2) => (str2 || "").replace(/\\n/g, `
23154
- `).replace(/\\t/g, "\t").replace(/\\r/g, "\r").replace(/\\"/g, '"').replace(/\\'/g, "'");
23155
23441
  server.setRequestHandler(ListToolsRequestSchema, async () => {
23156
23442
  return {
23157
23443
  tools: [
@@ -23193,65 +23479,68 @@ async function startMcpServer(rootOpts, extra) {
23193
23479
  required: ["issue_id", "content"],
23194
23480
  additionalProperties: false
23195
23481
  }
23482
+ },
23483
+ {
23484
+ name: "create_issue",
23485
+ description: "Create a new issue in PostgresAI",
23486
+ inputSchema: {
23487
+ type: "object",
23488
+ properties: {
23489
+ title: { type: "string", description: "Issue title (required)" },
23490
+ description: { type: "string", description: "Issue description (supports \\n as newline)" },
23491
+ org_id: { type: "number", description: "Organization ID (uses config value if not provided)" },
23492
+ project_id: { type: "number", description: "Project ID to associate the issue with" },
23493
+ labels: {
23494
+ type: "array",
23495
+ items: { type: "string" },
23496
+ description: "Labels to apply to the issue"
23497
+ },
23498
+ debug: { type: "boolean", description: "Enable verbose debug logs" }
23499
+ },
23500
+ required: ["title"],
23501
+ additionalProperties: false
23502
+ }
23503
+ },
23504
+ {
23505
+ name: "update_issue",
23506
+ description: "Update an existing issue (title, description, status, labels). Use status=1 to close, status=0 to reopen.",
23507
+ inputSchema: {
23508
+ type: "object",
23509
+ properties: {
23510
+ issue_id: { type: "string", description: "Issue ID (UUID)" },
23511
+ title: { type: "string", description: "New title (supports \\n as newline)" },
23512
+ description: { type: "string", description: "New description (supports \\n as newline)" },
23513
+ status: { type: "number", description: "Status: 0=open, 1=closed" },
23514
+ labels: {
23515
+ type: "array",
23516
+ items: { type: "string" },
23517
+ description: "Labels to set on the issue"
23518
+ },
23519
+ debug: { type: "boolean", description: "Enable verbose debug logs" }
23520
+ },
23521
+ required: ["issue_id"],
23522
+ additionalProperties: false
23523
+ }
23524
+ },
23525
+ {
23526
+ name: "update_issue_comment",
23527
+ description: "Update an existing issue comment",
23528
+ inputSchema: {
23529
+ type: "object",
23530
+ properties: {
23531
+ comment_id: { type: "string", description: "Comment ID (UUID)" },
23532
+ content: { type: "string", description: "New comment text (supports \\n as newline)" },
23533
+ debug: { type: "boolean", description: "Enable verbose debug logs" }
23534
+ },
23535
+ required: ["comment_id", "content"],
23536
+ additionalProperties: false
23537
+ }
23196
23538
  }
23197
23539
  ]
23198
23540
  };
23199
23541
  });
23200
23542
  server.setRequestHandler(CallToolRequestSchema, async (req) => {
23201
- const toolName = req.params.name;
23202
- const args = req.params.arguments || {};
23203
- const cfg = readConfig2();
23204
- const apiKey = (rootOpts?.apiKey || process.env.PGAI_API_KEY || cfg.apiKey || "").toString();
23205
- const { apiBaseUrl } = resolveBaseUrls(rootOpts, cfg);
23206
- const debug = Boolean(args.debug ?? extra?.debug);
23207
- if (!apiKey) {
23208
- return {
23209
- content: [
23210
- {
23211
- type: "text",
23212
- text: "API key is required. Run 'pgai auth' or set PGAI_API_KEY."
23213
- }
23214
- ],
23215
- isError: true
23216
- };
23217
- }
23218
- try {
23219
- if (toolName === "list_issues") {
23220
- const issues = await fetchIssues({ apiKey, apiBaseUrl, debug });
23221
- return { content: [{ type: "text", text: JSON.stringify(issues, null, 2) }] };
23222
- }
23223
- if (toolName === "view_issue") {
23224
- const issueId = String(args.issue_id || "").trim();
23225
- if (!issueId) {
23226
- return { content: [{ type: "text", text: "issue_id is required" }], isError: true };
23227
- }
23228
- const issue2 = await fetchIssue({ apiKey, apiBaseUrl, issueId, debug });
23229
- if (!issue2) {
23230
- return { content: [{ type: "text", text: "Issue not found" }], isError: true };
23231
- }
23232
- const comments = await fetchIssueComments({ apiKey, apiBaseUrl, issueId, debug });
23233
- const combined = { issue: issue2, comments };
23234
- return { content: [{ type: "text", text: JSON.stringify(combined, null, 2) }] };
23235
- }
23236
- if (toolName === "post_issue_comment") {
23237
- const issueId = String(args.issue_id || "").trim();
23238
- const rawContent = String(args.content || "");
23239
- const parentCommentId = args.parent_comment_id ? String(args.parent_comment_id) : undefined;
23240
- if (!issueId) {
23241
- return { content: [{ type: "text", text: "issue_id is required" }], isError: true };
23242
- }
23243
- if (!rawContent) {
23244
- return { content: [{ type: "text", text: "content is required" }], isError: true };
23245
- }
23246
- const content = interpretEscapes(rawContent);
23247
- const result = await createIssueComment({ apiKey, apiBaseUrl, issueId, content, parentCommentId, debug });
23248
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
23249
- }
23250
- throw new Error(`Unknown tool: ${toolName}`);
23251
- } catch (err) {
23252
- const message = err instanceof Error ? err.message : String(err);
23253
- return { content: [{ type: "text", text: message }], isError: true };
23254
- }
23543
+ return handleToolCall(req, rootOpts, extra);
23255
23544
  });
23256
23545
  const transport = new StdioServerTransport;
23257
23546
  await server.connect(transport);
@@ -23269,7 +23558,8 @@ async function fetchIssues2(params) {
23269
23558
  const headers = {
23270
23559
  "access-token": apiKey,
23271
23560
  Prefer: "return=representation",
23272
- "Content-Type": "application/json"
23561
+ "Content-Type": "application/json",
23562
+ Connection: "close"
23273
23563
  };
23274
23564
  if (debug) {
23275
23565
  const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
@@ -23310,7 +23600,8 @@ async function fetchIssueComments2(params) {
23310
23600
  const headers = {
23311
23601
  "access-token": apiKey,
23312
23602
  Prefer: "return=representation",
23313
- "Content-Type": "application/json"
23603
+ "Content-Type": "application/json",
23604
+ Connection: "close"
23314
23605
  };
23315
23606
  if (debug) {
23316
23607
  const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
@@ -23354,7 +23645,8 @@ async function fetchIssue2(params) {
23354
23645
  const headers = {
23355
23646
  "access-token": apiKey,
23356
23647
  Prefer: "return=representation",
23357
- "Content-Type": "application/json"
23648
+ "Content-Type": "application/json",
23649
+ Connection: "close"
23358
23650
  };
23359
23651
  if (debug) {
23360
23652
  const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
@@ -23387,6 +23679,67 @@ async function fetchIssue2(params) {
23387
23679
  throw new Error(formatHttpError("Failed to fetch issue", response.status, data));
23388
23680
  }
23389
23681
  }
23682
+ async function createIssue2(params) {
23683
+ const { apiKey, apiBaseUrl, title, orgId, description, projectId, labels, debug } = params;
23684
+ if (!apiKey) {
23685
+ throw new Error("API key is required");
23686
+ }
23687
+ if (!title) {
23688
+ throw new Error("title is required");
23689
+ }
23690
+ if (typeof orgId !== "number") {
23691
+ throw new Error("orgId is required");
23692
+ }
23693
+ const base = normalizeBaseUrl(apiBaseUrl);
23694
+ const url = new URL(`${base}/rpc/issue_create`);
23695
+ const bodyObj = {
23696
+ title,
23697
+ org_id: orgId
23698
+ };
23699
+ if (description !== undefined) {
23700
+ bodyObj.description = description;
23701
+ }
23702
+ if (projectId !== undefined) {
23703
+ bodyObj.project_id = projectId;
23704
+ }
23705
+ if (labels && labels.length > 0) {
23706
+ bodyObj.labels = labels;
23707
+ }
23708
+ const body = JSON.stringify(bodyObj);
23709
+ const headers = {
23710
+ "access-token": apiKey,
23711
+ Prefer: "return=representation",
23712
+ "Content-Type": "application/json",
23713
+ Connection: "close"
23714
+ };
23715
+ if (debug) {
23716
+ const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
23717
+ console.log(`Debug: Resolved API base URL: ${base}`);
23718
+ console.log(`Debug: POST URL: ${url.toString()}`);
23719
+ console.log(`Debug: Auth scheme: access-token`);
23720
+ console.log(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
23721
+ console.log(`Debug: Request body: ${body}`);
23722
+ }
23723
+ const response = await fetch(url.toString(), {
23724
+ method: "POST",
23725
+ headers,
23726
+ body
23727
+ });
23728
+ if (debug) {
23729
+ console.log(`Debug: Response status: ${response.status}`);
23730
+ console.log(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
23731
+ }
23732
+ const data = await response.text();
23733
+ if (response.ok) {
23734
+ try {
23735
+ return JSON.parse(data);
23736
+ } catch {
23737
+ throw new Error(`Failed to parse create issue response: ${data}`);
23738
+ }
23739
+ } else {
23740
+ throw new Error(formatHttpError("Failed to create issue", response.status, data));
23741
+ }
23742
+ }
23390
23743
  async function createIssueComment2(params) {
23391
23744
  const { apiKey, apiBaseUrl, issueId, content, parentCommentId, debug } = params;
23392
23745
  if (!apiKey) {
@@ -23411,7 +23764,8 @@ async function createIssueComment2(params) {
23411
23764
  const headers = {
23412
23765
  "access-token": apiKey,
23413
23766
  Prefer: "return=representation",
23414
- "Content-Type": "application/json"
23767
+ "Content-Type": "application/json",
23768
+ Connection: "close"
23415
23769
  };
23416
23770
  if (debug) {
23417
23771
  const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
@@ -23441,6 +23795,121 @@ async function createIssueComment2(params) {
23441
23795
  throw new Error(formatHttpError("Failed to create issue comment", response.status, data));
23442
23796
  }
23443
23797
  }
23798
+ async function updateIssue2(params) {
23799
+ const { apiKey, apiBaseUrl, issueId, title, description, status, labels, debug } = params;
23800
+ if (!apiKey) {
23801
+ throw new Error("API key is required");
23802
+ }
23803
+ if (!issueId) {
23804
+ throw new Error("issueId is required");
23805
+ }
23806
+ if (title === undefined && description === undefined && status === undefined && labels === undefined) {
23807
+ throw new Error("At least one field to update is required (title, description, status, or labels)");
23808
+ }
23809
+ const base = normalizeBaseUrl(apiBaseUrl);
23810
+ const url = new URL(`${base}/rpc/issue_update`);
23811
+ const bodyObj = {
23812
+ p_id: issueId
23813
+ };
23814
+ if (title !== undefined) {
23815
+ bodyObj.p_title = title;
23816
+ }
23817
+ if (description !== undefined) {
23818
+ bodyObj.p_description = description;
23819
+ }
23820
+ if (status !== undefined) {
23821
+ bodyObj.p_status = status;
23822
+ }
23823
+ if (labels !== undefined) {
23824
+ bodyObj.p_labels = labels;
23825
+ }
23826
+ const body = JSON.stringify(bodyObj);
23827
+ const headers = {
23828
+ "access-token": apiKey,
23829
+ Prefer: "return=representation",
23830
+ "Content-Type": "application/json",
23831
+ Connection: "close"
23832
+ };
23833
+ if (debug) {
23834
+ const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
23835
+ console.log(`Debug: Resolved API base URL: ${base}`);
23836
+ console.log(`Debug: POST URL: ${url.toString()}`);
23837
+ console.log(`Debug: Auth scheme: access-token`);
23838
+ console.log(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
23839
+ console.log(`Debug: Request body: ${body}`);
23840
+ }
23841
+ const response = await fetch(url.toString(), {
23842
+ method: "POST",
23843
+ headers,
23844
+ body
23845
+ });
23846
+ if (debug) {
23847
+ console.log(`Debug: Response status: ${response.status}`);
23848
+ console.log(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
23849
+ }
23850
+ const data = await response.text();
23851
+ if (response.ok) {
23852
+ try {
23853
+ return JSON.parse(data);
23854
+ } catch {
23855
+ throw new Error(`Failed to parse update issue response: ${data}`);
23856
+ }
23857
+ } else {
23858
+ throw new Error(formatHttpError("Failed to update issue", response.status, data));
23859
+ }
23860
+ }
23861
+ async function updateIssueComment2(params) {
23862
+ const { apiKey, apiBaseUrl, commentId, content, debug } = params;
23863
+ if (!apiKey) {
23864
+ throw new Error("API key is required");
23865
+ }
23866
+ if (!commentId) {
23867
+ throw new Error("commentId is required");
23868
+ }
23869
+ if (!content) {
23870
+ throw new Error("content is required");
23871
+ }
23872
+ const base = normalizeBaseUrl(apiBaseUrl);
23873
+ const url = new URL(`${base}/rpc/issue_comment_update`);
23874
+ const bodyObj = {
23875
+ p_id: commentId,
23876
+ p_content: content
23877
+ };
23878
+ const body = JSON.stringify(bodyObj);
23879
+ const headers = {
23880
+ "access-token": apiKey,
23881
+ Prefer: "return=representation",
23882
+ "Content-Type": "application/json",
23883
+ Connection: "close"
23884
+ };
23885
+ if (debug) {
23886
+ const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
23887
+ console.log(`Debug: Resolved API base URL: ${base}`);
23888
+ console.log(`Debug: POST URL: ${url.toString()}`);
23889
+ console.log(`Debug: Auth scheme: access-token`);
23890
+ console.log(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
23891
+ console.log(`Debug: Request body: ${body}`);
23892
+ }
23893
+ const response = await fetch(url.toString(), {
23894
+ method: "POST",
23895
+ headers,
23896
+ body
23897
+ });
23898
+ if (debug) {
23899
+ console.log(`Debug: Response status: ${response.status}`);
23900
+ console.log(`Debug: Response headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()))}`);
23901
+ }
23902
+ const data = await response.text();
23903
+ if (response.ok) {
23904
+ try {
23905
+ return JSON.parse(data);
23906
+ } catch {
23907
+ throw new Error(`Failed to parse update comment response: ${data}`);
23908
+ }
23909
+ } else {
23910
+ throw new Error(formatHttpError("Failed to update issue comment", response.status, data));
23911
+ }
23912
+ }
23444
23913
 
23445
23914
  // lib/util.ts
23446
23915
  function maskSecret2(secret) {
@@ -25910,7 +26379,7 @@ async function execPromise(command) {
25910
26379
  childProcess.exec(command, (error2, stdout, stderr) => {
25911
26380
  if (error2) {
25912
26381
  const err = error2;
25913
- err.code = error2.code ?? 1;
26382
+ err.code = typeof error2.code === "number" ? error2.code : 1;
25914
26383
  reject(err);
25915
26384
  } else {
25916
26385
  resolve6({ stdout, stderr });
@@ -25923,7 +26392,7 @@ async function execFilePromise(file, args) {
25923
26392
  childProcess.execFile(file, args, (error2, stdout, stderr) => {
25924
26393
  if (error2) {
25925
26394
  const err = error2;
25926
- err.code = error2.code ?? 1;
26395
+ err.code = typeof error2.code === "number" ? error2.code : 1;
25927
26396
  reject(err);
25928
26397
  } else {
25929
26398
  resolve6({ stdout, stderr });
@@ -26719,6 +27188,8 @@ program2.command("help", { isDefault: true }).description("show help").action(()
26719
27188
  });
26720
27189
  var mon = program2.command("mon").description("monitoring services management");
26721
27190
  mon.command("local-install").description("install local monitoring stack (generate config, start services)").option("--demo", "demo mode with sample database", false).option("--api-key <key>", "Postgres AI API key for automated report uploads").option("--db-url <url>", "PostgreSQL connection URL to monitor").option("--tag <tag>", "Docker image tag to use (e.g., 0.14.0, 0.14.0-dev.33)").option("-y, --yes", "accept all defaults and skip interactive prompts", false).action(async (opts) => {
27191
+ const globalOpts = program2.opts();
27192
+ const apiKey = opts.apiKey || globalOpts.apiKey;
26722
27193
  console.log(`
26723
27194
  =================================`);
26724
27195
  console.log(" PostgresAI monitoring local install");
@@ -26766,7 +27237,7 @@ mon.command("local-install").description("install local monitoring stack (genera
26766
27237
  `);
26767
27238
  opts.dbUrl = undefined;
26768
27239
  }
26769
- if (opts.demo && opts.apiKey) {
27240
+ if (opts.demo && apiKey) {
26770
27241
  console.error("\u2717 Cannot use --api-key with --demo mode");
26771
27242
  console.error("\u2717 Demo mode is for testing only and does not support API key integration");
26772
27243
  console.error(`
@@ -26786,10 +27257,10 @@ Use demo mode without API key: postgres-ai mon local-install --demo`);
26786
27257
  console.log("Step 1: Postgres AI API Configuration (Optional)");
26787
27258
  console.log(`An API key enables automatic upload of PostgreSQL reports to Postgres AI
26788
27259
  `);
26789
- if (opts.apiKey) {
27260
+ if (apiKey) {
26790
27261
  console.log("Using API key provided via --api-key parameter");
26791
- writeConfig({ apiKey: opts.apiKey });
26792
- fs5.writeFileSync(path5.resolve(projectDir, ".pgwatch-config"), `api_key=${opts.apiKey}
27262
+ writeConfig({ apiKey });
27263
+ fs5.writeFileSync(path5.resolve(projectDir, ".pgwatch-config"), `api_key=${apiKey}
26793
27264
  `, {
26794
27265
  encoding: "utf8",
26795
27266
  mode: 384
@@ -27801,7 +28272,7 @@ Grafana credentials:`);
27801
28272
  console.log(` Password: ${password}`);
27802
28273
  console.log("");
27803
28274
  });
27804
- function interpretEscapes(str2) {
28275
+ function interpretEscapes2(str2) {
27805
28276
  return str2.replace(/\\\\/g, "\x00").replace(/\\n/g, `
27806
28277
  `).replace(/\\t/g, "\t").replace(/\\r/g, "\r").replace(/\\"/g, '"').replace(/\\'/g, "'").replace(/\x00/g, "\\");
27807
28278
  }
@@ -27857,12 +28328,12 @@ issues.command("view <issueId>").description("view issue details and comments").
27857
28328
  process.exitCode = 1;
27858
28329
  }
27859
28330
  });
27860
- issues.command("post_comment <issueId> <content>").description("post a new comment to an issue").option("--parent <uuid>", "parent comment id").option("--debug", "enable debug output").option("--json", "output raw JSON").action(async (issueId, content, opts) => {
28331
+ issues.command("post-comment <issueId> <content>").description("post a new comment to an issue").option("--parent <uuid>", "parent comment id").option("--debug", "enable debug output").option("--json", "output raw JSON").action(async (issueId, content, opts) => {
27861
28332
  try {
27862
28333
  if (opts.debug) {
27863
28334
  console.log(`Debug: Original content: ${JSON.stringify(content)}`);
27864
28335
  }
27865
- content = interpretEscapes(content);
28336
+ content = interpretEscapes2(content);
27866
28337
  if (opts.debug) {
27867
28338
  console.log(`Debug: Interpreted content: ${JSON.stringify(content)}`);
27868
28339
  }
@@ -27890,6 +28361,145 @@ issues.command("post_comment <issueId> <content>").description("post a new comme
27890
28361
  process.exitCode = 1;
27891
28362
  }
27892
28363
  });
28364
+ issues.command("create <title>").description("create a new issue").option("--org-id <id>", "organization id (defaults to config orgId)", (v) => parseInt(v, 10)).option("--project-id <id>", "project id", (v) => parseInt(v, 10)).option("--description <text>", "issue description (supports \\\\n)").option("--label <label>", "issue label (repeatable)", (value, previous) => {
28365
+ previous.push(value);
28366
+ return previous;
28367
+ }, []).option("--debug", "enable debug output").option("--json", "output raw JSON").action(async (rawTitle, opts) => {
28368
+ try {
28369
+ const rootOpts = program2.opts();
28370
+ const cfg = readConfig();
28371
+ const { apiKey } = getConfig(rootOpts);
28372
+ if (!apiKey) {
28373
+ console.error("API key is required. Run 'pgai auth' first or set --api-key.");
28374
+ process.exitCode = 1;
28375
+ return;
28376
+ }
28377
+ const title = interpretEscapes2(String(rawTitle || "").trim());
28378
+ if (!title) {
28379
+ console.error("title is required");
28380
+ process.exitCode = 1;
28381
+ return;
28382
+ }
28383
+ const orgId = typeof opts.orgId === "number" && !Number.isNaN(opts.orgId) ? opts.orgId : cfg.orgId;
28384
+ if (typeof orgId !== "number") {
28385
+ console.error("org_id is required. Either pass --org-id or run 'pgai auth' to store it in config.");
28386
+ process.exitCode = 1;
28387
+ return;
28388
+ }
28389
+ const description = opts.description !== undefined ? interpretEscapes2(String(opts.description)) : undefined;
28390
+ const labels = Array.isArray(opts.label) && opts.label.length > 0 ? opts.label.map(String) : undefined;
28391
+ const projectId = typeof opts.projectId === "number" && !Number.isNaN(opts.projectId) ? opts.projectId : undefined;
28392
+ const { apiBaseUrl } = resolveBaseUrls2(rootOpts, cfg);
28393
+ const result = await createIssue2({
28394
+ apiKey,
28395
+ apiBaseUrl,
28396
+ title,
28397
+ orgId,
28398
+ description,
28399
+ projectId,
28400
+ labels,
28401
+ debug: !!opts.debug
28402
+ });
28403
+ printResult(result, opts.json);
28404
+ } catch (err) {
28405
+ const message = err instanceof Error ? err.message : String(err);
28406
+ console.error(message);
28407
+ process.exitCode = 1;
28408
+ }
28409
+ });
28410
+ issues.command("update <issueId>").description("update an existing issue (title/description/status/labels)").option("--title <text>", "new title (supports \\\\n)").option("--description <text>", "new description (supports \\\\n)").option("--status <value>", "status: open|closed|0|1").option("--label <label>", "set labels (repeatable). If provided, replaces existing labels.", (value, previous) => {
28411
+ previous.push(value);
28412
+ return previous;
28413
+ }, []).option("--clear-labels", "set labels to an empty list").option("--debug", "enable debug output").option("--json", "output raw JSON").action(async (issueId, opts) => {
28414
+ try {
28415
+ const rootOpts = program2.opts();
28416
+ const cfg = readConfig();
28417
+ const { apiKey } = getConfig(rootOpts);
28418
+ if (!apiKey) {
28419
+ console.error("API key is required. Run 'pgai auth' first or set --api-key.");
28420
+ process.exitCode = 1;
28421
+ return;
28422
+ }
28423
+ const { apiBaseUrl } = resolveBaseUrls2(rootOpts, cfg);
28424
+ const title = opts.title !== undefined ? interpretEscapes2(String(opts.title)) : undefined;
28425
+ const description = opts.description !== undefined ? interpretEscapes2(String(opts.description)) : undefined;
28426
+ let status = undefined;
28427
+ if (opts.status !== undefined) {
28428
+ const raw = String(opts.status).trim().toLowerCase();
28429
+ if (raw === "open")
28430
+ status = 0;
28431
+ else if (raw === "closed")
28432
+ status = 1;
28433
+ else {
28434
+ const n = Number(raw);
28435
+ if (!Number.isFinite(n)) {
28436
+ console.error("status must be open|closed|0|1");
28437
+ process.exitCode = 1;
28438
+ return;
28439
+ }
28440
+ status = n;
28441
+ }
28442
+ if (status !== 0 && status !== 1) {
28443
+ console.error("status must be 0 (open) or 1 (closed)");
28444
+ process.exitCode = 1;
28445
+ return;
28446
+ }
28447
+ }
28448
+ let labels = undefined;
28449
+ if (opts.clearLabels) {
28450
+ labels = [];
28451
+ } else if (Array.isArray(opts.label) && opts.label.length > 0) {
28452
+ labels = opts.label.map(String);
28453
+ }
28454
+ const result = await updateIssue2({
28455
+ apiKey,
28456
+ apiBaseUrl,
28457
+ issueId,
28458
+ title,
28459
+ description,
28460
+ status,
28461
+ labels,
28462
+ debug: !!opts.debug
28463
+ });
28464
+ printResult(result, opts.json);
28465
+ } catch (err) {
28466
+ const message = err instanceof Error ? err.message : String(err);
28467
+ console.error(message);
28468
+ process.exitCode = 1;
28469
+ }
28470
+ });
28471
+ issues.command("update-comment <commentId> <content>").description("update an existing issue comment").option("--debug", "enable debug output").option("--json", "output raw JSON").action(async (commentId, content, opts) => {
28472
+ try {
28473
+ if (opts.debug) {
28474
+ console.log(`Debug: Original content: ${JSON.stringify(content)}`);
28475
+ }
28476
+ content = interpretEscapes2(content);
28477
+ if (opts.debug) {
28478
+ console.log(`Debug: Interpreted content: ${JSON.stringify(content)}`);
28479
+ }
28480
+ const rootOpts = program2.opts();
28481
+ const cfg = readConfig();
28482
+ const { apiKey } = getConfig(rootOpts);
28483
+ if (!apiKey) {
28484
+ console.error("API key is required. Run 'pgai auth' first or set --api-key.");
28485
+ process.exitCode = 1;
28486
+ return;
28487
+ }
28488
+ const { apiBaseUrl } = resolveBaseUrls2(rootOpts, cfg);
28489
+ const result = await updateIssueComment2({
28490
+ apiKey,
28491
+ apiBaseUrl,
28492
+ commentId,
28493
+ content,
28494
+ debug: !!opts.debug
28495
+ });
28496
+ printResult(result, opts.json);
28497
+ } catch (err) {
28498
+ const message = err instanceof Error ? err.message : String(err);
28499
+ console.error(message);
28500
+ process.exitCode = 1;
28501
+ }
28502
+ });
27893
28503
  var mcp = program2.command("mcp").description("MCP server integration");
27894
28504
  mcp.command("start").description("start MCP stdio server").option("--debug", "enable debug output").action(async (opts) => {
27895
28505
  const rootOpts = program2.opts();