automata-cli 0.1.0-feature-021-execute-command.125 → 0.1.0-feature-021-execute-command.127

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +70 -22
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -557,6 +557,14 @@ function extractSonarProjectKey(url) {
557
557
  return null;
558
558
  }
559
559
  }
560
+ function buildSonarPullRequestUrl(projectKey, prNumber) {
561
+ return `https://sonarcloud.io/summary/new_code?id=${encodeURIComponent(projectKey)}&pullRequest=${String(prNumber)}`;
562
+ }
563
+ function resolveSonarPullRequestUrl(url, prNumber) {
564
+ const projectKey = extractSonarProjectKey(url);
565
+ if (!projectKey) return url;
566
+ return buildSonarPullRequestUrl(projectKey, prNumber);
567
+ }
560
568
  function isSonarUrl(url) {
561
569
  try {
562
570
  const hostname = new URL(url).hostname;
@@ -671,13 +679,28 @@ async function fetchSonarJson(apiUrl) {
671
679
  clearTimeout(timeoutId);
672
680
  }
673
681
  }
674
- async function fetchSonarNewIssues(projectKey, prNumber) {
682
+ async function fetchSonarNewIssues(projectKey, prNumber, sonarcloudUrl) {
675
683
  const apiUrl = `https://sonarcloud.io/api/issues/search?componentKeys=${encodeURIComponent(projectKey)}&pullRequest=${String(prNumber)}&resolved=false&ps=1`;
676
684
  const response = await fetchSonarJson(apiUrl);
677
685
  if (!response.ok) {
678
- return null;
686
+ if (response.status === 401) {
687
+ return {
688
+ total: null,
689
+ note: `SonarCloud project is private. Open the Sonar URL in an authenticated browser: ${sonarcloudUrl}`
690
+ };
691
+ }
692
+ return {
693
+ total: null,
694
+ note: "SonarCloud new-issue count is unavailable right now."
695
+ };
696
+ }
697
+ if (typeof response.data.paging?.total === "number") {
698
+ return { total: response.data.paging.total };
679
699
  }
680
- return response.data.paging?.total ?? null;
700
+ return {
701
+ total: null,
702
+ note: "SonarCloud did not return a new-issue count for this pull request."
703
+ };
681
704
  }
682
705
  async function fetchSonarGateViolations(projectKey, prNumber) {
683
706
  const apiUrl = `https://sonarcloud.io/api/qualitygates/project_status?projectKey=${encodeURIComponent(projectKey)}&pullRequest=${String(prNumber)}`;
@@ -793,19 +816,22 @@ async function fetchSonarFailureSummary(projectKey, prNumber, sonarcloudUrl) {
793
816
  if (!gateResult.ok && gateResult.status === 401) {
794
817
  return {
795
818
  summary: sonarPrivateFailureSummary(sonarcloudUrl),
796
- issueTotal: null
819
+ issueTotal: null,
820
+ issueNote: `SonarCloud project is private. Open the Sonar URL in an authenticated browser: ${sonarcloudUrl}`
797
821
  };
798
822
  }
799
823
  if (!issuesResult.ok && issuesResult.status === 401) {
800
824
  return {
801
825
  summary: sonarPrivateFailureSummary(sonarcloudUrl),
802
- issueTotal: null
826
+ issueTotal: null,
827
+ issueNote: `SonarCloud project is private. Open the Sonar URL in an authenticated browser: ${sonarcloudUrl}`
803
828
  };
804
829
  }
805
830
  if (!hotspotsResult.ok && hotspotsResult.status === 401) {
806
831
  return {
807
832
  summary: sonarPrivateFailureSummary(sonarcloudUrl),
808
- issueTotal: null
833
+ issueTotal: null,
834
+ issueNote: `SonarCloud project is private. Open the Sonar URL in an authenticated browser: ${sonarcloudUrl}`
809
835
  };
810
836
  }
811
837
  const gateViolations = gateResult.ok ? gateResult.data.gateViolations : [];
@@ -813,6 +839,7 @@ async function fetchSonarFailureSummary(projectKey, prNumber, sonarcloudUrl) {
813
839
  const securityHotspots = hotspotsResult.ok ? hotspotsResult.data : [];
814
840
  const qualityGateStatus = gateResult.ok ? gateResult.data.qualityGateStatus : void 0;
815
841
  const issueTotal = issuesResult.ok ? issuesResult.data.total : null;
842
+ const issueNote = issuesResult.ok ? void 0 : "SonarCloud new-issue count is unavailable right now.";
816
843
  if (gateViolations.length === 0 && issues.length === 0 && securityHotspots.length === 0 && !qualityGateStatus) {
817
844
  return {
818
845
  summary: {
@@ -822,7 +849,8 @@ async function fetchSonarFailureSummary(projectKey, prNumber, sonarcloudUrl) {
822
849
  securityHotspots: [],
823
850
  unavailableMessage: "SonarCloud failure details are unavailable right now."
824
851
  },
825
- issueTotal
852
+ issueTotal,
853
+ issueNote
826
854
  };
827
855
  }
828
856
  return {
@@ -833,7 +861,8 @@ async function fetchSonarFailureSummary(projectKey, prNumber, sonarcloudUrl) {
833
861
  issues,
834
862
  securityHotspots
835
863
  },
836
- issueTotal
864
+ issueTotal,
865
+ issueNote
837
866
  };
838
867
  }
839
868
  async function getPrInfoGh(branch) {
@@ -851,12 +880,16 @@ async function getPrInfoGh(branch) {
851
880
  throw new Error(stderr.trim() || "Failed to query GitHub. Is `gh` installed and authenticated?");
852
881
  }
853
882
  const raw = JSON.parse(stdout);
854
- const failedChecks = (raw.statusCheckRollup ?? []).filter(
883
+ const statusChecks = raw.statusCheckRollup ?? [];
884
+ const failedChecks = statusChecks.filter(
855
885
  (c) => c.conclusion !== null && ["FAILURE", "TIMED_OUT", "ACTION_REQUIRED", "CANCELLED"].includes(c.conclusion)
856
886
  );
857
- const ownerRepo = failedChecks.length > 0 ? parseOwnerRepo() : null;
887
+ const sonarNeedsCheckRunOutput = statusChecks.some(
888
+ (c) => (isSonarUrl(c.detailsUrl) || c.name.toLowerCase().includes("sonar")) && !extractSonarProjectKey(c.detailsUrl)
889
+ );
890
+ const ownerRepo = failedChecks.length > 0 || sonarNeedsCheckRunOutput ? parseOwnerRepo() : null;
858
891
  const checkOutputs = ownerRepo ? fetchCheckRunOutputs(ownerRepo, raw.headRefOid) : /* @__PURE__ */ new Map();
859
- const checks = (raw.statusCheckRollup ?? []).map((c) => {
892
+ const checks = statusChecks.map((c) => {
860
893
  const enriched = checkOutputs.get(c.name);
861
894
  return {
862
895
  name: c.name,
@@ -869,20 +902,25 @@ async function getPrInfoGh(branch) {
869
902
  const sonarCheck = checks.find((c) => isSonarUrl(c.detailsUrl));
870
903
  let sonarcloudUrl;
871
904
  let sonarNewIssues;
905
+ let sonarNewIssuesNote;
872
906
  let sonarFailures;
873
907
  if (sonarCheck) {
874
- sonarcloudUrl = sonarCheck.detailsUrl;
875
- const projectKey = extractSonarProjectKey(sonarCheck.detailsUrl);
908
+ sonarcloudUrl = resolveSonarPullRequestUrl(sonarCheck.detailsUrl, raw.number);
909
+ const projectKey = extractSonarProjectKey(sonarcloudUrl);
876
910
  if (projectKey) {
877
911
  if (sonarCheck.conclusion !== null && SONAR_FAIL_CONCLUSIONS.has(sonarCheck.conclusion)) {
878
912
  const sonarSummary = await fetchSonarFailureSummary(projectKey, raw.number, sonarcloudUrl);
879
913
  sonarFailures = sonarSummary.summary;
880
914
  sonarNewIssues = sonarSummary.issueTotal;
915
+ sonarNewIssuesNote = sonarSummary.issueNote;
881
916
  } else {
882
- sonarNewIssues = await fetchSonarNewIssues(projectKey, raw.number);
917
+ const sonarNewIssuesResult = await fetchSonarNewIssues(projectKey, raw.number, sonarcloudUrl);
918
+ sonarNewIssues = sonarNewIssuesResult.total;
919
+ sonarNewIssuesNote = sonarNewIssuesResult.note;
883
920
  }
884
921
  } else {
885
922
  sonarNewIssues = null;
923
+ sonarNewIssuesNote = "Could not determine the SonarCloud project key from the check URL.";
886
924
  }
887
925
  }
888
926
  return {
@@ -891,7 +929,7 @@ async function getPrInfoGh(branch) {
891
929
  state: raw.state,
892
930
  url: raw.url,
893
931
  checks,
894
- ...sonarcloudUrl === void 0 ? {} : { sonarcloudUrl, sonarNewIssues },
932
+ ...sonarcloudUrl === void 0 ? {} : { sonarcloudUrl, sonarNewIssues, sonarNewIssuesNote },
895
933
  ...sonarFailures === void 0 ? {} : { sonarFailures }
896
934
  };
897
935
  }
@@ -1221,6 +1259,12 @@ function formatSonarFailures(sonarFailures, sonarcloudUrl) {
1221
1259
  }
1222
1260
  return lines.join("\n") + "\n";
1223
1261
  }
1262
+ function sonarFailureNote(sonarFailures) {
1263
+ if (!sonarFailures) return void 0;
1264
+ if (sonarFailures.status === "private") return sonarFailures.privateMessage;
1265
+ if (sonarFailures.status === "unavailable") return sonarFailures.unavailableMessage;
1266
+ return void 0;
1267
+ }
1224
1268
  var POLL_INTERVAL_MS = 1e4;
1225
1269
  var getPrInfoCmd = new Command2("get-pr-info").description("Show pull request info for the current branch").option("--json", "Output as JSON").option("--wait-finish-checks", "Poll until all checks complete, then print the normal get-pr-info output").addHelpText(
1226
1270
  "after",
@@ -1290,6 +1334,11 @@ URL: ${pr.url}
1290
1334
  const issueStr = pr.sonarNewIssues === null || pr.sonarNewIssues === void 0 ? "unavailable" : String(pr.sonarNewIssues);
1291
1335
  process.stdout.write(`Sonar New Issues: ${issueStr}
1292
1336
  `);
1337
+ const failureNote = sonarFailureNote(pr.sonarFailures);
1338
+ if (pr.sonarNewIssuesNote && pr.sonarNewIssuesNote !== failureNote) {
1339
+ process.stdout.write(`Sonar Note: ${sanitizeText(pr.sonarNewIssuesNote)}
1340
+ `);
1341
+ }
1293
1342
  }
1294
1343
  process.stdout.write(formatCheckSummary(pr.checks));
1295
1344
  process.stdout.write(formatChecks(pr.checks));
@@ -1889,16 +1938,15 @@ function readStdin() {
1889
1938
  return new Promise((resolve3, reject) => {
1890
1939
  const chunks = [];
1891
1940
  process.stdin.on("data", (chunk) => chunks.push(chunk));
1892
- process.stdin.on("end", () => resolve3(Buffer.concat(chunks).toString("utf8").trim()));
1941
+ process.stdin.on("end", () => resolve3(Buffer.concat(chunks).toString("utf8")));
1893
1942
  process.stdin.on("error", reject);
1894
1943
  });
1895
1944
  }
1896
1945
  async function resolvePrompt(options) {
1897
1946
  const hasCli = options.prompt !== void 0;
1898
1947
  const hasFile = options.filePrompt !== void 0;
1899
- const hasStdin = !process.stdin.isTTY;
1900
- if (hasCli && (hasFile || hasStdin)) {
1901
- process.stderr.write("Error: --prompt is mutually exclusive with --file-prompt and stdin.\n");
1948
+ if (hasCli && hasFile) {
1949
+ process.stderr.write("Error: --prompt is mutually exclusive with --file-prompt.\n");
1902
1950
  process.exit(1);
1903
1951
  }
1904
1952
  if (hasCli) {
@@ -1914,9 +1962,9 @@ async function resolvePrompt(options) {
1914
1962
  process.exit(1);
1915
1963
  }
1916
1964
  }
1917
- if (hasStdin) {
1965
+ if (!process.stdin.isTTY) {
1918
1966
  const content = await readStdin();
1919
- if (content.length === 0) {
1967
+ if (content.trim().length === 0) {
1920
1968
  process.stderr.write("Error: No prompt provided. Use --prompt, --file-prompt, or pipe via stdin.\n");
1921
1969
  process.exit(1);
1922
1970
  }
@@ -1925,7 +1973,7 @@ async function resolvePrompt(options) {
1925
1973
  process.stderr.write("Error: No prompt provided. Use --prompt, --file-prompt, or pipe via stdin.\n");
1926
1974
  process.exit(1);
1927
1975
  }
1928
- var executeCommand = new Command4("execute").description("Delegate work to an AI executor").requiredOption("--with <executor>", "Executor to use: claude or codex").option("--prompt <string>", "Prompt to send to the executor").option("--file-prompt <path>", "Path to a file whose content is used as the prompt").option("--silent", "Suppress step-by-step output; show only the final summary").option("--model <string>", "Model identifier to pass to the executor").action(async (options) => {
1976
+ var executeCommand = new Command4("execute").description("Delegate work to an AI executor").requiredOption("--with <executor>", "Executor to use: claude or codex").option("--prompt <string>", "Prompt to send to the executor").option("--file-prompt <path>", "Path to a file whose content is used as the prompt").option("--silent", "Suppress step-by-step Claude output; show only the final summary").option("--model <string>", "Model identifier to pass to the executor").action(async (options) => {
1929
1977
  const executor = options.with.toLowerCase();
1930
1978
  if (executor !== "claude" && executor !== "codex") {
1931
1979
  process.stderr.write(`Error: --with must be 'claude' or 'codex', got '${options.with}'.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "automata-cli",
3
- "version": "0.1.0-feature-021-execute-command.125",
3
+ "version": "0.1.0-feature-021-execute-command.127",
4
4
  "description": "Automata CLI tool",
5
5
  "type": "module",
6
6
  "bin": {