Repository navigation
feat(api): pick, look up, run by id and install choices from other plugins - #2247
Conversation
The CLI imported a package without the modal: it analysed it, decided every choice and file, applied the import, stored the choices, synced their commands and saved. That body moves to importPackageUnattended, which takes the decisions as a function of whether each choice or file already exists. The CLI keeps its flags, its trust gate and its report, and imports stay queued.
…ugins
Another plugin that runs a QuickAdd choice had to ask its user to type
the choice's name, then run it by that name, which breaks when the
choice is renamed or shares its name. The API now lets it use
QuickAdd's own picker and keep the choice's id:
- pickChoice opens the picker QuickAdd: Run opens, in a pick mode that
resolves the Template, Capture or Macro the user picks and runs
nothing. Typing searches nested choices whatever Search nested
choices says. Escape rejects with QuickAdd's usual cancellation.
- getChoice looks a choice up by name or { id } and returns its id,
name, type and folder path, or null.
- executeChoice also takes { id }. A name still runs the first match.
- importPackage imports a package the plugin ships, with no review
modal. A choice whose id is already in the vault, and a file that
already exists, are skipped. It throws before changing anything for
an invalid package or one that runs code.
applyPackageImport decoded each file inside its write loop, so a package whose second file was not valid base64 wrote the first file and its folders before it failed. The modal, the CLI and the API all apply imports through it. It now decodes every file while it plans the writes, so an undecodable file stops the import before anything changes.
…place - main.ts and the API now find a choice through findChoiceWithPath, so a name or id resolves the same way in the API, URIs, commands and the CLI. getChoice walks the tree once. - importPackageUnattended runs the trust gate itself and returns needsReview instead of importing, so no caller can skip it. The CLI passes acknowledge=true through. - It asks whether a file exists at the validated path it will write, so a padded path cannot overwrite a file that importPackage must skip. - pickChoice reads the picked choice from the live tree, and executeChoice names a non-object ref as a name in its error.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Merge Risk: ⚪ Minimal · up to
|
Why
A plugin that runs a QuickAdd choice has to ask its user to type the choice's name, then run it by that name. A rename or a second choice with the same name breaks that without a word. PodNotes wants its users to pick a QuickAdd choice for timestamp captures from QuickAdd's own picker, keep the choice's id, and add a ready-made capture choice in one click. This adds the four API members that need.
What changed
pickChoice(options?)opens the QuickAdd: Run picker in a pick mode, through a newonPickoption onChoiceSuggester. It resolves{ id, name, type, path }for the picked Template, Capture, or Macro and runs nothing. Typing searches nested choices whatever Search nested choices says.getChoice(ref)looks a choice up by name or{ id }, andexecuteChoicenow also takes{ id }. Both use one lookup,findChoiceWithPath, whichmain.tsnow uses as well.importPackage(json)imports a package with no modal through the CLI's import body, moved toimportPackageUnattended, which now owns the trust gate too. It skips choice ids and files that already exist, and it refuses a package that needs review.Scope
master. The text to add at release is below.Tradeoffs
importPackagerefuses a package that runs code instead of installing it unreviewed. Plugins are trusted code, but a plugin that passes on someone else's package would skip the review that every other import path enforces.Blast Radius
The modal, the CLI, and the API share the changed import code.
applyPackageImportnow decodes every file before the first write, so a package with an undecodable file fails before it changes anything, where it used to write the files before it. The CLI keeps its flags, gate, and report, and its tests pass unchanged. URI, command, and CLI lookups by name or id keep the same first-match order.Verification
pnpm run lint,pnpm run build,pnpm run test(505 files), and the docs build withscripts/check-links.pypass.tests/e2e/api-pick-choice.test.tsruns on the isolated runner. It callspickChoicefromeval, types, and presses Enter. The promise resolves the nested choice, no prompt opens, and the note stays empty. After a rename,executeChoice({ id })runs the choice. The full e2e suite passes: 100 files and 468 tests, with 2 files and 24 tests skipped because they need Templater.{ id }looked up by name, andimportPackagethat overwrites an existing id.Release impact: a
feat, so the next minor. No settings or storage change. The name form ofexecuteChoicebehaves as before.Pick mode with Search nested choices off, after typing "time":
Docs at release
docs/src/content/docs/docs/QuickAddAPI.mdIn the module table at the top, replace the Choice execution row and add a Packages row after it:
In Choice Execution, replace the
executeChoiceheading, its Cancellation line, and itschoiceNameparameter line, and add one example line:### `executeChoice(choice: string | { id: string }, variables?: {[key: string]: any}, options?: { date?: string | Date }): Promise<void>`Then add these two sections after the
executeChoicesection, beforeapplyTemplateToActiveFile:Add this section after Choice Execution, before Formatting:
docs/src/content/docs/docs/Advanced/APIOverview.mdAdd this row to Common tasks, after Run another choice from a script:
Note
Medium Risk
Touches shared package import (modal/CLI/API), choice picker execution paths, and settings mutation; import semantics changed to fail before writes on decode errors.
Overview
Adds QuickAdd API support for other plugins to pick, look up, and run choices by stable id, and to install bundled packages without the settings modal.
Choice execution & lookup:
executeChoicenow accepts a name or{ id }via sharedfindChoice/findChoiceWithPath(also used bymain.tslookup). NewgetChoice(ref)returns{ id, name, type, path }without running. NewpickChoice()opens the Run QuickAdd picker in pick mode (ChoiceSuggester.onPick): it returns a summary and does not execute; nested search is forced while picking.Packages: New
importPackage(json)skips existing choice ids and vault files, refuses packages that need code review, and delegates toimportPackageUnattended—the same path the CLI now uses after removing duplicated import logic.applyPackageImportdecodes all asset content before any write so a bad file aborts with no partial changes.Tests cover API import/pick, id-based execution, CLI behavior, and an e2e pick-then-run-by-id flow.
Reviewed by Cursor Bugbot for commit 4804745. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Add
getChoice,pickChoiceandimportPackageto the QuickAdd APIAdds new public API methods so other plugins can work with choices without accessing settings.
getChoicereturns a summary by name or id,pickChoiceopens the choice picker without executing the choice, andimportPackageimports a package without user interaction.findChoiceWithPathin choiceUtils.ts; it resolves by name (first match) or stable id and returns the folder path.executeChoicein quickAddApi.ts now accepts aChoiceRefand reports missing choices without throwing.ChoiceSuggesterin choiceSuggester.ts gains anonPickcallback for pick mode; folder rows still navigate and nested choices stay searchable even when nested search is disabled.importPackageUnattendedin packageImportUnattended.ts; imports run serially, skip existing choice ids and asset paths, and reject packages that require code review.applyPackageImportin packageImportService.ts now decodes all assets before any write, so a decode failure aborts with no partial import;QuickAdd.getChoiceByNameis replaced by the shared traversal, so out-of-tree callers using that method must switch to the new API.Macroscope summarized 4804745.
Summary by CodeRabbit