-
Notifications
You must be signed in to change notification settings - Fork 0
Roadmap
Rewritten 2026-07-02 as the single authoritative plan. It consolidates the previous roadmap (2026-06-05) with a new feature push: menu bar command, per-server CPU/memory, and Raycast AI tools. Changes from the previous version: the Shopify start/restart section shipped in the 2026-06-10 release and now lives in Shopify Integration; the one-line "menu-bar mode" idea grew into Phase 1; kill-undo and default-folder kept their scoped notes as Phases 4 and 6; per-project script override moved to "Later, deliberately".
Current phase: 2 (CPU and memory per server). Phase 1.5 shipped 2026-07-26; the next update starts here, with Retry-on-a-failed-row and Phase 4's undo as the two smaller items queued alongside it. Update this line and the checkboxes as phases complete.
Project-first dev server control everywhere Raycast reaches: a dashboard for working with servers, a menu bar for glancing at and poking them without opening anything, and AI tools for doing it in plain language. Every surface reuses the same headless core (src/servers.ts), so a feature lands once and shows up everywhere.
The plan relies on these; doc paths are from developers.raycast.com. Re-verify if the API version moves past ^1.104.
-
Menu bar lifecycle:
mode: "menu-bar"commands are not long-lived. Raycast loads the command when the user opens the menu (or on background refresh), waits forisLoadingto become false, then unloads it. Every menu open re-runs the command, so data can be fetched fresh on open;Cachegives an instant stale paint first. (/api-reference/menu-bar-commands) -
Background refresh: add
"interval": "1m"to the command manifest. The manifest page documents a 1m minimum while the background-refresh page says 10s; use 1m for store safety. Raycast auto-adds enable/disable preferences, prevents overlapping runs, and timing is inexact on battery. (/information/lifecycle/background-refresh,/information/manifest#command-properties) -
Immediate refresh pattern: after a mutation elsewhere,
launchCommand({ name: "menubar", type: LaunchType.Background })is the docs-recommended way to refresh a menu bar command right away. (/api-reference/command) -
Menu items:
MenuBarExtra.ItemgetsonAction(arbitrary code:open(), shell, toasts,launchCommand), an Option-keyalternateitem, shortcuts, icons, subtitles. No ActionPanel, no search, no forms, no pagination inside the menu; sections and sparing submenus are the only structure. Items with identical titles at the same level can misfireonAction; disambiguate titles. (/api-reference/menu-bar-commands) -
Root-search subtitle:
updateCommandMetadata({ subtitle: "3 running" })persists a subtitle under the command in root search. (/api-reference/command) -
AI tools: declare
toolsin package.json; each maps tosrc/tools/<name>.tswhose default export is the tool function. Input is a single typed object; JSDoc on the type fields is what the AI reads.Tool.Confirmation<Input>(exported asconfirmation) runs before the tool;Action.Style.Destructivefor kills. Instructions and evals go inai.yaml(recommended over inline package.json); evals run withnpx ray evalsand double as suggested prompts. Tools shipped in API 1.93.0. Users invoke them by @-mentioning the extension in Quick AI / AI Chat, which requires the user to have Raycast AI access. (/ai/learn-core-concepts-of-ai-extensions,/api-reference/tool,/ai/write-evals-for-your-ai-extension) -
Frecency:
useFrecencySorting(data, { namespace, key })from @raycast/utils returns sorted data plusvisitItem; rankings persist between runs and can be shared across commands via the same namespace and keys. It is a React hook, so it works in view and menu-bar commands but not in plain tool functions. (/utilities/react-hooks/usefrecencysorting) -
No native process stats: no Raycast API exposes per-PID CPU/memory. Extending our existing
pscall with extra columns is the documented-adjacent path. List accessories support colored tags/text with tooltips for display. (/api-reference/utilities,/api-reference/user-interface/list) -
State sharing:
Cache(10 MB default, string data) is shared across commands in the extension and is the right home for server snapshots;LocalStoragestays for durable small config (recents already live there). (/api-reference/cache,/api-reference/storage) - Menu bar commands are macOS-only; irrelevant today (extension is macOS-only) but it pins the Windows port even further out.
-
F1: Snapshot cache. Whichever surface runs
fetchServerswrites the result (plus a timestamp) toCacheunder one key. The menu bar paints instantly from the snapshot, then fetches fresh. The dashboard keeps polling as today and just gains the write. Consumers: Phases 1, 2, 3. -
F2: Stats in the scan.
fetchServers's existingpsinvocation gainsppid,%cpu, andrsscolumns; per-server stats roll up the listening process plus its descendants (dev servers are process trees: package manager → node → workers). One scan, no extra spawns. Consumers: Phases 2, 3 (and menu bar subtitles later if wanted). -
F3: Frecency namespace. One
useFrecencySortingnamespace keyed by canonical cwd, shared by the menu bar start section and the Start picker.visitItemfires on every successful start regardless of surface. Consumers: Phases 1, 5.
src/servers.ts already has zero @raycast/api imports (verified 2026-07-02), so menu bar onAction handlers and AI tools can call fetchServers / killServer / restartServer / startDevServer directly. Keep it that way.
Each phase ships independently: code + README + CHANGELOG in one commit series, npm run lint clean.
- Phase 1: Menu bar command (with F1 and F3). Shipped in the 2026-07-02 release, then polished in the 2026-07-09 release: project-name-first rows, real favicons, and pruning of deleted-folder recents. F1 (snapshot cache) lives in
src/snapshot.ts; F3 (frecency) is wired insrc/menubar.tsxand still awaits Phase 5 to reach the Start picker. Decisions documented in Menu Bar.- Manifest:
"mode": "menu-bar","interval": "1m". Title shows the running-server count (icon only when zero, so an empty menu bar stays quiet). Tooltip "Dev Servers". - Menu: one section per project (matching the dashboard's grouping), one submenu per server titled
name · portwith framework icon; inside: Open in Browser, Open Localhost URL, Restart, Kill, Copy URL, Copy Port, Open in Editor / Terminal (when the preference is set). A final section starts recent projects, frecency-sorted (F3), plus "Open Dashboard" and "Start Dev Server…" items thatlaunchCommandthe existing view commands. - Interaction decisions: kills and restarts run directly in
onActionagainstservers.ts(no window opens), then background-relaunch the menu bar command to refresh itself and callupdateCommandMetadatafor the root-search subtitle. Starts launch the dashboard with the existinglaunchContextspawn flow instead of spawning inline: the dashboard owns the spawn state machine, bind-polling, and failure toasts (see Spawn Flow), and the user gets visible progress for an action whose outcome takes seconds. - Guard:
launchCommandto a disabled command throws; wrap the dashboard→menubar refresh poke in try/catch so users who disable the menu bar item lose nothing. - Done when: menu shows live grouped servers with working kill/restart/open/copy; count stays fresh via interval + pokes; recents start from the menu; lint clean; README + CHANGELOG updated.
- Manifest:
- Phase 1.5: Pending start rows (specced 2026-07-25, shipped on
pending-start-rows2026-07-26)- Spawn feedback moved from the toast into the list: every start gets a spinning row that hands off to the real row on bind, or turns red in place with the remedies (View Startup Log, Copy Fix Command, Open in Terminal, Dismiss) when the 15s watchdog fires.
- Grew past its spec during testing, all deliberate: restarts got rows too (a non-goal in the spec, taken on once the machinery existed), rows live in their project's section rather than a separate
Startingone, list order became newest-first, and the spinner replaced the progress ring because a ring that vanishes at 20% reads as a cancelled job. Reasoning in Spawn Flow and UI Conventions. - Testing it also turned up four defects worth naming, all fixed on the branch: reparented
workerdhelpers posing as servers, those helpers surviving kills and piling up, orphaned helpers ignoring SIGTERM, and the auto-open bug that turned out to be a preference silently reset by a scope move (Auto-Open Investigation, Raycast Quirks). - Spec: Spec: Pending Start Rows.
- Phase 2: CPU and memory per server (with F2)
- Dashboard rows gain a stats accessory:
4.2% · 182 MBas secondary text, switching to an orange tag past attention thresholds (exact numbers tunable in the spec; start around 150% CPU / 1.5 GB RSS). Tooltip breaks down the process tree. Toggleable row-accessory preference like the existing ones, default on. - Menu bar server submenus show the same numbers as subtitles for free once F2 is in the snapshot.
- Non-goal: historical graphs. This is a "which one do I kill" glance, not monitoring.
- Done when: a busy Vite build visibly climbs, numbers match Activity Monitor within a poll interval, lint clean, docs updated.
- Dashboard rows gain a stats accessory:
- Phase 3: AI extension tools
- Tools:
list-servers(no confirmation; returns project, framework, port, URL, uptime, stats),kill-server(destructive confirmation),restart-server(confirmation),start-server(resolves a project by name against recents + running set; confirmation shows what will be spawned where). All import fromservers.ts/recents.tsonly. -
ai.yaml: instructions telling the AI to calllist-serversfirst when a reference is ambiguous ("kill the storefront one"), plus evals with mocks for each tool (npx ray evalspassing is part of done). - Start feedback: tools return text, so
start-serverreports "spawned, watching for the port" and a headless bind-wait (reusing the dashboard's timeout constants) upgrades the reply to the bound URL or a log-path failure within 15s. - Done when: "@dev-servers what's running?", "kill the vite server", and "start my shopify theme" work end-to-end in AI Chat; evals pass; store metadata (tool titles/descriptions) reads well.
- Tools:
- Phase 4: Undo a kill (
⌘Z) (rescoped 2026-07-26; supersedes the 2026-06-05 "kill toast with undo")- Killed the wrong server,
⌘Zbrings it back. The undo respawns throughstartDevServer(cwd), so it is a fresh process rather than a true undo, which for a dev server is the same thing a restart gives you. - The undo lives on the dashboard, not on a toast. The 2026-06-05 scope hung it off a toast's primary action, which this update's own experience argues against: a toast's actions die with the toast, and with the Raycast window closed it degrades to an actionless HUD (the exact reasoning that moved start failures onto rows, see Spawn Flow). Right after a kill the user is looking at the dashboard, so that is where the affordance belongs.
- Shape: an Undo Kill action in the row action panel, bound to
⌘Z, present only while an undo is available, titled with what it would bring back (Undo Kill (Explan),Undo Kill (3 servers)). The panel makes it discoverable and the chord makes it fast, which a bare hidden shortcut would not. A brief toast on kill (Killed Explan) can carry the hint, but must not be the only way to reach it. - Cheaper now than when it was first scoped: the pending-row machinery from Phase 1.5 means an undone kill comes back as a visible
Starting…row in its project's section, with the failure path already handled. - Settled already: if the user started a replacement in the same cwd meanwhile, spawn alongside rather than killing it (no magic); bulk undo does not re-confirm; the kill handlers in
src/index.tsxalready snapshot their targets throughmutate's optimistic update, which is the data an undo needs. -
Verify before designing around it:
⌘Zis not on Raycast's documented reserved list (⌘K,⌘W,⌘Escare) andzis a validKeyboard.KeyEquivalent, but the docs do not promise the list is complete. Bind it inray devand confirm the action actually fires before building anything on top; fall back to⌘⇧Zif it is swallowed. - Open questions: how long an undo stays available (a timer, or until the next kill supersedes it, or until the command unloads); whether the stack is one level (the last kill action, which may cover N servers) or deeper; and whether Kill All is undoable at all, or too big to hand to a single chord.
- Killed the wrong server,
- Phase 5: Frecency in the Start picker
- Swap the picker's last-seen ordering for the shared F3 frecency sort (falling back to last-seen for never-started entries via
sortUnvisited). Small; depends on Phase 1 landing F3.
- Swap the picker's last-seen ordering for the shared F3 frecency sort (falling back to last-seen for never-started entries via
- Phase 6: Default folder for "Choose Folder…" (scoped 2026-06-05, unchanged)
-
defaultStartFolderdirectory preference on the start command; injectdefault location (POSIX file "<path>")into the existingchoose folderAppleScript inpickFolderNative, escaping\and".statSync-check first and omit on any doubt: a stale preference must never break the picker. Scope: changes the dialog's starting directory only; never auto-treats the folder as a project.
-
The branch closed 2026-07-26 with the user calling the update complete. Everything it set out to do shipped, plus the Finder-selection row and a quality pass over the whole extension. One item was deliberately left for the next update.
-
Retry on a failed row. A failed row explains the problem and hands over the fix, then makes the user leave the dashboard to apply it. Deliberately deferred in the original spec because the spawn flow is once-per-mount behind the
spawnFlowFiredref, but the watch and watchdog effects both key onspawnState.phase, so setting the phase back tospawningre-arms them. No dependency-array surgery needed.-
Open design question, settle before building: what happens when a second retry starts while the first is still in flight. The phase machine holds one batch, so a naive second retry clobbers the first's
expectingmap. Options are to merge into the existing map (the newcomer then inherits the incumbent's deadline, which is wrong but harmless), to refuse while one is in flight, or to give each pending entry its own deadline, which is the clean answer and the largest change. - Worth pairing with Phase 4's undo when it comes up: both are "the dashboard lets you correct what just happened without leaving it", and both re-enter the spawn flow from outside its once-only path.
-
Open design question, settle before building: what happens when a second retry starts while the first is still in flight. The phase machine holds one batch, so a naive second retry clobbers the first's
Small, user-reported, none started. Raise them at the start of the next session; item 3 is a discussion before it is a change.
- The Kill icon does not read as a trash can. The user's words: it looks like "a strange container with a cork on top". The icon choice is right; the fill is what breaks it. Confirm the surface first, because three different glyphs are in play: the dashboard's per-server Kill uses
Icon.Stop(a rounded square, not a bin at all), project and all-server kills useIcon.Trash, and the menu bar uses the bundledassets/menubar-kill.svgtinted solid red, which is the likeliest culprit since a flat fill merges the lid into the body. - Menu bar fallback icons should take their color from the favicon, not the framework badge. With no favicon available,
serverIconrenderstintedMenuIcon("server", toolColor(server.tool)), so every SvelteKit project is badge orange and every project of a given framework looks alike. The glyph is fine; the color should be the dominant color of that project's real favicon, which the dashboard already fetches and stores in recents (favicon/faviconRaster). Derive it once at fetch time, persist it beside the icon, and tint with it wherever the image itself cannot render. Point is fast visual scanning: color becomes identity per project rather than per framework. - Show the branch only when it disambiguates. Hide it when it is
main; show it,mainincluded, only when the project has more than one worktree, since otherwise every server of a project shares a branch and the tag says nothing. Priority is the menu bar, which prints the branch unconditionally in the submenu title (menubarTitle) and offers no way to turn it off. The dashboard already has theshowBranchcommand preference (default on) and stays as-is pending that discussion; note its section header and per-row tooltip already carry the worktree, so the tag is doing less work there than it looks.
- Non-Node runtime detection (Deno, Rails, Django, PHP, Go, …): the biggest audience expansion and the biggest detection matrix. Promote once Phases 1–3 ship and store feedback shows demand; design would extend the two-path detection in Framework Detection.
- Kill Port command: "kill whatever holds 3000" as a no-view command with an argument. Deliberately Port Manager's turf (see README positioning); promote if users keep asking.
-
Per-project script override (from 2026-06-05 roadmap): pin a
package.jsonscript per canonical cwd for whenpickDevScriptguesses wrong. Needs a small pref store; promote on real user misses. See Script Picker. - Shopify follow-ups: open theme editor / copy preview link actions. See Shopify Integration.
-
Per-project Quicklinks:
Action.CreateQuicklinkfor pinned one-keystroke starts. Cheap; promote on request. - QR code for the network URL: scan-to-open on a phone. Needs a bundled QR generator (offline); promote when mobile-testing users ask.
- Tunnel sharing (cloudflared / ngrok): expose a dev server publicly from the action panel. New dependency + auth surface; promote on demand.
- Browser-tab awareness: focus an already-open localhost tab instead of opening a duplicate, via the Browser Extension API (requires the user to install the Raycast Browser Extension). Spark stage.
- Windows port: platform primitives stay isolated (see Process Detection); menu bar commands are macOS-only, which raises the port's cost further. Promote when Raycast-on-Windows matures.
- Assumptions to re-verify before building on them:
servers.tshas no@raycast/apiimports; the dashboard owns all spawn lifecycle (Spawn Flow); recents live in LocalStorage (Recents); API is ^1.104. - Verification:
npm run lintandray build -e dist; manual check viaray dev. No test suite exists. - Every shipped phase updates README, CHANGELOG, this page's checkbox and "Current phase" line, and any wiki page whose claims it changes.
- Docs live in this wiki only; the extension repo must stay documentation-free apart from README/CHANGELOG (it is PR'd to the Raycast Store verbatim). Session context for AI-assisted work:
.claude/PROJECT_BRIEF.mdin the extension repo (gitignored). - Formatting: never the U+2014 long dash; the U+2013 short dash is fine, including ranges like 200–500ms.