diff --git a/.prompt-hub/memory.md b/.prompt-hub/memory.md index 5bf3197..7b13623 100644 --- a/.prompt-hub/memory.md +++ b/.prompt-hub/memory.md @@ -69,3 +69,4 @@ | 2026-05-19 23:08:00 CEST | agent | Implemented the Automations feature on `feature/automation`: new `automation.ts` (pure parser/scheduler logic with 28 vitest cases), plugin-level scheduler in `main.ts` (ticks every 30 s, vault `create/modify/delete/rename` listeners, `triggerAutomation` with `scheduler` vs `manual` source, history ring-buffer capped at 200), `sendAutomationPrompt` method on `ClaudeCliView`, new "Automations" toolbar button, new `AutomationsModal` with **Automations** + **History** tabs (parse errors surfaced, Run now per row, Clear history, Export as markdown), settings section with `Automations folder` + `Reload now`, modal styles in `styles.css`, README updated with feature + format docs. Added `cron-parser` (runtime) and `yaml` (dev). Validated `npm run lint`, `npm run build`, `npm test` (55 tests pass). Bumped 0.2.0 → 0.2.1. | `automation.ts`, `automations-modal.ts`, `tests/automation.test.ts`, `main.ts`, `styles.css`, `README.md`, `package.json`, `package-lock.json`, `.prompt-hub/version.md`, `.prompt-hub/releases.md`, `.prompt-hub/todo/todo-20260519-225317-automations-recurring-prompts.md` | success | Commit and push to `feature/automation`. | | 2026-05-19 23:21:00 CEST | agent | Fixed automation Enter not registering: switched `appendNewline` suffix from `\n` to `\r` in `triggerAutomation`. Root cause — PTY-attached raw-mode TUIs (Claude, Codex) read keypresses directly and only treat `\r` as Enter; `\n` just sits in the input buffer. Added a lesson capturing this. Bumped 0.2.1 → 0.2.2 (`manifest.json` + `.prompt-hub/version.md`), updated releases, rebuilt `dist/any-ai-cli/` and `dist/any-ai-cli-0.2.2-local.zip` for local re-install. | `main.ts`, `README.md`, `.prompt-hub/lessons.md`, `.prompt-hub/version.md`, `.prompt-hub/releases.md`, `manifest.json`, `main.js` (rebuilt), `dist/any-ai-cli/`, `dist/any-ai-cli-0.2.2-local.zip`, `npm run lint`, `npm run build`, `npm test` | success | Commit and push to `feature/automation`. | | 2026-05-19 23:25:00 CEST | agent | Fixed automation prompts not submitting in Codex (worked in Claude). Root cause — Codex uses bracketed-paste-style heuristics: when body + `\r` arrives in a single stdin write, the `\r` is treated as a literal newline inside the input field rather than the submit key. Split `sendAutomationPrompt(text, submitWithEnter)` into two writes: body first, then `\r` ~120 ms later via `activeWindow.setTimeout`. Added a lesson capturing the pattern. Bumped 0.2.2 → 0.2.3, rebuilt `dist/any-ai-cli/` and `dist/any-ai-cli-0.2.3-local.zip`. | `main.ts`, `manifest.json`, `.prompt-hub/lessons.md`, `.prompt-hub/version.md`, `.prompt-hub/releases.md`, `main.js` (rebuilt), `dist/`, `npm run lint`, `npm run build`, `npm test` (55 pass) | success | Commit and push to `feature/automation`. | +| 2026-05-19 23:39:00 CEST | agent | Added "Create example automation" button to the Automations settings section. New `createExampleAutomation()` on the plugin writes a documented `hello-world.md` (every frontmatter field explained inline) into the configured folder, creating the folder if missing, picking a non-clashing name (`hello-world-N.md`), reloading automations and opening the file. Throws a clear error (shown as Notice) if no folder set. Bumped 0.2.3 → 0.2.4, README updated, rebuilt dist bundle. | `main.ts`, `manifest.json`, `README.md`, `.prompt-hub/version.md`, `.prompt-hub/releases.md`, `main.js` (rebuilt), `dist/`, `npm run lint`, `npm run build`, `npm test` (55 pass) | success | Commit and push to `feature/automation`. | diff --git a/.prompt-hub/releases.md b/.prompt-hub/releases.md index 2f9e5b7..325ad2b 100644 --- a/.prompt-hub/releases.md +++ b/.prompt-hub/releases.md @@ -1,5 +1,8 @@ # Releases +## 0.2.4 - 2026-05-19 +- Added a "Create example automation" button to the Automations settings section. It writes a documented `hello-world.md` (every frontmatter field explained with inline comments, simple "say hello and tell me the time" prompt) into the configured automations folder, creating the folder if needed and picking a non-clashing filename, then opens the file. Surfaces a clear notice if no folder is configured. + ## 0.2.3 - 2026-05-19 - Fixed Automations not submitting in Codex (worked in Claude). Codex uses bracketed-paste-style heuristics: when `body + \r` arrives in a single stdin write, the `\r` is treated as a literal newline inside the input field, not as the submit key. Split the write — body first, then `\r` ~120 ms later — so the paste-detection window closes before Enter is registered. Claude behaviour unchanged. diff --git a/.prompt-hub/version.md b/.prompt-hub/version.md index 7179039..abd4105 100644 --- a/.prompt-hub/version.md +++ b/.prompt-hub/version.md @@ -1 +1 @@ -0.2.3 +0.2.4 diff --git a/README.md b/README.md index c06cd51..c866bee 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,8 @@ Automations let you store reusable prompts as markdown files in your vault and h 2. In plugin settings → **Automations folder**, set that path. Leave empty to disable the feature. 3. Drop one markdown file per automation in that folder. The plugin scans the folder on startup and live-updates on vault changes (create / modify / delete / rename). +> Tip: in plugin settings, click **Create example** to drop a fully documented `hello-world.md` (every field explained) into the configured folder — the fastest way to see the format. + ### File format Each automation is a regular markdown file with YAML frontmatter that sets the schedule, plus a body containing the prompt that will be sent to the CLI verbatim. @@ -154,6 +156,7 @@ Automations section: - **Automations folder** — vault-relative path to the folder holding automation markdown files. Leave empty to disable. See the [Automations](#automations) section above for the file format. - **Reload automations** — force a re-scan (otherwise the plugin already refreshes on any vault change inside the folder). +- **Create example automation** — write a documented `hello-world.md` (all fields explained) into the configured folder and open it. Advanced: diff --git a/main.js b/main.js index 0a2bc15..01c9b06 100644 --- a/main.js +++ b/main.js @@ -18974,6 +18974,25 @@ var DEFAULT_SETTINGS = { automationsHistoryLimit: 200 }; var AUTOMATION_TICK_MS = 3e4; +var EXAMPLE_AUTOMATION_CONTENT = `--- +# Display name shown in the Automations modal. Optional \u2014 defaults to the filename. +name: Hello world +# Set to false to keep the entry listed but never auto-fire. Optional, default true. +enabled: true +# Run every N minutes (whole number >= 1). Mutually exclusive with "cron". +interval: 60 +# Or schedule with a standard 5-field cron expression instead of "interval". +# Uncomment the next line AND remove "interval" above to use cron: +# cron: "0 9 * * 1-5" +# Only fire when this runtime (id or display name) is the one currently running. +# Optional \u2014 remove to send to whichever runtime is active. +# runtime: Claude +# Append Enter so the CLI executes the prompt. Optional, default true. +appendNewline: true +--- + +Say hello and tell me the current date and time. +`; function cloneDefaultRuntimes() { return DEFAULT_RUNTIMES.map((runtime) => ({ ...runtime })); } @@ -19605,6 +19624,24 @@ var ClaudeCliPlugin = class extends import_obsidian2.Plugin { void this.saveSettings(); this.notifyAutomationsChanged(); } + async createExampleAutomation() { + const folder = this.settings.automationsFolder.trim().replace(/\/+$/, ""); + if (!folder) { + throw new Error("Set an automations folder first."); + } + if (!this.app.vault.getFolderByPath(folder)) { + await this.app.vault.createFolder(folder); + } + let target = `${folder}/hello-world.md`; + let counter = 1; + while (this.app.vault.getAbstractFileByPath(target)) { + target = `${folder}/hello-world-${counter}.md`; + counter += 1; + } + const file = await this.app.vault.create(target, EXAMPLE_AUTOMATION_CONTENT); + this.loadAutomations(); + return file; + } async activateView() { var _a5; const { workspace } = this.app; @@ -19765,6 +19802,17 @@ var ClaudeCliSettingTab = class extends import_obsidian2.PluginSettingTab { new import_obsidian2.Notice("Automations reloaded.", 2500); }) ); + new import_obsidian2.Setting(containerEl).setName("Create example automation").setDesc("Write a documented hello-world automation file (all fields explained) into the folder above.").addButton( + (btn) => btn.setButtonText("Create example").setIcon("file-plus").onClick(async () => { + try { + const file = await this.plugin.createExampleAutomation(); + new import_obsidian2.Notice(`Created ${file.path}`, 3e3); + await this.app.workspace.openLinkText(file.path, "", true); + } catch (err) { + new import_obsidian2.Notice(err.message, 5e3); + } + }) + ); new import_obsidian2.Setting(containerEl).setName("Advanced").setHeading(); new import_obsidian2.Setting(containerEl).setName("Node executable").setDesc("Path to the node binary used by the proxy. Leave as 'auto' for automatic detection.").addText( (text) => text.setPlaceholder("Auto").setValue(this.plugin.settings.nodeExecutable).onChange(async (value) => { diff --git a/main.ts b/main.ts index c4531ce..fb92891 100644 --- a/main.ts +++ b/main.ts @@ -70,6 +70,26 @@ const DEFAULT_SETTINGS: ClaudeCliPluginSettings = { const AUTOMATION_TICK_MS = 30_000; +const EXAMPLE_AUTOMATION_CONTENT = `--- +# Display name shown in the Automations modal. Optional — defaults to the filename. +name: Hello world +# Set to false to keep the entry listed but never auto-fire. Optional, default true. +enabled: true +# Run every N minutes (whole number >= 1). Mutually exclusive with "cron". +interval: 60 +# Or schedule with a standard 5-field cron expression instead of "interval". +# Uncomment the next line AND remove "interval" above to use cron: +# cron: "0 9 * * 1-5" +# Only fire when this runtime (id or display name) is the one currently running. +# Optional — remove to send to whichever runtime is active. +# runtime: Claude +# Append Enter so the CLI executes the prompt. Optional, default true. +appendNewline: true +--- + +Say hello and tell me the current date and time. +`; + function cloneDefaultRuntimes(): CliRuntimeConfig[] { return DEFAULT_RUNTIMES.map((runtime) => ({ ...runtime })); } @@ -793,6 +813,27 @@ export default class ClaudeCliPlugin extends Plugin { this.notifyAutomationsChanged(); } + async createExampleAutomation(): Promise { + const folder = this.settings.automationsFolder.trim().replace(/\/+$/, ""); + if (!folder) { + throw new Error("Set an automations folder first."); + } + if (!this.app.vault.getFolderByPath(folder)) { + await this.app.vault.createFolder(folder); + } + + let target = `${folder}/hello-world.md`; + let counter = 1; + while (this.app.vault.getAbstractFileByPath(target)) { + target = `${folder}/hello-world-${counter}.md`; + counter += 1; + } + + const file = await this.app.vault.create(target, EXAMPLE_AUTOMATION_CONTENT); + this.loadAutomations(); + return file; + } + async activateView(): Promise { const { workspace } = this.app; let leaf: WorkspaceLeaf | null = workspace.getLeavesOfType(VIEW_TYPE_CLAUDE)[0] ?? null; @@ -1025,6 +1066,24 @@ class ClaudeCliSettingTab extends PluginSettingTab { }) ); + new Setting(containerEl) + .setName("Create example automation") + .setDesc("Write a documented hello-world automation file (all fields explained) into the folder above.") + .addButton((btn) => + btn + .setButtonText("Create example") + .setIcon("file-plus") + .onClick(async () => { + try { + const file = await this.plugin.createExampleAutomation(); + new Notice(`Created ${file.path}`, 3000); + await this.app.workspace.openLinkText(file.path, "", true); + } catch (err) { + new Notice((err as Error).message, 5000); + } + }) + ); + new Setting(containerEl).setName("Advanced").setHeading(); new Setting(containerEl) diff --git a/manifest.json b/manifest.json index 5f4737e..8c5e0f7 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "any-ai-cli", "name": "Any AI CLI", - "version": "0.2.3", + "version": "0.2.4", "minAppVersion": "1.7.2", "description": "Run an AI assistant CLI like Claude from a right sidebar terminal panel, with a customizable runtime list.", "author": "Benoit Lamouche",