Install
$ agentstack add skill-kaynetik-skills-flowd-rules ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ● Shell / process execution Used
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
flowd Rules Author
What the engine actually sees
The flowd rules engine evaluates a ProposedAction carrying only three fields:
tool: String // e.g. "Bash", "Edit", "Write"
file_path: Option // target path of the action
project: Option // current project context
It does not see command arguments, edit bodies, diff contents, or source text. Schema lives in crates/flowd-core/src/rules.rs.
A rule has five fields:
id: # stable identifier, unique across loaded rules
scope: # glob over file path or project name
level: # warn | deny
description: # multi-line guidance shown to the agent
match: # regex matched against tool name OR file_path
deny vs warn -- the decision rule
Use deny only when the gate (scope glob plus match regex) can be evaluated purely from {tool, file_path} and a true match means the action itself is wrong.
Use warn when the rule depends on content the engine cannot see -- command arguments, diff body, intent. The agent honors warnings by reading them via rules_list or rules_check.
| Constraint | Engine can enforce? | Level | | ----------------------------------------- | -------------------- | ------ | | "Don't edit this Cargo.toml" | Yes (file_path) | deny | | "Don't run cargo build" | No (args invisible) | warn | | "Don't add println! to mcp/src" | No (diff invisible) | warn | | "Don't run any shell tool in test dirs" | Yes (tool + scope) | deny |
Promoting an advisory rule to deny blocks legitimate edits across the whole scope.
scope vs match
scope is a glob; match is a regex. They look similar in YAML and use different engines.
scope: "**"matches every action. Use for tool-name-only rules.scope: "crates/flowd-core/**"matches by file path.scope: "my-project"matches by project name (no globbing).match: "^(Bash|...)$"matches the tool name.match: "crates/flowd-mcp/src/.*\\.rs$"matches the file path.
Either scope or match may carry the path constraint; pick one and keep the other broad. Mixing both narrows the rule unintentionally.
Canonical tool-name regexes
Copy verbatim. Shell-class tools across Claude Code, Cursor, and generic MCP wire:
^(Bash|shell|shell_exec|run_command|run_terminal_cmd)$
Edit-class plus shell-class:
^(Bash|Write|Edit|MultiEdit|shell|shell_exec|run_command|run_terminal_cmd)$
Global vs project placement
| Rule references... | Place under | | ----------------------------------------------- | ------------------- | | Specific paths in this repo | .flowd/rules/ | | Engineering principles, language conventions | ~/.flowd/rules/ | | Tool or CLI invocations regardless of project | ~/.flowd/rules/ |
Project rules augment globals. Uniqueness is enforced by id, so prefix repo-local ids with the repo name when collision is plausible.
Description template
Every rule description follows this four-part structure:
- What and why -- one or two sentences of intent.
- The footgun -- the future mistake this rule prevents (often "a future agent will look at X and try to fix it").
- Engine caveat -- when the rule is advisory, state the limitation so the next author does not promote it to
deny. - Cross-reference -- README section, sibling rule id, or source file path.
Skip step 3 when the rule is enforceable (deny on observable file_path).
File layout
Prefer one rule per file, named after the rule id. Both single-rule and list layouts are accepted by crates/flowd-core/src/rules/loader.rs. Use the list layout only for tightly-coupled rule families sharing intent (for example multiple cargo-* rules grouped in cargo-discipline.yaml).
Worked examples
Enforceable deny
id: forward-only-migrations
scope: "crates/flowd-storage/src/**"
level: deny
description: |
Schema migrations are forward-only by project policy and live as inline
`const MIGRATION_NNN: &str` constants in
`crates/flowd-storage/src/migrations.rs` (not as .sql files).
Editing or reordering an existing MIGRATION_NNN constant is a breaking
change for every installed user -- append a new, higher-numbered constant
and add it to the MIGRATIONS slice instead.
match: "crates/flowd-storage/src/migrations\\.rs$"
Advisory warn
id: mcp-wire-discipline
scope: "crates/flowd-mcp/src/**"
level: warn
description: |
Stdout is reserved for JSON-RPC 2.0 frames when flowd runs as an MCP stdio
subprocess. Diagnostics MUST go to stderr -- use `eprintln!`, `tracing::*`,
or write to `std::io::stderr()`. A single stray `println!`, `print!`, or
`dbg!` corrupts the wire and breaks every connected MCP client silently.
Note: the rules engine cannot read source contents (a ProposedAction carries
only { tool, file_path, project }), so this rule is an advisory triggered on
any edit under crates/flowd-mcp/src/. The agent is responsible for honoring
the constraint in the diff itself.
match: "crates/flowd-mcp/src/.*\\.rs$"
Validation
After writing or editing a rule:
- Parse and compile check:
``bash cargo test -p flowd-core --lib rules:: ``
- Confirm the daemon loaded it for the intended scope:
``bash flowd rules list -p flowd rules list -f ``
Bare flowd rules list evaluates against an empty scope and returns no matches even when rules are loaded.
Anti-patterns
level: denyon a rule whosematchonly constrains the tool name with nofile_pathpredicate. Blocks the entire toolclass across the scope.- File name differs from the
id. Breaks grep-by-id. - Rule body embedded in
matchinstead ofdescription.matchis a regex, not prose. - Globally scoped rules referencing repo-specific paths. They fire in unrelated projects.
- Adding
level: denyto advisory rules because "the comment said it was a mistake". If the engine cannot observe the violation,denyblocks legitimate work.
Reference
- Schema:
crates/flowd-core/src/rules.rs(Rule,RuleLevel,ProposedAction). - Loader:
crates/flowd-core/src/rules/loader.rs(single vs list layout, recursive YAML discovery, no symlink follow). - Evaluator:
crates/flowd-core/src/rules/evaluator.rs(glob compilation, regex compilation, lookup). - README sections: "Use the agent" (MCP tool table) and "Recommendations" (rule placement guidance).
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: kaynetik
- Source: kaynetik/skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.