# ActionForge (CLI prototype) ActionForge is a working prototype of a multi-rule, config-file-driven automation daemon: you describe many independent, named "watch a folder and run a command" rules in a single JSON file, and ActionForge runs all of them at once, in one process, each with its own directory, its own file-extension filter, and its own shell command. It is the "Pro" tier of the same product line as the sibling tool MacroDeck. MacroDeck watches exactly one folder with one rule supplied entirely as CLI flags (`macrodeck watch --run ""`); ActionForge instead loads a config file that can define any number of rules and treats each one as an independently tracked, independently reportable unit of work, so a problem with one rule (for example its watch directory not existing yet) never affects the others — this per-rule fault isolation, not a bigger CLI surface, is the real difference in mechanism between the two tools. Text expansion, clipboard variables, and global keyboard/mouse macros are explicitly NOT implemented here: they require OS-level UI-automation APIs (accessibility hooks, global input hooks, clipboard managers) that differ per platform and go well beyond a stdlib-only file-watching CLI. They are recorded as roadmap items; see ../plan.md for the full product plan. ## Build from source Requires Go 1.24+, no external dependencies. go build -o actionforge . Cross-compile for another platform: GOOS=windows GOARCH=amd64 go build -o actionforge.exe . GOOS=darwin GOARCH=arm64 go build -o actionforge . ## Usage actionforge run --config [--interval 2s] [--once] [--run-existing] [--log events.log] actionforge validate --config actionforge -h | --help ### `run` Loads the config file and polls every rule's watch directory. Each rule tracks its own "known files" state completely independently of every other rule (same new-or-changed-since-first-seen semantics as MacroDeck): files that are already present when a rule starts watching do NOT trigger by default, only files that appear or change afterwards. If a rule's watch_dir does not exist (or stops existing) at poll time, ActionForge prints a per-rule error to stderr and retries that rule again on the next poll -- it does not crash, and it does not affect any other rule. When the directory later appears, that rule establishes a fresh baseline and resumes normal triggering with no restart required. Flags: --config Path to the JSON rules file (required) --interval Poll interval, e.g. 500ms, 2s, 1m (default 2s) --once Single pass over every rule's directory, then exit. Every file currently present in each rule's watch_dir is treated as a trigger for this one pass (same as --run-existing, but only for this pass). Useful for testing or cron-style invocation instead of running as a long-lived daemon. --run-existing In continuous (non --once) mode, treat files already present in each rule's watch_dir at startup as triggers too, instead of only using them to seed the baseline. Applies to every rule in the config. --log Append one JSON object per triggered event to this file (JSON Lines format): rule_name, file, command, timestamp. For every trigger, ActionForge prints: [rule-name] /absolute/path/to/file -> expanded command and then executes the command through the OS shell (`/bin/sh -c` on Unix, `cmd /C` on Windows). ### `validate` actionforge validate --config Parses the config file and checks that it is valid JSON, that every rule has a non-empty name, watch_dir, and run command, and that no two rules share the same name. It does not start watching anything. Prints "OK" and exits 0 if the config is valid; otherwise it lists every specific problem found and exits non-zero. ## Config file format { "rules": [ { "name": "screenshots-to-archive", "watch_dir": "/tmp/actionforge-demo/incoming", "extensions": [".png", ".jpg"], "run": "cp {file} /tmp/actionforge-demo/archive/" }, { "name": "logs-cleanup-alert", "watch_dir": "/tmp/actionforge-demo/logs", "extensions": [".log"], "run": "echo new log: {file}" } ] } - `name` (required): a unique label for the rule, used in log output and the JSON event log. - `watch_dir` (required): the folder this rule watches. Not recursive. - `extensions` (optional): a list of file extensions (e.g. ".png") this rule reacts to. Omit or leave empty to match every file in the directory. - `run` (required): a shell command to run when a matching file is added or changed. `{file}` is replaced with the triggering file's absolute path. ## Roadmap (not implemented in this prototype) - Text expansion (typed shortcuts expanding into snippets/templates) - Clipboard variables (referencing recent clipboard history in a rule's command) - Global keyboard/mouse macros (recording and replaying input system-wide) These all require OS-specific UI-automation APIs (accessibility frameworks, global input hooks, clipboard managers) rather than plain filesystem polling, so they are out of scope for this stdlib-only, cross-platform CLI prototype. See ../plan.md for how they fit into the broader product plan. ## Prebuilt binaries See ../downloads/ for prebuilt binaries (Windows/macOS/Linux) and CHECKSUMS.txt for their SHA-256 hashes. These are unsigned indie builds -- Windows SmartScreen and macOS Gatekeeper will warn on first run, which is expected until a code-signing certificate is in place.