@blogic-cz/agent-tools 0.5.0 → 0.5.1

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.5.0",
3
+ "version": "0.5.1",
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",
@@ -48,6 +48,10 @@ type LogEntry = {
48
48
  message: string;
49
49
  };
50
50
 
51
+ const repoOption = Flag.string("repo").pipe(
52
+ Flag.withDescription("Target repository (owner/name). Defaults to current repo"),
53
+ Flag.optional,
54
+ );
51
55
  // ---------------------------------------------------------------------------
52
56
  // Internal handlers
53
57
  // ---------------------------------------------------------------------------
@@ -89,26 +93,34 @@ const listRuns = Effect.fn("workflow.listRuns")(function* (opts: {
89
93
  return yield* gh.runGhJson<WorkflowRun[]>(args);
90
94
  });
91
95
 
92
- const viewRun = Effect.fn("workflow.viewRun")(function* (runId: number) {
96
+ const viewRun = Effect.fn("workflow.viewRun")(function* (runId: number, repo: string | null) {
93
97
  const gh = yield* GitHubService;
94
98
 
95
- const run = yield* gh.runGhJson<WorkflowRunDetail>([
99
+ const args = [
96
100
  "run",
97
101
  "view",
98
102
  String(runId),
99
103
  "--json",
100
104
  "databaseId,displayTitle,status,conclusion,headBranch,createdAt,event,url,workflowName,jobs",
101
- ]);
105
+ ];
106
+ if (repo !== null) {
107
+ args.push("--repo", repo);
108
+ }
102
109
 
103
- return run;
110
+ return yield* gh.runGhJson<WorkflowRunDetail>(args);
104
111
  });
105
112
 
106
- const listJobs = Effect.fn("workflow.listJobs")(function* (runId: number) {
113
+ const listJobs = Effect.fn("workflow.listJobs")(function* (runId: number, repo: string | null) {
107
114
  const gh = yield* GitHubService;
108
115
 
116
+ const args = ["run", "view", String(runId), "--json", "jobs"];
117
+ if (repo !== null) {
118
+ args.push("--repo", repo);
119
+ }
120
+
109
121
  const run = yield* gh.runGhJson<{
110
122
  jobs: WorkflowJob[];
111
- }>(["run", "view", String(runId), "--json", "jobs"]);
123
+ }>(args);
112
124
 
113
125
  return run.jobs;
114
126
  });
@@ -117,10 +129,15 @@ const fetchLogs = Effect.fn("workflow.fetchLogs")(function* (
117
129
  runId: number,
118
130
  failedOnly: boolean,
119
131
  jobId: number | null = null,
132
+ repo: string | null = null,
120
133
  ) {
121
134
  const gh = yield* GitHubService;
122
135
  const args = ["run", "view", String(runId)];
123
136
 
137
+ if (repo !== null) {
138
+ args.push("--repo", repo);
139
+ }
140
+
124
141
  if (jobId !== null) {
125
142
  args.push("--log", "--job", String(jobId));
126
143
  } else if (failedOnly) {
@@ -164,10 +181,15 @@ const rerunWorkflow = Effect.fn("workflow.rerunWorkflow")(function* (
164
181
  };
165
182
  });
166
183
 
167
- const cancelRun = Effect.fn("workflow.cancelRun")(function* (runId: number) {
184
+ const cancelRun = Effect.fn("workflow.cancelRun")(function* (runId: number, repo: string | null) {
168
185
  const gh = yield* GitHubService;
169
186
 
170
- yield* gh.runGh(["run", "cancel", String(runId)]);
187
+ const args = ["run", "cancel", String(runId)];
188
+ if (repo !== null) {
189
+ args.push("--repo", repo);
190
+ }
191
+
192
+ yield* gh.runGh(args);
171
193
 
172
194
  return {
173
195
  cancelled: true as const,
@@ -176,10 +198,15 @@ const cancelRun = Effect.fn("workflow.cancelRun")(function* (runId: number) {
176
198
  };
177
199
  });
178
200
 
179
- const watchRun = Effect.fn("workflow.watchRun")(function* (runId: number) {
201
+ const watchRun = Effect.fn("workflow.watchRun")(function* (runId: number, repo: string | null) {
180
202
  const gh = yield* GitHubService;
181
203
 
182
- const result = yield* gh.runGh(["run", "watch", String(runId), "--exit-status"]).pipe(
204
+ const watchArgs = ["run", "watch", String(runId), "--exit-status"];
205
+ if (repo !== null) {
206
+ watchArgs.push("--repo", repo);
207
+ }
208
+
209
+ const result = yield* gh.runGh(watchArgs).pipe(
183
210
  Effect.catchTag("GitHubCommandError", (error) => {
184
211
  // exit-status returns non-zero if run failed, but we still want the output
185
212
  if (error.exitCode > 0 && error.stderr === "") {
@@ -193,7 +220,7 @@ const watchRun = Effect.fn("workflow.watchRun")(function* (runId: number) {
193
220
  }),
194
221
  );
195
222
 
196
- const finalState = yield* viewRun(runId);
223
+ const finalState = yield* viewRun(runId, repo);
197
224
 
198
225
  return {
199
226
  runId,
@@ -268,8 +295,12 @@ export function formatLogEntries(entries: LogEntry[]): string {
268
295
  // Job-level log handlers
269
296
  // ---------------------------------------------------------------------------
270
297
 
271
- const resolveJobId = Effect.fn("workflow.resolveJobId")(function* (runId: number, jobName: string) {
272
- const jobs = yield* listJobs(runId);
298
+ const resolveJobId = Effect.fn("workflow.resolveJobId")(function* (
299
+ runId: number,
300
+ jobName: string,
301
+ repo: string | null,
302
+ ) {
303
+ const jobs = yield* listJobs(runId, repo);
273
304
 
274
305
  // Exact match first
275
306
  const exact = jobs.find((j) => j.name === jobName);
@@ -305,8 +336,9 @@ const filterFailedStepEntries = Effect.fn("workflow.filterFailedStepEntries")(fu
305
336
  runId: number,
306
337
  jobId: number,
307
338
  entries: LogEntry[],
339
+ repo: string | null,
308
340
  ) {
309
- const jobs = yield* listJobs(runId);
341
+ const jobs = yield* listJobs(runId, repo);
310
342
  const job = jobs.find((j) => j.databaseId === jobId);
311
343
  if (!job) return entries;
312
344
 
@@ -324,25 +356,39 @@ const fetchJobLogs = Effect.fn("workflow.fetchJobLogs")(function* (opts: {
324
356
  job: string;
325
357
  failedStepsOnly: boolean;
326
358
  format: string;
359
+ repo: string | null;
327
360
  }) {
328
361
  const gh = yield* GitHubService;
329
- const { owner, name: repo } = yield* gh.getRepoInfo();
330
362
 
331
- const jobId = yield* resolveJobId(opts.runId, opts.job);
363
+ let owner: string;
364
+ let repoName: string;
365
+ if (opts.repo !== null) {
366
+ const parts = opts.repo.split("/");
367
+ owner = parts[0];
368
+ repoName = parts[1];
369
+ } else {
370
+ const info = yield* gh.getRepoInfo();
371
+ owner = info.owner;
372
+ repoName = info.name;
373
+ }
374
+
375
+ const jobId = yield* resolveJobId(opts.runId, opts.job, opts.repo);
332
376
 
333
377
  // Fetch raw logs via API (follows 302 redirect automatically)
334
- const raw = yield* gh.runGh(["api", `repos/${owner}/${repo}/actions/jobs/${jobId}/logs`]).pipe(
335
- Effect.map((r) => r.stdout),
336
- Effect.catchTag("GitHubCommandError", () => {
337
- // Fallback: use gh run view --log --job
338
- return fetchLogs(opts.runId, false, jobId).pipe(Effect.map((r) => r.log));
339
- }),
340
- );
378
+ const raw = yield* gh
379
+ .runGh(["api", `repos/${owner}/${repoName}/actions/jobs/${jobId}/logs`])
380
+ .pipe(
381
+ Effect.map((r) => r.stdout),
382
+ Effect.catchTag("GitHubCommandError", () => {
383
+ // Fallback: use gh run view --log --job
384
+ return fetchLogs(opts.runId, false, jobId, opts.repo).pipe(Effect.map((r) => r.log));
385
+ }),
386
+ );
341
387
 
342
388
  let entries = parseRawJobLogs(raw);
343
389
 
344
390
  if (opts.failedStepsOnly) {
345
- entries = yield* filterFailedStepEntries(opts.runId, jobId, entries);
391
+ entries = yield* filterFailedStepEntries(opts.runId, jobId, entries, opts.repo);
346
392
  }
347
393
 
348
394
  if (opts.format === "json") {
@@ -420,11 +466,12 @@ export const workflowViewCommand = Command.make(
420
466
  "view",
421
467
  {
422
468
  format: formatOption,
469
+ repo: repoOption,
423
470
  run: Flag.integer("run").pipe(Flag.withDescription("Workflow run ID")),
424
471
  },
425
- ({ format, run }) =>
472
+ ({ format, repo, run }) =>
426
473
  Effect.gen(function* () {
427
- const detail = yield* viewRun(run);
474
+ const detail = yield* viewRun(run, Option.getOrNull(repo));
428
475
  yield* logFormatted(detail, format);
429
476
  }),
430
477
  ).pipe(Command.withDescription("View workflow run details including jobs and steps"));
@@ -433,11 +480,12 @@ export const workflowJobsCommand = Command.make(
433
480
  "jobs",
434
481
  {
435
482
  format: formatOption,
483
+ repo: repoOption,
436
484
  run: Flag.integer("run").pipe(Flag.withDescription("Workflow run ID")),
437
485
  },
438
- ({ format, run }) =>
486
+ ({ format, repo, run }) =>
439
487
  Effect.gen(function* () {
440
- const jobs = yield* listJobs(run);
488
+ const jobs = yield* listJobs(run, Option.getOrNull(repo));
441
489
  yield* logFormatted(jobs, format);
442
490
  }),
443
491
  ).pipe(Command.withDescription("List jobs and their steps for a workflow run"));
@@ -450,11 +498,12 @@ export const workflowLogsCommand = Command.make(
450
498
  Flag.withDefault(true),
451
499
  ),
452
500
  format: formatOption,
501
+ repo: repoOption,
453
502
  run: Flag.integer("run").pipe(Flag.withDescription("Workflow run ID")),
454
503
  },
455
- ({ failedOnly, format, run }) =>
504
+ ({ failedOnly, format, repo, run }) =>
456
505
  Effect.gen(function* () {
457
- const logs = yield* fetchLogs(run, failedOnly);
506
+ const logs = yield* fetchLogs(run, failedOnly, null, Option.getOrNull(repo));
458
507
 
459
508
  if (format === "toon" || format === "json") {
460
509
  yield* logFormatted(logs, format);
@@ -489,11 +538,12 @@ export const workflowCancelCommand = Command.make(
489
538
  "cancel",
490
539
  {
491
540
  format: formatOption,
541
+ repo: repoOption,
492
542
  run: Flag.integer("run").pipe(Flag.withDescription("Workflow run ID to cancel")),
493
543
  },
494
- ({ format, run }) =>
544
+ ({ format, repo, run }) =>
495
545
  Effect.gen(function* () {
496
- const result = yield* cancelRun(run);
546
+ const result = yield* cancelRun(run, Option.getOrNull(repo));
497
547
  yield* logFormatted(result, format);
498
548
  }),
499
549
  ).pipe(Command.withDescription("Cancel an in-progress workflow run"));
@@ -502,11 +552,12 @@ export const workflowWatchCommand = Command.make(
502
552
  "watch",
503
553
  {
504
554
  format: formatOption,
555
+ repo: repoOption,
505
556
  run: Flag.integer("run").pipe(Flag.withDescription("Workflow run ID to watch")),
506
557
  },
507
- ({ format, run }) =>
558
+ ({ format, repo, run }) =>
508
559
  Effect.gen(function* () {
509
- const result = yield* watchRun(run);
560
+ const result = yield* watchRun(run, Option.getOrNull(repo));
510
561
  yield* logFormatted(result, format);
511
562
  }),
512
563
  ).pipe(Command.withDescription("Watch a workflow run until it completes, then show final status"));
@@ -522,15 +573,17 @@ export const workflowJobLogsCommand = Command.make(
522
573
  job: Flag.string("job").pipe(
523
574
  Flag.withDescription("Job name to fetch logs for (exact or partial match)"),
524
575
  ),
576
+ repo: repoOption,
525
577
  run: Flag.integer("run").pipe(Flag.withDescription("Workflow run ID")),
526
578
  },
527
- ({ failedStepsOnly, format, job, run }) =>
579
+ ({ failedStepsOnly, format, job, repo, run }) =>
528
580
  Effect.gen(function* () {
529
581
  const result = yield* fetchJobLogs({
530
582
  runId: run,
531
583
  job,
532
584
  failedStepsOnly,
533
585
  format,
586
+ repo: Option.getOrNull(repo),
534
587
  });
535
588
 
536
589
  if ("formatted" in result) {