Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name: Check references
on:
push:
pull_request:
jobs:
refs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: python3 scripts/check_refs.py
15 changes: 8 additions & 7 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ Hermes Agent plugin. No build, no deps, no tests. Pure markdown skills + a tiny

```
plugin.yaml # name, version, description
__init__.py # register(ctx) — registers 7 skills, nothing else
__init__.py # register(ctx), auto-registers every skill directory
skills/
poteto-mode/ # orchestrator — routes tasks to playbooks
poteto-mode/ # orchestrator - routes tasks to playbooks
SKILL.md
playbooks/*.md # 7 step-by-step procedures
how/SKILL.md # codebase exploration
Expand All @@ -24,14 +24,15 @@ skills/
- **SKILL.md frontmatter** must have: `name`, `description` (≤60 chars, one sentence, period at end), `version`, `author`, `license`, `platforms`, `metadata.hermes.tags`, `metadata.hermes.related_skills`.
- **Cross-references** between skills use `pstack:<name>` (e.g. `pstack:how`, `pstack:architect`).
- **Playbook references** use `skill_view(name='pstack:poteto-mode', file_path='playbooks/<name>.md')`.
- **Hermes tools** to reference: `terminal`, `search_files`, `read_file`, `write_file`, `patch`, `delegate_task`, `web_search`, `web_extract`, `browser_navigate`. No Cursor-specific tools (Task, /loop, subagent_type, etc.).
- **Hermes tools** to reference: `terminal`, `search_files`, `read_file`, `write_file`, `patch`, `delegate_task`, `web_search`, `web_extract`, `browser_navigate`. Use `delegate_task` for subagents, `session_search` for session history, and `cronjob` for scheduled runs.
- **No em dashes** in any prose. Use periods or commas. This is an unslop rule that applies to the project itself.

## Adding a skill

1. Create `skills/<name>/SKILL.md` with proper frontmatter.
2. Add the name to the `SKILLS` tuple in `__init__.py`.
3. That's it. No config, no registration boilerplate.
2. The loader discovers every directory containing `SKILL.md` automatically.
3. Run `python3 scripts/check_refs.py` before every commit.
4. CI runs the checker on every push and pull request.

## Adding a playbook

Expand All @@ -42,9 +43,9 @@ skills/

- Don't add Python logic beyond the loader. Skills are markdown.
- Don't add hooks or tools in `__init__.py`. This plugin registers skills only.
- Don't reference Cursor-specific features (worktrees, /loop, cloud environment, sticky mode).
- Don't reference Hermes Agent-specific features (worktrees, cronjob, cloud environment, sticky mode).
- Don't write promotional or padded prose. Run unslop on yourself.

## Origin

Adapted from [poteto's pstack](https://github.com/cursor/plugins/tree/main/pstack) for Hermes Agent. The original is a Cursor plugin. This version replaces Cursor's Task tool with `delegate_task`, removes cloud/worktree assumptions, and reformats everything as Hermes SKILL.md files.
Adapted from [poteto's pstack](https://github.com/cursor/plugins/tree/main/pstack) for Hermes Agent. The original is a Cursor plugin. This version replaces Cursor's task orchestration with `delegate_task`, removes cloud/worktree assumptions, and reformats everything as Hermes SKILL.md files.
10 changes: 5 additions & 5 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ Hermes Agent plugin. No build, no deps, no tests. Pure markdown skills + a tiny

```
plugin.yaml # name, version, description
__init__.py # register(ctx) — registers 6 skills, nothing else
__init__.py # register(ctx) - registers 6 skills, nothing else
skills/
poteto-mode/ # orchestrator — routes tasks to playbooks
poteto-mode/ # orchestrator - routes tasks to playbooks
SKILL.md
playbooks/*.md # 7 step-by-step procedures
how/SKILL.md # codebase exploration
Expand All @@ -23,7 +23,7 @@ skills/
- **SKILL.md frontmatter** must have: `name`, `description` (≤60 chars, one sentence, period at end), `version`, `author`, `license`, `platforms`, `metadata.hermes.tags`, `metadata.hermes.related_skills`.
- **Cross-references** between skills use `pstack:<name>` (e.g. `pstack:how`, `pstack:architect`).
- **Playbook references** use `skill_view(name='pstack:poteto-mode', file_path='playbooks/<name>.md')`.
- **Hermes tools** to reference: `terminal`, `search_files`, `read_file`, `write_file`, `patch`, `delegate_task`, `web_search`, `web_extract`, `browser_navigate`. No Cursor-specific tools (Task, /loop, subagent_type, etc.).
- **Hermes tools** to reference: `terminal`, `search_files`, `read_file`, `write_file`, `patch`, `delegate_task`, `web_search`, `web_extract`, `browser_navigate`. No Hermes Agent-specific tools (Task, cronjob, toolsets, etc.).
- **No em dashes** in any prose. Use periods or commas. This is an unslop rule that applies to the project itself.

## Adding a skill
Expand All @@ -41,9 +41,9 @@ skills/

- Don't add Python logic beyond the loader. Skills are markdown.
- Don't add hooks or tools in `__init__.py`. This plugin registers skills only.
- Don't reference Cursor-specific features (worktrees, /loop, cloud environment, sticky mode).
- Don't reference Hermes Agent-specific features (worktrees, cronjob, cloud environment, sticky mode).
- Don't write promotional or padded prose. Run unslop on yourself.

## Origin

Adapted from [poteto's pstack](https://github.com/cursor/plugins/tree/main/pstack) for Hermes Agent. The original is a Cursor plugin. This version replaces Cursor's Task tool with `delegate_task`, removes cloud/worktree assumptions, and reformats everything as Hermes SKILL.md files.
Adapted from [poteto's pstack](https://github.com/cursor/plugins/tree/main/pstack) for Hermes Agent. The original is a Hermes Agent plugin. This version replaces Hermes Agent's Task tool with `delegate_task`, removes cloud/worktree assumptions, and reformats everything as Hermes SKILL.md files.
59 changes: 32 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,49 +1,54 @@
# pstack — Hermes Plugin
# pstack - Hermes Plugin

Poteto's engineering rigor stack, adapted for [Hermes Agent](https://hermes-agent.nousresearch.com).

Fork of [pstack](https://github.com/cursor/plugins/tree/main/pstack) by poteto (Meta/Netflix/Cursor, React core team). Originally built as a Cursor plugin, rebuilt here as a Hermes plugin with native skills, delegate_task orchestration, and playbook routing.
Fork of [pstack](https://github.com/cursor/plugins/tree/main/pstack) by poteto (Meta/Netflix, React core team). Originally built as a Cursor plugin, adapted here for Hermes Agent with native skills, delegate_task orchestration, and playbook routing.

## What it does

One orchestrator skill (`poteto-mode`) that routes tasks to playbooks and enforces principles. Five satellite skills for specific capabilities.
One orchestrator skill (`poteto-mode`) routes tasks to playbooks. Supporting skills cover exploration, design, review, delegation, cleanup, memory, teaching, verification, and workflow.

| Skill | Purpose |
|-------|---------|
| `pstack:poteto-mode` | Orchestrator. Task → playbook routing, principles, autonomy rules, delegation patterns. |
| `pstack:how` | Codebase exploration. "How does X work?" with explain/critique modes. |
| `pstack:architect` | Design types and module boundaries before implementing. "Design it twice." |
| `pstack:interrogate` | Multi-reviewer adversarial code review via parallel delegates. |
| `pstack:swarm` | Fan out N parallel workers, drain, report. |
| `pstack:unslop` | Strip AI writing patterns. Apply to all prose. |
| `pstack:setup-pstack` | Pick the model and reasoning budget delegates use. Writes Hermes `delegation` config. |
| `pstack:poteto-mode` | Orchestrator and playbook routing. |
| `pstack:how` | Codebase exploration. |
| `pstack:architect` | Design types and module boundaries. |
| `pstack:interrogate` | Adversarial multi-reviewer review. |
| `pstack:swarm` | Parallel workers and aggregation. |
| `pstack:unslop` | Clean AI writing patterns from prose. |
| `pstack:deslop` | Clean AI code slop from diffs. |
| `pstack:setup-pstack` | Configure the delegate model and reasoning budget. |
| `pstack:automate-me`, `pstack:recall`, `pstack:reflect`, `pstack:show-me-your-work`, `pstack:why` | Personalization, history, reflection, evidence, and intent. |
| `pstack:benchmark-checklist`, `pstack:blast-radius`, `pstack:bro`, `pstack:create-verification-skill`, `pstack:figure-it-out`, `pstack:maintain-verification-skill`, `pstack:no-comments`, `pstack:tdd`, `pstack:teach`, `pstack:technical-writing`, `pstack:typescript-best-practices` | Focused engineering workflows. |
| `principle-*` (20 skills) | Engineering principles used by the orchestrator. |


## Playbooks

Loaded via `skill_view(name='pstack:poteto-mode', file_path='playbooks/<name>.md')`:

- `investigation.md` — read-only question, trace behavior
- `bug-fix.md` — reproduce, root-cause, fix with evidence
- `feature.md` — plan, design, delegate, verify
- `perf-issue.md` — measure, profile, fix, measure again
- `refactoring.md` — behavior-preserving restructuring
- `autonomous-run.md` — long task with falsifiable exit predicate
- `figure-it-out.md` — large/cross-cutting work, design your own workflow
- `investigation.md` - read-only question, trace behavior
- `bug-fix.md` - reproduce, root-cause, fix with evidence
- `feature.md` - plan, design, delegate, verify
- `perf-issue.md` - measure, profile, fix, measure again
- `refactoring.md` - behavior-preserving restructuring
- `autonomous-run.md` - long task with falsifiable exit predicate
- `figure-it-out.md` - large/cross-cutting work, design your own workflow

## Guide

Read the guide in order the first time. After that, each page stands alone.

1. [Set up pstack](docs/guide/01-setup.md) — install, pick models, first task
2. [Route work through poteto-mode](docs/guide/02-poteto-mode.md) — give it a goal, watch it pick a playbook
3. [Understand the code](docs/guide/03-understand.md) — pstack:how before you edit anything
4. [Design the change](docs/guide/04-design.md) — pstack:architect and pstack:interrogate before code locks in
5. [Build and clean](docs/guide/05-build-and-clean.md) — build playbooks, TDD, unslop
6. [Verify and ship](docs/guide/06-verify-and-ship.md) — prove behavior, open a PR, merge
7. [Run work overnight](docs/guide/07-overnight.md) — autonomous contracts, decision logs
8. [Principles](docs/guide/08-principles.md) — the full set in detail
9. [Make it yours](docs/guide/09-make-it-yours.md) — customize skills and playbooks
10. [Recipes and pitfalls](docs/guide/10-recipes-and-pitfalls.md) — prompts to copy, mistakes to skip
1. [Set up pstack](docs/guide/01-setup.md) - install, pick models, first task
2. [Route work through poteto-mode](docs/guide/02-poteto-mode.md) - give it a goal, watch it pick a playbook
3. [Understand the code](docs/guide/03-understand.md) - pstack:how before you edit anything
4. [Design the change](docs/guide/04-design.md) - pstack:architect and pstack:interrogate before code locks in
5. [Build and clean](docs/guide/05-build-and-clean.md) - build playbooks, TDD, unslop
6. [Verify and ship](docs/guide/06-verify-and-ship.md) - prove behavior, open a PR, merge
7. [Run work overnight](docs/guide/07-overnight.md) - autonomous contracts, decision logs
8. [Principles](docs/guide/08-principles.md) - the full set in detail
9. [Make it yours](docs/guide/09-make-it-yours.md) - customize skills and playbooks
10. [Recipes and pitfalls](docs/guide/10-recipes-and-pitfalls.md) - prompts to copy, mistakes to skip

## Slash commands

Expand Down
5 changes: 2 additions & 3 deletions __init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from pathlib import Path

SKILLS = ("poteto-mode", "unslop", "how", "interrogate", "architect", "swarm", "setup-pstack")

def register(ctx):
skills_dir = Path(__file__).parent / "skills"
for name in SKILLS:
ctx.register_skill(name, skills_dir / name)
for skill_dir in sorted(p for p in skills_dir.iterdir() if (p / "SKILL.md").is_file()):
ctx.register_skill(skill_dir.name, skill_dir)
6 changes: 3 additions & 3 deletions docs/guide/05-build-and-clean.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ In context, that's enough. [`pstack:tdd`](../../skills/tdd/SKILL.md) writes the

## Clean before you commit

The [Opening a PR playbook](../../skills/poteto-mode/playbooks/opening-a-pr.md) runs `pstack:deslop` on the diff before each commit and applies [`pstack:unslop`](../../skills/unslop/SKILL.md) to the PR description and commit bodies. `pstack:deslop` ships separately from pstack. If you don't have it, ask for the same outcome in plain words: remove narrating comments, unsupported guards, dead compatibility paths, and unrelated edits.
The [Opening a PR playbook](../../skills/poteto-mode/playbooks/opening-a-pr.md) runs [`pstack:deslop`](../../skills/deslop/SKILL.md) on the diff before each commit and applies [`pstack:unslop`](../../skills/unslop/SKILL.md) to the PR description and commit bodies.

For prose, `pstack:unslop` takes a target and any extra rules you have:

Expand All @@ -68,10 +68,10 @@ Comments need their own pass, and not from the agent that wrote them. An author
load pstack:no-comments the diff
```

[`pstack:no-comments`](../../skills/no-comments/SKILL.md) spawns [Comment Sicko](../../agents/comment-sicko.md), a read-only reviewer with a short keep list: license headers, doc comments on a public API, links that explain what code can't, behavior forced by an external dependency you can't reshape. Everything else goes. A surprise in your own code gets no such pass. The comment comes back as a refactor flag, and `pstack:no-comments` fixes the flags it accepts at the root cause. When a comment claims a constraint, "do not remove", the skill offers to encode the claim as a type, test, or lint. Either way, the comment comes out.
[`pstack:no-comments`](../../skills/no-comments/SKILL.md) spawns [Comment Sicko](../../skills/no-comments/references/comment-sicko.md), a read-only reviewer with a short keep list: license headers, doc comments on a public API, links that explain what code can't, behavior forced by an external dependency you can't reshape. Everything else goes. A surprise in your own code gets no such pass. The comment comes back as a refactor flag, and `pstack:no-comments` fixes the flags it accepts at the root cause. When a comment claims a constraint, "do not remove", the skill offers to encode the claim as a type, test, or lint. Either way, the comment comes out.

The division of labor is worth keeping straight. `pstack:deslop` cleans slop out of the code, `pstack:unslop` cleans it out of prose, and `pstack:no-comments` hands the comments to a reviewer who didn't write them.

**Pitfall:** cleanup is not optional polish. A diff with narrating comments and defensive dead weight reads as unfinished to reviewers, and the extra code is where the next bug hides. If the diff feels padded, say `deslop it` before you commit, not after review calls it out.
**Pitfall:** cleanup is not optional polish. A diff with narrating comments and defensive dead weight reads as unfinished to reviewers, and the extra code is where the next bug hides. If the diff feels padded, say `pstack:deslop it` before you commit, not after review calls it out.

Next: [Verify and ship](./06-verify-and-ship.md).
2 changes: 1 addition & 1 deletion docs/guide/10-recipes-and-pitfalls.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ URL: https://raw.githubusercontent.com/cursor/plugins/main/pstack/docs/guide/10-

Prompts worth copying, then the mistakes everyone makes once. Swap in your own paths and finish conditions. The recipes are deliberately informal. That's how they get typed in practice, and the skills read intent fine.

![She tastes a finished dish while robots cook from a recipe box, with pinned cards reading /how, /tdd, and /loop above the counter.](./images/recipes.jpg)
![She tastes a finished dish while robots cook from a recipe box, with pinned cards reading /how, /tdd, and cronjob above the counter.](./images/recipes.jpg)

## Understand an unfamiliar subsystem

Expand Down
Binary file added docs/guide/images/recipes.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
49 changes: 49 additions & 0 deletions scripts/check_refs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/usr/bin/env python3
"""Reject broken markdown links, skill references, playbook references, Cursor leftovers, and prose drift."""
from pathlib import Path
import re, sys

ROOT = Path(__file__).resolve().parents[1]
SKILLS = {p.name for p in (ROOT / "skills").iterdir() if (p / "SKILL.md").is_file()}
errors = []
files = list(ROOT.rglob("*.md"))

CURSOR_LEFTOVERS = re.compile(
r"\.cursor/|~/\.cursor|pstack-models\.mdc|AskQuestion|subagent_type"
r"|\bTask tool\b|\bTask\(|`Read`|`Grep`|`Glob`|`Shell`|StrReplace"
r"|agent-transcripts|orch\.ts|Hermes session history/"
)

for path in files:
text = path.read_text(encoding="utf-8")
no_fenced = re.sub(r"```.*?```", "", text, flags=re.S)
clean = re.sub(r"`[^`]*`", "", no_fenced)
for target in re.findall(r"\[[^]]*\]\(([^)]+)\)", clean):
target = target.split("#", 1)[0]
if target and not re.match(r"(?:https?:|mailto:|#|<)", target):
if not (path.parent / target).resolve().exists():
errors.append(f"{path.relative_to(ROOT)}: broken link {target}")
for name in re.findall(r"pstack:([A-Za-z0-9_-]+)", text):
if name not in SKILLS:
errors.append(f"{path.relative_to(ROOT)}: missing skill pstack:{name}")
for name in re.findall(r"file_path=['\"]playbooks/([^'\"]+)", clean):
if not (ROOT / "skills/poteto-mode/playbooks" / name).exists():
errors.append(f"{path.relative_to(ROOT)}: missing playbook {name}")
if path.is_relative_to(ROOT / "skills") or path.is_relative_to(ROOT / "docs"):
if "—" in text:
errors.append(f"{path.relative_to(ROOT)}: em dash")
for lineno, line in enumerate(text.splitlines(), 1):
if "github.com/cursor/plugins" in line:
continue
m = CURSOR_LEFTOVERS.search(line)
if m:
errors.append(f"{path.relative_to(ROOT)}:{lineno}: Cursor leftover {m.group(0)!r}")
if path.name == "SKILL.md":
head = text.split("---", 2)[1] if text.startswith("---") else ""
for field in ("name:", "description:", "version:", "author:", "license:", "platforms:", "metadata:", "hermes:", "tags:", "related_skills:"):
if field not in head: errors.append(f"{path.relative_to(ROOT)}: missing frontmatter {field}")
m = re.search(r"^description:\s*[\"']?(.*?)[\"']?\s*$", head, re.M)
if m and len(m.group(1).strip('"')) > 60: errors.append(f"{path.relative_to(ROOT)}: description over 60 chars")
if errors:
print("\n".join(errors)); sys.exit(1)
print(f"checked {len(files)} markdown files, {len(SKILLS)} skills")
Loading
Loading