@blogic-cz/agent-tools 0.8.17 → 0.8.18

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blogic-cz/agent-tools",
3
- "version": "0.8.17",
3
+ "version": "0.8.18",
4
4
  "description": "CLI tools for AI coding agent workflows — GitHub, database, Kubernetes, Azure DevOps, logs, sessions, and audit",
5
5
  "keywords": [
6
6
  "agent",
@@ -20,6 +20,16 @@ import { runLocalCommand } from "./helpers";
20
20
  const CHECK_JSON_FIELDS = "name,state,bucket,link";
21
21
  const GITHUB_ACTIONS_RUN_ID_RE = /github\.com\/[^/]+\/[^/]+\/actions\/runs\/(\d+)/;
22
22
 
23
+ type WorkflowRunJobsForRerun = {
24
+ databaseId: number;
25
+ jobs: Array<{
26
+ databaseId: number;
27
+ name: string;
28
+ status: string;
29
+ conclusion: string | null;
30
+ }>;
31
+ };
32
+
23
33
  const buildChecksCommand = (pr: number | null, includeWatch: boolean): string =>
24
34
  `bun agent-tools-gh pr checks${pr !== null ? ` --pr ${pr}` : ""}${includeWatch ? " --watch" : ""}`;
25
35
 
@@ -36,6 +46,47 @@ const extractRunIdFromCheckLink = (link: string): number | null => {
36
46
  return Number.isFinite(runId) ? runId : null;
37
47
  };
38
48
 
49
+ const isFailedWorkflowJob = (job: { status: string; conclusion: string | null }) =>
50
+ job.conclusion === "failure" || job.status === "failure";
51
+
52
+ const getCheckJobNameCandidates = (checkName: string): string[] => {
53
+ const exact = checkName.trim();
54
+ const suffixParts = exact.split("/").map((part) => part.trim());
55
+ let suffix: string | undefined;
56
+ for (let index = suffixParts.length - 1; index >= 0; index -= 1) {
57
+ const part = suffixParts[index];
58
+ if (part !== undefined && part.length > 0) {
59
+ suffix = part;
60
+ break;
61
+ }
62
+ }
63
+
64
+ return [...new Set([exact, suffix].filter((value): value is string => value !== undefined))];
65
+ };
66
+
67
+ const resolveJobIdsForFailedChecks = (
68
+ checks: CheckResult[],
69
+ jobs: WorkflowRunJobsForRerun["jobs"],
70
+ ): number[] | null => {
71
+ const failedJobs = jobs.filter(isFailedWorkflowJob);
72
+ const jobIds = new Set<number>();
73
+
74
+ for (const check of checks) {
75
+ const candidates = getCheckJobNameCandidates(check.name);
76
+ const matches = failedJobs.filter((job) =>
77
+ candidates.some((candidate) => job.name.toLowerCase() === candidate.toLowerCase()),
78
+ );
79
+
80
+ if (matches.length !== 1) {
81
+ return null;
82
+ }
83
+
84
+ jobIds.add(matches[0].databaseId);
85
+ }
86
+
87
+ return [...jobIds];
88
+ };
89
+
39
90
  const fetchWorkflowRunFailureContext = Effect.fn("pr.fetchWorkflowRunFailureContext")(function* (
40
91
  runId: number,
41
92
  ) {
@@ -680,21 +731,20 @@ export const rerunChecks = Effect.fn("pr.rerunChecks")(function* (
680
731
  ) {
681
732
  const gh = yield* GitHubService;
682
733
 
683
- const checks = yield* gh.runGhJson<
684
- Array<{
685
- name: string;
686
- link: string;
687
- bucket: string;
688
- state: string;
689
- }>
690
- >(["pr", "checks", ...(pr !== null ? [String(pr)] : []), "--json", "name,link,bucket,state"]);
734
+ const checks = yield* fetchCheckResults(pr);
735
+
736
+ const targetChecks = failedOnly ? checks.filter((check) => check.bucket === "fail") : checks;
691
737
 
692
738
  // Extract unique GitHub Actions run IDs from links
693
739
  const runIds = new Set<string>();
694
- for (const check of failedOnly ? checks.filter((c) => c.bucket === "fail") : checks) {
695
- const match = check.link.match(/github\.com\/[^/]+\/[^/]+\/actions\/runs\/(\d+)/);
740
+ const checksByRun = new Map<string, CheckResult[]>();
741
+ for (const check of targetChecks) {
742
+ const match = check.link.match(GITHUB_ACTIONS_RUN_ID_RE);
696
743
  if (match?.[1]) {
697
744
  runIds.add(match[1]);
745
+ const existing = checksByRun.get(match[1]) ?? [];
746
+ existing.push(check);
747
+ checksByRun.set(match[1], existing);
698
748
  }
699
749
  }
700
750
 
@@ -712,11 +762,40 @@ export const rerunChecks = Effect.fn("pr.rerunChecks")(function* (
712
762
  success: boolean;
713
763
  }> = [];
714
764
  for (const runId of runIds) {
715
- const rerunArgs = failedOnly ? ["run", "rerun", runId, "--failed"] : ["run", "rerun", runId];
716
- const success = yield* gh.runGh(rerunArgs).pipe(
717
- Effect.map(() => true),
718
- Effect.catch(() => Effect.succeed(false)),
719
- );
765
+ const success = yield* Effect.gen(function* () {
766
+ if (!failedOnly) {
767
+ return yield* gh.runGh(["run", "rerun", runId]).pipe(
768
+ Effect.map(() => true),
769
+ Effect.catch(() => Effect.succeed(false)),
770
+ );
771
+ }
772
+
773
+ const checksForRun = checksByRun.get(runId) ?? [];
774
+ const run = yield* gh
775
+ .runGhJson<WorkflowRunJobsForRerun>(["run", "view", runId, "--json", "databaseId,jobs"])
776
+ .pipe(Effect.catchTag("GitHubCommandError", () => Effect.succeed(null)));
777
+
778
+ const jobIds = run === null ? null : resolveJobIdsForFailedChecks(checksForRun, run.jobs);
779
+ if (jobIds === null || jobIds.length === 0) {
780
+ return yield* gh.runGh(["run", "rerun", runId, "--failed"]).pipe(
781
+ Effect.map(() => true),
782
+ Effect.catch(() => Effect.succeed(false)),
783
+ );
784
+ }
785
+
786
+ const rerunResults = yield* Effect.forEach(
787
+ jobIds,
788
+ (jobId) =>
789
+ gh.runGh(["run", "rerun", "--job", String(jobId)]).pipe(
790
+ Effect.map(() => true),
791
+ Effect.catch(() => Effect.succeed(false)),
792
+ ),
793
+ { concurrency: 1 },
794
+ );
795
+
796
+ return rerunResults.every(Boolean);
797
+ });
798
+
720
799
  results.push({ runId, success });
721
800
  }
722
801