feat(orchestrator): create_skill action + wire per-project watchers (§14.3)

- domain: OrchestratorCommand::CreateSkill with optional scope field
  (defaults to Project, case-insensitive, UnknownScope on bad value)
- application: dispatch CreateSkill through the CreateSkill use case
- app-tauri: build OrchestratorService and start/stop per-project request
  watchers on open/create/close_project (ensure/stop_orchestrator_watch)
- tests: domain validation, service dispatch, infra watcher e2e,
  app-tauri wiring lifecycle (idempotent, isolated, stop)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 13:06:41 +02:00
parent b9fd2fb925
commit d11eaaa8c0
13 changed files with 619 additions and 52 deletions

View File

@ -113,7 +113,14 @@ interface LeafViewProps {
}
function LeafView({ id, session, agent, cwd, vm, parentSplit, projectId }: LeafViewProps) {
const canMerge = parentSplit !== null && parentSplit.siblings > 1;
// A cell can be closed only when it lives inside a (binary) split: closing it
// collapses the parent split, keeping the *sibling*. Splits are always binary
// in this model (a split wraps a leaf into a 2-child container), so the kept
// child is simply the other index. Guarding on `siblings === 2` keeps the
// operation correct (merge keeps exactly one child) and avoids ever dropping
// the wrong terminal. The root cell (no parent split) cannot be closed.
const canClose = parentSplit !== null && parentSplit.siblings === 2;
const siblingIndex = parentSplit ? (parentSplit.index === 0 ? 1 : 0) : 0;
const { agent: agentGateway } = useGateways();
// Load the project's agents for the dropdown.
@ -159,7 +166,10 @@ function LeafView({ id, session, agent, cwd, vm, parentSplit, projectId }: LeafV
style={{
position: "absolute",
top: 2,
right: 2,
// Clear the xterm viewport scrollbar (rendered flush against the right
// edge, ~15px wide). Without this offset the right-most control — the
// close button — sits *behind* the scrollbar and is hard to click.
right: 16,
zIndex: 2,
display: "flex",
gap: 2,
@ -208,16 +218,14 @@ function LeafView({ id, session, agent, cwd, vm, parentSplit, projectId }: LeafV
>
</button>
{canMerge && (
{canClose && (
<button
type="button"
title="Merge: keep this cell, drop its siblings"
aria-label={`merge ${id}`}
onClick={() =>
void vm.merge(parentSplit.container, parentSplit.index)
}
title="Close this terminal"
aria-label={`close ${id}`}
onClick={() => void vm.merge(parentSplit.container, siblingIndex)}
>
</button>
)}
</div>