Skip to content

feat(template): run a template from the configured folder without a per-file choice (#1023)#1343

Open
chhoumann wants to merge 1 commit into
masterfrom
chhoumann/1023-run-template-from-folder
Open

feat(template): run a template from the configured folder without a per-file choice (#1023)#1343
chhoumann wants to merge 1 commit into
masterfrom
chhoumann/1023-run-template-from-folder

Conversation

@chhoumann

@chhoumann chhoumann commented Jun 13, 2026

Copy link
Copy Markdown
Owner

Closes #1023.

What

Adds New note from template — run a template straight from your configured template folder, prompting for the new note's name, without maintaining a Template choice per file.

Three entry points, all backed by one reusable core (runTemplateFromFolder) that builds a throwaway, non-persisted TemplateChoice and runs it through the existing ChoiceExecutorTemplateChoiceEngine:

  1. Command New note from template (palette / hotkey / ribbon).
  2. Run QuickAdd launcher row — a single 📄 New note from template… row at the top of the picker, gated on a configured template folder + the new Show "New note from template" in the launcher toggle (Settings → Choice Picker).
  3. CLI quickadd:run-template path=<vault-path> value-value=<name> — the scriptable form.

No new persisted choice type (rejected as too much surface); no migration.

Why this shape

  • Reuses getTemplateFiles() (the listing seam) + TemplateChoiceEngine (the run seam) — the only thing missing was the thin join.
  • The ephemeral choice uses fileNameFormat = { enabled: true, format: {{value}} } on purpose: runtime is identical to enabled:false, but collectChoiceRequirements only scans the file-name format when enabled, so this makes the note-name visible to the non-interactive CLI guard and the one-page form (otherwise the CLI would hang / report a false success).
  • When no template folder is configured, the interactive flow shows a notice and opens settings instead of flooding a whole-vault picker.

Review

Designed and reviewed before coding (ultracode multi-lens + 2 opposite-model adversarial reviewers), then the implementation was reviewed the same way. Findings fixed:

  • Blocker — a blank/empty note name no longer reports a false ok:true with no note created (the collector consumes a provided-but-empty value, so run-template rejects a blank name up front for non-interactive runs).
  • CLI path now resolves via getTemplateFile (accepts Templates/Daily and /Templates/Daily.md like the engine); path= is reserved so it can't leak as a template variable; up-front file-existence check.

Known, documented limitations (inherited from the Template engine, shared with quickadd:run)

  • A name collision on the target note still prompts interactively (file-exists isn't a pre-collected input).
  • With an active editor selection, {{value}} uses the selection as the name (intrinsic {{VALUE}} behavior).
  • Canvas/base templates: choosing an "append" mode on an existing canvas/base target can corrupt its JSON.

Verified

  • pnpm build + pnpm lint clean; 2042 tests pass (new unit coverage: ephemeral-choice defaults, real-collector integration + negative control, picker resolve/cancel, launcher-row injection/gating/dispatch/exclusion, CLI envelopes incl. blank-name + extensionless paths).
  • Dev-vault e2e (obsidian CLI): happy path creates the note with {{value}} resolved; blank/empty/omitted name → ok:false + missingFlags; launcher row appears/dispatches; require-config notice + opens settings; extensionless & leading-slash paths resolve.

Release impact

feat → minor bump. New setting showTemplateFolderInLauncher (default true; only surfaces the row when a template folder is configured). main.js/styles.css are gitignored and regenerated by CI.

Summary by CodeRabbit

  • New Features
    • Added “New note from template” to create notes from your configured template files, including an optional top-level launcher row.
    • Added quickadd:run-template CLI support to create notes from a specified vault-relative template path (supports vars JSON and interactive/non-interactive modes).
  • Settings
    • Added a “Choice Picker” toggle: “Show ‘New note from template’ in the launcher” (enabled by default).
  • Documentation
    • Updated Advanced CLI docs with quickadd:run-template syntax and failure/cancellation behavior, plus guidance on when to use this vs a full template choice.

@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a "New note from template" feature enabling note creation from configured template files. A new runTemplateFromFolder engine module manages interactive template picker and non-interactive execution via ephemeral TemplateChoice objects. The feature is exposed through a ChoiceSuggester launcher row, a dedicated Obsidian command, the ribbon handler, a new quickadd:run-template CLI command, a settings toggle, and accompanying documentation. Refactors shared CLI execution to support outcome-based Template/Capture result handling.

Changes

New note from template feature

Layer / File(s) Summary
Choice executor outcome support
src/IChoiceExecutor.ts
Optional executeWithOutcome? method added to IChoiceExecutor for template/capture choices to return structured ChoiceOutcome (success, error, cancelled) with type imports for ITemplateChoice and ICaptureChoice.
Settings contract and command labels
src/settings.ts, src/commandLabels.ts, src/main.commandLabels.test.ts, src/quickAddSettingsTab.ts, src/quickAddSettingsTab.test.ts
showTemplateFolderInLauncher: boolean added to QuickAddSettings (default true) and DEFAULT_SETTINGS; runTemplateFromFolder label added to QUICK_ADD_COMMAND_LABELS; settings tab gains toggle; tests updated.
Engine: Template choice factory and folder configuration
src/engine/runTemplateFromFolder.ts
New module: createFolderTemplateChoice builds ephemeral TemplateChoice with fileNameFormat and openFile enabled; hasConfiguredTemplateFolders gates interactive mode; helper functions render picker rows and open settings; pickTemplateFile presents sorted template files via GenericSuggester.
Engine: Template execution flow
src/engine/runTemplateFromFolder.ts, src/engine/runTemplateFromFolder.test.ts
runTemplateFromFolder supports interactive mode (requires configured folders, shows picker) and non-interactive mode (templatePath bypasses picker). Executes choice through supplied executor, handles cancellation and errors. Comprehensive tests cover choice construction, folder detection, picker behavior, cancellation, and requirements integration.
CLI: quickadd:run-template handler
src/cli/registerQuickAddCliHandlers.ts, src/cli/registerQuickAddCliHandlers.test.ts
Introduces RUN_TEMPLATE_FLAGS and RESERVED_RUN_TEMPLATE_PARAMS; runTemplateHandler validates path, resolves file, enforces non-interactive value presence. Shared runResolvedChoice tail refactored to handle variable extraction, missing-input validation, choice execution with useOutcome support, and JSON envelope serialization. Tests cover path resolution, name validation, executor outcomes, and variable handling.
Launcher: ChoiceSuggester sentinel row
src/gui/suggesters/choiceSuggester.ts, src/gui/suggesters/choiceSuggester.test.ts
Exports RUN_TEMPLATE_FROM_FOLDER_ID sentinel; extends ChoiceSuggesterOptions with includeTemplateFolderRow flag; conditionally prepends action row when enabled and folders configured; filters sentinel from nested search; selection intercepts and routes to runTemplateFromFolder. Tests verify injection rules, omission cases, and dispatch behavior.
Plugin command registration
src/main.ts
runTemplateFromFolder command registered with ChoiceExecutor; runQuickAdd command and ribbon icon updated to pass { includeTemplateFolderRow: true } to ChoiceSuggester.Open.
Documentation
docs/docs/Advanced/CLI.md, docs/docs/Choices/TemplateChoice.md, docs/docs/Settings.md
quickadd:run-template CLI reference added with path resolution and naming behavior; tip callout added to TemplateChoice docs; Settings.md documents showTemplateFolderInLauncher toggle.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant ChoiceSuggester
  participant runTemplateFromFolder
  participant GenericSuggester
  participant ChoiceExecutor

  rect rgba(100, 149, 237, 0.5)
    note over User,ChoiceSuggester: Interactive launcher
    User->>ChoiceSuggester: Opens Run QuickAdd (includeTemplateFolderRow: true)
    ChoiceSuggester-->>User: Displays "New note from template" sentinel row
    User->>ChoiceSuggester: Selects template launcher row
    ChoiceSuggester->>runTemplateFromFolder: runTemplateFromFolder(app, plugin, {choiceExecutor})
    runTemplateFromFolder->>GenericSuggester: Suggest(templateFiles, renderTemplateRow)
    GenericSuggester-->>runTemplateFromFolder: User selects TFile
    runTemplateFromFolder->>ChoiceExecutor: execute(createFolderTemplateChoice(templatePath))
    ChoiceExecutor-->>User: Note created in default location
  end

  rect rgba(60, 179, 113, 0.5)
    note over User,ChoiceExecutor: CLI (non-interactive)
    User->>runTemplateFromFolder: quickadd:run-template path=templates/note.md value-value=MyNote
    runTemplateFromFolder->>runTemplateFromFolder: resolve path, enforce value, build choice
    runTemplateFromFolder->>ChoiceExecutor: executeWithOutcome(ephemeral TemplateChoice)
    ChoiceExecutor-->>User: {ok:true, file: "MyNote.md", duration: ...}
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • chhoumann/quickadd#1129: Both PRs extend src/cli/registerQuickAddCliHandlers.ts by adding new CLI command handling and refactoring the shared execution/error-envelope flow used by quickadd:run.
  • chhoumann/quickadd#1339: Both PRs use the same execution-outcome plumbing for Template/Capture through IChoiceExecutor.executeWithOutcome and structured ChoiceOutcome.
  • chhoumann/quickadd#1325: Main PR's template-folder infrastructure and getTemplateFile resolution build directly on multi-template-folder support introduced in that PR.

Suggested labels

released

🐇 A template awaits in the vault, so neat,
With one little command, a new note complete!
The launcher glows with a sentinel row,
And quickadd:run-template puts on quite a show.
No choice needed—just folders and flair,
The rabbit hops in and creates notes with care! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main feature being added: enabling users to run a template from a configured folder without per-file choices.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chhoumann/1023-run-template-from-folder

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 13, 2026

Copy link
Copy Markdown

Deploying quickadd with  Cloudflare Pages  Cloudflare Pages

Latest commit: 4620c53
Status: ✅  Deploy successful!
Preview URL: https://9f350943.quickadd.pages.dev
Branch Preview URL: https://chhoumann-1023-run-template.quickadd.pages.dev

View logs

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e4bd85da1c

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cli/registerQuickAddCliHandlers.ts Outdated
// front for non-interactive runs (ui mode can still prompt). Headless runs have
// no editor selection, so {{value}} comes solely from this variable.
if (!isTruthy(params.ui)) {
const variables = extractVariables(params, RESERVED_RUN_TEMPLATE_PARAMS);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Return JSON errors for malformed run-template vars

When quickadd:run-template is invoked non-interactively with a valid path but malformed vars= JSON, this precheck calls extractVariables before entering runResolvedChoice's try/catch, so parseVarsJson rejects the handler instead of returning the usual {ok:false, error: ...} CLI envelope. This differs from quickadd:run and from run-template ui, and can surface an unhandled CLI failure for a documented input path; wrap this parse in the same error-envelope handling or move it into the guarded common path.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — fixed in cb84d6d. runTemplateHandler is now wrapped in a try/catch that returns the standard {ok:false, error} envelope, so malformed vars= JSON in the up-front note-name check surfaces like quickadd:run / run-template ui instead of rejecting the handler. Added a regression test (returns a JSON error envelope for malformed vars).

@chhoumann chhoumann force-pushed the chhoumann/1023-run-template-from-folder branch 2 times, most recently from 2218229 to cb84d6d Compare June 13, 2026 23:49
…er-file choice (#1023)

Adds 'New note from template': a command + a gated Run QuickAdd launcher row + a quickadd:run-template CLI seam that create a note from any template in the configured template folder, prompting for the note name — no dedicated Template choice required.

- runTemplateFromFolder engine builds a throwaway TemplateChoice (fileNameFormat enabled with VALUE_SYNTAX so the note-name requirement is visible to preflight/CLI/one-page) and runs it through the existing ChoiceExecutor/TemplateChoiceEngine.
- Requires a configured template folder (notice + opens settings) rather than flooding a whole-vault picker; launcher row is gated on a configured folder + the showTemplateFolderInLauncher toggle.
- CLI: shared runResolvedChoice; resolve the path via getTemplateFile (accepts leading-slash/extensionless like the engine); reserve path= so it can't leak as a variable; up-front file-existence check and blank-note-name rejection for an honest non-interactive envelope.
- Docs: CLI.md (quickadd:run-template), Settings.md (toggle), TemplateChoice.md (discoverability).
@chhoumann chhoumann force-pushed the chhoumann/1023-run-template-from-folder branch from cb84d6d to 4620c53 Compare June 13, 2026 23:58
@chhoumann

Copy link
Copy Markdown
Owner Author

Post-review hardening (adversarial re-review): quickadd:run-template now reports the real execution outcome via executeWithOutcome (the channel #1070 added) instead of trusting that a resolved void execute() means success. The Template engine swallows runtime failures (empty/missing file name — including the ui=true + blank-name path — failing inline scripts, write errors), which previously could surface as ok:true with no note created. Now: success → ok:true (+ created file path), swallowed failure → ok:false, cancellation → ok:false, aborted:true. quickadd:run keeps its legacy path unchanged. Verified live (ui=true value-value=ok:false; success returns the file path) and with new unit tests.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/cli/registerQuickAddCliHandlers.test.ts (1)

419-436: ⚡ Quick win

Add explicit coverage for leading-slash template paths.

This block validates extensionless input, but the stated CLI contract also includes leading-slash paths (for example /Templates/Daily.md). Please add a companion case to prevent regressions in path normalization.

Suggested test addition
 it("accepts extensionless template paths the engine would resolve", async () => {
 	const { plugin, handlers } = createPlugin([]);
 	// Engine resolver appends .md; the CLI must accept "Templates/Daily" too.
 	withTemplateFile(plugin, "Templates/Daily.md");
 	registerQuickAddCliHandlers(plugin);
 	const run = handlers.find((h) => h.command === "quickadd:run-template");

 	const payload = JSON.parse(
 		String(
 			await run!.handler({ path: "Templates/Daily", "value-value": "Note" }),
 		),
 	);
 	expect(payload.ok).toBe(true);
 	const executed = (
 		executors[0].executeWithOutcome as ReturnType<typeof vi.fn>
 	).mock.calls[0][0];
 	expect(executed.templatePath).toBe("Templates/Daily.md");
 });
+
+it("accepts leading-slash template paths", async () => {
+	const { plugin, handlers } = createPlugin([]);
+	withTemplateFile(plugin, "Templates/Daily.md");
+	registerQuickAddCliHandlers(plugin);
+	const run = handlers.find((h) => h.command === "quickadd:run-template");
+
+	const payload = JSON.parse(
+		String(
+			await run!.handler({ path: "/Templates/Daily.md", "value-value": "Note" }),
+		),
+	);
+	expect(payload.ok).toBe(true);
+	const executed = (
+		executors[0].executeWithOutcome as ReturnType<typeof vi.fn>
+	).mock.calls[0][0];
+	expect(executed.templatePath).toBe("Templates/Daily.md");
+});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cli/registerQuickAddCliHandlers.test.ts` around lines 419 - 436, Add a
new test case to validate that leading-slash template paths (such as
"/Templates/Daily.md") are properly normalized by the CLI handler. Create a
companion test similar to the existing "accepts extensionless template paths the
engine would resolve" test: use withTemplateFile to register the template file,
call registerQuickAddCliHandlers to set up handlers, find the handler with
command "quickadd:run-template", invoke the handler with a leading-slash path
like "/Templates/Daily", and assert that the executed templatePath is normalized
to the correct form without the leading slash (such as "Templates/Daily.md").
This ensures path normalization handles both extensionless and leading-slash
input formats as part of the CLI contract.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/cli/registerQuickAddCliHandlers.test.ts`:
- Around line 419-436: Add a new test case to validate that leading-slash
template paths (such as "/Templates/Daily.md") are properly normalized by the
CLI handler. Create a companion test similar to the existing "accepts
extensionless template paths the engine would resolve" test: use
withTemplateFile to register the template file, call registerQuickAddCliHandlers
to set up handlers, find the handler with command "quickadd:run-template",
invoke the handler with a leading-slash path like "/Templates/Daily", and assert
that the executed templatePath is normalized to the correct form without the
leading slash (such as "Templates/Daily.md"). This ensures path normalization
handles both extensionless and leading-slash input formats as part of the CLI
contract.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7a9108a0-8394-4e60-be5d-0a4f7dd25ae1

📥 Commits

Reviewing files that changed from the base of the PR and between cb84d6d and 4620c53.

📒 Files selected for processing (16)
  • docs/docs/Advanced/CLI.md
  • docs/docs/Choices/TemplateChoice.md
  • docs/docs/Settings.md
  • src/IChoiceExecutor.ts
  • src/cli/registerQuickAddCliHandlers.test.ts
  • src/cli/registerQuickAddCliHandlers.ts
  • src/commandLabels.ts
  • src/engine/runTemplateFromFolder.test.ts
  • src/engine/runTemplateFromFolder.ts
  • src/gui/suggesters/choiceSuggester.test.ts
  • src/gui/suggesters/choiceSuggester.ts
  • src/main.commandLabels.test.ts
  • src/main.ts
  • src/quickAddSettingsTab.test.ts
  • src/quickAddSettingsTab.ts
  • src/settings.ts
✅ Files skipped from review due to trivial changes (6)
  • src/commandLabels.ts
  • src/quickAddSettingsTab.test.ts
  • docs/docs/Choices/TemplateChoice.md
  • docs/docs/Advanced/CLI.md
  • src/main.commandLabels.test.ts
  • docs/docs/Settings.md
🚧 Files skipped from review as they are similar to previous changes (8)
  • src/quickAddSettingsTab.ts
  • src/settings.ts
  • src/main.ts
  • src/gui/suggesters/choiceSuggester.ts
  • src/gui/suggesters/choiceSuggester.test.ts
  • src/engine/runTemplateFromFolder.test.ts
  • src/cli/registerQuickAddCliHandlers.ts
  • src/engine/runTemplateFromFolder.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: list templates from configured folder in Run QuickAdd

1 participant