feat(palette): COLLIE_COMMANDS β the Agent-commands palette becomes yours - #109
Closed
enieuwy wants to merge 1 commit into
Closed
feat(palette): COLLIE_COMMANDS β the Agent-commands palette becomes yours#109enieuwy wants to merge 1 commit into
enieuwy wants to merge 1 commit into
Conversation
β¦ours The shipped catalogs in web/src/lib/agent-commands.ts are limited, by their own sourcing rules, to commands every user of that harness has. A command registered by a PLUGIN or by the user exists on one machine only, so it can never be vouched for there β and it is exactly the command worth one tap: omp's /fork-in-herdr, a Claude Code custom command, your own /deploy. COLLIE_COMMANDS declares them, per pane or globally: COLLIE_COMMANDS=omp:/fork-in-herdr=Fork this conversation into a herdr tab A pane your rows address shows YOUR rows and nothing else. A pane none of them address keeps the shipped catalog; declare nothing and every pane is as shipped. Replacing rather than merging is the point: this surface is a handful of one-thumb shortcuts, and the value of the shipped ten is that someone chose them β a list half-chosen by you and half-guessed for you is worse than either whole one. Nothing is lost by it. The agent's own `/` completion renders in the mirrored pane, live and complete, which no list on this side could stay: the set is registered at runtime inside the agent process, so there is nothing to enumerate from out here. Discovery belongs to the harness; this pane is for the handful you reach for without looking. Four rules, all so that adding a row narrows or clarifies, never widens: 1. Your list is the palette, on the panes it addresses. 2. A pane you did not address keeps its catalog. 3. Danger is inherited, not reset β naming a shipped command takes over its description but keeps its confirm, so re-wording a session wipe cannot make it one-tap. 4. The narrowest scope wins and one /name is one row: exact beats family beats unscoped. A family scope is only ever the catalog's own name for the family, so `claude-local:` addresses claude-local and not the family the catalog lookup would fold it onto. The grammar reuses the existing comma-separated list style, so a description cannot contain a comma and an arg hint cannot contain `=`; both are documented in .env.example and pinned by tests. Per CLAUDE.md, a fork PR leaves the version files and CHANGELOG alone.
Owner
|
Thanks @enieuwy β second one this week, and again the hard part arrived already done: the four resolution rules, the store, and the replace-not-merge argument all landed as you wrote them (your commit is cherry-picked into #112, authorship preserved). What changed on top: rows now live in a |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The gap
The shipped catalogs in
web/src/lib/agent-commands.tsare limited, by their own sourcing rules, to commands every user of that harness has. A command registered by a plugin or by the user exists on one machine only, so it can never be vouched for there β and it's exactly the command worth one tap: omp's/fork-in-herdr, a Claude Code custom command, your own/deploy.Today the only way to send one from Collie is to type it, or fight terminal pass-through.
The change
A pane your rows address shows your rows and nothing else. A pane none of them address keeps the shipped catalog. Declare nothing and every pane is exactly as shipped.
Replacing rather than merging is the point. This surface is a handful of one-thumb shortcuts, and the value of the shipped ten is that someone chose them β a list half-chosen by the operator and half-guessed for them is worse than either whole one.
Nothing is lost by replacing it. The agent's own
/completion already renders in the mirrored pane, live and complete. I checked what the alternative would even be: the set is registered at runtime inside the agent process (omp has nocommands --json, no on-disk list;fork-ingets its two by callingregisterCommandat load), so there is nothing to enumerate from out here. Scraping the popup back throughpane.readyields four wrapped visible rows and a scrollbar, per-agent grammar, in the user's live session. Discovery belongs to the harness. This pane is for the handful you reach for without looking.Rules
Four, all chosen so adding a row narrows or clarifies, never widens:
omp:says nothing about your claude panes. (One unscoped row addresses everything, and.env.examplesays so.)/nameis one row. Exact beats family beats unscoped, so/deploy=Everywhere,omp:/deploy=On ompis one button whose text depends on the pane (two would also collide on the palette'skey={c.command}). A family scope is only ever the catalog's own name for the family:claude:reaches aclaude-codepane because CLAUDE's shipped rows do, butclaude-local:addressesclaude-localonly β folding an arbitrary operator string through the lookup ladder would widen a scope written to be narrow.Grammar reuses the existing comma-separated list style (
envList), so a description can't contain a comma and an arg hint can't contain=. Both documented in.env.exampleand pinned by tests. An empty scope (:/wipe) is rejected rather than read as "every agent" β the operator was reaching for something narrower than that, so the failure should be the narrow one.Shape
bridge/config.tsβparseOperatorCommands(), pure and unit-tested withoutprocess.envbridge/server.tsβ served on/api/config; the key is omitted entirely when unset, so an operator who never touched this ships the same payload as beforeweb/src/lib/operator-commands.tsβ module-state store +useSyncExternalStore, thelib/server-build.tsidiom. One successful read cached per page load; a failed read isn't cached, and the kick lives in an effect rather than the render body so a refusal can't become a request per 1.5s tickweb/src/lib/agent-commands.tsβcommandsFor(agent, mine)resolves the four rulesTests
24 new, all green. Parser grammar (scope, arg hints, duplicates, empty scope, the
=and comma costs), the store's caching/retry/concurrency contract, the four resolution rules, and the rendered palette (replacement, one-button dedupe, inherited confirm).bunx tsc --noEmitclean both packages. Bridge suite 569/569. Web suite: 2117 pass / 48 fail β the same 48 fail onmain(localStorage-less environment: theme, dash/display prefs, draft persistence), none in touched files.Verified end-to-end on a live bridge:
/api/configcarries the rows, and the screenshot above is the real sheet on an omp pane β "Search 2 commandsβ¦", shipped catalog gone.Versioning
Per
CLAUDE.md, fork PRs leave the version files and CHANGELOG alone. Suggested entry:Note
Built on top of #108 conceptually but textually independent β no overlapping files, either order merges clean.