feat: add developer-gated Prompt Shortcuts [risk:high] - #575
Conversation
Add scoped private/shared shortcut catalogs, lazy LoroDoc bodies, durable local publication recovery, settings authoring and Storybook prototypes. Slash invocation and composer send integration remain follow-up work. Model: gpt-6-astra
…sk:high] Settings now reads as the same surface as Agent Roles and MCP: one dialog header/scroll/footer editor built from the shared `form-primitives` grammar, Radix selects for the three scope axes, a Switch for sharing with its visibility warning, and the shared catalog row. Adds scope pills, per-row publication state, a read-only view for another member's shared Shortcut, and disabled mention kinds that name the scope axes they still need. Also carries this branch's local-first storage work: a separate local ledger, write intents and immutable publication jobs, so a pending publication stays durable without ever blocking Save or Delete. Slash invocation, the variable argument tray and composer send integration remain follow-up work. Model: claude-opus-5
Scope moves out of its own section and sits inline above the prompt input, each axis naming itself in its trigger, with the explanation folded into the prompt hint. The description field says what it is and where it shows, a variable's default sits beside its name instead of under it, and the prompt box starts shorter. Model: claude-opus-5
A default value is usually a few words, so the field starts at a single row and tracks its content instead of reserving two. `AutoGrowTextarea` joins the shared settings primitives, and re-measures on width changes because wrapping is what decides the row count. Model: claude-opus-5
Add independent Slash discovery, cancellable snapshot selection, inline invocation parameters, isolated draft recovery, and ordered prompt compilation with expand-and-edit support. Model: gpt-6
Reuse the shared composer mention portal within the nearest modal and constrain desktop and mobile positioning to its bounds. Cover real Shortcut creation dialogs in Storybook and modal selection in regression tests. Model: gpt-6
…sk:medium]
The tray now reads as the settings variable editor it mirrors: the `!{name}`
token beside its value, growing from one row, a missing value tinting its own
token instead of adding an asterisk. Desktop keeps a section header with the
dismiss action; mobile keeps the sheet with a full-width Done.
"Expand and edit" is removed. It existed to change a template's body, which
Settings already does — durably, for every invocation — and it paid for that
with a whole draft vocabulary of its own: generated unresolved markers, literal
ranges that stop substituted text being rescanned, their persistence and undo
rules, and an extra send-time check for leftover markers. A chip is atomic
again, so all of that goes with it.
Model: claude-opus-5
The catalog row was four left-aligned lines against an empty right half. It is now two: identity reads down the left, and what the author set — scope, variable count — plus what is happening to it sit against the right edge. Both halves wrap rather than switching on a viewport breakpoint, because settings render in a panel far narrower than the window. Status copy shortens to phrases that fit that column. The tray drops the "fill these variables" line between the prompt and the fields and sits against the invocation instead. The chip's own badge and the tinted token already name what is missing; a third sentence only pushed the fields away from the thing they belong to. Model: claude-opus-5
A Shortcut carries an optional `emoji`, normalized on the way in and projected into the index so the list can show it without loading a body. It replaces the `/` glyph in the catalog tile; unset renders the default rather than an empty slot. The emoji control itself moves out of the Agent Role editor into `settings/emoji-field.tsx`, so the two editors cannot grow two of them. Also drops the `!N` badge painted over an invocation chip — the chip already turns red and the tray names the values — and with it the mention primitive's `badge` slot, which had no other caller. While a parameter tray is open the composer stops reserving its blank writing rows: those rows are room for writing, and reserving them above the tray left a band of empty box between the `/command` and the fields it belongs to. Model: claude-opus-5
…k:medium] Four things the composer and the catalog were getting wrong: Clearing the prompt to empty dropped focus. The reset remounts the mention tree to re-arm the hydrators, which replaces the textarea node; restoring focus was conditional on the caller having passed a ref, and the settings template editor passes none. The component keeps its own ref now and hands the node to the forwarded one as well. The catalog reported background publication. A local save is already durable and the runtime retries on its own, so neither state is something a reader can act on from a row. The only status left is the one its author must repair: a reference that no longer fits the saved scope. The `/` menu shows each Shortcut's own emoji, the way an Agent Role's row does. A chip now carries the values that were filled into it (`shortcutChipText`, applied on tray close), capped by code points so a truncation cannot split a surrogate pair. A draft of three identical `/review` chips was not something anyone could check before sending. Both forms of the text stay valid, so a draft stored before its values still restores. Model: claude-opus-5
A Shortcut is a Prompt and a scope now, nothing else. `!{name}` is ordinary
text: stored, expanded and sent exactly as written.
Gone from the model: the `variables` field, its schema and byte limits, the
index's `variableCount`, placeholder parsing and escaping, value injection, the
`missing_variables` error code, and `updateShortcutInvocation` (which existed
only to migrate values across a reload). `ShortcutInvocation` is `{ id,
snapshot }`, so `expandShortcut` is a straight lowering of the snapshot's text
and mentions.
Gone from the UI: the Settings variables section, the composer's parameter tray
and bottom sheet, their send gate, the chip's missing-count colouring, and the
value label a chip carried. What remains of the composer bridge is
`shortcut-invocation-status.tsx` — why an inserted chip cannot be sent, which is
about live context rather than anything the caller types.
The invocation schema is deliberately not strict: a draft stored while values
existed still restores, minus the ignored key.
Model: claude-opus-5
Default the opt-in off and share the developer-mode gate across settings navigation, direct panel access, mention selection and the workspace runtime. Preserve saved data and cover toggle transitions and late selection results. Model: gpt-6
Clear the Shortcut checkpoint before successful landing navigation, fenced by composer mount and draft identity so late session acceptance cannot delete or navigate a replacement draft. Add deterministic recovery regressions and satisfy the branch formatting gate. Model: gpt-6
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Preserve composer submission ownership and Shortcut preparation while integrating main. Clear retired accepted Shortcut checkpoints only when their submitted version remains current. Model: gpt-6
Model: gpt-6
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 362a08f32b
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| await this.put({ | ||
| entry, | ||
| published: current?.operation ? current.published : entry, | ||
| operation: 'delete', | ||
| deleted: true, |
There was a problem hiding this comment.
Revoke the published body on delete
When an author deletes a successfully published Shortcut from the authoring device, entry is the local working projection, while preparePublication() uploaded a fresh body and stored that distinct projection in current.published. Persisting the caller's working entry here therefore makes publishRecord() call revokeShortcut with a bodyDocId that was never activated; a host enforcing the request's body/visibility fence cannot revoke it, so a shared Shortcut remains accessible and every retry repeats the wrong request. Enqueue the published projection for remote deletion while retaining the local deletion state.
AGENTS.md reference: packages/shared/src/prompt-shortcuts/AGENTS.md:L22-L25
Useful? React with 👍 / 👎.
| await this.recover(); | ||
| const existing = this.publication(id); | ||
| const current = this.get(id); | ||
| if (existing && !current?.deleted) return existing; |
There was a problem hiding this comment.
Preserve an existing publication job through deletion
If Delete is selected while a save/activation job is pending or its reply was lost, current.deleted makes this condition skip the durable existing job; the method then overwrites the publication row with the delete record below. Consequently PromptShortcutRuntime.flush() never sees the obsolete save job and never calls settle(), losing the only identity capable of fencing a staged or already-active body and potentially leaving the supposedly deleted publication or its quota reservation behind. Preserve and settle the existing job before replacing it with deletion.
AGENTS.md reference: packages/shared/src/prompt-shortcuts/AGENTS.md:L36-L37
Useful? React with 👍 / 👎.
Remove the superseded design prototype, unused editable annotations and replacement API, test-only composer compiler and publication helper. Keep real component stories and move stale/duplicate invocation assertions onto the actual send path. Consolidate repeated documentation and preserve locale ordering. Ablations retain semantic Undo and stale-preparation fences: negative controls fail when either protection is removed. Full workspace typecheck and test:ci pass; installed-workspace lint, formatting, docs, i18n and boundary checks pass. Model: gpt-6
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a4e4e11477
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| <StuckConnectionBannerContainer /> | ||
| {workspaceReady ? <DesktopSettingsModal /> : null} | ||
| </WorkspaceRuntimeShell> | ||
| <PromptShortcutProvider enabled={workspaceReady}> |
There was a problem hiding this comment.
Gate the Shortcut runtime on the workspace window owner
When Prompt Shortcuts are enabled in Electron, every primary or auxiliary window mounts this provider even though owner already identifies the single workspace window permitted to run singleton background work. Opening a second window for the same workspace therefore creates another PromptShortcutRuntime over the same account/workspace IndexedDB and lets both instances drain the same publication outbox, causing competing ledger writes and duplicate or conflicting cloud publication attempts. Mount the runtime only for the workspace owner or provide a genuinely shared single owner.
AGENTS.md reference: packages/components/src/providers/AGENTS.md:L41-L47
Useful? React with 👍 / 👎.
| getCandidates: (term, limit) => | ||
| selectPromptShortcutCandidates({ entries, context, loading }, term, t, limit).map( | ||
| (candidate) => { |
There was a problem hiding this comment.
Wire live dependency resolution into Shortcut discovery
For every Shortcut containing a file, Skill, Role, issue, or PR, this production call omits resolveDependency, so selectPromptShortcutCandidates uses unverifiedShortcutDependency and always marks the entry unknown; non-exact matches disappear and exact matches are disabled. The selection, status, and compilation paths likewise never provide a resolver, so users can author reference-bearing Shortcuts but can never insert or send them even when all dependencies are available. Supply the live dependency resolver throughout this composer path.
AGENTS.md reference: packages/components/src/components/mentions/AGENTS.md:L114-L116
Useful? React with 👍 / 👎.
…edium] Remove dedicated Shortcut suites, stories and feature-only additions to shared fixtures. Retain existing general composer and mention tests, and document the temporary coverage scope without changing production behavior. Model: gpt-6
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3aca38b3f9
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if (trigger === '/') { | ||
| const directCategories = categories.filter((category) => category.directTrigger === '/'); |
There was a problem hiding this comment.
Hide ACP commands from inline slash menus
When Prompt Shortcuts are enabled and the composer already contains ordinary text, / remains active for inline Shortcut insertion, but this branch also includes every ACP command category. Selecting one then replaces only the slash-to-caret query, producing text such as explain this /review; the agent receives an ordinary message instead of the advertised whole-prompt command. Filter ACP commands out whenever the slash trigger is not the entire prompt.
AGENTS.md reference: packages/components/src/components/mentions/AGENTS.md:L117-L118
Useful? React with 👍 / 👎.
| export function promptShortcutDatabaseName(workspaceId: string, userId: string): string { | ||
| return `${PROMPT_SHORTCUT_DATA_PREFIX}${encodeURIComponent(workspaceId)}:${encodeURIComponent(userId)}`; |
There was a problem hiding this comment.
Register Shortcut databases for hard-reset fallback
On engines where indexedDB.databases() is unavailable or throws, a crash-screen hard reset cannot discover this new account/workspace-specific database name. clearAllLodyLocalData() only falls back to static known names, and the later RuntimeProvider extras contain only the workspace repo and cursor databases, so saved Prompt bodies and outbox data survive the promised full wipe/sign-out. Record these dynamic database names for the hard-reset fallback or pass them explicitly before identity storage is cleared.
AGENTS.md reference: packages/components/src/lib/AGENTS.md:L34-L37
Useful? React with 👍 / 👎.
Risk: 🔴 high | Confidence: medium — durable drafts and private/shared sync; local regression and adversarial reviews pass, live gateway and complete production-build validation remain open.
Prompt Shortcuts save scoped private or workspace-shared Prompts and insert frozen snapshots inline from
/. They default off behind Settings → About → Developer mode → Beta features. Templates have no variables or expand-and-edit UI.This is a temporary developer-gated feature. Cleanup removed the obsolete prototype,
unused replacement/annotation APIs, duplicate compiler, dedicated Shortcut test suites
and Storybook stories. Existing general composer/mention coverage remains.
PR additions decreased from 14,009 to 6,635 lines; scope decreased from 122 to 84 files.
Validation
65 retained general composer/mention tests, formatting, docs and i18n passed.
removed; these are historical evidence, not retained feature-specific coverage.
are unchanged by the test/story cleanup.
Remaining limits