Operator Handler System (IDE / diff / browser launchers)
Status: Open — design proposal (Phase 1, Agent Orchestrator Research Program)
Problem
Section titled “Problem”When an operator wants to act on something the agent produced — review a diff, open the workspace in an IDE, follow a link the agent printed — they have to context-switch to a separate shell, copy the path, run the right launcher manually. Every adjacent tool in this space (multicode, Conductor, Docker Sandboxes) ships an external-launcher abstraction precisely because this friction kills the “left it running overnight” workflow.
jackin’ has no such abstraction today, so each future feature that needs to launch an external tool would invent its own.
Why It Matters
Section titled “Why It Matters”- The console (today) and any future TUI need to know how to “open” a path,
URL, or repo without hardcoding
open/xdg-open. - Multiple downstream items in this program need it: GitHub link tracking opens issue/PR URLs, agent tag protocol opens repo paths, custom operator tools expand exec templates that eventually want the same launcher resolution.
- Designing the abstraction once means cross-platform handling (
openon macOS,xdg-openon Linux, OS-native file URLs) lives in one place.
Inspiration in multicode
Section titled “Inspiration in multicode”Sources:
- README — Editor Tool (covers the
[compare]block for IDE selection) - Config —
config.toml[handler]block
[handler]review = "/usr/bin/smerge ." # chdir-mode launcher (no {} placeholder)review-pty = false # allocate a PTY for the launcher?web = "/usr/bin/firefox {}" # template-mode launcher (single {} required)multicode also exposes a [compare] block in the current reference
implementation that specifies the IDE for “open in editor”:
[compare]tool = "vscode" # or "intellij"command = "..." # optional overrideThe TUI keystrokes r (review = open diff viewer for repo) and e (open in
IDE) call the resolved handler. Both blocks are loaded once at startup; no
hot-reload.
Recommended Shape
Section titled “Recommended Shape”One unified [handler] block. The [compare]-style IDE selector folds in
as another handler kind, not a separate block.
Config
Section titled “Config”[handler.diff]# Diff viewer for a repo path. Invoked chdir-mode by default (cwd = arg).command = "/opt/homebrew/bin/smerge"mode = "chdir"
[handler.web]# URL launcher.command = "open {}" # macOS default; Linux default would be `xdg-open {}`mode = "template"
[handler.ide]# IDE on a workspace path. Either a tool name (resolved via well-known list)# or a full command.tool = "vscode"# command = "code -n -g {}" # optional explicit overrideResolution rules:
diffdefaults togit diff(in-terminal) if no command is configured.webdefaults toxdg-open {}on Linux,open {}on macOS, none on unsupported platforms (handler returns “no web handler configured”).ide.toolaccepts at leastvscode,intellij,zed,cursor, plusnoneto disable. The well-known table is small and operator-extensible viacommand = "...".mode = "chdir"means the handler runs with cwd set to the argument and no argument substitution;mode = "template"requires a single{}and substitutes verbatim.
jackin open <selector> # IDE handlerjackin diff <selector> # diff handler (in-terminal `git diff` by default)jackin web <url> # web handler — explicit URL formSubcommands accept the same selector grammar as jackin load. They resolve
the workspace’s primary mount path (or, if isolated, the materialized
worktree path under
<data_dir>/jackin-<container>/git/worktree/repo/<dst-tree>/<container>/)
before invoking the handler.
Console
Section titled “Console”Single keystroke per handler when a workspace row is selected: o (open in
IDE), d (diff), w (web — only meaningful when a link is selected).
Scope (V1)
Section titled “Scope (V1)”[handler.diff],[handler.web],[handler.ide]config blocks at the operator-config level.- Cross-platform defaults baked in for
weband a sensible default fordiff(git diffin terminal). - IDE well-known table covering the four named editors above.
- CLI subcommands
jackin open,jackin diff,jackin web. - Console keybindings for diff and IDE.
- For worktree-isolated mounts, default to opening the materialized worktree; provide a flag (CLI) and Shift-modifier (console) to open the host repo path instead.
- Per-workspace handler overrides. Operator-config-level only in V1; if a use case surfaces (e.g. one workspace wants its own IDE), revisit.
- In-console diff renderer. multicode uses an external viewer; jackin
console gets
git diffin a paged view, that’s enough. - PTY allocation for the diff handler (
review-ptyin multicode). Defer until someone reports an issue with a TTY-aware diff tool.
Open Questions
Section titled “Open Questions”- Where does the operator-config file live for handlers? Options: extend
the existing operator config under
[handler], or a new~/.config/jackin/handlers.toml. Recommended default: extend operator config — single-file ergonomics matter. - Should
jackin diffdefault to in-terminalgit diff(current recommendation) or refuse to run without a configured handler? Recommended default: in-terminal default; explicit configuration is a polish, not a prerequisite. - For worktree-isolated mounts, should
jackin opendefault to the materialized worktree or the host repo? See default above; this might flip after operators try V1.
Related Files
Section titled “Related Files”- New module (e.g.
src/handler.rs) — config + resolution + execution src/cli/role.rs— addsjackin open/jackin diff/jackin websubcommandssrc/console/manager/state.rs— keybindingssrc/workspace/resolve.rs— primary-mount resolution for the handler argument
See Also
Section titled “See Also”- GitHub link tracking —
consumes
webhandler - Agent tag protocol — emitted
<jackin:repo>tags resolve throughdiffandide - Custom operator tools — also uses the handler resolution machinery