@omercnet/paseo-pr-radar 0.3.4 → 0.4.0-next.88.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/README.md +40 -11
- package/client/pr-radar.tsx +20 -4
- package/client/radar.ts +24 -6
- package/package.json +23 -13
- package/paseo-plugin.json +1 -1
- package/shared/viewer-scope.ts +6 -4
- package/bun.lock +0 -1213
package/README.md
CHANGED
|
@@ -30,29 +30,58 @@ Paseo supplies normalized workspace pull request status. A daemon-side plugin ha
|
|
|
30
30
|
|
|
31
31
|
## Install
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
### Paseo 0.9 beta
|
|
34
|
+
|
|
35
|
+
Paseo 0.9 supports npm plugin sources. Install the published package on the daemon host:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
paseo plugin install npm:@omercnet/paseo-pr-radar
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Update an npm installation to the latest published release:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
paseo plugin update pr-radar
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The npm install and update flow requires Paseo 0.9. Paseo 0.8 does not accept npm plugin sources.
|
|
48
|
+
|
|
49
|
+
### Paseo 0.8
|
|
50
|
+
|
|
51
|
+
Install from the Git monorepo:
|
|
34
52
|
|
|
35
53
|
```bash
|
|
36
54
|
paseo plugin add omercnet/paseo-plugins:pr-radar
|
|
37
55
|
```
|
|
38
56
|
|
|
57
|
+
Or install a local checkout by absolute path on the daemon host:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
paseo plugin install /absolute/path/to/paseo-plugins/pr-radar
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
On Paseo 0.8, `plugin update` updates Git-managed installations only. Local directory
|
|
64
|
+
installations continue to use their checked-out source and require `plugin reload` after edits.
|
|
65
|
+
|
|
39
66
|
The daemon must have plugins enabled and `gh` authenticated for GitHub viewer-aware triage.
|
|
40
67
|
|
|
41
68
|
## Develop
|
|
42
69
|
|
|
43
70
|
```bash
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
71
|
+
npm ci
|
|
72
|
+
npm run check
|
|
73
|
+
npm test
|
|
74
|
+
npm run test:coverage
|
|
75
|
+
npm run typecheck
|
|
76
|
+
npx paseo plugin install "$PWD"
|
|
77
|
+
npx paseo plugin reload pr-radar
|
|
51
78
|
```
|
|
52
79
|
|
|
53
80
|
Release Please maintains the version, changelog, component tag, and GitHub release from
|
|
54
81
|
Conventional Commits in the monorepo.
|
|
55
82
|
|
|
56
|
-
The
|
|
57
|
-
|
|
58
|
-
|
|
83
|
+
The manifest supports Paseo `0.8.x`, including compatible `0.8` prereleases, and Paseo
|
|
84
|
+
`0.9.0-beta.1`. Development uses the `0.9.0-beta.1` CLI, client, plugin SDK, and protocol packages
|
|
85
|
+
together while preserving runtime compatibility with 0.8 hosts. Host-owned navigation opens
|
|
86
|
+
linked agents and workspaces without private routes or page reloads on web, desktop, iOS, and
|
|
87
|
+
Android. React `19.1` and React Native `0.81` match the versions supplied by the plugin host.
|
package/client/pr-radar.tsx
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as PaseoClient from "@getpaseo/plugin/client";
|
|
1
2
|
import { type PluginSurfaceProps, usePaseo, useRpc } from "@getpaseo/plugin/client";
|
|
2
3
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
3
4
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
@@ -24,6 +25,7 @@ import {
|
|
|
24
25
|
hasActiveAgent,
|
|
25
26
|
matchesRow,
|
|
26
27
|
mergeInboxRows,
|
|
28
|
+
openPullRequestUrl,
|
|
27
29
|
type PaseoApi,
|
|
28
30
|
type PaseoWorkspace,
|
|
29
31
|
type RadarBucket,
|
|
@@ -38,6 +40,11 @@ const CLOCK_TICK_MS = 30_000;
|
|
|
38
40
|
|
|
39
41
|
type SavedView = "security" | "updated" | "stale" | "automation";
|
|
40
42
|
|
|
43
|
+
const openExternalUrlCandidate: unknown = Reflect.get(PaseoClient, "openExternalUrl");
|
|
44
|
+
const guardedOpenExternalUrl =
|
|
45
|
+
typeof openExternalUrlCandidate === "function"
|
|
46
|
+
? (openExternalUrlCandidate as (url: string) => Promise<void>)
|
|
47
|
+
: undefined;
|
|
41
48
|
const SAVED_VIEW_TITLES: Record<SavedView, string> = {
|
|
42
49
|
security: "Security",
|
|
43
50
|
updated: "Updated",
|
|
@@ -235,7 +242,10 @@ export function PrRadar({ theme, layout, host, navigation }: PluginSurfaceProps)
|
|
|
235
242
|
})
|
|
236
243
|
: paseo.workspaces.ref(action.workspaceId);
|
|
237
244
|
const created = await targetWorkspace.agents.create(agentOptions);
|
|
238
|
-
navigation
|
|
245
|
+
if (navigation) {
|
|
246
|
+
const target = { agentId: created.id, serverId: host.id };
|
|
247
|
+
navigation.openAgent(target);
|
|
248
|
+
}
|
|
239
249
|
return `Started ${profile.name} for ${row.repository}#${row.number ?? "PR"}.`;
|
|
240
250
|
},
|
|
241
251
|
onMutate: () => {
|
|
@@ -513,7 +523,7 @@ export function PrRadar({ theme, layout, host, navigation }: PluginSurfaceProps)
|
|
|
513
523
|
const openPr = useCallback(async (row: RadarRow) => {
|
|
514
524
|
setOpenError(null);
|
|
515
525
|
try {
|
|
516
|
-
await
|
|
526
|
+
await openPullRequestUrl(row.url, guardedOpenExternalUrl, Linking);
|
|
517
527
|
} catch {
|
|
518
528
|
setOpenError(`Could not open ${row.repository}#${row.number ?? "PR"}.`);
|
|
519
529
|
}
|
|
@@ -566,7 +576,10 @@ export function PrRadar({ theme, layout, host, navigation }: PluginSurfaceProps)
|
|
|
566
576
|
<Pressable
|
|
567
577
|
accessibilityRole="button"
|
|
568
578
|
accessibilityLabel={`Open agent ${primaryAgent.title}`}
|
|
569
|
-
onPress={() =>
|
|
579
|
+
onPress={() => {
|
|
580
|
+
const target = { agentId: primaryAgent.id, serverId: host.id };
|
|
581
|
+
navigation.openAgent(target);
|
|
582
|
+
}}
|
|
570
583
|
style={({ pressed }) => [styles.action, pressed && styles.refreshPressed]}
|
|
571
584
|
>
|
|
572
585
|
<Text style={styles.actionText}>Open agent</Text>
|
|
@@ -576,7 +589,10 @@ export function PrRadar({ theme, layout, host, navigation }: PluginSurfaceProps)
|
|
|
576
589
|
<Pressable
|
|
577
590
|
accessibilityRole="button"
|
|
578
591
|
accessibilityLabel={`Open workspace for ${item.repository} ${item.number ?? ""}`}
|
|
579
|
-
onPress={() =>
|
|
592
|
+
onPress={() => {
|
|
593
|
+
const target = { workspaceId: item.workspaceIds[0], serverId: host.id };
|
|
594
|
+
navigation.openWorkspace(target);
|
|
595
|
+
}}
|
|
580
596
|
style={({ pressed }) => [styles.action, pressed && styles.refreshPressed]}
|
|
581
597
|
>
|
|
582
598
|
<Text style={styles.actionText}>Open workspace</Text>
|
package/client/radar.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { usePaseo } from "@getpaseo/plugin/client";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import type
|
|
3
|
+
import { type GitHubInboxItem, HttpsUrlSchema } from "../shared/viewer-scope";
|
|
4
4
|
|
|
5
5
|
export type PaseoApi = ReturnType<typeof usePaseo>;
|
|
6
6
|
export type PaseoWorkspace = Awaited<ReturnType<PaseoApi["workspaces"]["list"]>>["entries"][number];
|
|
@@ -312,6 +312,23 @@ export function buildAgentPrompt(row: RadarRow): string {
|
|
|
312
312
|
return `Continue work on ${row.url}. Current state: ${row.reason}. Inspect the pull request and workspace, resolve the actionable blocker, run relevant validation, push the fix, and report the result. Do not merge the pull request.`;
|
|
313
313
|
}
|
|
314
314
|
|
|
315
|
+
type UrlOpener = (url: string) => Promise<unknown>;
|
|
316
|
+
|
|
317
|
+
export async function openPullRequestUrl(
|
|
318
|
+
url: string,
|
|
319
|
+
guardedOpen: UrlOpener | undefined,
|
|
320
|
+
fallback: { openURL: UrlOpener },
|
|
321
|
+
): Promise<void> {
|
|
322
|
+
const parsed = HttpsUrlSchema.safeParse(url);
|
|
323
|
+
if (!parsed.success) throw new Error("Only HTTPS pull request URLs are supported.");
|
|
324
|
+
|
|
325
|
+
if (guardedOpen) {
|
|
326
|
+
await guardedOpen(parsed.data);
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
await fallback.openURL(parsed.data);
|
|
330
|
+
}
|
|
331
|
+
|
|
315
332
|
export function buildRadarSnapshot(
|
|
316
333
|
workspaces: readonly PaseoWorkspace[],
|
|
317
334
|
entries: readonly AgentEntry[],
|
|
@@ -349,14 +366,15 @@ export function buildRadarSnapshot(
|
|
|
349
366
|
|
|
350
367
|
const pullRequest = runtime?.pullRequest;
|
|
351
368
|
if (!pullRequest || !isOpenPullRequest(pullRequest.state, pullRequest.isMerged)) continue;
|
|
369
|
+
const parsedUrl = HttpsUrlSchema.safeParse(pullRequest.url);
|
|
370
|
+
if (!parsedUrl.success) continue;
|
|
371
|
+
const url = parsedUrl.data;
|
|
352
372
|
|
|
353
373
|
const repository =
|
|
354
374
|
pullRequest.repoOwner && pullRequest.repoName
|
|
355
375
|
? `${pullRequest.repoOwner}/${pullRequest.repoName}`
|
|
356
|
-
: parseRepository(
|
|
357
|
-
const id = pullRequest.number
|
|
358
|
-
? `${repository.toLowerCase()}#${pullRequest.number}`
|
|
359
|
-
: pullRequest.url;
|
|
376
|
+
: parseRepository(url);
|
|
377
|
+
const id = pullRequest.number ? `${repository.toLowerCase()}#${pullRequest.number}` : url;
|
|
360
378
|
const parsedFacts = PullRequestFactsSchema.safeParse(pullRequest);
|
|
361
379
|
const facts = parsedFacts.success
|
|
362
380
|
? (parsedFacts.data.forgeSpecific ?? parsedFacts.data.github ?? null)
|
|
@@ -386,7 +404,7 @@ export function buildRadarSnapshot(
|
|
|
386
404
|
const row: RadarRow = {
|
|
387
405
|
id,
|
|
388
406
|
number: pullRequest.number ?? null,
|
|
389
|
-
url
|
|
407
|
+
url,
|
|
390
408
|
title: pullRequest.title,
|
|
391
409
|
repository,
|
|
392
410
|
baseRefName: pullRequest.baseRefName,
|
package/package.json
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omercnet/paseo-pr-radar",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0-next.88.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A viewer-aware delivery queue for pull requests linked to Paseo workspaces.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "Omer Cohen",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/omercnet/paseo-plugins.git",
|
|
11
|
+
"directory": "pr-radar"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/omercnet/paseo-plugins/tree/main/pr-radar#readme",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/omercnet/paseo-plugins/issues"
|
|
16
|
+
},
|
|
8
17
|
"publishConfig": {
|
|
9
18
|
"access": "public"
|
|
10
19
|
},
|
|
11
|
-
"packageManager": "
|
|
20
|
+
"packageManager": "npm@11.19.1",
|
|
12
21
|
"engines": {
|
|
13
|
-
"
|
|
22
|
+
"node": ">=24"
|
|
14
23
|
},
|
|
15
24
|
"keywords": [
|
|
16
25
|
"paseo",
|
|
@@ -19,7 +28,6 @@
|
|
|
19
28
|
"coding-agents"
|
|
20
29
|
],
|
|
21
30
|
"files": [
|
|
22
|
-
"bun.lock",
|
|
23
31
|
"index.client.tsx",
|
|
24
32
|
"index.server.ts",
|
|
25
33
|
"client",
|
|
@@ -33,24 +41,26 @@
|
|
|
33
41
|
"scripts": {
|
|
34
42
|
"check": "biome check .",
|
|
35
43
|
"check:write": "biome check --write .",
|
|
36
|
-
"test": "
|
|
37
|
-
"test:coverage": "
|
|
44
|
+
"test": "vitest run",
|
|
45
|
+
"test:coverage": "vitest run --coverage --coverage.reporter=text --coverage.reporter=lcov --coverage.thresholds.lines=90 --coverage.thresholds.functions=85",
|
|
38
46
|
"typecheck": "tsc --noEmit",
|
|
39
|
-
"package:release": "
|
|
47
|
+
"package:release": "node scripts/package-release.ts"
|
|
40
48
|
},
|
|
41
49
|
"devDependencies": {
|
|
42
50
|
"@biomejs/biome": "^2.5.10",
|
|
43
|
-
"@getpaseo/cli": "0.
|
|
44
|
-
"@getpaseo/client": "0.
|
|
45
|
-
"@getpaseo/plugin": "0.
|
|
46
|
-
"@getpaseo/protocol": "0.
|
|
51
|
+
"@getpaseo/cli": "0.9.0-beta.1",
|
|
52
|
+
"@getpaseo/client": "0.9.0-beta.1",
|
|
53
|
+
"@getpaseo/plugin": "0.9.0-beta.1",
|
|
54
|
+
"@getpaseo/protocol": "0.9.0-beta.1",
|
|
47
55
|
"@tanstack/react-query": "^5.102.3",
|
|
48
|
-
"@types/
|
|
56
|
+
"@types/node": "^24.5.2",
|
|
49
57
|
"@types/react": "~19.2.0",
|
|
58
|
+
"@vitest/coverage-v8": "^5.0.0",
|
|
50
59
|
"fflate": "^0.8.3",
|
|
51
60
|
"react": "19.1.0",
|
|
52
61
|
"react-native": "0.81.5",
|
|
53
|
-
"typescript": "^
|
|
62
|
+
"typescript": "^7.0.0",
|
|
63
|
+
"vitest": "^5.0.0",
|
|
54
64
|
"zod": "^4.4.3"
|
|
55
65
|
}
|
|
56
66
|
}
|
package/paseo-plugin.json
CHANGED
package/shared/viewer-scope.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { defineRpc } from "@getpaseo/plugin";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
|
|
4
|
+
export const HttpsUrlSchema = z.url({ protocol: /^https$/ });
|
|
5
|
+
|
|
4
6
|
export const GitHubInboxItemSchema = z.object({
|
|
5
7
|
id: z.string(),
|
|
6
8
|
number: z.number().int().positive(),
|
|
7
|
-
url:
|
|
9
|
+
url: HttpsUrlSchema,
|
|
8
10
|
title: z.string(),
|
|
9
11
|
repository: z.string(),
|
|
10
12
|
author: z.string().nullable(),
|
|
@@ -30,13 +32,13 @@ export type GitHubInboxItem = z.infer<typeof GitHubInboxItemSchema>;
|
|
|
30
32
|
export const viewerScope = defineRpc({
|
|
31
33
|
name: "pr-radar.viewer-scope",
|
|
32
34
|
input: z.object({
|
|
33
|
-
urls: z.array(
|
|
35
|
+
urls: z.array(HttpsUrlSchema).max(200),
|
|
34
36
|
windowDays: z.number().int().min(1).max(365).default(30),
|
|
35
37
|
}),
|
|
36
38
|
output: z.object({
|
|
37
39
|
viewer: z.string().nullable(),
|
|
38
|
-
authoredUrls: z.array(
|
|
39
|
-
reviewRequestedUrls: z.array(
|
|
40
|
+
authoredUrls: z.array(HttpsUrlSchema),
|
|
41
|
+
reviewRequestedUrls: z.array(HttpsUrlSchema),
|
|
40
42
|
inboxItems: z.array(GitHubInboxItemSchema),
|
|
41
43
|
truncated: z.boolean(),
|
|
42
44
|
coverageNote: z.string(),
|