@blogic-cz/agent-tools 0.14.28 → 0.14.30

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.14.28",
3
+ "version": "0.14.30",
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",
@@ -434,7 +434,8 @@ export class DbService extends Context.Service<
434
434
  startTimeMs: number,
435
435
  applyTransform = false,
436
436
  ) {
437
- const wrappedSql = `SELECT json_agg(t) FROM (${sql}) t;`;
437
+ const selectSql = sql.trim().replace(/;\s*$/, "");
438
+ const wrappedSql = `SELECT json_agg(t) FROM (${selectSql}) t;`;
438
439
  const command = buildPsqlCommand(config, wrappedSql, password, true);
439
440
  const result = yield* executeShellCommand(command);
440
441
  const endTime = yield* Clock.currentTimeMillis;
@@ -50,9 +50,22 @@ type LogEntry = {
50
50
  };
51
51
 
52
52
  const repoOption = Flag.string("repo").pipe(
53
- Flag.withDescription("Target repository (owner/name). Defaults to current repo"),
53
+ Flag.withDescription("Target repository profile name or owner/name. Defaults to current repo"),
54
54
  Flag.optional,
55
55
  );
56
+
57
+ const resolveRepoArg = Effect.fn("workflow.resolveRepoArg")(function* (
58
+ repo: Option.Option<string>,
59
+ ) {
60
+ const target = Option.getOrNull(repo);
61
+ if (target === null) {
62
+ return null;
63
+ }
64
+
65
+ const gh = yield* GitHubService;
66
+ const info = yield* gh.withRepoTarget(target, gh.getRepoInfo());
67
+ return `${info.owner}/${info.name}`;
68
+ });
56
69
  // ---------------------------------------------------------------------------
57
70
  // Internal handlers
58
71
  // ---------------------------------------------------------------------------
@@ -496,10 +509,7 @@ export const workflowListCommand = Command.make(
496
509
  Flag.withDescription("Maximum number of runs to return"),
497
510
  Flag.withDefault(10),
498
511
  ),
499
- repo: Flag.string("repo").pipe(
500
- Flag.withDescription("Target repository (owner/name). Defaults to current repo"),
501
- Flag.optional,
502
- ),
512
+ repo: repoOption,
503
513
  status: Flag.choice("status", [
504
514
  "queued",
505
515
  "in_progress",
@@ -521,10 +531,11 @@ export const workflowListCommand = Command.make(
521
531
  },
522
532
  ({ branch, format, limit, repo, status, workflow }) =>
523
533
  Effect.gen(function* () {
534
+ const resolvedRepo = yield* resolveRepoArg(repo);
524
535
  const runs = yield* listRuns({
525
536
  branch: Option.getOrNull(branch),
526
537
  limit,
527
- repo: Option.getOrNull(repo),
538
+ repo: resolvedRepo,
528
539
  status: Option.getOrNull(status),
529
540
  workflow: Option.getOrNull(workflow),
530
541
  });
@@ -543,7 +554,8 @@ export const workflowViewCommand = Command.make(
543
554
  },
544
555
  ({ format, repo, run }) =>
545
556
  Effect.gen(function* () {
546
- const detail = yield* viewRun(run, Option.getOrNull(repo));
557
+ const resolvedRepo = yield* resolveRepoArg(repo);
558
+ const detail = yield* viewRun(run, resolvedRepo);
547
559
  yield* logFormatted(detail, format);
548
560
  }),
549
561
  ).pipe(Command.withDescription("View workflow run details including jobs and steps"));
@@ -557,7 +569,8 @@ export const workflowJobsCommand = Command.make(
557
569
  },
558
570
  ({ format, repo, run }) =>
559
571
  Effect.gen(function* () {
560
- const jobs = yield* listJobs(run, Option.getOrNull(repo));
572
+ const resolvedRepo = yield* resolveRepoArg(repo);
573
+ const jobs = yield* listJobs(run, resolvedRepo);
561
574
  yield* logFormatted(jobs, format);
562
575
  }),
563
576
  ).pipe(Command.withDescription("List jobs and their steps for a workflow run"));
@@ -575,7 +588,8 @@ export const workflowLogsCommand = Command.make(
575
588
  },
576
589
  ({ failedOnly, format, repo, run }) =>
577
590
  Effect.gen(function* () {
578
- const logs = yield* fetchLogs(run, failedOnly, null, Option.getOrNull(repo));
591
+ const resolvedRepo = yield* resolveRepoArg(repo);
592
+ const logs = yield* fetchLogs(run, failedOnly, null, resolvedRepo);
579
593
 
580
594
  if (format === "toon" || format === "json") {
581
595
  yield* logFormatted(logs, format);
@@ -593,15 +607,13 @@ export const workflowRerunCommand = Command.make(
593
607
  Flag.withDefault(true),
594
608
  ),
595
609
  format: formatOption,
596
- repo: Flag.string("repo").pipe(
597
- Flag.withDescription("Target repository (owner/name). Defaults to current repo"),
598
- Flag.optional,
599
- ),
610
+ repo: repoOption,
600
611
  run: Flag.integer("run").pipe(Flag.withDescription("Workflow run ID to rerun")),
601
612
  },
602
613
  ({ failedOnly, format, repo, run }) =>
603
614
  Effect.gen(function* () {
604
- const result = yield* rerunWorkflow(run, failedOnly, Option.getOrNull(repo));
615
+ const resolvedRepo = yield* resolveRepoArg(repo);
616
+ const result = yield* rerunWorkflow(run, failedOnly, resolvedRepo);
605
617
  yield* logFormatted(result, format);
606
618
  }),
607
619
  ).pipe(Command.withDescription("Rerun a workflow run (failed jobs only by default)"));
@@ -615,7 +627,8 @@ export const workflowCancelCommand = Command.make(
615
627
  },
616
628
  ({ format, repo, run }) =>
617
629
  Effect.gen(function* () {
618
- const result = yield* cancelRun(run, Option.getOrNull(repo));
630
+ const resolvedRepo = yield* resolveRepoArg(repo);
631
+ const result = yield* cancelRun(run, resolvedRepo);
619
632
  yield* logFormatted(result, format);
620
633
  }),
621
634
  ).pipe(Command.withDescription("Cancel an in-progress workflow run"));
@@ -629,7 +642,8 @@ export const workflowWatchCommand = Command.make(
629
642
  },
630
643
  ({ format, repo, run }) =>
631
644
  Effect.gen(function* () {
632
- const result = yield* watchRun(run, Option.getOrNull(repo));
645
+ const resolvedRepo = yield* resolveRepoArg(repo);
646
+ const result = yield* watchRun(run, resolvedRepo);
633
647
  yield* logFormatted(result, format);
634
648
  }),
635
649
  ).pipe(Command.withDescription("Watch a workflow run until it completes, then show final status"));
@@ -650,12 +664,13 @@ export const workflowJobLogsCommand = Command.make(
650
664
  },
651
665
  ({ failedStepsOnly, format, job, repo, run }) =>
652
666
  Effect.gen(function* () {
667
+ const resolvedRepo = yield* resolveRepoArg(repo);
653
668
  const result = yield* fetchJobLogs({
654
669
  runId: run,
655
670
  job,
656
671
  failedStepsOnly,
657
672
  format,
658
- repo: Option.getOrNull(repo),
673
+ repo: resolvedRepo,
659
674
  });
660
675
 
661
676
  if ("formatted" in result) {
@@ -683,10 +698,11 @@ export const workflowAnnotationsCommand = Command.make(
683
698
  },
684
699
  ({ format, job, repo, run }) =>
685
700
  Effect.gen(function* () {
701
+ const resolvedRepo = yield* resolveRepoArg(repo);
686
702
  const result = yield* fetchAnnotations({
687
703
  runId: run,
688
704
  job: Option.getOrNull(job),
689
- repo: Option.getOrNull(repo),
705
+ repo: resolvedRepo,
690
706
  });
691
707
  yield* logFormatted(result, format);
692
708
  }),
@@ -31,6 +31,20 @@ const filterBySource = (summaries: MessageSummary[], source: string): MessageSum
31
31
  return summaries.filter((s) => s.source === (source as SessionSource));
32
32
  };
33
33
 
34
+ const latestSessionSummaries = (summaries: MessageSummary[]): MessageSummary[] => {
35
+ const bySession = new Map<string, MessageSummary>();
36
+
37
+ for (const summary of summaries) {
38
+ const key = `${summary.source}:${summary.sessionID}`;
39
+ const previous = bySession.get(key);
40
+ if (previous === undefined || summary.created > previous.created) {
41
+ bySession.set(key, summary);
42
+ }
43
+ }
44
+
45
+ return [...bySession.values()].toSorted((left, right) => right.created - left.created);
46
+ };
47
+
34
48
  const buildScopeLabel = (searchAll: boolean, currentDir: string) => {
35
49
  if (searchAll) {
36
50
  return "all projects";
@@ -100,7 +114,7 @@ const listCommand = Command.make(
100
114
  }
101
115
 
102
116
  const allSummaries = yield* sessionService.getMessageSummaries(sessionFilter);
103
- const summaries = filterBySource(allSummaries, source);
117
+ const summaries = latestSessionSummaries(filterBySource(allSummaries, source));
104
118
  const results = summaries.slice(0, limit).map((summary) => ({
105
119
  created: formatDate(summary.created),
106
120
  sessionID: summary.sessionID,