mirror of
https://github.com/blamouche/obsidian-any-ai-code.git
synced 2026-07-22 06:53:38 +00:00
Add "Create example automation" button to settings
Generates a documented hello-world.md (every frontmatter field explained with inline comments, plus a simple "say hello and tell me the time" prompt) into the configured automations folder. Creates the folder if it doesn't exist, avoids clobbering an existing file by suffixing the name, reloads the automation cache, and opens the new file. Shows a clear notice when no automations folder is configured. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9b9a356a31
commit
d2e907cf2d
7 changed files with 116 additions and 2 deletions
|
|
@ -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`. |
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
0.2.3
|
||||
0.2.4
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
48
main.js
48
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) => {
|
||||
|
|
|
|||
59
main.ts
59
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<TFile> {
|
||||
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<void> {
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Reference in a new issue