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.
- package/dist/index.js +70 -22
- 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
|
-
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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(
|
|
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
|
-
|
|
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")
|
|
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
|
-
|
|
1900
|
-
|
|
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 (
|
|
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}'.
|