Skip to content

feat: support custom plugin marketplaces#34

Merged
donbeave merged 7 commits into
mainfrom
feature/custom-plugin-marketplaces
Apr 10, 2026
Merged

feat: support custom plugin marketplaces#34
donbeave merged 7 commits into
mainfrom
feature/custom-plugin-marketplaces

Conversation

@donbeave

Copy link
Copy Markdown
Member

Summary

  • extend jackin.agent.toml to support [[claude.marketplaces]] entries with source and optional sparse paths
  • persist marketplace data into runtime plugins.json and register custom marketplaces during container bootstrap before plugin installation
  • add integration coverage for marketplace bootstrap ordering and docs/todo updates for the new manifest shape

Test Plan

  • cargo fmt -- --check
  • cargo clippy
  • cargo nextest run
  • bun run build (in docs/)
  • manually verified with the real Claude CLI in isolated temp HOME directories that repeated claude plugin marketplace add anthropics/claude-plugins-official and claude plugin marketplace add obra/superpowers-marketplace --sparse plugins .claude-plugin both exit 0 on the second run and report already on disk

@donbeave donbeave merged commit 0e6ba3d into main Apr 10, 2026
6 checks passed
@donbeave donbeave deleted the feature/custom-plugin-marketplaces branch April 10, 2026 17:08
donbeave added a commit that referenced this pull request Apr 20, 2026
…etplaces

feat: support custom plugin marketplaces
donbeave added a commit that referenced this pull request Apr 21, 2026
…etplaces

feat: support custom plugin marketplaces
donbeave added a commit that referenced this pull request Apr 21, 2026
feat: support custom plugin marketplaces
donbeave added a commit that referenced this pull request Apr 21, 2026
feat: support custom plugin marketplaces
donbeave added a commit that referenced this pull request Apr 21, 2026
feat: support custom plugin marketplaces
donbeave added a commit that referenced this pull request May 7, 2026
feat: support custom plugin marketplaces
Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Co-authored-by: Codex <codex@openai.com>
donbeave added a commit that referenced this pull request May 7, 2026
feat: support custom plugin marketplaces

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Co-authored-by: Codex <codex@openai.com>
donbeave added a commit that referenced this pull request May 7, 2026
Amp-Thread-ID: https://ampcode.com/threads/T-019d77ce-fe34-713a-b0ce-4cfc7e60db14

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Co-authored-by: Amp <amp@ampcode.com>
donbeave added a commit that referenced this pull request May 7, 2026
Amp-Thread-ID: https://ampcode.com/threads/T-019d77ce-fe34-713a-b0ce-4cfc7e60db14

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Co-authored-by: Amp <amp@ampcode.com>
donbeave added a commit that referenced this pull request May 7, 2026
Amp-Thread-ID: https://ampcode.com/threads/T-019d77ce-fe34-713a-b0ce-4cfc7e60db14

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Co-authored-by: Amp <amp@ampcode.com>
donbeave added a commit that referenced this pull request May 7, 2026
Amp-Thread-ID: https://ampcode.com/threads/T-019d7824-917e-7381-ab6f-07cddb170800

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Co-authored-by: Amp <amp@ampcode.com>
donbeave added a commit that referenced this pull request May 7, 2026
Amp-Thread-ID: https://ampcode.com/threads/T-019d7824-917e-7381-ab6f-07cddb170800

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Co-authored-by: Amp <amp@ampcode.com>
donbeave added a commit that referenced this pull request May 7, 2026
Amp-Thread-ID: https://ampcode.com/threads/T-019d7824-917e-7381-ab6f-07cddb170800

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Co-authored-by: Amp <amp@ampcode.com>
donbeave added a commit that referenced this pull request May 7, 2026
Amp-Thread-ID: https://ampcode.com/threads/T-019d7824-917e-7381-ab6f-07cddb170800

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Co-authored-by: Amp <amp@ampcode.com>
donbeave added a commit that referenced this pull request May 7, 2026
feat: support custom plugin marketplaces

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Co-authored-by: Codex <codex@openai.com>
donbeave added a commit that referenced this pull request May 22, 2026
…escing

Four small daemon/dialog correctness fixes.

`dialog::box_rect` (#9)
- Clamp dialog height to the area below the status bar so a very
  small terminal does not paint past the bottom edge (which would
  scroll the host terminal and destroy the operator's pane content)
  and does not overlap row 0 (the brand pill / tab strip). Trade-off
  is that the dialog renders unusable when the terminal is
  pathologically small, but the host terminal stays in a recoverable
  state regardless.
- `render_bottom_hint`: tighten the bail to `total > term_cols` so
  an exact-fit hint renders instead of being dropped at `==`. Use
  saturating subtraction for the start_col centring math.

`Multiplexer::spawn_session` (#34, cap side)
- Cap tabs at `MAX_TABS=32` and sessions at `MAX_SESSIONS=64`. A
  runaway client (or an operator mis-click loop) cannot allocate
  unbounded PTYs — each session retains scrollback, a master+slave
  PTY pair, and a child process, so unbounded growth is a real OOM
  risk. The CLI surface returns an `anyhow::bail!` which
  `dispatch_spawn_intent` already clog's.

`SessionEvent::Output` (#36)
- Move `drain_mode_transitions` inside the `is_focused` gate. The
  previous code drained and discarded the bytes on every backgrounded
  pane — wasted work, plus a future change that reads
  `bracketed_paste_active` after drain on an unfocused pane would
  silently see the wrong state. Also restructured to collect the
  to-emit bytes into a local Vec so the `&mut Session` borrow ends
  before `mux.send_output` (which needs `&self`).

Render ticker (#37)
- Drop the `mux.dialog.is_none()` gate from the render ticker
  condition. `compose_frame` already includes the dialog overlay
  when one is open, so the open-dialog path now composes at the
  normal ~30 fps instead of accumulating `dirty` until dismiss —
  without this the dismiss frame was a sudden jump of N frames'
  worth of accumulated PTY output that the operator had no way to
  see coming.

Co-authored-by: Claude <noreply@anthropic.com>
Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
donbeave added a commit that referenced this pull request May 23, 2026
…escing

Four small daemon/dialog correctness fixes.

`dialog::box_rect` (#9)
- Clamp dialog height to the area below the status bar so a very
  small terminal does not paint past the bottom edge (which would
  scroll the host terminal and destroy the operator's pane content)
  and does not overlap row 0 (the brand pill / tab strip). Trade-off
  is that the dialog renders unusable when the terminal is
  pathologically small, but the host terminal stays in a recoverable
  state regardless.
- `render_bottom_hint`: tighten the bail to `total > term_cols` so
  an exact-fit hint renders instead of being dropped at `==`. Use
  saturating subtraction for the start_col centring math.

`Multiplexer::spawn_session` (#34, cap side)
- Cap tabs at `MAX_TABS=32` and sessions at `MAX_SESSIONS=64`. A
  runaway client (or an operator mis-click loop) cannot allocate
  unbounded PTYs — each session retains scrollback, a master+slave
  PTY pair, and a child process, so unbounded growth is a real OOM
  risk. The CLI surface returns an `anyhow::bail!` which
  `dispatch_spawn_intent` already clog's.

`SessionEvent::Output` (#36)
- Move `drain_mode_transitions` inside the `is_focused` gate. The
  previous code drained and discarded the bytes on every backgrounded
  pane — wasted work, plus a future change that reads
  `bracketed_paste_active` after drain on an unfocused pane would
  silently see the wrong state. Also restructured to collect the
  to-emit bytes into a local Vec so the `&mut Session` borrow ends
  before `mux.send_output` (which needs `&self`).

Render ticker (#37)
- Drop the `mux.dialog.is_none()` gate from the render ticker
  condition. `compose_frame` already includes the dialog overlay
  when one is open, so the open-dialog path now composes at the
  normal ~30 fps instead of accumulating `dirty` until dismiss —
  without this the dismiss frame was a sudden jump of N frames'
  worth of accumulated PTY output that the operator had no way to
  see coming.

Co-authored-by: Claude <noreply@anthropic.com>
Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
donbeave added a commit that referenced this pull request May 23, 2026
…escing

Four small daemon/dialog correctness fixes.

`dialog::box_rect` (#9)
- Clamp dialog height to the area below the status bar so a very
  small terminal does not paint past the bottom edge (which would
  scroll the host terminal and destroy the operator's pane content)
  and does not overlap row 0 (the brand pill / tab strip). Trade-off
  is that the dialog renders unusable when the terminal is
  pathologically small, but the host terminal stays in a recoverable
  state regardless.
- `render_bottom_hint`: tighten the bail to `total > term_cols` so
  an exact-fit hint renders instead of being dropped at `==`. Use
  saturating subtraction for the start_col centring math.

`Multiplexer::spawn_session` (#34, cap side)
- Cap tabs at `MAX_TABS=32` and sessions at `MAX_SESSIONS=64`. A
  runaway client (or an operator mis-click loop) cannot allocate
  unbounded PTYs — each session retains scrollback, a master+slave
  PTY pair, and a child process, so unbounded growth is a real OOM
  risk. The CLI surface returns an `anyhow::bail!` which
  `dispatch_spawn_intent` already clog's.

`SessionEvent::Output` (#36)
- Move `drain_mode_transitions` inside the `is_focused` gate. The
  previous code drained and discarded the bytes on every backgrounded
  pane — wasted work, plus a future change that reads
  `bracketed_paste_active` after drain on an unfocused pane would
  silently see the wrong state. Also restructured to collect the
  to-emit bytes into a local Vec so the `&mut Session` borrow ends
  before `mux.send_output` (which needs `&self`).

Render ticker (#37)
- Drop the `mux.dialog.is_none()` gate from the render ticker
  condition. `compose_frame` already includes the dialog overlay
  when one is open, so the open-dialog path now composes at the
  normal ~30 fps instead of accumulating `dirty` until dismiss —
  without this the dismiss frame was a sudden jump of N frames'
  worth of accumulated PTY output that the operator had no way to
  see coming.

Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
donbeave added a commit that referenced this pull request May 23, 2026
…escing

Four small daemon/dialog correctness fixes.

`dialog::box_rect` (#9)
- Clamp dialog height to the area below the status bar so a very
  small terminal does not paint past the bottom edge (which would
  scroll the host terminal and destroy the operator's pane content)
  and does not overlap row 0 (the brand pill / tab strip). Trade-off
  is that the dialog renders unusable when the terminal is
  pathologically small, but the host terminal stays in a recoverable
  state regardless.
- `render_bottom_hint`: tighten the bail to `total > term_cols` so
  an exact-fit hint renders instead of being dropped at `==`. Use
  saturating subtraction for the start_col centring math.

`Multiplexer::spawn_session` (#34, cap side)
- Cap tabs at `MAX_TABS=32` and sessions at `MAX_SESSIONS=64`. A
  runaway client (or an operator mis-click loop) cannot allocate
  unbounded PTYs — each session retains scrollback, a master+slave
  PTY pair, and a child process, so unbounded growth is a real OOM
  risk. The CLI surface returns an `anyhow::bail!` which
  `dispatch_spawn_intent` already clog's.

`SessionEvent::Output` (#36)
- Move `drain_mode_transitions` inside the `is_focused` gate. The
  previous code drained and discarded the bytes on every backgrounded
  pane — wasted work, plus a future change that reads
  `bracketed_paste_active` after drain on an unfocused pane would
  silently see the wrong state. Also restructured to collect the
  to-emit bytes into a local Vec so the `&mut Session` borrow ends
  before `mux.send_output` (which needs `&self`).

Render ticker (#37)
- Drop the `mux.dialog.is_none()` gate from the render ticker
  condition. `compose_frame` already includes the dialog overlay
  when one is open, so the open-dialog path now composes at the
  normal ~30 fps instead of accumulating `dirty` until dismiss —
  without this the dismiss frame was a sudden jump of N frames'
  worth of accumulated PTY output that the operator had no way to
  see coming.

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Co-authored-by: Codex <codex@openai.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant