Skip to content

Commit ae3255a

Browse files
arul28claude
andauthored
Mac Vm Updates (#324)
* feat(macos-vm): dedicated /vm tab with lifecycle, onboarding, runtime install, and credentials Move the macOS VM workflow out of the inline lane panel into a dedicated /vm tab. Add lifecycle/menu UI (MacVmPage, VmLifecycleMenu, FirstBootCard, CredentialsPromptDialog, PhaseStepper), a credentials store, ADE runtime bootstrap for guest VMs, and recovery for stale guest-created worktree dirs. Wire 20+ macos-vm adeActions and CLI subcommands; update docs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ship: iter 1 — address Greptile, CodeRabbit, Copilot review - runtimeBootstrap stub: mark install state failed instead of advancing to runtime_ready - runtimeBootstrap: resolveSshpassBinary() walks Homebrew/MacPorts/PATH - runtimeBootstrap: try/finally cleans staged install scripts - credentialsStore: SIGTERM → SIGKILL 500ms; settle promise on timeout - macosVmService: .part.url sidecar so partial download recovery is URL-keyed - macosVmService: /usr/bin/rsync absolute path - registerIpc: hasUserSelectedProject guard on project-root fallback - registerIpc: requireMacosVmEnabledInProduction gates VM IPC in packaged builds - registerIpc: typed MacosVmExtensionService interface (removed unknown casts) - adeActions/registry: drop detachLane (it lives on laneService, not macosVmService) - preload: route new VM IPC through callRemoteProjectRuntimeActionOr - App: /vm joins serializeProjectRoute allowedRoots - LanesPage: auth-confirm gate on status fetch + createVmRuntimeAvailable - LanesPage: fresh submit recheck mirrors full availability predicate - LanesPage: open-in-Work CTA navigates to /project - ade-cli: readVmTarget consumes positional lane arg - FirstBootCard: empty-steps guard - MacVmPage: shellSingleQuote() for safe sudo rm -rf cleanup string - PhaseStepper: import CSSProperties directly - macosVmRuntimeReadiness: validate vm.currentPhase bounds - shared/types/macosVm: confirm: true literal on wipe args Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ship: iter 2 — runtimeBootstrap defaultRunner SIGTERM → SIGKILL Address Greptile P1: defaultRunner's per-phase timeout sent SIGTERM but waited unconditionally for the exit event. If SSH/scp blocked in a kernel wait, SIGTERM was ignored and the promise never settled. Mirror the credentialsStore pattern: settle promise on timeout, schedule a SIGKILL 500ms after SIGTERM. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ship: iter 3 — fix SIGKILL regression, drop VNC password from argv, tighten action allowlist - credentialsStore + runtimeBootstrap: prior fix cleared killTimer in finish() unconditionally, defeating the 500 ms SIGKILL fallback. Pass clearKill=false from the timeout path so the SIGKILL still fires if SIGTERM is ignored. (CodeRabbit major, credentialsStore.ts:118 + runtimeBootstrap.ts:130) - macosVmService.openExternalVncClient: vnc:// URL no longer embeds the credential — it appeared in argv to /usr/bin/open (ps aux visible) for the brief lifetime of the child. macOS Screen Sharing prompts when no Keychain entry exists; matches the SIGSAFE pattern used for the SSH path with sshpass -e. (Greptile P1 security, macosVmService.ts:1487) - adeActions/registry macos_vm allowlist: removed setCredentials and getDisplaySession from the generic agent-callable surface. setCredentials mutates Keychain-backed VM credentials; getDisplaySession returns the live VNC password. Typed CLI / IPC paths (ade macos-vm set-credentials, ade macos-vm display-session) still work and run under CTO-level auth. (CodeRabbit major, registry.ts:786) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ship: iter 4 — extend RUNTIME_ACTION_CHANNEL to agent-callable VM ops Greptile P1: registry.ts allowlists macos_vm.restart/wipe/installRuntime (plus focusWindow/click/selectPoint/typeText) for agent invocation, but the RUNTIME_ACTION_CHANNEL map only covered provision/start/stop/delete/ captureScreenshot. Agent calls through localRuntimeCallAction / remoteRuntimeCallAction were falling back to the 30s default — restart sequences stop+start (up to 122 min), wipe drives deleteVm (~2 min), and installRuntime SSHes into the guest. All three were racing-lose to 30s. Map each agent-callable action to its IPC channel so the existing per-channel budgets apply. Add the matching cases to the switch (restart/installRuntime share start's 120-min budget; wipe shares delete's 2-min; UI actions share captureScreenshot's 60s). Extend tests to cover restart / wipe / installRuntime via the runtime channel and a UI action via the remote runtime. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ship: iter 5 — match wipe/delete IPC budget to lume; long-lived VM cache TTL Greptile P1 (ipcTimeouts.ts:54): macosVmWipe was capped at 2 min IPC budget, but wipe() and deleteVm() both call runLume("delete", 10 * 60_000) internally. The IPC's Promise.race in main.ts was firing first — renderer saw "timed out" while the underlying lume process kept running, leaving the store record unchanged. Lift both wipe and delete IPC budgets to 10 min so the renderer sees the real outcome. Update test to match. Greptile P1 (laneLaunchContext.ts:207): VM_LAUNCH_CONTEXT_TTL_MS was 5_000 ms, but the only refresh path is the placement-changed handler that fires once when a lane attaches to a VM. agentChatService and ptyService call the sync resolveLaneLaunchContext on every turn — after 5 s the cache returns null and throws VmNotReadyError even when the VM is fine. The cached SSH target (IP + username + vmName) is stable for the running VM lifetime; lifecycle events explicitly invalidate the cache. Bump TTL to 30 min so the cache stays warm across normal agent sessions. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 999ac55 commit ae3255a

77 files changed

Lines changed: 11915 additions & 1391 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agents/skills/plan/SKILL.md

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
---
2+
name: plan
3+
description: Deliberate a feature or change in three locked rounds — functional requirements, then UI design with wireframes, then extras/quirks/out-of-the-box ideas. Each round asks the user clarifying questions via AskUserQuestion before proceeding. New functional scope at any point cascade-restarts the new piece through all three rounds and merges it into the locked plan. Use when the user invokes `/plan`, when the user is in plan mode and asks for a design/spec/feature breakdown, or when a non-trivial change needs structured deliberation before implementation.
4+
metadata:
5+
author: ade
6+
version: "1.0"
7+
---
8+
9+
# /plan — Three-Round Locked Deliberation
10+
11+
A feature plan has three layers: **what it does**, **how it looks**, and **the delightful extras**. Cross-talk between them produces sloppy plans. This skill enforces a strict order: lock functional, then lock UI, then add extras. New functional scope discovered at any point cascades back through all three rounds *for that new piece only*, then merges into the locked plan.
12+
13+
## Activation
14+
15+
Activate when **any** of:
16+
- The user invokes `/plan` (with or without extra context).
17+
- The user is in plan mode and asks for a design, spec, breakdown, or feature plan.
18+
- A non-trivial change request would benefit from structured deliberation (multi-component features, UX-sensitive work, anything spanning >2 files).
19+
20+
## Pre-flight: plan mode gate
21+
22+
Before Round 1, verify plan mode is active. If not:
23+
24+
> This skill is meant to run in plan mode (read-only deliberation). Enter plan mode (Shift+Tab cycles to it) and re-invoke `/plan <your context>`.
25+
26+
Then stop. Do not proceed in edit/full-auto modes — the deliberation contract assumes no files will be touched mid-plan.
27+
28+
## State you must track
29+
30+
Maintain these in your head (or in scratch) across the whole skill:
31+
32+
- **LockedFunctional** — bullet list of functional requirements confirmed so far.
33+
- **LockedUI** — bullet list of UI decisions confirmed so far (with wireframe sketches).
34+
- **LockedExtras** — list of delightful extras confirmed.
35+
- **CurrentRound** — 1 (Functional), 2 (UI), or 3 (Extras).
36+
- **PendingNewPieces** — queue of new functional requirements detected mid-flight that need their own cascade.
37+
38+
When a cascade fires, you snapshot CurrentRound, run the cascade for the new piece, merge results back into the Locked* sets, then resume from the snapshotted round.
39+
40+
## Round 1 — Functional Requirements
41+
42+
Silently deliberate on what the user asked for. Pull out:
43+
- Core capabilities the feature must have.
44+
- Boundaries (what it does *not* do).
45+
- Inputs, outputs, edge cases that change behavior.
46+
- Ambiguities where multiple interpretations exist.
47+
48+
Then call **AskUserQuestion** with 1–4 questions that resolve the meaningful ambiguities. Each option should describe a concrete interpretation with its trade-off. Avoid asking the user to write prose — frame everything as concrete choices.
49+
50+
### Tool reference: AskUserQuestion
51+
52+
Use `AskUserQuestion(questions)` to gather structured choices during Round 1, Round 2, Round 3, and any cascade. `questions` is an array of question objects:
53+
54+
- `id`: stable snake_case identifier.
55+
- `title`: short user-facing label.
56+
- `text`: the actual question.
57+
- `multiSelect`: optional boolean for menus like LockedExtras.
58+
- `allowOther`: optional boolean that permits an "Other" selection.
59+
- `allowAnnotation`: optional boolean that permits free-text annotation alongside a selected option.
60+
- `options`: array of `{ id, label, tradeoffs, description?, preview? }`.
61+
62+
The return value is keyed by question id and contains `selectedOptionIds`, optional `otherText`, and optional `annotations` / `freeText`. Treat selected option ids as the locked choice. Treat `otherText` as a new option authored by the user; if it changes behavior, update **LockedFunctional** and trigger the **Cascade rule**. Treat annotations as refinements to the selected option; if an annotation adds behavior rather than clarifying wording or presentation, it also cascades.
63+
64+
When the user responds:
65+
- Treat selected options as additions to **LockedFunctional**.
66+
- If the user's free-text (the "Other" reply or annotation) adds new scope → see **Cascade rule** below.
67+
- If the user only clarifies an existing item → update **LockedFunctional** and proceed.
68+
69+
Once questions are answered, **do not ask for explicit confirmation**. Silently move to Round 2.
70+
71+
## Round 2 — UI Design
72+
73+
Now deliberate on how the feature presents to the user. Generate 1–3 candidate UI directions. Round 2 candidates may be full-layout wireframes when the whole surface is up for decision, or component-level candidates when only one area is ambiguous. Be explicit which level each candidate represents.
74+
75+
Call **AskUserQuestion** with options that carry **markdown wireframe previews** in the `preview` field. Wireframes are ASCII boxes:
76+
77+
```
78+
┌─────────────────────────────────┐
79+
│ Header · filter ▾ · +new │
80+
├─────────────────────────────────┤
81+
│ ▣ item ⋯ │
82+
│ ▢ item ⋯ │
83+
│ ▢ item ⋯ │
84+
└─────────────────────────────────┘
85+
```
86+
87+
Keep each preview readable in a chat-width column (~70 chars). Show real labels, real density, real affordances — not lorem ipsum.
88+
89+
Cover the meaningful axes (layout type, hierarchy of actions, empty/loading/error states, dense vs roomy) in **at most 4 questions total**. Don't waste a question on a decision that has one obvious answer.
90+
91+
When the user responds:
92+
- Selected wireframes/options → **LockedUI**.
93+
- If candidates were component-level, merge the selected components into **LockedUI** as integration decisions. The Final output still needs one composed/merged inline wireframe derived from **LockedFunctional**, **LockedUI**, and any selected **LockedExtras**.
94+
- Free-text that implies new functional behavior (e.g. "add export button" implies an export *flow*) → see **Cascade rule**.
95+
- Pure UI refinements stay in Round 2.
96+
97+
Silently proceed to Round 3.
98+
99+
## Round 3 — Extras, Quirks, Out-of-the-Box
100+
101+
Now generate 4–8 candidate ideas the user *didn't* ask for but might love: micro-interactions, keyboard shortcuts, empty-state delight, power-user features, animations, accessibility wins, surprise affordances, anti-aesthetics-of-AI touches (favor warmth and specificity over generic monochrome polish).
102+
103+
Call **AskUserQuestion** with `multiSelect: true` for the menu of extras. Each option's `description` should be one sentence explaining the idea and one sentence on the cost/benefit.
104+
105+
Selected items → **LockedExtras**.
106+
107+
If the user adds a new functional idea in free-text → **Cascade rule**.
108+
109+
## Cascade rule (the core contract)
110+
111+
A **new functional requirement** is any input — from the user OR self-detected from your own proposals — that adds, removes, or changes scope of the feature. Not a UI refinement. Not an extras pick. *New behavior the system must perform.*
112+
113+
When detected:
114+
115+
1. **Snapshot** the current round.
116+
2. **Queue** the new piece in **PendingNewPieces** with a one-line description.
117+
3. **Announce briefly**: "New functional piece detected: <X>. Cascading it through R1→R2→R3 before resuming Round <snapshot>."
118+
4. **Run a focused mini-cascade for that piece only**:
119+
- **R1 for new piece**: AskUserQuestion scoped to just the new piece's functional ambiguities.
120+
- **R2 for new piece**: AskUserQuestion with wireframes scoped to just how this new piece integrates into the already-locked UI (do NOT redesign locked layouts — show how the new piece slots in).
121+
- **R3 for new piece**: AskUserQuestion with extras *for this piece only*.
122+
5. **Merge** the results into the appropriate Locked* sets.
123+
6. **Resume** from the snapshotted round.
124+
125+
Multiple cascades may stack. Process them depth-first; never lose the snapshot.
126+
127+
### Self-detecting new functional scope
128+
129+
Before sending a UI option or an extra, ask yourself silently: *does this option require behavior that isn't already in LockedFunctional?* If yes, you have a choice:
130+
- Drop the option (cleanest).
131+
- Or, if it's genuinely a great idea, **fire the cascade yourself** before sending it. Don't sneak new functional scope into UI/extras questions.
132+
133+
## Final output
134+
135+
After Round 3 completes with no pending cascades:
136+
137+
1. Print a tight consolidated plan in chat with three sections:
138+
- **Functional** — bulleted LockedFunctional.
139+
- **UI** — bulleted LockedUI with one inline wireframe of the final composed layout.
140+
- **Extras** — bulleted LockedExtras.
141+
Keep total under ~50 lines. No filler.
142+
2. Call **ExitPlanMode** to formally request approval.
143+
144+
### Tool reference: ExitPlanMode
145+
146+
Use `ExitPlanMode(): void` immediately after the Final output. It has no parameters and returns no value. Calling it signals that **LockedFunctional**, **LockedUI**, and **LockedExtras** are complete, including the final composed wireframe, and asks the user to approve leaving plan mode. Do not call it before all pending cascades are processed.
147+
148+
## Anti-patterns (do not do)
149+
150+
- Asking the user to "describe what they want" in prose. Always frame as concrete options.
151+
- Sending more than 4 questions in a single AskUserQuestion call (tool max).
152+
- Slipping new functional behavior into UI or Extras rounds without cascading.
153+
- Asking explicit "lock in? yes/no" questions between rounds — proceed silently unless interrupted.
154+
- Re-litigating LockedFunctional during R2 or R3 unless a cascade explicitly opens that piece.
155+
- Generic AI aesthetics in wireframes — no `font-mono` aesthetic apologies, no centered-everything, no purple gradients in mocked copy. Be specific and warm.
156+
- Long preamble. The user invoked `/plan`; jump to Round 1.
157+
158+
## Minimal example flow
159+
160+
User: `/plan a markdown note app with tags`
161+
162+
You: silent deliberation → AskUserQuestion (R1):
163+
- Q1: "How should tags be created?" (inline `#tag` parsing / explicit tag field / both)
164+
- Q2: "Search scope when filtering by tag?" (notes containing tag / notes tagged-only / both modes)
165+
- Q3: "Multi-user or single-user?"
166+
167+
User selects + adds "and they sync to iCloud" (new functional scope).
168+
169+
You: "New piece detected: iCloud sync. Cascading."
170+
→ R1-for-sync: conflict resolution? offline-first?
171+
→ R2-for-sync: sync-status indicator placement?
172+
→ R3-for-sync: optimistic UI? merge-conflict modal?
173+
Merge → resume Round 1.
174+
175+
… and so on through R2 and R3 of the main plan, then ExitPlanMode.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
name: "source-command-audit"
3+
description: "Audit recent work — trace error paths, probe edge cases, fix any bugs or gaps found"
4+
---
5+
6+
# source-command-audit
7+
8+
Use this skill when the user asks to run the migrated source command `audit`.
9+
10+
## Command Template
11+
12+
Audit the work you just did in this session. Don't stop at "it compiles" or "it looks right" — actively go hunting for what's wrong.
13+
14+
1. **Retrace the changes.** List every file you touched. For each, re-read the final state (not just the diff you remember) so you see what actually lives there now.
15+
16+
2. **Trace every error path.** For each changed code path: what happens on empty / nil / malformed input? When an upstream caller passes something unexpected? When a dependency throws or times out? Walk the failure branches, not just the happy path.
17+
18+
3. **Hunt edge cases.** Off-by-ones, empty collections, unicode, concurrency, first-run vs. repeat-run, reduce-motion / accessibility, different device/viewport sizes, streaming vs. terminal states, cancellation, partial failure. Pick the categories that actually apply to what you changed and work through them.
19+
20+
4. **Check the surrounding contract.** Did the change break any callers, tests, types, styling, or invariants elsewhere? Grep for references to anything you removed or renamed and confirm.
21+
22+
5. **Fix what you find.** For each real bug or gap, make the fix directly. For anything genuinely ambiguous, call it out rather than guessing.
23+
24+
6. **Report.** End with a short list: what you checked, what you fixed, and anything you deliberately left alone (and why).
25+
26+
Be honest — if the work was already solid, say so in one line. Don't manufacture busywork.

apps/ade-cli/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,10 @@ ade --socket app-control launch --command "npm run dev" --text
264264
ade --socket browser open http://localhost:5173 --new-tab --text
265265
ade --socket macos-vm status --lane lane-id --text
266266
ade --socket macos-vm start --lane lane-id --create --no-display --text
267+
ade --socket macos-vm storage --text
268+
ade --socket macos-vm get-credentials --vm-name vm-name --text
269+
ade --socket macos-vm display-session --lane lane-id --text
270+
ade --socket macos-vm detach --lane lane-id --text
267271
ade --socket update status --text
268272
ade --socket update check --text
269273
ade --socket update install --text

0 commit comments

Comments
 (0)