feat(plugins): Recipes Pack — durable named playbooks - #31
Merged
Conversation
Saved sequences of MCP tool calls with parameter substitution. The
flywheel feature: once an agent figures out "to add a customer in
NowCerts, click X then Y then Z", that knowledge becomes a Recipe and
future runs replay the sequence instead of rediscovering it.
Tools shipped (4):
agentmark_recipe_save persist a recipe with parameters + steps
agentmark_recipe_list list saved recipes (filterable by target_app)
agentmark_recipe_get fetch + optionally resolve a recipe
agentmark_recipe_delete remove
Recipes are stored as JSON at ~/.thinkfleet/agentmark/recipes.json
(mode 0600), atomically via temp-write + rename. Each save bumps a
monotonic version counter and timestamps. Separate from the Foundations
StateStore so recipes don't bloat the general K/V file.
Substitution syntax: `{{param.name}}` in any string arg.
- Bare token "{{param.name}}" preserves the param's native type.
- Embedded "prefix {{param.name}} suffix" string-interpolates.
Parameter schema supports type validation, required flag, defaults.
String-to-number coercion is lossless. Each parameter is typed
(string | number | boolean).
Architectural choice: recipes are **playbooks**, not auto-executors.
agentmark_recipe_get returns a resolved plan; the agent dispatches
each step itself. Three reasons:
1. No recursive Dispatcher coupling — the recipes plugin needs no
reference to the surrounding server.
2. The AI sees each step in its reasoning chain (better for the
model + better for the user reviewing what was done).
3. Recipe steps can include `verify` hints describing what the next
diff should look like, and the agent decides what "verified"
means rather than the server enforcing rigid assertions.
Pairs naturally with agentmark_desktop_diff (PR #26) for step-by-step
verification during replay.
Tests (24 new, 431 total): substitution semantics (bare/embedded
tokens, recursion into nested structures, strict mode), parameter
schema (defaults, required, coercion), store CRUD (save/get/list/
delete, on_conflict semantics, durability across instances, target_app
filter), dispatcher integration (full save→resolve→get cycle).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Tools
Architectural choice: playbooks, not auto-executors
`agentmark_recipe_get` returns a resolved plan (parameter substitutions applied). The AI then dispatches each step itself, in its own reasoning chain. The plugin deliberately does NOT auto-execute server-side.
Three reasons:
Pairs naturally with `agentmark_desktop_diff` from PR #26.
Substitution syntax
`{{param.name}}` in any string arg.
Parameters carry a type schema (`string | number | boolean`), optional defaults, and a `required` flag.
Example
```json
agentmark_recipe_save {
"name": "fill-nowcerts-customer",
"target_app": "nowcerts",
"parameters": [
{ "name": "company_name", "type": "string", "required": true },
{ "name": "phone", "type": "string" }
],
"steps": [
{
"tool": "agentmark_desktop_execute",
"args": { "action_id": "act_in_company", "value": "{{param.company_name}}" },
"verify": { "expect_value_changes": [{ "element_id": "in_company", "to": "{{param.company_name}}" }] }
},
{
"tool": "agentmark_desktop_execute",
"args": { "action_id": "act_in_phone", "value": "{{param.phone}}" }
}
]
}
agentmark_recipe_get {
"name": "fill-nowcerts-customer",
"params": { "company_name": "Globex Corp", "phone": "555-0100" }
}
→ { resolved: true, steps: [...with substitutions applied...] }
```
Storage
Single JSON file at `~/.thinkfleet/agentmark/recipes.json` (mode 0600). Atomic writes via temp-file + rename. Separate from the Foundations `StateStore` so recipes don't bloat the general K/V file and so they can be backed up / synced independently.
Test plan
🤖 Generated with Claude Code