postgresai 0.15.0-dev.1 → 0.15.0-dev.2

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.15.0-dev.1",
13067
+ version: "0.15.0-dev.2",
13068
13068
  description: "postgres_ai CLI",
13069
13069
  license: "Apache-2.0",
13070
13070
  private: false,
@@ -15892,7 +15892,7 @@ var Result = import_lib.default.Result;
15892
15892
  var TypeOverrides = import_lib.default.TypeOverrides;
15893
15893
  var defaults = import_lib.default.defaults;
15894
15894
  // package.json
15895
- var version = "0.15.0-dev.1";
15895
+ var version = "0.15.0-dev.2";
15896
15896
  var package_default2 = {
15897
15897
  name: "postgresai",
15898
15898
  version,
@@ -16709,6 +16709,192 @@ async function updateActionItem(params) {
16709
16709
  }
16710
16710
  }
16711
16711
 
16712
+ // lib/reports.ts
16713
+ function parseFlexibleDate(input) {
16714
+ const s = input.trim();
16715
+ const dotMatch = s.match(/^(\d{1,2})\.(\d{1,2})\.(\d{4})(?:\s+(\d{1,2}):(\d{2})(?::(\d{2}))?)?$/);
16716
+ if (dotMatch) {
16717
+ const [, dd, mm, yyyy, hh, min, ss] = dotMatch;
16718
+ const iso = `${yyyy}-${mm.padStart(2, "0")}-${dd.padStart(2, "0")}T${(hh ?? "00").padStart(2, "0")}:${(min ?? "00").padStart(2, "0")}:${(ss ?? "00").padStart(2, "0")}Z`;
16719
+ const d = new Date(iso);
16720
+ if (isNaN(d.getTime()))
16721
+ throw new Error(`Invalid date: ${input}`);
16722
+ return d.toISOString();
16723
+ }
16724
+ const isoMatch = s.match(/^(\d{4})-(\d{2})-(\d{2})(?:[T ](\d{2}):(\d{2})(?::(\d{2}))?)?$/);
16725
+ if (isoMatch) {
16726
+ const [, yyyy, mm, dd, hh, min, ss] = isoMatch;
16727
+ const iso = `${yyyy}-${mm}-${dd}T${hh ?? "00"}:${min ?? "00"}:${ss ?? "00"}Z`;
16728
+ const d = new Date(iso);
16729
+ if (isNaN(d.getTime()))
16730
+ throw new Error(`Invalid date: ${input}`);
16731
+ return d.toISOString();
16732
+ }
16733
+ throw new Error(`Unrecognized date format: ${input}. Use YYYY-MM-DD or DD.MM.YYYY`);
16734
+ }
16735
+ async function fetchReports(params) {
16736
+ const { apiKey, apiBaseUrl, projectId, status, limit = 20, beforeDate, beforeId, debug } = params;
16737
+ if (!apiKey) {
16738
+ throw new Error("API key is required");
16739
+ }
16740
+ const base = normalizeBaseUrl(apiBaseUrl);
16741
+ const url = new URL(`${base}/checkup_reports`);
16742
+ url.searchParams.set("order", "id.desc");
16743
+ url.searchParams.set("limit", String(limit));
16744
+ if (typeof projectId === "number") {
16745
+ url.searchParams.set("project_id", `eq.${projectId}`);
16746
+ }
16747
+ if (status) {
16748
+ url.searchParams.set("status", `eq.${status}`);
16749
+ }
16750
+ if (beforeDate) {
16751
+ url.searchParams.set("created_at", `lt.${beforeDate}`);
16752
+ }
16753
+ if (typeof beforeId === "number") {
16754
+ url.searchParams.set("id", `lt.${beforeId}`);
16755
+ }
16756
+ const headers = {
16757
+ "access-token": apiKey,
16758
+ Prefer: "return=representation",
16759
+ "Content-Type": "application/json",
16760
+ Connection: "close"
16761
+ };
16762
+ if (debug) {
16763
+ const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
16764
+ console.log(`Debug: Resolved API base URL: ${base}`);
16765
+ console.log(`Debug: GET URL: ${url.toString()}`);
16766
+ console.log(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
16767
+ }
16768
+ const response = await fetch(url.toString(), { method: "GET", headers });
16769
+ if (debug) {
16770
+ console.log(`Debug: Response status: ${response.status}`);
16771
+ }
16772
+ const data = await response.text();
16773
+ if (response.ok) {
16774
+ try {
16775
+ return JSON.parse(data);
16776
+ } catch {
16777
+ throw new Error(`Failed to parse reports response: ${data}`);
16778
+ }
16779
+ } else {
16780
+ throw new Error(formatHttpError("Failed to fetch reports", response.status, data));
16781
+ }
16782
+ }
16783
+ var MAX_ALL_REPORTS = 1e4;
16784
+ async function fetchAllReports(params) {
16785
+ const pageSize = params.limit ?? 100;
16786
+ const all = [];
16787
+ let beforeId;
16788
+ while (true) {
16789
+ const page = await fetchReports({ ...params, limit: pageSize, beforeId });
16790
+ if (page.length === 0)
16791
+ break;
16792
+ all.push(...page);
16793
+ if (all.length >= MAX_ALL_REPORTS) {
16794
+ console.warn(`Warning: reached maximum of ${MAX_ALL_REPORTS} reports, stopping pagination`);
16795
+ break;
16796
+ }
16797
+ beforeId = page[page.length - 1].id;
16798
+ if (page.length < pageSize)
16799
+ break;
16800
+ }
16801
+ return all;
16802
+ }
16803
+ async function fetchReportFiles(params) {
16804
+ const { apiKey, apiBaseUrl, reportId, type: type2, checkId, debug } = params;
16805
+ if (!apiKey) {
16806
+ throw new Error("API key is required");
16807
+ }
16808
+ if (reportId === undefined && !checkId) {
16809
+ throw new Error("Either reportId or checkId is required");
16810
+ }
16811
+ const base = normalizeBaseUrl(apiBaseUrl);
16812
+ const url = new URL(`${base}/checkup_report_files`);
16813
+ if (typeof reportId === "number") {
16814
+ url.searchParams.set("checkup_report_id", `eq.${reportId}`);
16815
+ }
16816
+ url.searchParams.set("order", "id.asc");
16817
+ if (type2) {
16818
+ url.searchParams.set("type", `eq.${type2}`);
16819
+ }
16820
+ if (checkId) {
16821
+ url.searchParams.set("check_id", `eq.${checkId}`);
16822
+ }
16823
+ const headers = {
16824
+ "access-token": apiKey,
16825
+ Prefer: "return=representation",
16826
+ "Content-Type": "application/json",
16827
+ Connection: "close"
16828
+ };
16829
+ if (debug) {
16830
+ const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
16831
+ console.log(`Debug: Resolved API base URL: ${base}`);
16832
+ console.log(`Debug: GET URL: ${url.toString()}`);
16833
+ console.log(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
16834
+ }
16835
+ const response = await fetch(url.toString(), { method: "GET", headers });
16836
+ if (debug) {
16837
+ console.log(`Debug: Response status: ${response.status}`);
16838
+ }
16839
+ const data = await response.text();
16840
+ if (response.ok) {
16841
+ try {
16842
+ return JSON.parse(data);
16843
+ } catch {
16844
+ throw new Error(`Failed to parse report files response: ${data}`);
16845
+ }
16846
+ } else {
16847
+ throw new Error(formatHttpError("Failed to fetch report files", response.status, data));
16848
+ }
16849
+ }
16850
+ async function fetchReportFileData(params) {
16851
+ const { apiKey, apiBaseUrl, reportId, type: type2, checkId, debug } = params;
16852
+ if (!apiKey) {
16853
+ throw new Error("API key is required");
16854
+ }
16855
+ if (reportId === undefined && !checkId) {
16856
+ throw new Error("Either reportId or checkId is required");
16857
+ }
16858
+ const base = normalizeBaseUrl(apiBaseUrl);
16859
+ const url = new URL(`${base}/checkup_report_file_data`);
16860
+ if (typeof reportId === "number") {
16861
+ url.searchParams.set("checkup_report_id", `eq.${reportId}`);
16862
+ }
16863
+ url.searchParams.set("order", "id.asc");
16864
+ if (type2) {
16865
+ url.searchParams.set("type", `eq.${type2}`);
16866
+ }
16867
+ if (checkId) {
16868
+ url.searchParams.set("check_id", `eq.${checkId}`);
16869
+ }
16870
+ const headers = {
16871
+ "access-token": apiKey,
16872
+ Prefer: "return=representation",
16873
+ "Content-Type": "application/json",
16874
+ Connection: "close"
16875
+ };
16876
+ if (debug) {
16877
+ const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
16878
+ console.log(`Debug: Resolved API base URL: ${base}`);
16879
+ console.log(`Debug: GET URL: ${url.toString()}`);
16880
+ console.log(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
16881
+ }
16882
+ const response = await fetch(url.toString(), { method: "GET", headers });
16883
+ if (debug) {
16884
+ console.log(`Debug: Response status: ${response.status}`);
16885
+ }
16886
+ const data = await response.text();
16887
+ if (response.ok) {
16888
+ try {
16889
+ return JSON.parse(data);
16890
+ } catch {
16891
+ throw new Error(`Failed to parse report file data response: ${data}`);
16892
+ }
16893
+ } else {
16894
+ throw new Error(formatHttpError("Failed to fetch report file data", response.status, data));
16895
+ }
16896
+ }
16897
+
16712
16898
  // node_modules/zod/v4/core/core.js
16713
16899
  var NEVER = Object.freeze({
16714
16900
  status: "aborted"
@@ -23770,6 +23956,46 @@ async function handleToolCall(req, rootOpts, extra) {
23770
23956
  await updateActionItem({ apiKey, apiBaseUrl, actionItemId, title, description, isDone, status, statusReason, debug });
23771
23957
  return { content: [{ type: "text", text: JSON.stringify({ success: true }, null, 2) }] };
23772
23958
  }
23959
+ if (toolName === "list_reports") {
23960
+ const projectId = args.project_id !== undefined ? Number(args.project_id) : undefined;
23961
+ const status = args.status ? String(args.status) : undefined;
23962
+ const limit = args.limit !== undefined ? Number(args.limit) : undefined;
23963
+ const beforeDate = args.before_date ? parseFlexibleDate(String(args.before_date)) : undefined;
23964
+ const all = args.all === true;
23965
+ let reports;
23966
+ if (all) {
23967
+ reports = await fetchAllReports({ apiKey, apiBaseUrl, projectId, status, limit, debug });
23968
+ } else {
23969
+ reports = await fetchReports({ apiKey, apiBaseUrl, projectId, status, limit, beforeDate, debug });
23970
+ }
23971
+ return { content: [{ type: "text", text: JSON.stringify(reports, null, 2) }] };
23972
+ }
23973
+ if (toolName === "list_report_files") {
23974
+ const reportId = args.report_id !== undefined ? Number(args.report_id) : undefined;
23975
+ if (reportId !== undefined && isNaN(reportId)) {
23976
+ return { content: [{ type: "text", text: "report_id must be a number" }], isError: true };
23977
+ }
23978
+ const type2 = args.type ? String(args.type) : undefined;
23979
+ const checkId = args.check_id ? String(args.check_id) : undefined;
23980
+ if (reportId === undefined && !checkId) {
23981
+ return { content: [{ type: "text", text: "Either report_id or check_id is required" }], isError: true };
23982
+ }
23983
+ const files = await fetchReportFiles({ apiKey, apiBaseUrl, reportId, type: type2, checkId, debug });
23984
+ return { content: [{ type: "text", text: JSON.stringify(files, null, 2) }] };
23985
+ }
23986
+ if (toolName === "get_report_data") {
23987
+ const reportId = args.report_id !== undefined ? Number(args.report_id) : undefined;
23988
+ if (reportId !== undefined && isNaN(reportId)) {
23989
+ return { content: [{ type: "text", text: "report_id must be a number" }], isError: true };
23990
+ }
23991
+ const type2 = args.type ? String(args.type) : undefined;
23992
+ const checkId = args.check_id ? String(args.check_id) : undefined;
23993
+ if (reportId === undefined && !checkId) {
23994
+ return { content: [{ type: "text", text: "Either report_id or check_id is required" }], isError: true };
23995
+ }
23996
+ const files = await fetchReportFileData({ apiKey, apiBaseUrl, reportId, type: type2, checkId, debug });
23997
+ return { content: [{ type: "text", text: JSON.stringify(files, null, 2) }] };
23998
+ }
23773
23999
  throw new Error(`Unknown tool: ${toolName}`);
23774
24000
  } catch (err) {
23775
24001
  const message = err instanceof Error ? err.message : String(err);
@@ -23955,6 +24181,50 @@ async function startMcpServer(rootOpts, extra) {
23955
24181
  required: ["action_item_id"],
23956
24182
  additionalProperties: false
23957
24183
  }
24184
+ },
24185
+ {
24186
+ name: "list_reports",
24187
+ description: "List checkup reports. Returns report metadata: id, project, status, timestamps. Use get_report_data to fetch actual report content. Supports date-based filtering with before_date.",
24188
+ inputSchema: {
24189
+ type: "object",
24190
+ properties: {
24191
+ project_id: { type: "number", description: "Filter by project ID" },
24192
+ status: { type: "string", description: "Filter by status (e.g., 'completed')" },
24193
+ limit: { type: "number", description: "Max number of reports to return (default: 20)" },
24194
+ before_date: { type: "string", description: "Show reports created before this date (YYYY-MM-DD, DD.MM.YYYY, YYYY-MM-DD HH:mm, etc.)" },
24195
+ all: { type: "boolean", description: "Fetch all reports (paginated automatically)" },
24196
+ debug: { type: "boolean", description: "Enable verbose debug logs" }
24197
+ },
24198
+ additionalProperties: false
24199
+ }
24200
+ },
24201
+ {
24202
+ name: "list_report_files",
24203
+ description: "List files in a checkup report (metadata only, no content). Each report contains json (raw data) and md (markdown analysis) files per check. Either report_id or check_id must be provided.",
24204
+ inputSchema: {
24205
+ type: "object",
24206
+ properties: {
24207
+ report_id: { type: "number", description: "Checkup report ID (optional if check_id is provided)" },
24208
+ type: { type: "string", description: "Filter by file type: 'json' or 'md'" },
24209
+ check_id: { type: "string", description: "Filter by check ID (e.g., 'H002', 'F004')" },
24210
+ debug: { type: "boolean", description: "Enable verbose debug logs" }
24211
+ },
24212
+ additionalProperties: false
24213
+ }
24214
+ },
24215
+ {
24216
+ name: "get_report_data",
24217
+ description: "Get checkup report file content. Returns files with a 'data' field containing the actual content: markdown analysis or JSON raw data. Use type='md' for human-readable analysis with recommendations, type='json' for raw check data. Either report_id or check_id must be provided.",
24218
+ inputSchema: {
24219
+ type: "object",
24220
+ properties: {
24221
+ report_id: { type: "number", description: "Checkup report ID (optional if check_id is provided)" },
24222
+ type: { type: "string", description: "Filter by file type: 'json' for raw data, 'md' for markdown analysis" },
24223
+ check_id: { type: "string", description: "Filter by check ID (e.g., 'H002', 'F004')" },
24224
+ debug: { type: "boolean", description: "Enable verbose debug logs" }
24225
+ },
24226
+ additionalProperties: false
24227
+ }
23958
24228
  }
23959
24229
  ]
23960
24230
  };
@@ -24588,6 +24858,245 @@ async function updateActionItem2(params) {
24588
24858
  }
24589
24859
  }
24590
24860
 
24861
+ // lib/reports.ts
24862
+ function parseFlexibleDate2(input) {
24863
+ const s = input.trim();
24864
+ const dotMatch = s.match(/^(\d{1,2})\.(\d{1,2})\.(\d{4})(?:\s+(\d{1,2}):(\d{2})(?::(\d{2}))?)?$/);
24865
+ if (dotMatch) {
24866
+ const [, dd, mm, yyyy, hh, min, ss] = dotMatch;
24867
+ const iso = `${yyyy}-${mm.padStart(2, "0")}-${dd.padStart(2, "0")}T${(hh ?? "00").padStart(2, "0")}:${(min ?? "00").padStart(2, "0")}:${(ss ?? "00").padStart(2, "0")}Z`;
24868
+ const d = new Date(iso);
24869
+ if (isNaN(d.getTime()))
24870
+ throw new Error(`Invalid date: ${input}`);
24871
+ return d.toISOString();
24872
+ }
24873
+ const isoMatch = s.match(/^(\d{4})-(\d{2})-(\d{2})(?:[T ](\d{2}):(\d{2})(?::(\d{2}))?)?$/);
24874
+ if (isoMatch) {
24875
+ const [, yyyy, mm, dd, hh, min, ss] = isoMatch;
24876
+ const iso = `${yyyy}-${mm}-${dd}T${hh ?? "00"}:${min ?? "00"}:${ss ?? "00"}Z`;
24877
+ const d = new Date(iso);
24878
+ if (isNaN(d.getTime()))
24879
+ throw new Error(`Invalid date: ${input}`);
24880
+ return d.toISOString();
24881
+ }
24882
+ throw new Error(`Unrecognized date format: ${input}. Use YYYY-MM-DD or DD.MM.YYYY`);
24883
+ }
24884
+ async function fetchReports2(params) {
24885
+ const { apiKey, apiBaseUrl, projectId, status, limit = 20, beforeDate, beforeId, debug } = params;
24886
+ if (!apiKey) {
24887
+ throw new Error("API key is required");
24888
+ }
24889
+ const base = normalizeBaseUrl(apiBaseUrl);
24890
+ const url = new URL(`${base}/checkup_reports`);
24891
+ url.searchParams.set("order", "id.desc");
24892
+ url.searchParams.set("limit", String(limit));
24893
+ if (typeof projectId === "number") {
24894
+ url.searchParams.set("project_id", `eq.${projectId}`);
24895
+ }
24896
+ if (status) {
24897
+ url.searchParams.set("status", `eq.${status}`);
24898
+ }
24899
+ if (beforeDate) {
24900
+ url.searchParams.set("created_at", `lt.${beforeDate}`);
24901
+ }
24902
+ if (typeof beforeId === "number") {
24903
+ url.searchParams.set("id", `lt.${beforeId}`);
24904
+ }
24905
+ const headers = {
24906
+ "access-token": apiKey,
24907
+ Prefer: "return=representation",
24908
+ "Content-Type": "application/json",
24909
+ Connection: "close"
24910
+ };
24911
+ if (debug) {
24912
+ const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
24913
+ console.log(`Debug: Resolved API base URL: ${base}`);
24914
+ console.log(`Debug: GET URL: ${url.toString()}`);
24915
+ console.log(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
24916
+ }
24917
+ const response = await fetch(url.toString(), { method: "GET", headers });
24918
+ if (debug) {
24919
+ console.log(`Debug: Response status: ${response.status}`);
24920
+ }
24921
+ const data = await response.text();
24922
+ if (response.ok) {
24923
+ try {
24924
+ return JSON.parse(data);
24925
+ } catch {
24926
+ throw new Error(`Failed to parse reports response: ${data}`);
24927
+ }
24928
+ } else {
24929
+ throw new Error(formatHttpError("Failed to fetch reports", response.status, data));
24930
+ }
24931
+ }
24932
+ var MAX_ALL_REPORTS2 = 1e4;
24933
+ async function fetchAllReports2(params) {
24934
+ const pageSize = params.limit ?? 100;
24935
+ const all = [];
24936
+ let beforeId;
24937
+ while (true) {
24938
+ const page = await fetchReports2({ ...params, limit: pageSize, beforeId });
24939
+ if (page.length === 0)
24940
+ break;
24941
+ all.push(...page);
24942
+ if (all.length >= MAX_ALL_REPORTS2) {
24943
+ console.warn(`Warning: reached maximum of ${MAX_ALL_REPORTS2} reports, stopping pagination`);
24944
+ break;
24945
+ }
24946
+ beforeId = page[page.length - 1].id;
24947
+ if (page.length < pageSize)
24948
+ break;
24949
+ }
24950
+ return all;
24951
+ }
24952
+ async function fetchReportFiles2(params) {
24953
+ const { apiKey, apiBaseUrl, reportId, type: type2, checkId, debug } = params;
24954
+ if (!apiKey) {
24955
+ throw new Error("API key is required");
24956
+ }
24957
+ if (reportId === undefined && !checkId) {
24958
+ throw new Error("Either reportId or checkId is required");
24959
+ }
24960
+ const base = normalizeBaseUrl(apiBaseUrl);
24961
+ const url = new URL(`${base}/checkup_report_files`);
24962
+ if (typeof reportId === "number") {
24963
+ url.searchParams.set("checkup_report_id", `eq.${reportId}`);
24964
+ }
24965
+ url.searchParams.set("order", "id.asc");
24966
+ if (type2) {
24967
+ url.searchParams.set("type", `eq.${type2}`);
24968
+ }
24969
+ if (checkId) {
24970
+ url.searchParams.set("check_id", `eq.${checkId}`);
24971
+ }
24972
+ const headers = {
24973
+ "access-token": apiKey,
24974
+ Prefer: "return=representation",
24975
+ "Content-Type": "application/json",
24976
+ Connection: "close"
24977
+ };
24978
+ if (debug) {
24979
+ const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
24980
+ console.log(`Debug: Resolved API base URL: ${base}`);
24981
+ console.log(`Debug: GET URL: ${url.toString()}`);
24982
+ console.log(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
24983
+ }
24984
+ const response = await fetch(url.toString(), { method: "GET", headers });
24985
+ if (debug) {
24986
+ console.log(`Debug: Response status: ${response.status}`);
24987
+ }
24988
+ const data = await response.text();
24989
+ if (response.ok) {
24990
+ try {
24991
+ return JSON.parse(data);
24992
+ } catch {
24993
+ throw new Error(`Failed to parse report files response: ${data}`);
24994
+ }
24995
+ } else {
24996
+ throw new Error(formatHttpError("Failed to fetch report files", response.status, data));
24997
+ }
24998
+ }
24999
+ async function fetchReportFileData2(params) {
25000
+ const { apiKey, apiBaseUrl, reportId, type: type2, checkId, debug } = params;
25001
+ if (!apiKey) {
25002
+ throw new Error("API key is required");
25003
+ }
25004
+ if (reportId === undefined && !checkId) {
25005
+ throw new Error("Either reportId or checkId is required");
25006
+ }
25007
+ const base = normalizeBaseUrl(apiBaseUrl);
25008
+ const url = new URL(`${base}/checkup_report_file_data`);
25009
+ if (typeof reportId === "number") {
25010
+ url.searchParams.set("checkup_report_id", `eq.${reportId}`);
25011
+ }
25012
+ url.searchParams.set("order", "id.asc");
25013
+ if (type2) {
25014
+ url.searchParams.set("type", `eq.${type2}`);
25015
+ }
25016
+ if (checkId) {
25017
+ url.searchParams.set("check_id", `eq.${checkId}`);
25018
+ }
25019
+ const headers = {
25020
+ "access-token": apiKey,
25021
+ Prefer: "return=representation",
25022
+ "Content-Type": "application/json",
25023
+ Connection: "close"
25024
+ };
25025
+ if (debug) {
25026
+ const debugHeaders = { ...headers, "access-token": maskSecret(apiKey) };
25027
+ console.log(`Debug: Resolved API base URL: ${base}`);
25028
+ console.log(`Debug: GET URL: ${url.toString()}`);
25029
+ console.log(`Debug: Request headers: ${JSON.stringify(debugHeaders)}`);
25030
+ }
25031
+ const response = await fetch(url.toString(), { method: "GET", headers });
25032
+ if (debug) {
25033
+ console.log(`Debug: Response status: ${response.status}`);
25034
+ }
25035
+ const data = await response.text();
25036
+ if (response.ok) {
25037
+ try {
25038
+ return JSON.parse(data);
25039
+ } catch {
25040
+ throw new Error(`Failed to parse report file data response: ${data}`);
25041
+ }
25042
+ } else {
25043
+ throw new Error(formatHttpError("Failed to fetch report file data", response.status, data));
25044
+ }
25045
+ }
25046
+ function renderMarkdownForTerminal(md) {
25047
+ if (!md)
25048
+ return "";
25049
+ const RESET = "\x1B[0m";
25050
+ const BOLD = "\x1B[1m";
25051
+ const BOLD_UNDERLINE = "\x1B[1;4m";
25052
+ const DIM = "\x1B[2m";
25053
+ const ITALIC = "\x1B[3m";
25054
+ const CYAN = "\x1B[36m";
25055
+ const lines = md.split(`
25056
+ `);
25057
+ const output = [];
25058
+ let inCodeBlock = false;
25059
+ for (const line of lines) {
25060
+ if (line.trimStart().startsWith("```")) {
25061
+ inCodeBlock = !inCodeBlock;
25062
+ if (inCodeBlock) {
25063
+ output.push(`${DIM}${"─".repeat(40)}${RESET}`);
25064
+ } else {
25065
+ output.push(`${DIM}${"─".repeat(40)}${RESET}`);
25066
+ }
25067
+ continue;
25068
+ }
25069
+ if (inCodeBlock) {
25070
+ output.push(`${DIM} ${line}${RESET}`);
25071
+ continue;
25072
+ }
25073
+ if (/^-{3,}$/.test(line.trim()) || /^\*{3,}$/.test(line.trim()) || /^_{3,}$/.test(line.trim())) {
25074
+ output.push(`${DIM}${"─".repeat(60)}${RESET}`);
25075
+ continue;
25076
+ }
25077
+ const headingMatch = line.match(/^(#{1,6})\s+(.*)/);
25078
+ if (headingMatch) {
25079
+ const level = headingMatch[1].length;
25080
+ const text = headingMatch[2];
25081
+ if (level === 1) {
25082
+ output.push(`${BOLD_UNDERLINE}${text}${RESET}`);
25083
+ } else {
25084
+ output.push(`${BOLD}${text}${RESET}`);
25085
+ }
25086
+ continue;
25087
+ }
25088
+ let formatted = line;
25089
+ formatted = formatted.replace(/\*\*(.+?)\*\*/g, `${BOLD}$1${RESET}`);
25090
+ formatted = formatted.replace(/__(.+?)__/g, `${BOLD}$1${RESET}`);
25091
+ formatted = formatted.replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, `${ITALIC}$1${RESET}`);
25092
+ formatted = formatted.replace(/(?<=^|[\s(])_([^\s_](?:.*?[^\s_])?)_(?=$|[\s),.:;!?])/g, `${ITALIC}$1${RESET}`);
25093
+ formatted = formatted.replace(/`([^`]+)`/g, `${CYAN}$1${RESET}`);
25094
+ output.push(formatted);
25095
+ }
25096
+ return output.join(`
25097
+ `);
25098
+ }
25099
+
24591
25100
  // lib/util.ts
24592
25101
  function maskSecret2(secret) {
24593
25102
  if (!secret)
@@ -32081,6 +32590,193 @@ issues.command("update-action-item <actionItemId>").description("update an actio
32081
32590
  process.exitCode = 1;
32082
32591
  }
32083
32592
  });
32593
+ var reports = program2.command("reports").description("checkup reports management");
32594
+ reports.command("list").description("list checkup reports").option("--project-id <id>", "filter by project id", (v) => parseInt(v, 10)).option("--status <status>", "filter by status (e.g., completed)").option("--limit <n>", "max number of reports to return (default: 20)", (v) => parseInt(v, 10)).option("--before <date>", "show reports created before this date (YYYY-MM-DD, DD.MM.YYYY, etc.)").option("--all", "fetch all reports (paginated automatically)").option("--debug", "enable debug output").option("--json", "output raw JSON").action(async (opts) => {
32595
+ const spinner = createTtySpinner(process.stdout.isTTY ?? false, "Fetching reports...");
32596
+ try {
32597
+ const rootOpts = program2.opts();
32598
+ const cfg = readConfig();
32599
+ const { apiKey } = getConfig(rootOpts);
32600
+ if (!apiKey) {
32601
+ spinner.stop();
32602
+ console.error("API key is required. Run 'pgai auth' first or set --api-key.");
32603
+ process.exitCode = 1;
32604
+ return;
32605
+ }
32606
+ if (opts.all && opts.before) {
32607
+ spinner.stop();
32608
+ console.error("--all and --before cannot be used together");
32609
+ process.exitCode = 1;
32610
+ return;
32611
+ }
32612
+ const { apiBaseUrl } = resolveBaseUrls2(rootOpts, cfg);
32613
+ let result;
32614
+ if (opts.all) {
32615
+ result = await fetchAllReports2({
32616
+ apiKey,
32617
+ apiBaseUrl,
32618
+ projectId: opts.projectId,
32619
+ status: opts.status,
32620
+ limit: opts.limit,
32621
+ debug: !!opts.debug
32622
+ });
32623
+ } else {
32624
+ result = await fetchReports2({
32625
+ apiKey,
32626
+ apiBaseUrl,
32627
+ projectId: opts.projectId,
32628
+ status: opts.status,
32629
+ limit: opts.limit,
32630
+ beforeDate: opts.before ? parseFlexibleDate2(opts.before) : undefined,
32631
+ debug: !!opts.debug
32632
+ });
32633
+ }
32634
+ spinner.stop();
32635
+ printResult(result, opts.json);
32636
+ } catch (err) {
32637
+ spinner.stop();
32638
+ const message = err instanceof Error ? err.message : String(err);
32639
+ console.error(message);
32640
+ process.exitCode = 1;
32641
+ }
32642
+ });
32643
+ reports.command("files [reportId]").description("list files of a checkup report (metadata only, no content)").option("--type <type>", "filter by file type: json, md").option("--check-id <id>", "filter by check ID (e.g., H002)").option("--debug", "enable debug output").option("--json", "output raw JSON").action(async (reportId, opts) => {
32644
+ const spinner = createTtySpinner(process.stdout.isTTY ?? false, "Fetching report files...");
32645
+ try {
32646
+ const rootOpts = program2.opts();
32647
+ const cfg = readConfig();
32648
+ const { apiKey } = getConfig(rootOpts);
32649
+ if (!apiKey) {
32650
+ spinner.stop();
32651
+ console.error("API key is required. Run 'pgai auth' first or set --api-key.");
32652
+ process.exitCode = 1;
32653
+ return;
32654
+ }
32655
+ let numericId;
32656
+ if (reportId !== undefined) {
32657
+ numericId = parseInt(reportId, 10);
32658
+ if (isNaN(numericId)) {
32659
+ spinner.stop();
32660
+ console.error("reportId must be a number");
32661
+ process.exitCode = 1;
32662
+ return;
32663
+ }
32664
+ }
32665
+ if (numericId === undefined && !opts.checkId) {
32666
+ spinner.stop();
32667
+ console.error("Either reportId or --check-id is required");
32668
+ process.exitCode = 1;
32669
+ return;
32670
+ }
32671
+ const { apiBaseUrl } = resolveBaseUrls2(rootOpts, cfg);
32672
+ const result = await fetchReportFiles2({
32673
+ apiKey,
32674
+ apiBaseUrl,
32675
+ reportId: numericId,
32676
+ type: opts.type,
32677
+ checkId: opts.checkId,
32678
+ debug: !!opts.debug
32679
+ });
32680
+ spinner.stop();
32681
+ printResult(result, opts.json);
32682
+ } catch (err) {
32683
+ spinner.stop();
32684
+ const message = err instanceof Error ? err.message : String(err);
32685
+ console.error(message);
32686
+ process.exitCode = 1;
32687
+ }
32688
+ });
32689
+ reports.command("data [reportId]").description("get checkup report file data (includes content)").option("--type <type>", "filter by file type: json, md").option("--check-id <id>", "filter by check ID (e.g., H002)").option("--formatted", "render markdown with ANSI styling (experimental)").option("-o, --output <dir>", "save files to directory (uses original filenames)").option("--debug", "enable debug output").option("--json", "output raw JSON").action(async (reportId, opts) => {
32690
+ const spinner = createTtySpinner(process.stdout.isTTY ?? false, "Fetching report data...");
32691
+ try {
32692
+ const rootOpts = program2.opts();
32693
+ const cfg = readConfig();
32694
+ const { apiKey } = getConfig(rootOpts);
32695
+ if (!apiKey) {
32696
+ spinner.stop();
32697
+ console.error("API key is required. Run 'pgai auth' first or set --api-key.");
32698
+ process.exitCode = 1;
32699
+ return;
32700
+ }
32701
+ let numericId;
32702
+ if (reportId !== undefined) {
32703
+ numericId = parseInt(reportId, 10);
32704
+ if (isNaN(numericId)) {
32705
+ spinner.stop();
32706
+ console.error("reportId must be a number");
32707
+ process.exitCode = 1;
32708
+ return;
32709
+ }
32710
+ }
32711
+ if (numericId === undefined && !opts.checkId) {
32712
+ spinner.stop();
32713
+ console.error("Either reportId or --check-id is required");
32714
+ process.exitCode = 1;
32715
+ return;
32716
+ }
32717
+ const { apiBaseUrl } = resolveBaseUrls2(rootOpts, cfg);
32718
+ const effectiveType = opts.type ?? (!opts.json && !opts.output ? "md" : undefined);
32719
+ const result = await fetchReportFileData2({
32720
+ apiKey,
32721
+ apiBaseUrl,
32722
+ reportId: numericId,
32723
+ type: effectiveType,
32724
+ checkId: opts.checkId,
32725
+ debug: !!opts.debug
32726
+ });
32727
+ spinner.stop();
32728
+ if (opts.output) {
32729
+ const dir = path5.resolve(opts.output);
32730
+ fs5.mkdirSync(dir, { recursive: true });
32731
+ for (const f of result) {
32732
+ const safeName = path5.basename(f.filename);
32733
+ const filePath = path5.join(dir, safeName);
32734
+ const content = f.type === "json" ? JSON.stringify(tryParseJson(f.data), null, 2) : f.data;
32735
+ fs5.writeFileSync(filePath, content, "utf-8");
32736
+ console.log(filePath);
32737
+ }
32738
+ } else if (opts.json) {
32739
+ const processed = result.map((f) => ({
32740
+ ...f,
32741
+ data: f.type === "json" ? tryParseJson(f.data) : f.data
32742
+ }));
32743
+ printResult(processed, true);
32744
+ } else if (opts.formatted && process.stdout.isTTY) {
32745
+ for (const f of result) {
32746
+ if (result.length > 1) {
32747
+ console.log(`\x1B[1m--- ${f.filename} (${f.check_id}, ${f.type}) ---\x1B[0m`);
32748
+ }
32749
+ if (f.type === "md") {
32750
+ console.log(renderMarkdownForTerminal(f.data));
32751
+ } else if (f.type === "json") {
32752
+ const parsed = tryParseJson(f.data);
32753
+ console.log(typeof parsed === "string" ? parsed : JSON.stringify(parsed, null, 2));
32754
+ } else {
32755
+ console.log(f.data);
32756
+ }
32757
+ }
32758
+ } else {
32759
+ for (const f of result) {
32760
+ if (result.length > 1) {
32761
+ console.log(`--- ${f.filename} (${f.check_id}, ${f.type}) ---`);
32762
+ }
32763
+ console.log(f.data);
32764
+ }
32765
+ }
32766
+ } catch (err) {
32767
+ spinner.stop();
32768
+ const message = err instanceof Error ? err.message : String(err);
32769
+ console.error(message);
32770
+ process.exitCode = 1;
32771
+ }
32772
+ });
32773
+ function tryParseJson(s) {
32774
+ try {
32775
+ return JSON.parse(s);
32776
+ } catch {
32777
+ return s;
32778
+ }
32779
+ }
32084
32780
  var mcp = program2.command("mcp").description("MCP server integration");
32085
32781
  mcp.command("start").description("start MCP stdio server").option("--debug", "enable debug output").action(async (opts) => {
32086
32782
  const rootOpts = program2.opts();