paseo-follow-up 0.3.5 → 0.4.0

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.
Files changed (64) hide show
  1. package/OVERVIEW.md +19 -7
  2. package/README.md +70 -79
  3. package/THIRD_PARTY_NOTICES.md +33 -0
  4. package/assets/LICENSE.lucide +43 -0
  5. package/assets/icon.png +0 -0
  6. package/assets/icon.svg +1 -0
  7. package/client/button-visibility.ts +2 -1
  8. package/client/chat.tsx +163 -0
  9. package/client/display.tsx +12 -17
  10. package/client/header.tsx +92 -0
  11. package/client/keys.ts +3 -0
  12. package/client/kit.tsx +119 -0
  13. package/client/large.tsx +58 -0
  14. package/client/live.tsx +159 -0
  15. package/client/motion.tsx +93 -0
  16. package/client/needs-you.tsx +121 -0
  17. package/client/overview.tsx +111 -16
  18. package/client/panel.tsx +257 -68
  19. package/client/pr-controls.tsx +50 -33
  20. package/client/pr-sheet.tsx +63 -0
  21. package/client/questions.tsx +307 -0
  22. package/client/settings.tsx +55 -20
  23. package/client/spec.tsx +113 -0
  24. package/client/stacks.tsx +128 -0
  25. package/client/tasks.tsx +113 -22
  26. package/client/theme.ts +69 -0
  27. package/docs/screenshots/follow-up-chat.png +0 -0
  28. package/docs/screenshots/follow-up-hero.png +0 -0
  29. package/docs/screenshots/follow-up-large.png +0 -0
  30. package/docs/screenshots/follow-up-mobile.png +0 -0
  31. package/docs/screenshots/follow-up-overview.png +0 -0
  32. package/docs/screenshots/follow-up-pill.png +0 -0
  33. package/docs/screenshots/follow-up-pr.png +0 -0
  34. package/docs/screenshots/follow-up-questions.png +0 -0
  35. package/docs/screenshots/follow-up-states.png +0 -0
  36. package/index.client.tsx +47 -16
  37. package/index.server.ts +53 -0
  38. package/package.json +4 -2
  39. package/paseo-plugin.json +11 -3
  40. package/server/agent-hook.mjs +2 -0
  41. package/server/daemon.ts +37 -6
  42. package/server/discovery.ts +6 -3
  43. package/server/engine.ts +39 -0
  44. package/server/hook-source.ts +1 -1
  45. package/server/mcp-source.ts +1 -1
  46. package/server/mcp.mjs +12 -2
  47. package/server/questions.ts +112 -0
  48. package/server/spec.ts +102 -0
  49. package/shared/activity.ts +56 -0
  50. package/shared/attention.ts +212 -0
  51. package/shared/chat.ts +130 -0
  52. package/shared/contracts.ts +21 -0
  53. package/shared/display.ts +2 -1
  54. package/shared/errors.ts +5 -0
  55. package/shared/model.ts +10 -0
  56. package/shared/pr-actions.ts +8 -1
  57. package/shared/presentation.ts +2 -1
  58. package/shared/questions.ts +123 -0
  59. package/shared/spec.ts +46 -0
  60. package/skills/follow-through/SKILL.md +4 -0
  61. package/client/components.tsx +0 -58
  62. package/client/navigation.tsx +0 -23
  63. package/docs/screenshots/follow-up-settings.png +0 -0
  64. package/docs/screenshots/follow-up-workspace.png +0 -0
package/OVERVIEW.md CHANGED
@@ -1,15 +1,27 @@
1
1
  # Follow-up
2
2
 
3
- Follow-up keeps PR stacks, remaining tasks and explicit agent completion attached to a Paseo workspace. It groups workspaces by whether you or an agent can act next and retains settled PR history.
3
+ Follow-up watches every pull request a Paseo workspace opens until the work is actually done. It shows each PR stack at a glance, gathers conflicts, failing required checks and review threads into one repair batch for the workspace's main agent, and tells you when something needs you, such as an approved PR to merge, a question the agent asked or a batch to send. Answer a question in Follow-up and the answer goes straight to the agent's chat.
4
4
 
5
- Requires Paseo 0.14+, Node 22+ and authenticated gh on the daemon host. Graphite CLI authentication is used for readiness and explicit Merge. The optional Send to Paseo browser companion enables Graphite's Merge when ready setting through the existing pairing, with confirmation or remembered permission for toggle changes in Chrome; it is not needed for tracking, recovery or follow-up.
5
+ ## What you see
6
6
 
7
- Open the sidebar or Command-K, then Track this workspace. Project settings can enroll existing and new workspaces with original main-agent ownership; Auto always starts off. The retained development default enables canonical Git repositories named hero-poc, while other projects require opt-in. Disable that project default to stop further enrollment without losing records.
7
+ - A workspace panel that leads with status: PRs merged, what needs you, what needs fixing, a bar with one segment per PR, and what the agent is doing now.
8
+ - A Needs you card with one button per decision: Send repairs, Merge, Resume, Retry, or Answer.
9
+ - Questions from the main agent, each with a permanent ref (Q1, Q2…), lettered options, the agent's recommendation and what it does while it waits. Pick an option, add a note and Send it to the agent, or Copy it instead. Answered questions stay listed under their ref, and the question mode setting (Stop, Work around, Keep building) tells agents what to do until you answer.
10
+ - A Spec link in the header to the page that sums up the workspace, usually the Claude artifact the main agent keeps up to date.
11
+ - An Activity card: repairs sent and finished with how long they took, merges, PR actions and questions.
12
+ - Each stack drawn top to base like Graphite, merged PRs folded away, and a sheet per PR with checks, reviewers and Merge.
13
+ - Tasks for work that outlives a merge, manual or scheduled after a merge or deploy, with an optional delay.
14
+ - A composer pill and a sidebar count that appear only when something needs you; the pill steps through open questions so you can answer without leaving the chat. One screen lists every tracked workspace by who is up next, and a larger view opens a workspace in two columns.
15
+ - Compact rows for Follow-up's own tool calls in agent chats, a card for each repair batch, and a card when the agent declares Done.
8
16
 
9
- Every main view links to the overview, project settings and display settings. You can hide the workspace button and sidebar independently and still use Command-K. The installation id and local state directory remain follow-through for compatibility.
17
+ ## Setup
10
18
 
11
- Repair batches go to the original main agent after a click, or after explicit per-workspace Auto consent and fresh delivery checks. Recovery is a separate manual action. Done requires the main agent's declaration and settled requirements; merging and archiving remain your actions. Tasks are manual unless you explicitly schedule Now, After merge or After deploy, optionally with a delay. Deployment gates require serving-revision evidence; merges alone never prove deployment.
19
+ Requires Paseo 0.14+, Node 22+ and an authenticated `gh` on the daemon host. Merge uses saved Graphite CLI authentication. Open a workspace, open Follow-up from its header or Command-K, and choose Track this workspace. Project settings can track every workspace in a project. The optional Send to Paseo browser companion adds Graphite's Merge when ready switch through its existing pairing; it is not needed for anything else.
12
20
 
13
- This trusted unsandboxed plugin reads selected workspace metadata, saved agent history/configuration, GitHub status and existing CLI authentication. State and private recovery evidence remain in plugin-data/follow-through. Scoped MCP registration and reversible provider hooks support tracked root sessions; contributors can register PRs from any worktree without taking main-agent authority. Successful submission receipts are discovered automatically within the workspace repository, while mentions remain unowned; Codex's trust review remains in force. Prompts and repair context are sent to your configured agent/provider. Site-specific reviewer and Cloud Run adapters only apply to the configured repository; unsupported deployments remain unknown.
21
+ ## What it does on its own, and what it never does
14
22
 
15
- Merge, Merge when ready and reviewer requests require explicit developer controls. Optional direct browser grants trust that machine to change the PR toggle for the allowed repository, including later pushes; the existing Send pairing permits reads for active tracked repositories, while toggle changes require confirmation in Chrome unless explicitly remembered. Auto and agent tools cannot authorize them. Follow-up has no Slack posting identity or background channel scanner. Stakeholder replies require the developer's authorization, original destination and a verified sent-message receipt from the agent.
23
+ Auto-send starts off for every workspace. Until you turn it on, each repair batch waits for your click; with it on, Follow-up sends batches once the main agent is idle, after fresh permission and Session Guard checks. Follow-up never merges, turns on merge when ready, requests reviewers or archives on its own, and Merge always re-checks the stack and asks again. Done needs the main agent's declaration, settled PRs and tasks, and no open questions. Answers are sent only when you click Send; an answer the agent can't take yet waits and goes out on the next check, and one Paseo didn't confirm waits for you to Resend. An optional recovery agent can rebuild missed PRs and tasks from a workspace's history; it runs only when you start it and never edits code. Deployment checks and reviewer shortcuts exist only for specifically configured repositories; elsewhere deployment stays unknown.
24
+
25
+ ## Data and access
26
+
27
+ This is a trusted, unsandboxed plugin. It reads workspace metadata, saved agent history and configuration, GitHub status and existing CLI authentication, and keeps its state in `plugin-data/follow-through` on the daemon host. It registers scoped MCP tools and reversible provider hooks only for tracked sessions. To find the spec link it reads completed Claude Artifact publish results in the workspace's root sessions, never page content, chat links or the repository; a link you set wins over the agent's, which wins over detection. Repair prompts go to your configured agent and provider. It has no Slack identity and no background channel scanning; replies to stakeholders need your authorization and a sent-message receipt from the agent.
package/README.md CHANGED
@@ -1,126 +1,117 @@
1
- # Paseo Follow-up
1
+ # paseo-follow-up
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/paseo-follow-up?style=for-the-badge&color=cb3837)](https://www.npmjs.com/package/paseo-follow-up)
4
4
  [![npm downloads](https://img.shields.io/npm/dm/paseo-follow-up?style=for-the-badge&color=cb3837)](https://www.npmjs.com/package/paseo-follow-up)
5
5
  [![Paseo](https://img.shields.io/badge/Paseo-%E2%89%A5%200.14.0-8A63D2?style=for-the-badge)](https://paseo.sh)
6
6
  [![License](https://img.shields.io/github/license/tomgrin10/paseo-follow-up?style=for-the-badge&color=2563eb)](LICENSE)
7
7
 
8
- Keep long-lived Paseo workspaces moving through review, deployment, and completion. Follow-up retains PR stacks and remaining tasks, batches repairs for the existing main agent, and records the agent's explicit Done declaration.
8
+ Follow-up watches every pull request a Paseo workspace opens until the work is actually done. It shows the whole stack at a glance, gathers conflicts, failing checks and review threads into one repair batch for the workspace's main agent, and tells you when something needs you: an approved PR to merge, a question the agent asked, or a batch to send. Answer a question in Follow-up and the answer goes straight to the agent's chat.
9
9
 
10
- ![Follow-up workspace panel with sample pull requests and remaining tasks](docs/screenshots/follow-up-workspace.png)
10
+ <picture>
11
+ <source media="(prefers-color-scheme: light)" srcset="docs/media/follow-up-hero-light.png">
12
+ <img alt="A Paseo workspace with the Follow-up panel open: a Spec link, 1 of 5 PRs merged, 4 need you, 2 to fix, a coloured stack bar, and a Needs you card with two questions from the agent, three repairs to send and Merge #418, beside the agent's chat" src="docs/screenshots/follow-up-hero.png">
13
+ </picture>
11
14
 
12
15
  Screenshots use synthetic workspaces and PRs.
13
16
 
14
- ## Requirements
15
-
16
- - Paseo 0.14.0 or newer and Node 22 or newer.
17
- - Authenticated GitHub CLI (`gh`) on the daemon host.
18
- - Saved Graphite CLI authentication for Graphite readiness and explicit Merge controls.
19
- - Optional Send to Paseo browser companion for Graphite's actual Merge when ready switch. Core tracking and follow-up work without it.
20
-
21
17
  ## Install
22
18
 
23
19
  ```sh
24
20
  paseo plugin install paseo-follow-up
25
21
  ```
26
22
 
27
- The installation ID remains `follow-through` to preserve existing development installations, saved settings and tracked work. The public name is **Follow-up**.
23
+ Needs Node 22+ and an authenticated GitHub CLI (`gh`) on the daemon host. Merge buttons use your saved Graphite CLI authentication. The installed plugin ID stays `follow-through`, so existing settings and tracked work carry over.
28
24
 
29
25
  ## Use
30
26
 
31
- Open **Follow-up** from the sidebar or Command-K. The cog at the bottom opens native project settings; display settings remain in Paseo’s plugin settings. In a workspace, use the header button or the Follow-up panel and click **Track this workspace**. The original main agent owns PRs, tasks and completion; optional history recovery runs in a separate agent only after your click.
27
+ Open a workspace, click the Follow-up icon in its header (or press ⌘K / Ctrl+K and choose **Follow-up: open for this workspace**), then **Track this workspace**. To track every workspace in a project, open the settings (the gear in the panel, or **Follow-up: project settings** in ⌘K) and turn the project on.
32
28
 
33
- Use **Overview**, **Project settings** and **Display settings** to move between views. Settings are also available directly from Command-K. Display settings can hide the workspace header button or sidebar entry independently; Command-K remains available.
29
+ The panel reads top to bottom:
34
30
 
35
- Project settings control existing and future eligible workspaces by stable project ID. The retained development default enables canonical Git projects named `hero-poc`; other projects start off. Turning a project off stops enrollment while preserving tracked records. New enrollments always have Auto off.
31
+ - **Status.** How many PRs have merged, how many things need you, how many PRs need fixing, and a bar with one segment per PR in merge order. Below it, what the agent is doing right now.
32
+ - **Needs you.** One row per decision, each with its button: answer the agent's question, send repairs to the main agent, merge an approved PR, resume after you stopped the agent, or retry a batch that didn't finish.
33
+ - **Pull requests.** Each stack drawn top to base, the way Graphite shows it, with merged PRs folded away.
34
+ - **Tasks.** Work that outlives a merge, such as a production check or a backfill. Tasks wait for you unless you schedule them for after a merge or deploy.
35
+ - **Activity.** What happened and how long it took: repairs sent and finished, merges, merge and reviewer actions, and every question under its ref.
36
36
 
37
- With **Auto** off, review and send each repair batch yourself. Auto authorizes follow-up messages only for that workspace after fresh idle, permission and Session Guard checks. It never merges PRs, enables merge when ready, requests reviewers, starts recovery or archives workspaces.
37
+ The **Spec** chip in the header opens the page that sums up the workspace, usually the Claude artifact the main agent keeps up to date. The agent registers it with a tool call, Follow-up also picks up artifacts the workspace's sessions publish, and you can set, change or remove it under **Spec, agent and recovery**. Your link wins over the agent's, which wins over detection.
38
38
 
39
- Tasks default to manual. Explicit Now, After merge or After deploy triggers can include a delay; deployment tasks wait for verified serving-revision evidence. Merged PRs alone do not mean the work is Done. Agents retain evidence and record sent reply URLs for developer-authorized stakeholder updates.
39
+ ![Four Follow-up panels side by side: a workspace that needs you, one where the agent is fixing two repairs, one that has shipped, and one that is not tracked yet](docs/screenshots/follow-up-states.png)
40
40
 
41
- ![Follow-up overview with sample workspaces](docs/screenshots/follow-up-overview.png)
41
+ ### Questions
42
42
 
43
- ## Send to Paseo and Graphite
43
+ When the main agent needs a decision it asks in Follow-up instead of burying it in chat. Each question gets a permanent ref (Q1, Q2…), lettered options with what each means, the agent's recommendation, and a line saying what the agent does while it waits. Pick an option, add a note if you like, and **Send to agent**: the answer goes straight into its chat, even mid-turn, and stays recorded under its ref. **Copy** puts the same answer on the clipboard instead. If the agent can't take it yet (Session Guard paused it, or a permission prompt is open) the answer waits and goes out on the next check; if Paseo doesn't confirm the message, Follow-up asks you to check before resending. A task the agent marks blocked becomes a question too.
44
+
45
+ ![The Q3 question dialog with three lettered options, A recommended and B selected with a note, beside the Activity card listing every question under its ref](docs/screenshots/follow-up-questions.png)
46
+
47
+ The question mode in Follow-up's settings tells agents what to do until you answer: **Stop** (end the turn and wait), **Work around** (the default: build nothing the answer affects, keep working on the rest) or **Keep building** (build the recommendation and change course if you pick another). Big questions, ones that reach outside this computer or that most of the work depends on, always wait.
48
+
49
+ ### Everywhere else
50
+
51
+ When a workspace needs you, a pill appears above its main agent's message box with the same actions. It steps through open questions ("Question 1 of 2") so you can answer without leaving the chat. The sidebar's Follow-up row counts how many workspaces are waiting, and nothing shows when nothing needs you.
52
+
53
+ ![The composer pill "4 need you" opened into a popover: Question 1 of 2 with an answer field, then three repairs ready to send and an approved PR](docs/screenshots/follow-up-pill.png)
54
+
55
+ Tap a PR, or its segment in the bar, for a sheet with its checks, reviewers and Merge controls. When Merge isn't available yet, the sheet says what it is waiting for.
56
+
57
+ ![The PR sheet for a PR whose required checks failed](docs/screenshots/follow-up-pr.png)
58
+
59
+ The expand icon next to the gear (or **Follow-up: open the larger view** in ⌘K) opens the workspace as a full screen, in two columns when there is room: what needs you on the left, the stacks and activity on the right. Esc or the collapse icon takes you back.
44
60
 
45
- With Send to Paseo installed on the same daemon, its composer can return to the agent that
46
- registered a PR here. New registrations record that agent; older records retain the workspace’s
47
- main agent. You can still choose a fresh agent in Send to Paseo. Tracking and follow-up remain
48
- usable without the companion.
61
+ ![The larger view: Needs you, tasks and activity on the left, pull request stacks on the right](docs/screenshots/follow-up-large.png)
49
62
 
50
- To control Graphite’s **Merge when ready** here, use Send to Paseo plugin/extension 2.2+ and
51
- Follow Up 0.3.4+, and keep Graphite open and signed in. Follow Up connects automatically through
52
- your existing Send to Paseo pairing and advertises the repositories of active tracked PRs.
63
+ In the agent's chat, Follow-up's own tool calls collapse into one-line rows, your answers and repair batches show as short cards instead of raw prompts, and the agent's Done call gets a card of its own.
53
64
 
54
- Toggle changes require confirmation in Chrome. The confirmation can remember permission for
55
- future changes from that machine for the exact repository. Revoke that permission or turn off
56
- browser access in extension settings → **Follow Up**. This is a PR setting, including later pushes.
57
- See the [integration and security contract](https://github.com/tomgrin10/send-to-paseo/blob/main/docs/follow-up-integration.md).
65
+ ![Follow-up rows and cards in an agent chat](docs/screenshots/follow-up-chat.png)
58
66
 
59
- ## Data and permissions
67
+ **Follow-up: all tracked workspaces** (or the sidebar row) lists every tracked workspace, grouped by who's up next, with each stack's bar and its Spec link.
60
68
 
61
- This is a trusted, unsandboxed plugin. It reads selected workspace metadata, agent history/configuration and GitHub PR status using your existing host credentials. Local state and private recovery evidence live under `$PASEO_HOME/plugin-data/follow-through`; credentials and provider configuration are not published or transferred.
69
+ ![The tracked workspaces screen grouped into Needs you, Agent working, Waiting and Done](docs/screenshots/follow-up-overview.png)
62
70
 
63
- It registers scoped Claude/Codex MCP tools and reversible provider hooks for tracked root sessions. Contributor sessions receive PR registration, status and sync tools; goal, task and completion authority stays with the designated main agent. Codex keeps its normal hook trust review. Repair and recovery prompts reach your configured agent provider. Follow-up itself has no Slack posting identity and never scans channels or authorizes replies from historical mentions.
71
+ On a phone the panel opens as a tab, and the PR sheet and pill popover open as bottom sheets.
64
72
 
65
- Human-operated Merge and reviewer controls perform GitHub/Graphite writes only after an explicit click and fresh eligibility checks. Unknown or uncertain results require checking before retrying. Site-specific reviewer and Cloud Run deployment adapters remain limited to their configured repository; other deployment status remains unknown rather than being inferred from a merge.
73
+ ![Follow-up at phone width: the panel, a PR sheet and the pill popover](docs/screenshots/follow-up-mobile.png)
66
74
 
67
- ## Develop
75
+ ## How it works
76
+
77
+ - The workspace's main agent owns its PRs, tasks, questions, the spec link and the Done call. Other agents in the workspace can register PRs and check status.
78
+ - **Auto-send** is off for every newly tracked workspace, so each repair batch waits for your click. Turn it on per workspace and Follow-up sends batches itself once the agent is idle, after fresh permission and Session Guard checks.
79
+ - Follow-up never merges, turns on merge when ready, requests reviewers or archives on its own. **Merge** re-checks the stack, lists any PRs underneath that would merge first, and asks again.
80
+ - A merged PR is not the end: tasks such as production checks and open questions keep the workspace open until the main agent declares Done and everything has settled.
81
+ - Answers go out only when you click Send. Follow-up never answers for you.
82
+
83
+ ## Send to Paseo and Graphite
84
+
85
+ With [Send to Paseo](https://github.com/tomgrin10/send-to-paseo) on the same daemon, its composer picks the agent that registered the PR. With Send to Paseo 2.2+, Follow-up can also toggle Graphite's **Merge when ready** through your existing pairing, while Graphite is open and signed in. Each change is confirmed in Chrome, which can remember the permission per repository. See the [integration and security contract](https://github.com/tomgrin10/send-to-paseo/blob/main/docs/follow-up-integration.md).
86
+
87
+ ## Data and access
88
+
89
+ Paseo plugins run trusted, unsandboxed code on the daemon host. Follow-up reads workspace metadata, agent history and GitHub PR status with your existing credentials, and keeps its state in `$PASEO_HOME/plugin-data/follow-through`. It registers scoped MCP tools and reversible provider hooks only for tracked sessions. GitHub and Graphite writes happen only after an explicit click and a fresh eligibility check.
90
+
91
+ ## Development
68
92
 
69
93
  ```sh
70
94
  npm ci
71
95
  npm run verify
72
96
  npm run test:install
73
- npm pack --dry-run
97
+ paseo plugin reload follow-through # after changes
74
98
  ```
75
99
 
76
- Integration checks use a separate Paseo home, synthetic agents and fixtures. Never restart your main daemon for verification. Reload only this plugin with `paseo plugin reload follow-through`.
77
-
78
- ## Releases
100
+ Integration checks use a separate Paseo home with synthetic agents. Version tags (`vX.Y.Z`) publish to npm through Trusted Publishing.
79
101
 
80
- Release tags (`vX.Y.Z`) run `.github/workflows/publish-npm.yml`. GitHub Actions verifies the plugin and packed installation, publishes through npm Trusted Publishing, verifies registry integrity, then creates the GitHub release. No npm token is stored in repository secrets.
102
+ ## License
81
103
 
82
- ## Paseo 0.14 agent integration
104
+ [MIT](LICENSE) © 2026 Tom Gringauz.
83
105
 
84
- The Follow-up MCP attaches when a root agent in a tracked workspace or an active recovery agent opens a Claude or Codex session. Other root sessions get `workspace_status`, `register_pr` and `sync_workspace`, even when the originally recorded main agent is unavailable. Normal startup no longer writes global Claude/Codex MCP settings. Existing global entries are preserved. Turn-ended discovery now fetches recent history through the SDK because Paseo 0.13+ sends an empty timeline in lifecycle events.
85
-
86
- Requires Paseo **0.14.0 or newer**. Agent tools are scoped to interactive Claude
87
- and Codex sessions; history-only opens receive none. They become available on
88
- the next idle session open after install or reload. The agent label
89
- `plugin-tools/enabled: "false"` opts a session out. Existing plugin data and
90
- settings are retained.
106
+ Questions, question modes and the activity log are adapted from [Progress](https://github.com/stevecastaneda/paseo-plugins/tree/main/progress) by Steve Castaneda (MIT). Its notice is in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
91
107
 
92
108
  ## Other plugins
93
109
 
94
- Also available from [Tom Gringauz](https://github.com/tomgrin10):
95
-
96
- - [Defer](https://github.com/tomgrin10/paseo-defer) — Schedule messages to agents for later delivery.
97
- - [Graphite](https://github.com/tomgrin10/paseo-graphite) — Workspace stack status and PR action radar.
110
+ - [Defer](https://github.com/tomgrin10/paseo-defer) — Schedule messages for later delivery.
98
111
  - [Send to Paseo](https://github.com/tomgrin10/send-to-paseo) — Send GitHub and Graphite PRs to Paseo from Chrome.
99
- - [Smart Session](https://github.com/tomgrin10/paseo-smart-session) — Context-aware compaction and plan usage insights.
100
- - [Vitals](https://github.com/tomgrin10/paseo-vitals) — Host, Paseo, agent and Docker health.
101
- - [Session Guard](https://github.com/tomgrin10/paseo-session-guard) — Pause at usage limits and resume after reset.
102
- - [Transfer](https://github.com/tomgrin10/paseo-transfer) — Copy or move workspaces and agent conversations between hosts.
103
- - [Transcript Search](https://github.com/tomgrin10/paseo-transcript-search) — Search active and archived conversations across hosts.
104
-
105
-
106
- Successful `gt submit` and `gh pr create` receipts in root-session history also discover submitted
107
- PRs automatically, including from secondary git worktrees. Discovery checks the workspace's GitHub
108
- repository identity; merely mentioned PRs and read-only commands do not establish ownership.
109
- Explicit registrations, exclusions and another tracked workspace's ownership take precedence.
110
- Observed GitHub base/head branches supply inferred stack relationships; explicit edits override them.
111
- Tracking continues after the checkout branch's PR merges, and the submitting session remains the
112
- recipient for Send to Paseo. Existing sessions can use the hook-provided CLI fallback before their
113
- next MCP session refresh. Automatic discovery does not transfer main-agent authority or enable Auto.
114
-
115
-
116
- Agents launched by another agent into a separate workspace can be that new workspace’s main agent.
117
- The creator’s parent-agent label records launch provenance; it excludes an agent from local main
118
- selection only when the parent belongs to the same workspace. Parent membership is checked from
119
- the daemon, including direct lookup when the creator is no longer in the live directory. Unknown
120
- membership does not grant new scope. Workspace-first creation is supported: tracking waits for an
121
- available local main, then applies the project default. Explicit Track also supports these launches.
122
-
123
-
124
- Track persists activation and returns without waiting for transcript scans or the daemon-wide PR
125
- refresh. Initial session attachment also uses ownership metadata only. PR/history discovery and
126
- status refresh continue in the background; a slow GitHub or agent observation does not hold Track.
112
+ - [Session Guard](https://github.com/tomgrin10/paseo-session-guard) — Pause at session limits and continue after reset.
113
+ - [Smart Session](https://github.com/tomgrin10/paseo-smart-session) — Context compaction and usage insights.
114
+ - [Transcript Search](https://github.com/tomgrin10/paseo-transcript-search) — Search conversations across connected hosts.
115
+ - [Transfer](https://github.com/tomgrin10/paseo-transfer) — Copy or move workspaces between hosts.
116
+ - [Vitals](https://github.com/tomgrin10/paseo-vitals) — Host, process, agent and Docker monitoring.
117
+ - [Workspace Switcher](https://github.com/tomgrin10/paseo-switcher) — Arc-style Ctrl+Tab between recent workspaces.
@@ -0,0 +1,33 @@
1
+ # Third-party notices
2
+
3
+ ## Progress
4
+
5
+ Follow-up's questions (the ask, record and withdraw tools, the Stop, Work around and Keep building
6
+ question modes and their rules, the "big question" test, and the question dialog with lettered
7
+ options, a recommendation and Copy) and the activity log are adapted from
8
+ [Progress](https://github.com/stevecastaneda/paseo-plugins/tree/main/progress) (`@stevecastaneda/paseo-progress`
9
+ 1.14.0) by Steve Castaneda.
10
+
11
+ ```
12
+ MIT License
13
+
14
+ Copyright (c) 2026 Steve Castaneda
15
+
16
+ Permission is hereby granted, free of charge, to any person obtaining a copy
17
+ of this software and associated documentation files (the "Software"), to deal
18
+ in the Software without restriction, including without limitation the rights
19
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
20
+ copies of the Software, and to permit persons to whom the Software is
21
+ furnished to do so, subject to the following conditions:
22
+
23
+ The above copyright notice and this permission notice shall be included in all
24
+ copies or substantial portions of the Software.
25
+
26
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32
+ SOFTWARE.
33
+ ```
@@ -0,0 +1,43 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2026 Lucide Icons and Contributors
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
+
17
+ ---
18
+
19
+ The following Lucide icons are derived from the Feather project:
20
+
21
+ airplay, alert-circle, alert-octagon, alert-triangle, aperture, arrow-down-circle, arrow-down-left, arrow-down-right, arrow-down, arrow-left-circle, arrow-left, arrow-right-circle, arrow-right, arrow-up-circle, arrow-up-left, arrow-up-right, arrow-up, at-sign, calendar, cast, check, chevron-down, chevron-left, chevron-right, chevron-up, chevrons-down, chevrons-left, chevrons-right, chevrons-up, circle, clipboard, clock, code, columns, command, compass, corner-down-left, corner-down-right, corner-left-down, corner-left-up, corner-right-down, corner-right-up, corner-up-left, corner-up-right, crosshair, database, divide-circle, divide-square, dollar-sign, download, external-link, feather, frown, hash, headphones, help-circle, info, italic, key, layout, life-buoy, link-2, link, loader, lock, log-in, log-out, maximize, meh, minimize, minimize-2, minus-circle, minus-square, minus, monitor, moon, more-horizontal, more-vertical, move, music, navigation-2, navigation, octagon, pause-circle, percent, plus-circle, plus-square, plus, power, radio, rss, search, server, share, shopping-bag, sidebar, smartphone, smile, square, table-2, tablet, target, terminal, trash-2, trash, triangle, tv, type, upload, x-circle, x-octagon, x-square, x, zoom-in, zoom-out
22
+
23
+ The MIT License (MIT) (for the icons listed above)
24
+
25
+ Copyright (c) 2013-present Cole Bemis
26
+
27
+ Permission is hereby granted, free of charge, to any person obtaining a copy
28
+ of this software and associated documentation files (the "Software"), to deal
29
+ in the Software without restriction, including without limitation the rights
30
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
31
+ copies of the Software, and to permit persons to whom the Software is
32
+ furnished to do so, subject to the following conditions:
33
+
34
+ The above copyright notice and this permission notice shall be included in all
35
+ copies or substantial portions of the Software.
36
+
37
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
38
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
39
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
40
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
41
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
42
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
43
+ SOFTWARE.
Binary file
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="256" height="256" viewBox="0 0 256 256"><title>Follow-up</title><rect width="256" height="256" rx="52" fill="#1c2229"/><svg x="52" y="52" xmlns="http://www.w3.org/2000/svg" width="152" height="152" viewBox="0 0 24 24" fill="none" stroke="#f3f0e8" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-list-checks" aria-hidden="true"><path d="M13 5h8"></path><path d="M13 12h8"></path><path d="M13 19h8"></path><path d="m3 17 2 2 4-4"></path><path d="m3 7 2 2 4-4"></path></svg></svg>
@@ -25,7 +25,8 @@ export function withButtonVisibility(client: PluginClientContext) {
25
25
  buttons.addComposerPill = input => gated("addComposerPill", input);
26
26
  const unsubscribe = subscribeDisplay(values => {
27
27
  enableHeader = values.enable_header_button;
28
-
28
+ // Unknown is not off: older saved documents predate the pill preference.
29
+ enableComposer = values.enable_composer_pill !== false;
29
30
  for (const entry of entries) entry.reconcile();
30
31
  });
31
32
  return { client: buttons, dispose() { unsubscribe(); for (const entry of [...entries]) entry.remove(); } };
@@ -0,0 +1,163 @@
1
+ import { useState } from "react";
2
+ import { Platform, Pressable, Text, View } from "react-native";
3
+ import type { PluginClientContext, PluginTimelineItemProps } from "@getpaseo/plugin/client";
4
+ import { Icon, ScrollView } from "@getpaseo/plugin/client/ui";
5
+ import { answerCardSchema, batchCardSchema, followShellCall, followToolName, parseBatchPrompt, readText, toolRow, toolRowSchema, type AnswerCard, type BatchCard, type ToolRow } from "../shared/chat";
6
+ import { parseAnswerMessage } from "../shared/questions";
7
+ import { Reveal } from "./motion";
8
+ import { tint, withAlpha } from "./theme";
9
+
10
+ const mono = () => (Platform.OS === "ios" ? "Menlo" : "monospace");
11
+ const TOOL_KIND = "tool-row";
12
+ const BATCH_KIND = "batch-card";
13
+ const ANSWER_KIND = "answer-card";
14
+ const TOOL_ICON: Partial<Record<ToolRow["tool"], string>> = { ask_question: "MessageCircleQuestion", record_answer: "MessageCircleReply", withdraw_question: "MessageCircleOff", set_spec: "FileText" };
15
+
16
+ function record(value: unknown): value is Record<string, unknown> { return typeof value === "object" && value !== null && !Array.isArray(value); }
17
+
18
+ /** Expandable full text below a row, scrolling on its own so long output never moves the chat. */
19
+ function Expanded({ text, color, border }: { text: string; color: string; border: string }) {
20
+ return <View style={{ borderWidth: 1, borderTopWidth: 0, borderColor: border, borderBottomLeftRadius: 8, borderBottomRightRadius: 8, overflow: "hidden" }}>
21
+ <ScrollView style={[{ maxHeight: 300 }, Platform.OS === "web" ? ({ overscrollBehavior: "contain" } as object) : null]} contentContainerStyle={{ paddingHorizontal: 12, paddingVertical: 8 }}>
22
+ <Text selectable style={{ color, fontFamily: mono(), fontSize: 12, lineHeight: 18 }}>{text}</Text>
23
+ </ScrollView>
24
+ </View>;
25
+ }
26
+
27
+ /** Follow-up's own tool calls as one quiet line, sized like Paseo's tool rows; press to read the result. */
28
+ function ToolRowView({ item, theme }: PluginTimelineItemProps<ToolRow>) {
29
+ const c = theme.colors;
30
+ const row = item.data;
31
+ const [expanded, setExpanded] = useState(false);
32
+ const [hovered, setHovered] = useState(false);
33
+ if (row.done) return <DoneCard summary={row.done.summary} theme={theme} />;
34
+ const color = row.tone === "danger" ? c.statusDanger : row.tone === "warning" ? c.statusWarning : row.tone === "success" ? c.statusSuccess : c.foregroundMuted;
35
+ const openable = row.more !== null;
36
+ const active = openable && (hovered || expanded);
37
+ const open = expanded && openable;
38
+ return <View style={{ marginHorizontal: -13 }}>
39
+ <Pressable disabled={!openable} onPress={() => setExpanded(v => !v)} onHoverIn={() => setHovered(true)} onHoverOut={() => setHovered(false)}
40
+ accessibilityRole={openable ? "button" : undefined} aria-expanded={openable ? expanded : undefined} aria-busy={row.state === "running"}
41
+ style={{ flexDirection: "row", alignItems: "center", borderRadius: 8, borderWidth: 1, borderColor: open ? c.border : "transparent", borderBottomLeftRadius: open ? 0 : 8, borderBottomRightRadius: open ? 0 : 8, backgroundColor: open ? c.surface1 : "transparent", paddingHorizontal: 8, paddingVertical: 4 }}>
42
+ <View style={{ width: 22, height: 22, marginRight: 4, alignItems: "center", justifyContent: "center" }}>
43
+ {active ? <View style={{ transform: [{ rotate: expanded ? "90deg" : "0deg" }] }}><Icon name="ChevronRight" size={14} color={c.foreground} /></View>
44
+ : <Icon name={row.state === "failed" ? "TriangleAlert" : TOOL_ICON[row.tool] ?? (row.tone === "warning" ? "CircleAlert" : "GitPullRequest")} size={12} color={row.tone === "muted" ? c.foregroundMuted : color} />}
45
+ </View>
46
+ <Text numberOfLines={1} style={{ flexShrink: 1, fontSize: 14, color: row.tone === "muted" ? (active || row.state === "running" ? c.foreground : c.foregroundMuted) : color, opacity: row.state === "running" ? 0.72 : 1 }}>{row.line}</Text>
47
+ </Pressable>
48
+ {open && <Expanded text={row.more!} color={row.state === "failed" ? c.statusDanger : c.foreground} border={c.border} />}
49
+ </View>;
50
+ }
51
+
52
+ /** The agent declared the workspace done: the one row in the chat that is allowed to be a little proud. */
53
+ function DoneCard({ summary, theme }: { summary: string; theme: PluginTimelineItemProps["theme"] }) {
54
+ const c = theme.colors;
55
+ return <Reveal>
56
+ <View accessibilityRole="summary" style={{ flexDirection: "row", gap: 12, padding: 12, borderRadius: 10, backgroundColor: tint(c.statusSuccess, c), borderWidth: 1, borderColor: withAlpha(c.statusSuccess, 0.35), marginVertical: 4 }}>
57
+ <View style={{ width: 28, height: 28, borderRadius: 14, alignItems: "center", justifyContent: "center", backgroundColor: c.statusSuccess }}><Icon name="Check" size={16} color={c.surface0} /></View>
58
+ <View style={{ flex: 1, gap: 3 }}>
59
+ <Text style={{ color: c.foreground, fontSize: 14, lineHeight: 19, fontWeight: "600" }}>Workspace done</Text>
60
+ {!!summary && <Text selectable style={{ color: c.foreground, fontSize: 13, lineHeight: 19 }}>{summary}</Text>}
61
+ <Text style={{ color: c.foregroundMuted, fontSize: 12, lineHeight: 17 }}>Follow-up checked every PR and task. Archive the workspace when you're ready.</Text>
62
+ </View>
63
+ </View>
64
+ </Reveal>;
65
+ }
66
+
67
+ /** A repair batch Follow-up sent, as a card instead of a raw prompt bubble. */
68
+ function BatchCardView({ item, theme }: PluginTimelineItemProps<BatchCard>) {
69
+ const c = theme.colors;
70
+ const card = item.data;
71
+ const [expanded, setExpanded] = useState(false);
72
+ const recovery = card.kind === "recovery";
73
+ return <View style={{ alignSelf: "flex-end", width: "100%", maxWidth: 560, marginVertical: 4 }}>
74
+ <View style={{ borderRadius: 10, borderWidth: 1, borderColor: c.border, backgroundColor: c.surface1, overflow: "hidden" }}>
75
+ <View style={{ flexDirection: "row", alignItems: "center", gap: 8, paddingHorizontal: 12, paddingTop: 10, paddingBottom: recovery ? 10 : 6 }}>
76
+ <Icon name={recovery ? "History" : "Wrench"} size={14} color={c.foregroundMuted} />
77
+ <Text style={{ flex: 1, color: c.foreground, fontSize: 13, lineHeight: 18, fontWeight: "600" }}>{recovery ? "Follow-up recovery brief" : `Follow-up sent ${card.tasks.length} repair${card.tasks.length === 1 ? "" : "s"}`}</Text>
78
+ <Pressable accessibilityRole="button" aria-expanded={expanded} accessibilityLabel={expanded ? "Hide the full message" : "Show the full message"} onPress={() => setExpanded(v => !v)} hitSlop={6}
79
+ style={state => ({ paddingHorizontal: 6, paddingVertical: 2, borderRadius: 4, backgroundColor: state.pressed ? c.surface2 : "transparent" })}>
80
+ <Text style={{ color: c.foregroundMuted, fontSize: 12 }}>{expanded ? "Hide" : "Full message"}</Text>
81
+ </Pressable>
82
+ </View>
83
+ {!recovery && <View style={{ paddingHorizontal: 12, paddingBottom: 10, gap: 3 }}>
84
+ {card.tasks.map((task, i) => <View key={i} style={{ flexDirection: "row", gap: 6 }}>
85
+ <Text style={{ width: 18, color: c.foregroundMuted, fontSize: 13, lineHeight: 19, fontVariant: ["tabular-nums"] }}>{i + 1}.</Text>
86
+ <Text selectable style={{ flex: 1, color: c.foreground, fontSize: 13, lineHeight: 19 }}>{task}</Text>
87
+ </View>)}
88
+ </View>}
89
+ {expanded && <View style={{ borderTopWidth: 1, borderTopColor: c.border }}>
90
+ <ScrollView style={[{ maxHeight: 300 }, Platform.OS === "web" ? ({ overscrollBehavior: "contain" } as object) : null]} contentContainerStyle={{ padding: 12 }}>
91
+ <Text selectable style={{ color: c.foregroundMuted, fontFamily: mono(), fontSize: 12, lineHeight: 18 }}>{card.text}</Text>
92
+ </ScrollView>
93
+ </View>}
94
+ </View>
95
+ </View>;
96
+ }
97
+
98
+ /** The developer's answer, sent from Follow-up: the question, the choice and their note. */
99
+ function AnswerCardView({ item, theme }: PluginTimelineItemProps<AnswerCard>) {
100
+ const c = theme.colors;
101
+ const card = item.data;
102
+ const [expanded, setExpanded] = useState(false);
103
+ return <View style={{ alignSelf: "flex-end", width: "100%", maxWidth: 560, marginVertical: 4 }}>
104
+ <View style={{ borderRadius: 10, borderWidth: 1, borderColor: c.border, backgroundColor: c.surface1, overflow: "hidden" }}>
105
+ <View style={{ flexDirection: "row", alignItems: "center", gap: 8, paddingHorizontal: 12, paddingTop: 10, paddingBottom: 6 }}>
106
+ <Icon name="MessageCircleReply" size={14} color={c.statusSuccess} />
107
+ <Text numberOfLines={1} style={{ flex: 1, color: c.foreground, fontSize: 13, lineHeight: 18, fontWeight: "600" }}>Your answer to {card.id} · {card.title}</Text>
108
+ <Pressable accessibilityRole="button" aria-expanded={expanded} accessibilityLabel={expanded ? "Hide the full message" : "Show the full message"} onPress={() => setExpanded(v => !v)} hitSlop={6}
109
+ style={state => ({ paddingHorizontal: 6, paddingVertical: 2, borderRadius: 4, backgroundColor: state.pressed ? c.surface2 : "transparent" })}>
110
+ <Text style={{ color: c.foregroundMuted, fontSize: 12 }}>{expanded ? "Hide" : "Full message"}</Text>
111
+ </Pressable>
112
+ </View>
113
+ <View style={{ paddingHorizontal: 12, paddingBottom: 10, gap: 3 }}>
114
+ <Text selectable style={{ color: c.foreground, fontSize: 13, lineHeight: 19 }}><Text style={{ fontWeight: "600", fontVariant: ["tabular-nums"] }}>{card.choice}</Text>{card.label ? ` · ${card.label}` : ""}</Text>
115
+ {!!card.words && <Text selectable style={{ color: c.foregroundMuted, fontSize: 13, lineHeight: 19 }}>{card.words}</Text>}
116
+ </View>
117
+ {expanded && <View style={{ borderTopWidth: 1, borderTopColor: c.border }}>
118
+ <ScrollView style={[{ maxHeight: 300 }, Platform.OS === "web" ? ({ overscrollBehavior: "contain" } as object) : null]} contentContainerStyle={{ padding: 12 }}>
119
+ <Text selectable style={{ color: c.foregroundMuted, fontFamily: mono(), fontSize: 12, lineHeight: 18 }}>{card.text}</Text>
120
+ </ScrollView>
121
+ </View>}
122
+ </View>
123
+ </View>;
124
+ }
125
+
126
+ export function contributeChat(client: PluginClientContext) {
127
+ const removers = [
128
+ client.addTimelineTransformer({
129
+ id: "follow-tool-calls", query: { itemType: "tool_call" },
130
+ transform({ item }) {
131
+ const detail: unknown = item.detail;
132
+ let tool = followToolName(item.name);
133
+ let input: Record<string, unknown> = {};
134
+ let output: string | undefined;
135
+ if (tool) {
136
+ if (record(detail) && record(detail.input)) input = detail.input;
137
+ output = record(detail) && "output" in detail ? readText(detail.output) : undefined;
138
+ } else if (record(detail) && detail.type === "shell" && typeof detail.command === "string") {
139
+ const call = followShellCall(detail.command);
140
+ if (!call) return undefined;
141
+ tool = call.tool; input = call.input; output = typeof detail.output === "string" ? detail.output : undefined;
142
+ } else return undefined;
143
+ const error = item.status === "failed" ? readText(item.error) ?? "" : undefined;
144
+ if (item.status === "completed" && record(detail) && record(detail.output) && detail.output.isError === true)
145
+ return { items: [{ type: "plugin" as const, kind: TOOL_KIND, version: 1, data: toolRow(tool, "failed", input, undefined, output) }] };
146
+ return { items: [{ type: "plugin" as const, kind: TOOL_KIND, version: 1, data: toolRow(tool, item.status, input, output, error) }] };
147
+ },
148
+ }),
149
+ client.addTimelineTransformer({
150
+ id: "follow-batch-prompts", query: { itemType: "user_message" },
151
+ transform({ item }) {
152
+ const answer = parseAnswerMessage(item.text);
153
+ if (answer) return { items: [{ type: "plugin" as const, kind: ANSWER_KIND, version: 1, data: { ...answer, text: item.text } }] };
154
+ const card = parseBatchPrompt(item.text);
155
+ return card ? { items: [{ type: "plugin" as const, kind: BATCH_KIND, version: 1, data: card }] } : undefined;
156
+ },
157
+ }),
158
+ client.addTimelineRenderer({ kind: TOOL_KIND, version: 1, schema: toolRowSchema, Component: ToolRowView }),
159
+ client.addTimelineRenderer({ kind: BATCH_KIND, version: 1, schema: batchCardSchema, Component: BatchCardView }),
160
+ client.addTimelineRenderer({ kind: ANSWER_KIND, version: 1, schema: answerCardSchema, Component: AnswerCardView }),
161
+ ];
162
+ return () => { for (const remove of removers) void remove(); };
163
+ }
@@ -1,4 +1,4 @@
1
- import type { PluginClientContext, PluginSidebarContribution, PluginSurfaceProps } from "@getpaseo/plugin/client";
1
+ import type { PluginClientContext, PluginSidebarItemContribution, PluginSurfaceProps } from "@getpaseo/plugin/client";
2
2
  import { useSettings } from "@getpaseo/plugin/client";
3
3
  import { SettingsAction, SettingsCard, SettingsSection, SettingsSwitch } from "@getpaseo/plugin/client/ui";
4
4
  import { useEffect } from "react";
@@ -30,19 +30,14 @@ function DisplaySettings({ theme }: PluginSurfaceProps) {
30
30
  </View>
31
31
  );
32
32
 
33
+ const save = (patch: Partial<Display>) => void settings.save({ ...settings.values, ...patch }, settings.revision);
33
34
  return (
34
35
  <ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16, gap: 16 }}>
35
- <SettingsSection title="Navigation">
36
+ <SettingsSection title="Where Follow-up appears" info="Every entry point opens the same panel. Command-K (Ctrl-K on Windows and Linux) works whatever you hide here.">
36
37
  <SettingsCard>
37
- <SettingsSwitch
38
- label="Show in sidebar"
39
- hint="Open this plugin from Command-K (Ctrl-K on Windows and Linux) while viewing a workspace or plugin on this host."
40
- value={settings.values.showSidebar}
41
- disabled={settings.saving}
42
- onValueChange={(showSidebar) => void settings.save({ ...settings.values, showSidebar }, settings.revision)}
43
- />
44
-
45
- <SettingsSwitch label="Show workspace header button" hint="Command-K and the sidebar remain available when this button is hidden." value={settings.values.enable_header_button} disabled={settings.saving} onValueChange={(enable_header_button) => void settings.save({ ...settings.values, enable_header_button }, settings.revision)} />
38
+ <SettingsSwitch label="Sidebar row" hint="Opens all tracked workspaces, with a count when some need you." value={settings.values.showSidebar} disabled={settings.saving} onValueChange={(showSidebar) => save({ showSidebar })} />
39
+ <SettingsSwitch label="Workspace header button" hint="An icon in each workspace header. A dot appears when that workspace needs you." value={settings.values.enable_header_button} disabled={settings.saving} onValueChange={(enable_header_button) => save({ enable_header_button })} />
40
+ <SettingsSwitch label="Composer pill" hint="Shows above the message box only while the workspace needs you." value={settings.values.enable_composer_pill} disabled={settings.saving} onValueChange={(enable_composer_pill) => save({ enable_composer_pill })} />
46
41
  </SettingsCard>
47
42
  {settings.saveError && <Text accessibilityRole="alert" style={{ color: theme.colors.statusDanger }}>{settings.saveError}</Text>}
48
43
  </SettingsSection>
@@ -51,22 +46,22 @@ function DisplaySettings({ theme }: PluginSurfaceProps) {
51
46
  }
52
47
 
53
48
  /** Settings notifications reach mounted screens; poll for changes made on other clients. */
54
- export function contributeDisplay(client: PluginClientContext, item: PluginSidebarContribution) {
55
- const sidebar = sidebarVisibility(() => client.addSidebarItem(item));
49
+ export function contributeDisplay(client: PluginClientContext, item: PluginSidebarItemContribution) {
50
+ const sidebar = sidebarVisibility(() => client.addSidebarHeaderItem(item));
56
51
  // Paseo 0.10.1 memoizes sidebar route resolution when an installation mounts.
57
52
  // Register synchronously, then reconcile the saved preference, so that an
58
53
  // already-open sidebar route can resolve its surface during that first render.
59
54
  sidebar.set(true);
60
55
  const Settings = DisplaySettings;
61
- const removeSurface = client.addSurface("display-settings", Settings);
56
+ const removeSurface = client.addScreen({ id: "display-settings", title: "Follow-up display", Component: Settings });
62
57
  const removeSettings = client.addSettingsScreen({ id: "display", title: "Display", icon: "PanelLeft", Component: Settings });
63
58
  const removeCommand = client.addCommandCenterItem({
64
59
  id: "display-settings",
65
- title: `${item.title}: display settings`,
60
+ title: "Follow-up: display settings",
66
61
  icon: "PanelLeft",
67
- keywords: [item.title, "sidebar", "show", "hide", "display", "settings"],
62
+ keywords: ["Follow-up", "sidebar", "pill", "header", "show", "hide", "display", "settings"],
68
63
  context: "global",
69
- onSelect({ openSurface }) { openSurface("display-settings"); },
64
+ onSelect({ openScreen }) { openScreen({ screenId: "display-settings" }); },
70
65
  });
71
66
  let disposed = false;
72
67
  let pending = false;