Skip to content
Merged
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
26 changes: 26 additions & 0 deletions .claude/commands/designqc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
description: Screenshot-based design review of the running app via openwolf designqc
argument-hint: [--url <url>] [--routes <routes>]
---

Arguments: $ARGUMENTS

Evaluate and improve the design/UI of this app:

1. Run `openwolf designqc` via Bash to capture screenshots (pass through any arguments given above).
- The command auto-detects a running dev server, or starts one from package.json if needed.
- Use `--url <url>` only if auto-detection fails.
- Compressed JPEG screenshots land in `.wolf/designqc-captures/`; full pages are captured as sectioned viewport-height images (top, section2, ..., bottom).
2. Read the captured screenshots from `.wolf/designqc-captures/` with the Read tool.
3. Evaluate against modern standards (Shadcn UI, Tailwind, clean React patterns):
- Spacing and whitespace consistency
- Typography hierarchy and readability
- Color contrast and accessibility (WCAG)
- Visual hierarchy and focal points
- Component consistency
- Whether the design looks generic ("white-coded", no personality)
4. Provide specific, actionable feedback with fix suggestions.
5. If the user approves, implement the fixes directly in their code.
6. Re-run `openwolf designqc` to verify the improvement.

Token awareness: each screenshot costs about 2,500 tokens. For large apps, use `--routes / /specific-page` to limit captures.
16 changes: 16 additions & 0 deletions .claude/commands/handoff.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Regenerate `.wolf/STATUS.md` as a session handoff document. $ARGUMENTS

Build it from the session's actual state, not from memory of the conversation alone:

1. Read the current `.wolf/STATUS.md` to preserve its structure and any still-relevant open items.
2. Run `git status --short` and `git log --oneline -8` to see what actually changed.
3. Skim the latest session block of `.wolf/memory.md` for the action log.

Then rewrite `.wolf/STATUS.md` with:

- `## ✅ Done` : what this session completed, one line each, concrete (files, features, fixes). Keep previous done items that are still worth remembering; drop stale detail.
- `## 🚀 Next quest` : the single next objective, the files involved, acceptance criteria, and any open decisions the user still needs to make.
- `## Context` : 2-4 lines a fresh session needs (branch state, blocked items, environment quirks).
- Bump the date.

Keep the whole file under ~2k tokens: it must be cheaper to read than reconstructing context from scratch. Do not pad it; a short honest handoff beats a complete-looking one.
41 changes: 41 additions & 0 deletions .claude/commands/reframe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
description: OpenWolf's design brain — pick/migrate UI frameworks or audit/fix UI against the anti-generic design principles
argument-hint: [migrate [framework] | audit [target] | fix [target]]
---

Arguments: $ARGUMENTS

Read `.wolf/reframe-frameworks.md` first — it contains the **Design Principles
(anti-generic mandate)**, the framework knowledge base, and the migration prompts.
Use `.wolf/anatomy.md` to locate UI files instead of scanning.

Pick the mode from the arguments (default: `migrate` if a framework is named or the
user is choosing one; otherwise ask which mode they want):

## Mode: migrate [framework]
Framework selection and migration.
1. If no framework is named, ask the Decision Questions from the knowledge file
(stop early once the answer narrows to 1–2 options) and recommend one.
2. Use that framework's prompt from the knowledge file, adapted to this project's
real structure via `.wolf/anatomy.md`.
3. The Design Principles override anything generic in the prompt: no template
hero→features→CTA structures, no stock palettes — distinctive by default.

## Mode: audit [target]
Walk the target (default: the whole UI) and flag every match against the AI-tell
blocklist in the Design Principles: purple gradient heroes, glassmorphism-everything,
emoji headings, generic 3-column feature grids, stock Tailwind palette, Inter for
every role, template SaaS structure, filler microcopy. Produce a findings table —
component, tell matched, severity, specific replacement direction — and end with the
3 changes that would most increase distinctiveness.

## Mode: fix [target]
Run the audit, then fix findings in severity order. Fixes must move toward, not merely
away: typography chosen with intent, a palette derived from the product's actual
brand/domain, asymmetry where it serves hierarchy, copy specific to what the product
does, density appropriate to the audience. Preserve the existing framework and
component APIs — this is a design pass, not a rewrite. After each fix, state what
changed and why it reads as designed-on-purpose.

Acceptance criterion for every mode: **if the result could be swapped onto any other
product without anyone noticing, it fails.**
40 changes: 40 additions & 0 deletions .claude/commands/security-audit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
description: Layered security audit of the current project (dependencies → secrets → injection → authz → report)
argument-hint: [path or scope, e.g. src/api — omit for whole project]
---

Perform a layered security audit of: $ARGUMENTS (if empty: the whole project).

Use `.wolf/anatomy.md` to target files instead of scanning blindly, and check
`.wolf/buglog.json` for previously found security issues before re-reporting them.

Work through the layers in order. For each, report findings before moving on:

## Layer 1 — Dependencies
Run the ecosystem's audit tool (`npm audit` / `pnpm audit` / `pip-audit` / `cargo audit` …).
Flag known-vulnerable versions and unmaintained packages that handle untrusted input.

## Layer 2 — Secrets
Search for hardcoded credentials: API keys, tokens, passwords, connection strings,
private keys. Check committed env files, config files, and test fixtures. Verify
`.gitignore` covers secret-bearing files (.env*, *.pem, *.key, credentials*).

## Layer 3 — Injection surfaces
Find every place external input reaches an interpreter: shell commands built by string
interpolation (exec/execSync with template strings), SQL string concatenation, HTML
injection/XSS sinks, path traversal (user input joined into fs paths), deserialization
of untrusted data, SSRF (user-controlled URLs fetched server-side).

## Layer 4 — AuthN / AuthZ
Map endpoints and privileged operations. Check: missing auth middleware, IDOR (object
IDs without ownership checks), privilege escalation paths, session handling, CORS and
CSRF posture, servers bound to 0.0.0.0 without auth.

## Layer 5 — Report
Produce a severity-ranked table (Critical/High/Medium/Low): finding, file:line, attack
scenario, concrete fix. Log confirmed vulnerabilities to `.wolf/buglog.json` with tag
"security". Offer to fix Critical and High items immediately.

Rules: verify each finding against the actual code before reporting (no
pattern-match-only findings); prefer minimal, targeted fixes; never weaken existing
security to silence a warning.
94 changes: 94 additions & 0 deletions .claude/rules/no-real-infrastructure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Never Commit Real Infrastructure Identifiers or Personal Data

> **This is a public OSS repository.** Anything committed here is published,
> indexed, and permanent: a later commit that removes it does not un-publish
> it. Real hostnames, addresses, account identifiers, and personal data from
> the maintainer's own environment MUST NOT appear in tracked files.

This is not a style preference. A real hostname in a public repo is a free
reconnaissance gift: it names a host, implies what runs on it, and often
reveals the naming scheme for every other host beside it. A home directory
path names a user account on a real machine.

## The rule

**Never write a real hostname, IP address, username, home directory path,
email address, or account identifier belonging to the maintainer's
environment into any tracked file.** This applies to action code, workflows,
tests, docs, examples, scripts, comments, commit messages, and changelog
entries, everywhere, with no exceptions for "it's just a doc comment."

If you need a concrete value to make an example readable, use a placeholder
from the table below.

## Placeholders to use

| Kind | Use | Never use |
| --- | --- | --- |
| Hostname / domain | `bar.foo.io`, `baz.foo.io`, `example.com` | any real host the maintainer operates |
| Documentation IPv4 | `192.0.2.x`, `198.51.100.x`, `203.0.113.x` (RFC 5737) | any real routable address |
| Documentation IPv6 | `2001:db8::/32` (RFC 3849) | any real routable address |
| Private IPv4 | `10.0.0.x`, `192.168.x.x` (RFC 1918), only when the example is *semantically* a private network | a real private address that is actually in use |
| Username | `admin`, `runner`, `svc-example` | a real login |
| Home / project directory | `$CLAUDE_PROJECT_DIR`, `$HOME`, `~`, a repo-relative path | `/Users/<name>/...`, `/home/<name>/...` |
| Registry | `ghcr.io/firestoned/<image>`, `registry.internal:5000` | a real private registry host |

The RFC 5737 ranges are the right answer for "make up an IP": they are
reserved for documentation and are guaranteed never routable. A *genuinely*
random IP is worse than a reserved one: it probably belongs to somebody.

## The one legitimate exception

The `Copyright (c) 2025 Erick Bourgeois, firestoned` SPDX headers are an
author identity the maintainer chose to publish. Leave them alone. The
distinction is whether the string names *a host you could connect to* or *an
account on a real machine*.

## OpenWolf, opencode, and Claude Code files

The OpenWolf and opencode tooling (`.wolf/`, `.opencode/`, `.claude/`) is
generated on the maintainer's machine and tends to bake in absolute paths.
Every tracked file in those directories is held to the same rule:

- **Hook commands use `$CLAUDE_PROJECT_DIR`**, never an absolute path. Claude
Code sets it to the project root for every hook:

```json
// ✅ GOOD
"command": "node \"$CLAUDE_PROJECT_DIR/.wolf/hooks/session-start.js\""

// ❌ BAD: names a real user account on a real machine
"command": "node \"/Users/<name>/dev/github-actions/.wolf/hooks/session-start.js\""
```

`openwolf init` (and its upgrades) rewrite `.claude/settings.json` with
absolute paths. After running either, put `$CLAUDE_PROJECT_DIR` back before
committing.
- **`.wolf/anatomy.md` and `.wolf/anatomy-index.json`** accumulate entries for
any file a session touched, including scratchpads and plan directories
outside the repo. Run `openwolf scan` (a full rescan from the tree alone)
before committing a change to either.
- **`.wolf/memory.md`, `.wolf/STATUS.md`, `.wolf/cerebrum.md`, and
`.wolf/buglog.json`** are written from session activity and can quote
command lines, error messages, and paths verbatim. Read the diff before
committing them.
- `.claude/settings.local.json` is machine-local and must never be committed.

## Getting a real value in without committing it

Take the value from the environment at runtime and document it with a
placeholder. In a workflow, that means a secret or a variable
(`${{ vars.REGISTRY_HOST }}`), never a literal. In a shell script, default to
empty and require the caller to supply it, or derive it at runtime.

## Before finishing any task

Grep your own diff. It costs one command:

```sh
# Real-infrastructure and personal-data sweep over staged files
git diff --cached -U0 | rg -i '/Users/|/home/[a-z]|jeb\.ca|gmail\.com|\b(?:\d{1,3}\.){3}\d{1,3}\b'
```

Flag anything that is not in the placeholder table above. If you are unsure
whether a value is real, assume it is and replace it.
10 changes: 10 additions & 0 deletions .claude/rules/openwolf.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
description: OpenWolf protocol enforcement, active on all files
globs: **/*
---

- To locate a symbol or file, run `openwolf find <name>` first (ranked shortlist, under 1k tokens). For one file's description and symbol ranges: `openwolf find --file <path>`. Never read .wolf/anatomy.md whole; it is an index.
- Check .wolf/cerebrum.md Do-Not-Repeat list before generating code (grep "## Do-Not-Repeat"); after a user correction, update cerebrum.md immediately.
- Do NOT manually update .wolf/anatomy.md or .wolf/memory.md; the OpenWolf hooks maintain them.
- BEFORE fixing any bug: run `openwolf bug search "<error>"` or grep .wolf/buglog.json. AFTER fixing one: log it there (error_message, root_cause, fix, tags).
- When resuming a session, read .wolf/STATUS.md first; regenerate it with /handoff when a quest finishes.
140 changes: 140 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
{
"hooks": {
"SessionStart": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "node \"$CLAUDE_PROJECT_DIR/.wolf/hooks/session-start.js\"",
"timeout": 5
}
]
}
],
"UserPromptSubmit": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "node \"$CLAUDE_PROJECT_DIR/.wolf/hooks/user-prompt-submit.js\"",
"timeout": 5
}
]
}
],
"PreToolUse": [
{
"matcher": "Read",
"hooks": [
{
"type": "command",
"command": "node \"$CLAUDE_PROJECT_DIR/.wolf/hooks/pre-read.js\"",
"timeout": 5
}
]
},
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "node \"$CLAUDE_PROJECT_DIR/.wolf/hooks/pre-write.js\"",
"timeout": 5
}
]
},
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "node \"$CLAUDE_PROJECT_DIR/.wolf/hooks/pre-bash.js\"",
"timeout": 5
}
]
}
],
"PostToolUse": [
{
"matcher": "Read",
"hooks": [
{
"type": "command",
"command": "node \"$CLAUDE_PROJECT_DIR/.wolf/hooks/post-read.js\"",
"timeout": 5
}
]
},
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "node \"$CLAUDE_PROJECT_DIR/.wolf/hooks/post-write.js\"",
"timeout": 10
}
]
},
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "node \"$CLAUDE_PROJECT_DIR/.wolf/hooks/post-bash.js\"",
"timeout": 10
}
]
}
],
"PostToolBatch": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "node \"$CLAUDE_PROJECT_DIR/.wolf/hooks/post-batch.js\"",
"timeout": 5
}
]
}
],
"PreCompact": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "node \"$CLAUDE_PROJECT_DIR/.wolf/hooks/precompact.js\"",
"timeout": 5
}
]
}
],
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "node \"$CLAUDE_PROJECT_DIR/.wolf/hooks/stop.js\"",
"timeout": 10
}
]
}
],
"SessionEnd": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "node \"$CLAUDE_PROJECT_DIR/.wolf/hooks/session-end.js\"",
"timeout": 10
}
]
}
]
}
}
Loading
Loading