orcakit
Orca shows one
workspace per git worktree, with a card carrying the linked issue, a status, and any terminals running in it. Worktrees themselves come from plain
— gitkit's convention, issuekit's
, or your own hands — and Orca discovers them on its own.
What nothing owns is the
drift between the two. A worktree named
sits in the sidebar with no issue attached, so its card says nothing. An issue closes and its PR merges, but the workspace stays forever, and three weeks later the sidebar is mostly finished work. orcakit owns exactly that reconciliation:
Orca's view of your worktrees, made to match the tracker.
The line orcakit does not cross
Git is the source of truth for a worktree. orcakit never runs
to do real work and never invents a path — creating a worktree is native
under gitkit's convention, so a headless Linux box with no Orca on it runs identical commands. orcakit works one layer up, on things Orca alone knows:
- metadata — the linked issue, the workspace status, the comment, the display name;
- Orca-side resources git can't express — live terminals bound to a workspace, and a repo's archive/setup hooks.
Removing a worktree with git is enough: Orca notices within seconds and drops the entry by itself. The single exception is a workspace Orca created — see
Mode: , which documents why and confines it there.
orcakit never touches the tracker. It reads issues and PRs to judge state; it never closes an issue, moves a label, or edits a PR. When it finds a merged PR whose issue is still open, that's tracker drift and it says so, routing to
issuekit rather than reaching around it.
When this fires
- — "what's in my Orca workspaces", "which workspaces are stale", "show me my orca sidebar as a table". Read-only.
- — "link my worktrees to their issues", "my workspace cards are blank", "Orca doesn't know what #42's workspace is for".
- — "clean up my orca workspaces", "remove the workspaces whose issues merged", "my sidebar is full of finished work".
- — "where should Orca put new worktrees", "make Orca use my worktree root", "stop Orca creating worktrees in its own folder".
If no mode is clear, ask. is free and
deletes directories; never guess between them.
Not this skill: creating a worktree (gitkit), closing an issue or tearing down after a merge (issuekit
), driving Orca terminals, browser, or agents (that's the
CLI directly, or an Orca-provided skill if one is installed). orcakit is workspace
hygiene, not workspace
operation.
Preflight (every mode)
sh
orca status --json # app running? runtime reachable?
- not installed → this machine has no Orca, so there is nothing to reconcile. Say exactly that and stop. Do not fall back to anything: worktrees are already fine without Orca, and nothing else in the workflow depends on this skill.
- Runtime not reachable → starts the app and waits. Ask before launching a desktop app on someone's machine.
- missing or unauthenticated → still works but every verdict degrades to "unknown tracker state", and cannot run at all, because its whole safety rests on knowing a PR merged. Say which and stop rather than guessing.
- A rejected flag or selector → the CLI moves fast; check before concluding the operation is unsupported. The goal (the link, the status, the removal) is the contract — the exact flag spelling isn't.
Orca must also be configured to surface externally-created worktrees, or it won't see anything gitkit made:
sh
orca repo list --json # each repo carries externalWorktreeVisibility
If a repo reads
"externalWorktreeVisibility": "hide"
, its gitkit worktrees are invisible to every mode here. There is no CLI flag for this — it's a per-repo setting in the Orca UI. Report the repo by name and let the user flip it; don't pretend the empty result is an empty worktree root.
What Orca knows about a workspace
sh
orca worktree list --json
orca worktree show --worktree "branch:$BRANCH" --json
Each record carries
,
,
,
,
,
,
, and
. Selectors are
,
,
, and
/
—
prefer , for the same reason gitkit looks worktrees up by branch: a path is a guess, a branch is an identity.
Discovery is eventually consistent. A worktree created seconds ago may resolve via
before it appears in
; a removed one lingers briefly. Re-read rather than concluding from one stale listing.
Two kinds of workspace
Everything below turns on this distinction:
| kind | where it lives | who created it | how it goes away |
|---|
| git-native | under (default ~/worktrees/<repo>/<branch>
) | gitkit / issuekit / a human, with | git removes it; Orca drops the entry on its own |
| Orca-native | under Orca's worktree base path (default ~/orca/workspaces/<repo>/<name>
) | | , so hooks and terminals are handled |
Classify by path prefix. That's a heuristic, not a fact Orca records — so it is a
reason to confirm before deleting, never a thing to act on silently.
exists to collapse the two locations into one and retire the guesswork.
Mode:
Read-only. Changes nothing, asks nothing, and is the right first move whenever the state is unclear.
Take every workspace where
is false, and join it against the tracker:
sh
orca worktree list --json
gh pr list --head "$BRANCH" --state all --json number,state,url,mergedAt
gh issue view "$N" --json number,state,title,labels # $N parsed from an issue-<n>-<slug> branch
git -C "$WT" status --porcelain # dirty?
git -C "$WT" log --oneline "@{upstream}..HEAD" # unpushed?
Render one table — workspace, branch, issue, PR, Orca status, git state — and give each row a verdict:
| verdict | means | fix |
|---|
| open issue or open PR, work in flight | nothing |
| branch names an issue, is null | |
| Orca says , the PR is merged or in review | |
| PR merged, issue closed, tree clean | |
| PR merged, issue still open | issuekit — not orcakit's to fix |
| uncommitted or unpushed work, whatever the tracker says | a human, before anything else |
| no issue in the branch name and no PR | leave it; say so |
Close by naming which mode clears which rows, and put
first if any exist — that's the row where work gets lost.
Mode:
Attach the metadata Orca cannot infer, so a workspace card actually says what it's for.
For each git-native workspace whose branch matches
and whose
is null:
sh
orca worktree set --worktree "branch:$BRANCH" --issue "$N" --json
And bring
in line with the tracker. Orca's vocabulary is
,
,
,
,
; map it from the real state, not from the label alone:
| tracker state | |
|---|
| issue open, no PR, no commits on the branch | |
| issue open, no PR, work committed | |
| PR open | |
| PR merged | |
| issue closed and PR merged | (and it's a candidate) |
sh
orca worktree set --worktree "branch:$BRANCH" --workspace-status in-review --json
Rules:
- Preview every planned as one table, then take one OK for the batch. Nothing is destroyed here, but it rewrites the user's sidebar — that deserves a look, not a surprise.
- Never overwrite a that's already set to something different. A human or
orca worktree create --issue
put it there deliberately; report the disagreement and leave it.
- Never overwrite a hand-written . Set one only when it's empty.
- Leave the main worktree alone. It isn't feature work and a status on it means nothing.
- A branch that names no issue simply gets skipped. Say how many, don't invent links from slugs.
Hand off
Write every hand-off in this skill in the procedural register: one instruction per sentence, active voice, present tense, no metaphor.
What changed — how many workspaces gained an issue link, how many had their status corrected, how many were skipped and why.
Where it landed — in Orca's sidebar; nothing on disk moved and no git state changed.
Next — if
marked anything
, that set is exactly
's input, so crown that. Otherwise stop: linked cards are the end state, not a step toward one.
Mode:
Reclaim the workspaces whose work already landed. Every removal is irreversible, so the shape is fixed: gather, qualify, preview everything at once, take one confirm, then remove.
1. Qualify
A workspace is a candidate only when its work is provably merged and the tracker already agrees:
sh
gh pr list --head "$BRANCH" --state merged --json number,url,mergedAt
gh issue view "$N" --json state # must be CLOSED
- Primary qualifier — a merged PR for the branch, and either no issue in the branch name or an issue that is already closed.
- Secondary, shown as its own group — no PR ever existed, but the branch is fully merged into the base (
git branch --merged "origin/$BASE"
; base resolved gitkit's way, never hard-coded ). Real for direct pushes, weaker evidence, so it gets its own section in the preview and is never bundled in with the primary rows.
A merged PR with an open issue is not a candidate. That's tracker drift; list it and route to
issuekit , which closes the issue, unblocks its dependents,
and tears the worktree down — doing the job properly instead of deleting the evidence behind the tracker's back.
2. Disqualify
These are hard skips. Each appears in the preview under skipped, with its reason, so nothing vanishes from the report silently:
- uncommitted changes or untracked files — is non-empty;
- unpushed commits, or a branch with no upstream at all;
- the main worktree, always;
- the worktree you're currently inside — never delete the floor you're standing on;
- live Orca terminals —
orca terminal list --worktree "branch:$BRANCH" --json
. Offer orca terminal stop --worktree "branch:$BRANCH"
as a separate, explicitly confirmed step; never stop someone's running process as a side effect of tidying.
A merged PR does not imply an empty worktree. Scratch files, a stashed experiment, a follow-up commit that never got pushed — none of those are in the PR, and all of them live there.
3. Preview and confirm
One table, all candidates, each with the evidence that qualified it (PR number, merge date, issue state) and its kind (git-native or Orca-native). Skips listed below it with reasons. Then a single question, naming the count:
Remove 6 workspaces (4 git-native, 2 Orca-native)? 3 more were skipped — see above.
One OK covers the batch. If the user wants a subset, take the subset; don't re-prompt row by row.
4. Remove, by kind
git-native → hand it to gitkit's teardown, which looks the worktree up by branch and removes it with native git:
sh
git -C "$REPO" worktree remove "$WT"
git -C "$REPO" branch -d "$BRANCH" # -d, never -D
Then
stop. Do not also call
— Orca drops a discovered entry by itself, and a second removal on a path that's already gone just produces a confusing error. If gitkit isn't installed, the two commands above are the whole of it.
Orca-native → Orca owns it, so Orca removes it:
sh
orca worktree rm --worktree "path:$WT" --run-hooks --json
This is the one place orcakit runs a vendor command that also performs the git removal, and the reason is specific: Orca created this checkout, has an archive hook and terminal sessions bound to it, and its
sequences all three. Removing it with git first orphans Orca's metadata and skips the hook. The exception is confined to workspaces Orca created — it never extends to a gitkit worktree.
Never . Every removal here is already gated on a clean tree and a merged PR; if git refuses anyway, that refusal is information. Report it and move to the next row.
5. Hand off
What changed — how many workspaces removed, by kind, and how many skipped. Name the skipped ones with reasons; a dirty worktree that survived is the single most important line in the report and goes first.
Where it landed — the paths that are gone and the branches deleted with them. Note that Orca's sidebar catches up within a few seconds, so an entry still visible right now isn't a failure.
Next — crown one:
- anything skipped as dirty → that outranks everything. Name the path and tell the user to go look; unlanded work in a stale worktree is what actually gets lost.
- tracker drift found → issuekit on those issues, otherwise plus a manual teardown.
- nothing left to clean → say so plainly and stop. The right next move is somewhere else entirely — statuskit to re-orient if it's installed, otherwise nothing at all. Sidebar hygiene is not a loop worth repeating.
Mode:
Stop Orca creating worktrees somewhere gitkit will never look. Orca's default is
~/orca/workspaces/<repo>/<name>
; gitkit's convention is
$WORKTREE_ROOT/<repo>/<branch>
(
unless the environment says otherwise). Two roots means every sweep has to classify by path forever.
sh
orca project setups --json # find the setup id for the repo
orca project setup-update --setup "$SETUP_ID" \
--worktree-base-path "$WORKTREE_ROOT/$(basename "$REPO")" --json
Verify rather than assume. The setups listing does not echo the base path back, and whether Orca appends the repo name to it is not documented anywhere. So confirm it empirically, once per repo, with a throwaway:
sh
orca worktree create --repo "path:$REPO" --name orcakit-align-check --no-parent --json
# read the path it reports:
# .../worktrees/<repo>/orcakit-align-check → correct
# .../worktrees/<repo>/<repo>/orcakit-… → doubled; re-set the base to "$WORKTREE_ROOT"
orca worktree rm --worktree "name:orcakit-align-check" --json
This is the one
in the skill, and it exists only to read back a path — confirm it with the user, and remove the throwaway in the same breath. If the check is declined or can't run, say the base path was set
but not verified; do not report a convergence you didn't observe.
Existing worktrees are untouched. Ones already under
keep working exactly as they are; git stores absolute paths, and moving them by hand needs
.
only changes where the
next one goes. Say that out loud, because "aligned" reads like "migrated" and it isn't.
Hand off
What changed — which repos had their base path set, and whether the throwaway check confirmed the resulting path.
Where it landed — the new base path per repo, and a reminder that existing workspaces stayed where they were.
Next — with one root in play,
is worth a run to see the whole set in one table. If nothing else is pending, stop; this is a one-time-per-repo setting, not a routine.
Notes
- orcakit is machine-local and always optional. No Orca on the box means no-op, and nothing else in the workflow may depend on it. gitkit, issuekit, and the rest never call it — they'd break on every machine without the app. It's a janitor you run, not a link in a chain.
- Worktree facts belong to gitkit. The default path convention appears here only as a declared portability fallback for machines without gitkit; everything else — branch naming, base-ref resolution, teardown rules — lives there, and any other copy of a gitkit fact here is the bug.
- Tracker facts belong to issuekit. orcakit reads issue and PR state to judge a workspace; it writes none of it.
- Destructive steps preview and confirm; read-only ones run straight through. never asks. previews a batch. previews a batch and takes one OK. confirms before it creates its throwaway.
- No shell available? Then you can't reach the CLI or . Reason from what the user gives you and print the exact commands as a codeblock for them to run — never report a workspace linked or removed that you could not perform.