Skip to content
Back to skills

Claude Md From Profile

ASecurity

Generates the project's CLAUDE.md block FROM operator-profile.yaml - floor first (what the AI does not decide), then where things live, the rules of done and the flow. Idempotent, signed; refuses to overwrite a hand-edited block

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 24, 2026
ai-agentspythongobashrailsgit

Security analysis

A100/100

Scanned September 24, 2026

npx -y skills add rusharlabs/house-party-protocol --skill claude-md-from-profile --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Claude Md From Profile?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Claude Md From Profile
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/rusharlabs-claude-md-from-profile/badge)](https://www.skillsdirectory.com/skills/rusharlabs-claude-md-from-profile)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: claude-md-from-profile
description: Generates the project's CLAUDE.md block FROM operator-profile.yaml - floor first (what the AI does not decide), then where things live, the rules of done and the flow. Idempotent, signed; refuses to overwrite a hand-edited block
---

> **Auto-Trigger:** When creating or changing `operator-profile.yaml`; when opening a project that has a profile and no CLAUDE.md; when a project's AI ignores a gate the profile declares
> **Keywords:** "claude.md", "generate claude.md", "project instructions", "operator profile", "operator-profile", "floor", "what the AI does not decide", "regenerate block", "signature"
> **Priority:** HIGH
> **Tools:** Bash, Read
> **Related doctrine:** `rules/partial-autonomy-slider.md` (the HOW MUCH axis - `autonomy.by_action`), `rules/gateguard.md` (protected paths, blocked families), `rules/verification-before-completion.md` (the rules of done).

# claude-md-from-profile - CLAUDE.md is born from the profile, not the other way round

`operator-profile.yaml` already says everything that matters: which action is level 0, which
path is protected, which command family is blocked, where the inbox and the logs live, what
"done" requires. But the profile is read by **hooks and scripts** - the AI that opens the project
reads **CLAUDE.md**. Without this skill the two diverge in silence: the hook blocks what
CLAUDE.md never said was forbidden, and the AI learns to work around the gate instead of
respecting it.

This skill generates a **marked block** inside CLAUDE.md, with the floor in the first section.
What the person wrote outside the markers is never touched.

## When NOT to Activate
- The project has no `operator-profile.yaml` - create the profile first (`cp profile.example.yaml operator-profile.yaml`). The script refuses to generate from a profile that does not exist, and there is no hidden default.
- You want to write prose in CLAUDE.md - write it **outside** the markers; inside them the content is derived and will be regenerated.
- The block was hand-edited on purpose and you have not yet decided whether the edit goes to the profile - settle that first; `--force` erases the edit.

## Contract

**INPUT:** an `operator-profile.yaml` (found from the cwd, via `OPERATOR_PROFILE=`, or via `--profile`). No key is mandatory - a section without data is omitted, not invented.

**OUTPUT:** a block `<!-- operator-kit:claude-md:begin -->` … `<!-- operator-kit:claude-md:end -->` written to `./CLAUDE.md` (or `--out`). The last line of the block is the **signature** (profile hash + date). The example profile generates the block with a warning on the first line.

**EXIT CODES**

| Exit | Meaning |
|---|---|
| 0 | block written (new/replaced) or **no-op** - CLAUDE.md was already up to date with the profile |
| 1 | the existing block was **hand-edited** (signature does not match). Nothing written. See the diff with `--dry-run`; to overwrite, `--force` |
| 2 | usage or invalid profile: no profile found, unreadable YAML, PyYAML missing, unknown flag |
| 3 | the machine refused to write `CLAUDE.md` - the whole content is in `INSTRUCOES-DO-CLAUDE.md` next to it; rename it |

**STATE IT TOUCHES:**

| Resource | Reads/Writes | Purpose |
|---|---|---|
| `operator-profile.yaml` | Reads | the single source of the block |
| `CLAUDE.md` (or `--out`) | Writes - **only between the markers** | the generated block; text outside the markers is preserved byte for byte |
| `INSTRUCOES-DO-CLAUDE.md` | Writes (only on exit 3) | fallback when writing to `CLAUDE.md` is refused |

## Process
1. **See what would come out** before writing - `--dry-run` prints the whole block to stdout and touches nothing.
2. **Generate** at the project root: `python "${CLAUDE_PLUGIN_ROOT}/scripts/claude_md_from_profile.py"`. If CLAUDE.md already exists, the block goes at the **top**, before the person's text.
3. **Read the floor** that came out (`## WHAT YOU DO NOT DECIDE`). If something there is wrong, the fix is **in the profile**, never in the block - regenerate afterwards.
4. **Put it in the project setup** (SessionStart hook, `make setup`, whatever exists): running it again is cheap and idempotent - exit 0 with `no-op` when nothing changed.
5. **On exit 1**, someone hand-edited the block. Compare with `--dry-run`, carry what is worth keeping into the profile, and only then `--force`.

## Executed examples

The four below were really run (2026-09-22) in a temporary directory with
`profile.example.yaml` copied as `operator-profile.yaml` - zero effect on a real
project. The output is the real one.

**1 · First generation - CLAUDE.md is born:**

```
$ python "${CLAUDE_PLUGIN_ROOT}/scripts/claude_md_from_profile.py"
claude_md_from_profile: block new in CLAUDE.md (source: operator-profile.yaml)
$ echo $?
0
```
<!-- executed: 2026-09-22 · exit=0 -->

The block starts with the floor, and the floor comes from the profile, key by key - each line
says where it came from:

```
## WHAT YOU DO NOT DECIDE
- **rm_live_code**: level 0 (`autonomy.by_action.rm_live_code`). You PROPOSE, you do not execute. The person authorises.
- Any write to `settings*.json` falls into an automatic human gate (`autonomy.sensitive_paths_auto_gate`). Do not route around it.
- `src/**` is protected (`guardrails.protected_paths`): do not delete, do not move, do not mass-rewrite.
- Branch `main` is protected (`guardrails.protected_branches`): never push directly, never force.
- The command family **git-push-force** is BLOCKED (`guardrails.block_families`). Urgency is not an exception.
```

**2 · Run again without changing anything - the idempotence proof:**

```
$ python "${CLAUDE_PLUGIN_ROOT}/scripts/claude_md_from_profile.py"
claude_md_from_profile: CLAUDE.md is already up to date with operator-profile.yaml (no-op)
$ echo $?
0
```
<!-- executed: 2026-09-22 · exit=0 -->

No-op, not "replaced": the file is not rewritten, the mtime does not move, and a
SessionStart hook can call this every session without dirtying `git status`.

**3 · Someone hand-edited the block - the refusal:**

```
$ sed -i 's/## THE FLOW/## THE FLOW (mexi aqui)/' CLAUDE.md
$ python "${CLAUDE_PLUGIN_ROOT}/scripts/claude_md_from_profile.py"
claude_md_from_profile: the block in CLAUDE.md was HAND-EDITED (the signature does not match). Not overwriting. See what would change with --dry-run; to overwrite anyway, --force.
$ echo $?
1
```
<!-- executed: 2026-09-22 · exit=1 -->

The signature is a hash of the block's content: any byte changed between the
markers makes the script stop. That is what keeps an automatic regeneration from silently
erasing a fix someone made by hand and has not yet carried into the profile.

**4 · Profile that does not exist - there is no hidden default:**

```
$ python "${CLAUDE_PLUGIN_ROOT}/scripts/claude_md_from_profile.py" --profile does-not-exist.yaml
claude_md_from_profile: unreadable profile ([Errno 2] No such file or directory: 'does-not-exist.yaml'). I do not fix it blind - correct the YAML.
$ echo $?
2
```
<!-- executed: 2026-09-22 · exit=2 -->

## Proof

```bash
python "${CLAUDE_PLUGIN_ROOT}/scripts/claude_md_from_profile.py" --self-test   # -> self-test OK · exit 0
```

The self-test covers: the example profile generates the warning on line 1 and the floor comes
**before** the inventories; an empty profile generates a valid block without inventing a
section; the person's content outside the markers survives `new` and `replaced` byte for
byte; the second run is `no-op`; a hand edit inside the block is `refused` and `--force`
overrides it; the signature changes when the profile changes.

**Operational success criterion:** the AI that opens the project reads, in CLAUDE.md, the same
level-0 list that the autonomy hook blocks - `grep -c "level 0" CLAUDE.md` equal to the
number of actions with `level: 0` in the profile.

## Known failures / limits
- Generates **only the block**. Project description, code conventions, test commands: those belong to the person, outside the markers. The skill does not write what the profile does not know.
- Automatic profile discovery walks up from the cwd to the root; in a monorepo with two profiles, the closest one wins. Use `--profile` when that matters.
- Requires PyYAML (the profile is YAML). Without it, exit 2 with the message - there is no fallback parser.
- The text the script writes is ASCII by choice (no accents): the block goes into the system prompt of any harness, and some still trip on encoding. The profile's **values** go in as they are - the script does not transliterate what is yours.
- The file's EOL belongs to the person: a `CLAUDE.md` in CRLF stays CRLF after the block, and the signature matches in both cases. Without that, every file saved by a Windows editor would be "hand-edited" forever.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.