From 670e25358483eb9c36004af652eafc62ccf8c56b Mon Sep 17 00:00:00 2001 From: didericis Date: Mon, 4 May 2026 11:50:49 -0400 Subject: [PATCH] Initial import of personal Claude Code skills Co-Authored-By: Claude Opus 4.7 (1M context) --- didiosphere/SKILL.md | 30 ++++++ init-entry/SKILL.md | 114 ++++++++++++++++++++ init-free-agent/SKILL.md | 200 +++++++++++++++++++++++++++++++++++ init-prd/SKILL.md | 126 ++++++++++++++++++++++ init-project/SKILL.md | 222 +++++++++++++++++++++++++++++++++++++++ init-work/SKILL.md | 181 +++++++++++++++++++++++++++++++ tag-entries/SKILL.md | 117 +++++++++++++++++++++ 7 files changed, 990 insertions(+) create mode 100644 didiosphere/SKILL.md create mode 100644 init-entry/SKILL.md create mode 100644 init-free-agent/SKILL.md create mode 100644 init-prd/SKILL.md create mode 100644 init-project/SKILL.md create mode 100644 init-work/SKILL.md create mode 100644 tag-entries/SKILL.md diff --git a/didiosphere/SKILL.md b/didiosphere/SKILL.md new file mode 100644 index 0000000..61bf661 --- /dev/null +++ b/didiosphere/SKILL.md @@ -0,0 +1,30 @@ +--- +name: didiosphere +description: Look up information about a location (OS instance) or device (physical machine) in the user's didiosphere repo at ~/Code/didiosphere. Use when the user invokes /didiosphere or asks about a specific location/device by its Greek-named identifier (e.g. "delphi", "papyros"). +--- + +# Didiosphere lookup + +The user has a repository at `/Users/didericis/Code/didiosphere` that documents their personal network topology. Entities live in two directories: + +- `locations//` — an OS instance (often tied to a specific device). Named after ancient Greek islands/cities. +- `devices//` — a physical computer. Named after ancient Greek nouns. + +The argument to this skill is a single name (e.g. `delphi`, `papyros`). It may match a location, a device, or — rarely — both. + +## What to do + +1. Take the name from the skill argument. Lowercase it for matching. +2. Check both `/Users/didericis/Code/didiosphere/locations//` and `/Users/didericis/Code/didiosphere/devices//`. Report which (if either) exists. +3. For each match, Read every non-binary file in that directory tree (typically `README.md`, `JOURNAL.md`, and similar). Summarize what the entity is, its purpose, and any noteworthy state from the journal. +4. If the name doesn't match any directory, list the available locations and devices so the user can pick. + +## Hard rules (from the repo's CLAUDE.md) + +- **Never** read, cat, grep, or otherwise inspect any file ending in `.pem` or `.pub` under `locations/*/ssh/`. Treat them as opaque. +- Filenames in those `ssh/` directories are fine to list and discuss (filenames encode routing metadata) — only file _contents_ are off-limits. +- If a permission error fires on a `.pem`/`.pub` path, that is the repo's settings working as intended. Do not ask the user to override it. + +## Output + +Lead with one sentence identifying the entity (location vs device, what it's for). Then a tight summary of what the docs say. If a `JOURNAL.md` exists, note the most recent entry. Keep it short — the user knows their own repo; they want a refresher, not a recitation. diff --git a/init-entry/SKILL.md b/init-entry/SKILL.md new file mode 100644 index 0000000..e872177 --- /dev/null +++ b/init-entry/SKILL.md @@ -0,0 +1,114 @@ +--- +name: init-entry +description: Add a new timestamped entry to the project journal at ./docs/JOURNAL.md in the current working directory, creating the file (and `docs/`) if missing. Use when the user invokes /init-entry or asks to "log this in the journal", "add a journal entry", "record this", "write this down", or similar. Entries are stream-of-thought prose, not templates — newest on top, append-only. +--- + +# Add a journal entry + +Adds a new entry to `./docs/JOURNAL.md` in the current working directory. The journal is an append-only log of unstructured stream-of-thought entries, newest on top. Each entry is a timestamp heading followed by freeform prose — whatever was on the mind worth capturing. No title, no template, no required sections. + +## Format + +Each entry looks like: + +``` +## YYYY-MM-DD HH:MM + +[github](tag://github) [postgres](tag://postgres) + + +``` + +**Tag line rules:** + +- Tags appear on the line immediately under the timestamp heading (no blank line between header and tags), then a blank line before the body. +- Format is markdown links with the custom `tag://` URI scheme: `[name](tag://name)`. Multiple tags are space-separated on one line. +- The label and the URI value are usually the same word; differ only when there's a real reason (e.g. `[GitHub Actions](tag://github)`). +- **Tags are conditional.** Include them only when the entry has a coherent theme worth grouping across future entries. If nothing themes-up cleanly, omit the line entirely. Don't tag everything. + +## Step 1 — Get the entry text from the invoker + +The body of the entry must come from the invoker, verbatim. Your role is mechanical — timestamp, format, insert — not authorial. Do not draft, paraphrase, expand, summarize, or "improve" the prose. Do not draft from conversation context. Do not offer a draft for the invoker to react to. Do not propose what the entry "could be." + +Get a timestamp by shelling out: `date '+%Y-%m-%d %H:%M'`. Use that exact value as the entry heading. + +If the invoker passed content alongside the command (e.g. `/init-entry switched to postgres because sqlite locking was killing the worker pool`), use it as the body verbatim. If they passed nothing, ask what they want to capture and wait for their words. + +Show the entry as it will be written (their timestamp + their prose) before writing to disk, so they can confirm formatting and insertion. Don't edit the content unprompted. + +## Step 2 — Locate or create the journal + +From the current working directory: + +1. If `./docs/JOURNAL.md` exists → Step 3 (insert). +2. If `./docs/` exists but no `JOURNAL.md` → Step 4 (create). +3. If `./docs/` doesn't exist → create the directory, then Step 4. + +## Step 3 — Insert into existing journal + +Read `./docs/JOURNAL.md`. Find the insertion point: + +- Scan for the first line matching `^## ` (an existing entry heading). +- Insert the new entry **before** that line, separated by one blank line above and below. +- If no `^## ` line exists, append the entry to the end of the file with one blank line separating it from prior content. + +Never edit, reorder, or delete existing entries. The journal is append-only. + +## Step 4 — Create a new journal file + +Write `./docs/JOURNAL.md` with this skeleton followed by the new entry, so the format stays consistent regardless of which skill seeded the file: + +``` +# Journal + +Append-only stream of thought. Newest entries on top. Each entry is a timestamp +followed by freeform prose. Tag entries with `[name](tag://name)` links under +the header — only when a coherent theme emerges. Otherwise just write. + +## YYYY-MM-DD HH:MM + + +``` + +Unix line endings, no trailing whitespace, no emojis. + +## Step 5 — Commit and push + +After the entry is written, commit and push it so the journal stays in sync with the remote. This step is best-effort: report failures inline, never retry, and never unwind the on-disk write. + +1. If `git rev-parse --git-dir` fails (the cwd is not inside a git repo), skip this step entirely and note in the report that the entry was written but not committed. +2. Stage only the journal file: + ``` + git add docs/JOURNAL.md + ``` + Never use `git add -A` or `git add .` — unrelated working-tree changes are not the journal's concern and must not be swept into the commit. +3. Commit with a mechanical message derived from the timestamp (and tags, if any). Pass it via HEREDOC so quoting stays clean: + ``` + git commit -m "$(cat <<'EOF' + Journal: 2026-05-02 03:28 + EOF + )" + ``` + With tags, append them in parens after the timestamp: `Journal: 2026-05-02 03:28 (auth, postgres)`. The message must not paraphrase or summarize the entry body — the same no-synthesis rule that protects the body protects the commit log. +4. If the current branch has an upstream (`git rev-parse --abbrev-ref --symbolic-full-name @{u}` succeeds), run `git push`. If no upstream is configured or the push fails for any reason, report it and continue. Do not set an upstream, force-push, or otherwise paper over the failure. + +If any of steps 2–4 fail, surface the failure in the report but do not amend, reset, or modify the on-disk state. + +## Step 6 — Report + +One short line: path to the file, "created" or "updated", and the timestamp. Append commit sha (or "not committed: ") and push result (or "no upstream" / "push failed: "). Stop there. + +## Hard rules + +- **Human-authored body.** Entry prose comes from the invoker, not from you. Don't draft, paraphrase, expand, or summarize. If invoked without content, ask. The journal is a record of the human's thinking — your synthesis doesn't belong in it. +- **Append-only.** Never edit or delete existing entries. To revise a past thought, write a new entry that references the prior timestamp. +- **No title in the heading.** Just the timestamp. Resisting the urge to title each entry is the point — entries are stream of thought, not curated essays. +- **No template inside the body.** Don't write `**What changed:**`, `**Considered:**`, `**Ruled out:**` as sub-headings. Plain prose. +- **Tags are conditional and live directly under the header.** Use `[name](tag://name)` markdown link format. Omit the tag line when no coherent theme exists. Don't invent themes. +- **Today's timestamp only.** Don't backdate. +- **No emojis.** +- **Don't volunteer to add anything else** (indexes, table of contents, README links, summaries). The journal stays text-only. +- **Mechanical commit message.** The commit message is derived from the timestamp and tags only. Never paraphrase the entry body into the subject line. +- **Scoped commit.** Only `docs/JOURNAL.md` is staged. Never sweep in unrelated working-tree changes, never amend a previous commit, never bypass hooks. diff --git a/init-free-agent/SKILL.md b/init-free-agent/SKILL.md new file mode 100644 index 0000000..c49712f --- /dev/null +++ b/init-free-agent/SKILL.md @@ -0,0 +1,200 @@ +--- +name: init-free-agent +description: End-to-end workflow for delivering a new feature where the implementation runs inside a sandboxed iso-claude container instead of a host subagent — clean tree → PRD → branch → PRD-only PR → pick iso-claude agent → iso-claude implements (commits + pushes from inside the container) → host reviews → user reviews the review → merge. Use when the user says "start a new feature in an iso-claude agent", "kick off the free-agent workflow", or invokes /init-free-agent. Variant of /init-work that swaps the host implementation subagent for an iso-claude container session via the iso-claude MCP server. +--- + +# Agentic feature workflow (free-agent variant) + +Same eight steps as `/init-work`, with two changes: + +- **Step 4.5 (new):** ask the user which iso-claude agent to use, from the live list returned by `mcp__iso-claude__list_agents`. +- **Step 5 (modified):** delegate implementation to a sandboxed iso-claude container via `start_session` / `send_message` / `end_session` with `cwd: true`, instead of launching a host `Agent`. + +Everything else (preflight, clean main, PRD, branch, PR open, review, human checkpoint, merge) stays on the host. Do not skip steps. Do not collapse Step 7 — the human review of the agent's review is the load-bearing checkpoint. + +This skill orchestrates other tools: `/init-prd` for Step 2, the `mcp__iso-claude__*` tools for Step 5, and `/review` for Step 6. The skill itself should not implement the feature — delegate. + +## What the iso-claude variant trades off + +The container has **node, claude, and git** but no PHP, no composer, no docker, no glab. The implementation agent inside therefore cannot run `composer code-quality`, `composer phpunit-isolated`, or any docker-based test suite. Quality gates run on the host at Step 6 / Step 7 instead. + +If the feature requires running the project's quality gates *during* implementation (e.g. iterating against a failing test), prefer `/init-work` instead. + +## Step 0: Preflight + +Before doing anything destructive, check the basics. Stop and ask the user if any check fails. + +- `git rev-parse --is-inside-work-tree` → must be `true`. +- `git remote get-url origin` → must succeed. Save the URL; you'll need it for host detection. +- Detect host from the origin URL (same logic as `/init-work`): + - `github.com` → **GitHub**, CLI is `gh`. + - `gitlab.com` or any host with `gitlab` in the FQDN, or known self-hosted GitLab (e.g. `labs.gauntletai.com` for this repo) → **GitLab**, CLI is `glab`. + - `codeberg.org` or hosts running Gitea → **Gitea**, CLI is `tea`. + - Anything else → **stop and ask the user** which host this is. +- Verify the host CLI is installed (`command -v `). If missing, stop. +- Verify the iso-claude MCP server is reachable: call `mcp__iso-claude__list_agents`. If the call errors, stop and ask the user to check that the MCP server is registered and the iso-claude repo is built (`cd ~/Code/iso-claude/mcp-server && npm install && npm run build`). +- Identify the main branch: `git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@'`. Fall back to `master` then `main` if that fails. + +## Step 1: Land on a clean main/master + +Make the working tree match origin's main branch with no local clutter. + +1. **Capture the current branch** (`git rev-parse --abbrev-ref HEAD`). +2. **Stash dirty state.** If `git status --porcelain` is non-empty, run: + ```bash + git stash push --include-untracked --message "init-free-agent preflight $(date -u +%FT%TZ)" + ``` + Tell the user explicitly that you are about to stash *and what files are in scope* — quote `git status` first. Do not silently stash. +3. **Switch and update.** `git checkout
` then `git pull --ff-only origin
`. If the pull fails (non-FF), stop and ask — do not force. + +If the user objects to stashing, abort the skill and let them resolve it manually first. + +## Step 2: Run the PRD skill + +Invoke the `init-prd` skill via the Skill tool. That skill interrogates the user in three rounds and writes `docs/prds/-.md`. Do not pre-fill answers from prior conversation context — the interrogation is the point. + +When `/init-prd` finishes, capture two values from the file it created: + +- The PRD number `` (the 4-digit prefix on the new file). +- The slug `` (everything after the prefix, before `.md`). + +You will use these in Steps 3, 4, and 5. + +## Step 3: Branch off main + +```bash +git checkout -b prd-- +``` + +If the branch already exists locally or on the remote, stop and ask — never silently reset or rebase someone else's branch. + +Stage and commit the new PRD file: + +```bash +git add docs/prds/-.md +git commit -m "docs(prd): scaffold PRD + +Assisted-by: Claude Code" +``` + +Push with upstream tracking: + +```bash +git push -u origin prd-- +``` + +The PRD file is the only thing in this commit. No code, no scaffolding stubs. + +## Step 4: Open a PR with only the PRD + +Same as `/init-work` Step 4 — open the PR against `
` from the new branch. Title format: `PRD : `. Use the host's CLI (`gh` / `glab` / `tea`). Capture the URL. + +If the CLI is missing or auth fails, stop. Don't fall back to a different host's CLI; don't silently skip the PR step. + +## Step 4.5: Pick an iso-claude agent + +Call `mcp__iso-claude__list_agents`. Present the full list to the user with a one-line note for each (read from `iso-claude.json` if needed) and ask them to pick one. Recommend `implementer` (or any agent the user has configured to forward a push token) when the work needs to commit and push. Other agents may not have `GITLAB_TOKEN` (or equivalent) wired into their env and will fail at the push step. + +Stop and ask if the list is empty or the call errors. + +## Step 5: Delegate implementation to an iso-claude session + +Open a new session and drive it via the MCP tools. + +```text +mcp__iso-claude__start_session(agent="", cwd=true) + → returns { handle, agent, session_id, created_at } +``` + +`cwd: true` causes the iso-claude launcher to copy the host's current project into `/home/node/workspace` inside the container *and* `docker cp` the host's `.git` directory in at run time. The agent inside therefore sees the current branch (`prd--`), the PRD file, and the full history. + +Send one `send_message` with a self-contained prompt that: + +- Names the PRD file path (`docs/prds/-.md`) and the branch (`prd--`) it must commit to. +- States that it is implementing this PRD's *in-scope* items only — open questions stay open, non-goals stay out. +- Tells it to commit and push as it works, **once each semantically-distinct chunk of work is done** (not per-file, not all-at-once at the end). Conventional Commit messages, `Assisted-by: Claude Code` trailer. +- Tells it to honor the workspace's `CLAUDE.md` for coding standards. Note that **it cannot run the project's quality gates** (`composer code-quality`, `composer phpunit-isolated`, the docker test suites, `npm run lint:js`) because PHP, composer, docker, and the project's node deps are not installed in the iso-claude image — those will run on the host at Step 6. The agent should still write code that *would* pass the gates. +- Tells it to push using `GITLAB_TOKEN` (or whatever the chosen agent has wired) **without echoing or interpolating the token value**. The recipe — `git config --global credential.helper store` plus `~/.git-credentials` written via a here-doc / printf so the value never lands on argv — is in the agent profile's prompt. +- Tells it to **stop and report** when done — not to open or merge the PR, not to request review, not to bump versions. The skill drives those steps. +- Tells it to surface any blockers (open questions resurfacing, missing fixtures, ambiguous PRD wording) rather than guessing. + +`send_message` returns a single `SendResult` after the agent emits its `result` event for the turn. There is no streaming visibility — the call blocks until the agent declares the turn finished. For real features this can take many minutes. Do not poll, do not retry, do not call again until the first call returns. + +When `send_message` returns, do **not** end the session yet. First, on the host: + +```bash +git fetch origin prd-- +git log HEAD..origin/prd-- --oneline # verify the agent pushed something +git pull --ff-only origin prd-- # bring the agent's commits to the host +``` + +If the fetch shows no new commits beyond the PRD scaffold, the agent didn't push. Send a follow-up `send_message` asking it to push (or to surface the error if push failed). Don't proceed until the host has the agent's commits. + +Once the host is in sync: + +```text +mcp__iso-claude__end_session(handle="") +``` + +The container is removed. + +## Step 6: Delegate review to the host /review skill + +Invoke the `review` skill via the Skill tool — it is the project's documented PR-review entry point. The host now has the agent's commits locally (Step 5 pulled them) and can run the project's quality gates that the iso-claude container could not. + +If `/review` is unavailable, fall back to launching `Agent` (subagent_type=`general-purpose`) on the host with a prompt that: + +- References the PR URL from Step 4 and the PRD file. +- Asks for a structured review: correctness vs. PRD, adherence to `CLAUDE.md`, test coverage of in-scope items, any security/PHI concerns flagged by project conventions. +- Is told **not to push commits, not to fix issues, and not to merge** — review only, written output only. + +Capture the review output. Save it for Step 7. + +## Step 7: Review the review with the user (interactive) + +Same as `/init-work` Step 7 — non-negotiable human-in-the-loop checkpoint. + +Present the agent's review to the user in a readable form, then walk through it: + +- For each finding, ask: accept (fix it), reject (false positive — explain why), or defer (file as a follow-up). +- Apply accepted fixes directly (or delegate small ones to a fresh `Agent` call on the host), commit and push them on the same branch. **Fixes at this stage run on the host**, not in a fresh iso-claude session — the goal is for the user to see the same commits the reviewer did. +- Reject false positives with a one-line note in your reply so the user can see the reasoning. +- Defer items get added to the PR description as a "Follow-ups" checklist (use the host's CLI to edit the PR body). + +Do not move on to Step 8 until the user explicitly says "ship it" / "merge it" / "looks good". Open questions or ambiguity → stop and ask. + +## Step 8: Merge + +Same as `/init-work` Step 8. **Require explicit user confirmation in this turn.** A "go ahead" from earlier in the conversation does not count. + +Once confirmed, merge using the host's CLI: + +- **GitHub:** `gh pr merge --squash --delete-branch`. +- **GitLab:** `glab mr merge --squash --remove-source-branch`. +- **Gitea:** `tea pr merge --style squash --delete-branch`. + +Default to **squash** unless the user has expressed a different preference for this repo. After merge, return to `
` and `git pull --ff-only`. + +If there is a stash from Step 1, remind the user it's still in `git stash list` and offer to `git stash pop` it. + +## Final report + +One short paragraph naming: + +- The merged PR URL. +- The PRD path. +- The iso-claude agent that did the implementation. +- Any deferred follow-ups recorded on the PR. +- The state of the Step 1 stash (popped, still pending, or none). + +## Hard rules + +- **Never skip Step 7.** The human review of the agent review is non-negotiable. If the user wants to skip it, that's a different workflow — point them at it and stop. +- **Never merge without explicit in-turn confirmation.** Earlier "yes" answers don't carry across the implementation/review steps. +- **Never silently stash.** Always show `git status` and tell the user what's about to be stashed. +- **Never end the iso-claude session before verifying the host has the agent's commits.** If push didn't happen, the work is trapped in the container and lost when it's torn down. +- **Never skip `cwd: true` on `start_session`.** Without it the agent has no workspace and no git context. +- **Never let the implementation agent open or merge the PR.** Those are skill-driven steps, not agent-driven. +- **Never fall back to a different host's CLI.** If `glab` is missing on a GitLab repo, stop — do not use `gh` instead. +- **Never collapse multiple PRDs into one branch.** One PRD per invocation. +- **Honor CLAUDE.md and existing skill outputs.** The PRD that `/init-prd` produces is the source of truth for Step 5; don't rewrite it during implementation. diff --git a/init-prd/SKILL.md b/init-prd/SKILL.md new file mode 100644 index 0000000..2fd3ec5 --- /dev/null +++ b/init-prd/SKILL.md @@ -0,0 +1,126 @@ +--- +name: init-prd +description: Scaffold a new Product Requirements Document (PRD) in docs/prds/ by interrogating the user about the feature, success criteria, and expected services. Use when the user asks to "write a PRD", "scaffold a PRD", "draft a PRD for X", or invokes /init-prd. Interrogates first, then writes — does not guess answers from context. +--- + +# Scaffold a new PRD + +Interrogates the user, then writes a PRD file to `docs/prds/`. The interrogation is the point — do not skip it, do not infer answers from prior conversation. + +## Step 1: Pick the next number + +PRDs are numbered with a 4-digit zero-padded prefix (`0001-`, `0002-`, …). Determine the next number: + +```bash +ls docs/prds/ 2>/dev/null | grep -E '^[0-9]{4}-' | sort | tail -1 +``` + +Next number = highest existing + 1, formatted `%04d`. If `docs/prds/` does not exist, start at `0001`. + +Empty placeholder files (size 0) count as taken — do not overwrite them. If the user wants to fill an existing empty placeholder, they will say so explicitly; otherwise pick the next number after it. + +## Step 2: Interrogate the user + +Ask three rounds of questions using `AskUserQuestion`. Do **not** batch all three rounds into one call — each round's answers shape the next round's framing. Do **not** invent answers from session context, even if the topic was discussed earlier; the user invoked this skill because they want to think through the PRD explicitly. + +Each round below lists the *minimum* questions. Add follow-ups when an answer is vague (e.g. user says "make it faster" → ask "faster than what, measured how?"). + +### Round 1 — What is the feature? + +- **Working title.** A short name. Used in the filename slug. +- **One-sentence summary.** What does this feature do, in plain language? +- **Problem it solves.** What specifically is broken / missing / painful today? Who feels it? +- **Out of scope.** What might a reader assume is included that is *not*? + +### Round 2 — Success criteria + +- **How do we know it works?** Concrete, observable signals — not "users are happy". Prefer signals that could be checked in a test, a metric, or a demo. +- **How do we know it's done?** The minimum cut that ships. What's deferred to a follow-up? +- **Non-goals.** What outcomes are explicitly *not* success for this PRD? + +### Round 3 — Services and components + +- **New services.** What new services / classes / modules do you expect to create? (Names if you have them, rough responsibilities if you don't.) +- **Existing code touched.** What existing services, controllers, tables, or templates will this modify or extend? +- **External dependencies.** Any new packages, APIs, infrastructure, or third-party services? +- **Data model changes.** New tables, columns, migrations, or schema shifts? + +If the user pushes back ("I don't know yet, that's why I'm writing the PRD"), accept that and record the question itself in the PRD's **Open questions** section rather than forcing an answer. + +## Step 3: Derive the slug + +From the working title, produce a kebab-case slug: lowercase, alphanumeric + hyphens, no leading/trailing hyphens, no consecutive hyphens. Trim to ~50 chars at a word boundary. + +Filename: `docs/prds/-.md` + +## Step 4: Write the PRD + +Write the file using this template. Fill every section from the user's answers — do not invent content. Sections with no answer get an explicit `_TBD — see Open questions._` line, not silence. + +```markdown +# PRD : + +- **Status:** Draft +- **Author:** +- **Created:** + +## Summary + + + +## Problem + + + +## Goals / Success Criteria + + + +## Non-goals + + + +## Scope + +### In scope + + +### Out of scope + + +## Proposed Design + +### New services / components + + +### Existing code touched + + +### Data model changes + + +### External dependencies + + +## Open questions + + + +## References + + +``` + +Use the Write tool. Don't ask the user to confirm contents before writing — they can edit afterward. The PRD is a draft, not a contract. + +## Step 5: Report + +One line: path of the new file with the heading line number (`docs/prds/NNNN-slug.md:1`), and the next suggested action (e.g. "edit to refine before circulating", "open a ticket linking to it"). Stop. + +## Hard rules + +- **Always interrogate.** Even if the user has been talking about the feature for an hour, ask the three rounds explicitly. The act of answering structured questions is what produces a useful PRD. +- **Never guess answers** from earlier conversation. If the user already said something relevant, you may *quote it back* in the question ("earlier you said X — is that still the goal, or has it shifted?") but do not write it into the PRD without confirmation. +- **One PRD per invocation.** Don't bundle multiple features into one file. If the user describes two things, ask which one this PRD covers. +- **Don't create tickets.** This skill writes a markdown file. Filing in a tracker is a separate step the user initiates. +- **Don't number-collide.** Re-check the next number right before writing in case files were added during interrogation. diff --git a/init-project/SKILL.md b/init-project/SKILL.md new file mode 100644 index 0000000..7bc62bd --- /dev/null +++ b/init-project/SKILL.md @@ -0,0 +1,222 @@ +--- +name: init-project +description: Scaffold a new personal project at ~/Code/, push it to the user's Gitea server (didericis), and leave behind a low-dependency, text-driven repo (README, CLAUDE.md, docs/JOURNAL.md, docs/research, .claude/skills/init-entry). Interrogates the invoker for name, description, language, visibility, goals, non-goals, audience, and an optional Gitea workflow stub. Use when the user invokes /init-project or asks to "start a new project", "scaffold a repo", "spin up a new project", or similar. +--- + +# Scaffold a new personal project + +A guided flow for creating a fresh project locally and on the user's self-hosted Gitea server. The output is a minimal, text-driven repo whose purpose, audience, goals, and decisions live in the repo itself — not in a tracker, not in chat, not in someone's head. + +The invoker is an expert developer who values clarity and low dependencies. Don't add tooling, frameworks, or boilerplate beyond what's described here. + +## Preflight + +Before asking the invoker anything: + +1. Verify both `GITEA_URL` and `GITEA_TOKEN` are set in the environment. If either is missing, stop and tell the invoker exactly which one to set. Suggest: + - `GITEA_URL` — base URL of the Gitea server (e.g. `https://gitea.example.tld`), no trailing slash. + - `GITEA_TOKEN` — a personal access token with `write:repository` scope. +2. Verify `~/Code` exists; create it if not. +3. The Gitea repo owner is always `didericis`. Do not ask. + +## Step 1 — Interrogate + +Gather inputs in two batches. Keep it tight. + +**Batch A — free text (ask in one chat message, parse the reply):** + +- **Name** — a slug. Must match `^[a-z][a-z0-9_-]*$`. Used as both the directory under `~/Code/` and the Gitea repo name. If the invoker gives something invalid, normalize and confirm. +- **One-sentence description** — used in the Gitea repo metadata and as the README's first line. +- **Goals** — what the project is trying to do. Bulleted is fine. +- **Non-goals** — what it explicitly is _not_ trying to do. +- **Audience** — who this is for (just Eric, a small group, public users). + +**Batch B — structured choices (use `AskUserQuestion`):** + +- Language: **Node/TypeScript**, **Python**, **None / plain text**. +- Visibility: **Private (Recommended)**, **Public**. +- Gitea workflow stub: **No (Recommended)**, **Yes — stub `.gitea/workflows/ci.yml`**. + +Capture the answers verbatim where possible — especially goals and non-goals. Do not paraphrase or editorialize. + +## Step 2 — Check the local path + +If `~/Code/` already exists, stop. Tell the invoker and let them pick another name or remove the directory themselves. Never overwrite. + +## Step 3 — Scaffold the files + +Create `~/Code//` and write the files below. Unix line endings, no trailing whitespace, no emojis. + +### `README.md` + +``` +# + + + +## Goals + + + +## Non-goals + + + +## Audience + + +``` + +No badges, no install section, no license blurb. + +### `CLAUDE.md` + +Orientation for future Claude sessions. Sections: + +- **What this is** — one paragraph: name, description, why it exists. +- **Audience** — who it's for. +- **Goals / Non-goals** — verbatim from interrogation. +- **Repository layout** — describe what was actually scaffolded (don't list things that weren't created). +- **Conventions** — text-driven content; decisions, state, and stray thoughts logged in `docs/JOURNAL.md` as timestamped stream-of-thought entries (newest first, append-only, no titles, no template — tags `[name](tag://name)` go directly under the header only when a coherent theme emerges); research notes in `docs/research/`; product requirement docs in `docs/prds/`; low dependencies by default; ask before adding new tools. +- **When you're unsure** — final line: "Ask. Default to drafting in chat over editing files when the request is ambiguous." + +### `.gitignore` + +Always include OS junk: + +``` +.DS_Store +Thumbs.db +``` + +Plus, by language: + +- **Node/TS:** `node_modules/`, `dist/`, `.env`, `.env.*`, `*.log` +- **Python:** `__pycache__/`, `*.pyc`, `.venv/`, `dist/`, `build/`, `*.egg-info/`, `.env` +- **None:** nothing extra. + +### `docs/INDEX.md` + +Short pointer file. Two sentences max: "Decisions and state changes are logged in `JOURNAL.md`. Research notes live in `research/`." + +### `docs/prds/.gitkeep` + +Empty file so the directory is tracked. + +### `docs/research/.gitkeep` + +Empty file so the directory is tracked. + +### `docs/JOURNAL.md` + +A running log of decisions, state changes, and stray thoughts. Newest entries on top, append-only, unstructured prose that captures _what I was thinking at that point in time_. Each entry: a timestamp heading (`## YYYY-MM-DD HH:MM`) followed by freeform body. No title, no template, no required sections. Tags optional — formatted as `[name](tag://name)` markdown links on the line directly under the header, only when a coherent theme has emerged. + +Seed it with just the header and a one-line note describing the convention, so future-Eric (and future Claude, and the `init-entry` skill) know the format without opening this file: + +``` +# Journal + +Append-only stream of thought. Newest entries on top. Each entry is a timestamp +followed by freeform prose. Tag entries with `[name](tag://name)` links under +the header — only when a coherent theme emerges. Otherwise just write. +``` + +### `.claude/skills/init-entry/SKILL.md` + +Copy the global `init-entry` skill into the project so `/init-entry` is available within this repo without depending on the user's global skill set, and so the journal convention is pinned at scaffold time: + +``` +mkdir -p ~/Code//.claude/skills/init-entry +cp ~/.claude/skills/init-entry/SKILL.md ~/Code//.claude/skills/init-entry/SKILL.md +``` + +If `~/.claude/skills/init-entry/SKILL.md` doesn't exist on this machine, skip the copy and surface a warning in Step 7 — don't block the rest of the scaffold. + +### Language floor (minimal, no dependencies) + +- **Node/TypeScript:** `package.json` with only `name`, `version: "0.0.0"`, `description`, `private: true`. No scripts, no deps, no `tsconfig.json`. The invoker adds those when they need them. +- **Python:** `pyproject.toml` with a minimal `[project]` table — `name`, `version = "0.0.0"`, `description`. No build backend, no deps. The invoker chooses Hatch / Poetry / uv / setuptools when they're ready. +- **None:** skip. + +### `.gitea/workflows/ci.yml` (only if invoker said yes) + +```yaml +name: ci +on: [push, pull_request] +jobs: + hello: + runs-on: ubuntu-latest + steps: + - run: echo "hello from ${GITHUB_REPOSITORY:-$GITEA_REPOSITORY}" +``` + +## Step 4 — Initial commit + +Run, against `~/Code/`: + +``` +git init -b main +git add -A +git commit -m "Initial commit" +``` + +If `git init -b main` fails (older git), fall back to `git init && git checkout -b main` before staging. + +## Step 5 — Create the Gitea repo + +Build the request body with `jq -n` so quotes and special characters in the description don't break the JSON. Send: + +``` +curl -fsSL -X POST "$GITEA_URL/api/v1/user/repos" \ + -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d "$(jq -n \ + --arg name "" \ + --arg desc "" \ + --argjson private \ + '{name: $name, description: $desc, private: $private, default_branch: "main", auto_init: false}')" +``` + +**Never echo, log, or write `$GITEA_TOKEN` anywhere.** Not in error messages, not in commands you describe to the user, not in any scaffolded file. + +If the API call fails: + +- **409 Conflict** — repo already exists on Gitea. Stop. Tell the invoker, suggest they pick a different name or delete the existing repo. +- **401 Unauthorized** — token is wrong, revoked, or lacks scope. Stop. +- **422 Unprocessable** — name violates Gitea's rules. Stop. +- Anything else — surface the response body and stop. + +In every failure case the local repo is already committed and intact. Do **not** delete it. + +## Step 6 — Add remote and push + +Prefer SSH for the remote. Parse `ssh_url` from the API response (`jq -r '.ssh_url'`) — falls back to `clone_url` (HTTPS) only if `ssh_url` is missing or empty. Then: + +``` +git -C ~/Code/ remote add origin +git -C ~/Code/ push -u origin main +``` + +SSH avoids embedding tokens in `.git/config` and matches the invoker's setup (key-based auth to the Gitea host). If the push fails on auth, surface the exact git error — do not fall back to HTTPS-with-token as a workaround, since that leaks credentials into `.git/config`. + +## Step 7 — Report back + +One short summary: + +- Local path: `~/Code/` +- Gitea URL: `html_url` from the API response. +- What was scaffolded (language floor: yes/no/which; CI stub: yes/no; init-entry skill copied: yes/skipped-with-reason). +- Suggested next step: open `CLAUDE.md` and confirm goals / non-goals / audience read correctly. + +Stop there. Don't volunteer to add a license, tests, dependencies, a `src/` directory, or anything else unless the invoker asks. + +## Hard rules + +- **Never write the Gitea token to a file.** Not `.env`, not `CLAUDE.md`, not the `git remote` URL, not anywhere. It only lives in env. +- **Never overwrite an existing `~/Code/`.** Bail with a clear error. +- **Never push if the Gitea API call failed.** Local repo stays; let the invoker decide what to do next. +- **No frameworks, no deps, no scripts, no `src/`.** This skill scaffolds a _floor_. Tooling comes later, deliberately. +- **No license file by default.** Licensing is a deliberate choice; the invoker adds it when they want it. +- **No tests, no CI** beyond the optional stub the invoker explicitly opts into. +- **Don't paraphrase goals, non-goals, or audience.** Capture what the invoker said. They're the rubric, not a draft. +- **Owner is always `didericis`.** Do not ask, do not parameterize. diff --git a/init-work/SKILL.md b/init-work/SKILL.md new file mode 100644 index 0000000..f9838fd --- /dev/null +++ b/init-work/SKILL.md @@ -0,0 +1,181 @@ +--- +name: init-work +description: End-to-end workflow for delivering a new feature with agent assistance — clean tree → PRD → branch → PRD-only PR → agent implements → agent reviews → user reviews the review → merge. Use when the user says "start a new feature", "kick off the agentic workflow", or invokes /init-work. Orchestrates: a clean checkout, the /init-prd skill, branching, PR creation, an implementation Agent, a review Agent, an interactive review-of-review session with the user, and merge with explicit confirmation. Detects host (GitHub/GitLab/Gitea) from the origin URL. +--- + +# Agentic feature workflow + +Eight steps. Run them in order. Do not skip steps. Do not collapse Step 7 into "I'll just merge it" — the human review of the agent's review is the load-bearing checkpoint. + +This skill orchestrates other tools: it invokes `/init-prd` for Step 2, launches `Agent` for Steps 5 and 6, and invokes `/review` (via the Skill tool) where appropriate. The skill itself should not implement the feature — delegate. + +## Step 0: Preflight + +Before doing anything destructive, check the basics. Stop and ask the user if any check fails. + +- `git rev-parse --is-inside-work-tree` → must be `true`. +- `git remote get-url origin` → must succeed. Save the URL; you'll need it for host detection. +- Detect host from the origin URL: + - `github.com` → **GitHub**, CLI is `gh`. + - `gitlab.com` or any host with `gitlab` in the FQDN, or known self-hosted GitLab (e.g. `labs.gauntletai.com` for this repo) → **GitLab**, CLI is `glab`. + - `codeberg.org` or hosts running Gitea → **Gitea**, CLI is `tea`. + - Anything else → **stop and ask the user** which host this is. +- Verify the CLI is installed (`command -v `). If missing, stop and ask the user to install it (or to run Step 4's PR-open command themselves). For OpenEMR specifically, CLAUDE.md says GitLab CLI access is not yet configured in this fork — if `glab` is missing, surface that note and stop. +- Identify the main branch: `git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@'`. Fall back to `master` then `main` if that fails. + +## Step 1: Land on a clean main/master + +Make the working tree match origin's main branch with no local clutter. + +1. **Capture the current branch** (`git rev-parse --abbrev-ref HEAD`) — useful if anything goes sideways. +2. **Stash dirty state.** If `git status --porcelain` is non-empty, run: + ```bash + git stash push --include-untracked --message "init-work preflight $(date -u +%FT%TZ)" + ``` + Tell the user explicitly that you are about to stash *and what files are in scope* — quote `git status` first. Do not silently stash. The stash is recoverable via `git stash list` / `git stash pop`, but the user should know it happened. +3. **Switch and update.** `git checkout
` then `git pull --ff-only origin
`. If the pull fails (non-FF), stop and ask — do not force. + +If the user objects to stashing (says they have in-progress work elsewhere), abort the skill and let them resolve it manually first. + +## Step 2: Run the PRD skill + +Invoke the `init-prd` skill via the Skill tool. That skill interrogates the user in three rounds and writes `docs/prds/-.md`. Do not pre-fill answers from prior conversation context — the interrogation is the point. + +When `/init-prd` finishes, capture two values from the file it created: + +- The PRD number `` (the 4-digit prefix on the new file). +- The slug `` (everything after the prefix, before `.md`). + +You will use these in Steps 3, 4, and 5. + +## Step 3: Branch off main + +```bash +git checkout -b prd-- +``` + +Branch naming is `prd--` (e.g. `prd-0001-ai-agent-service`). If the branch already exists locally, stop and ask — never silently reset or rebase someone else's branch. If a branch with that name exists on the remote, also stop and ask. + +Stage and commit the new PRD file: + +```bash +git add docs/prds/-.md +git commit -m "docs(prd): scaffold PRD + +Assisted-by: Claude Code" +``` + +Then push with upstream tracking: + +```bash +git push -u origin prd-- +``` + +The PRD file is the only thing in this commit. No code, no scaffolding stubs, no "placeholder for later" files. + +## Step 4: Open a PR with only the PRD + +Open the PR against `
` from the new branch. The PR body should contain a one-line summary plus a link/reference to the PRD path. Title format: `PRD : `. + +Pick the right CLI based on Step 0's host detection: + +- **GitHub** (`gh`): + ```bash + gh pr create --base
--head prd-- \ + --title "PRD : " \ + --body "$(cat <<'EOF' + Tracking PR for [PRD ](docs/prds/-.md). Implementation will follow on this branch. + EOF + )" + ``` +- **GitLab** (`glab`): + ```bash + glab mr create --target-branch
--source-branch prd-- \ + --title "PRD : " \ + --description "Tracking MR for [PRD ](docs/prds/-.md). Implementation will follow on this branch." + ``` +- **Gitea** (`tea`): + ```bash + tea pr create --base
--head prd-- \ + --title "PRD : " \ + --description "Tracking PR for [PRD ](docs/prds/-.md). Implementation will follow on this branch." + ``` + +Capture the URL the CLI prints. You'll reference it in the final report. + +If the CLI is missing or auth fails, stop. Don't fall back to a different host's CLI; don't silently skip the PR step. + +## Step 5: Delegate implementation to an Agent + +Launch `Agent` (subagent_type=`general-purpose`) with a self-contained prompt that: + +- Names the PRD file path (`docs/prds/-.md`) and the branch (`prd--`) it must commit to. +- States that it is implementing this PRD's *in-scope* items only — open questions stay open, non-goals stay out. +- Tells it to commit and push as it works, **once each semantically-distinct chunk of work is done** (not after every file, not all-at-once at the end). Examples of "semantically distinct": a new service stub + its first passing test; a migration + the model that uses it; the seeder + a smoke test that runs it. Use Conventional Commit messages, `Assisted-by: Claude Code` trailer. +- Tells it to honor `CLAUDE.md` (PSR coding standards, `composer code-quality`, the testing workflow under `docker/development-easy`). +- Tells it to **stop and report** at the end — not to open or merge the PR, not to request review, not to bump versions. The skill drives those steps. +- Tells it to surface any blockers (open questions resurfacing, missing fixtures, ambiguous PRD wording) rather than guessing. + +Run the implementation agent in the foreground — Step 6 depends on its output. + +When the agent finishes, verify on the host: + +- `git status` → working tree clean (the agent should have committed everything it touched). +- `git log
..HEAD --oneline` → at least one commit beyond the PRD scaffold. +- `git rev-parse @{u}` and `git rev-parse HEAD` → equal (it pushed). + +If any of those fail, prompt the agent (or the user) to reconcile before moving on. + +## Step 6: Delegate review to an Agent + +Invoke the `review` skill via the Skill tool — it is the project's documented PR-review entry point. If for some reason `review` is unavailable, fall back to launching `Agent` (subagent_type=`general-purpose`) with a prompt that: + +- References the PR URL from Step 4 and the PRD file. +- Asks for a structured review: correctness vs. PRD, adherence to CLAUDE.md (strict types, dependency injection, no `$GLOBALS`, PSR-3 logging context, etc.), test coverage of in-scope items, and any security/PHI concerns flagged by the codebase's conventions. +- Is told **not to push commits, not to fix issues, and not to merge** — review only, written output only. + +Capture the review output. Save it for Step 7. + +## Step 7: Review the review with the user (interactive) + +This is the human-in-the-loop checkpoint. Present the agent's review to the user in a readable form, then walk through it with them: + +- For each finding, ask: accept (fix it), reject (false positive — explain why), or defer (file as a follow-up). +- Apply accepted fixes directly (or delegate small ones to a fresh `Agent` call), commit and push them on the same branch. +- Reject false positives with a one-line note in your reply so the user can see the reasoning. +- Defer items get added to the PR description as a "Follow-ups" checklist (use the host's CLI to edit the PR body). + +Do not move on to Step 8 until the user explicitly says "ship it" / "merge it" / "looks good". Open questions or ambiguity → stop and ask. + +## Step 8: Merge + +Merging is irrevocable on a shared branch. **Require explicit user confirmation in this turn** — a "go ahead" from earlier in the conversation does not count. + +Once confirmed, merge using the host's CLI: + +- **GitHub:** `gh pr merge --squash --delete-branch` (or `--merge` / `--rebase` if the user prefers — ask if unsure). +- **GitLab:** `glab mr merge --squash --remove-source-branch`. +- **Gitea:** `tea pr merge --style squash --delete-branch`. + +Default to **squash** unless the user has expressed a different preference for this repo. After merge, return to `
` and `git pull --ff-only`. + +If there is a stash from Step 1, remind the user it's still in `git stash list` and offer to `git stash pop` it. + +## Final report + +One short paragraph naming: + +- The merged PR URL. +- The PRD path. +- Any deferred follow-ups recorded on the PR. +- The state of the Step 1 stash (popped, still pending, or none). + +## Hard rules + +- **Never skip Step 7.** The human review of the agent review is non-negotiable. If the user wants to skip it, that's a different workflow — point them at it and stop. +- **Never merge without explicit in-turn confirmation.** Earlier "yes" answers don't carry across the implementation/review steps. +- **Never silently stash.** Always show `git status` and tell the user what's about to be stashed. +- **Never let the implementation agent open or merge the PR.** Those are skill-driven steps, not agent-driven. +- **Never fall back to a different host's CLI.** If `glab` is missing on a GitLab repo, stop — do not use `gh` instead. +- **Never collapse multiple PRDs into one branch.** One PRD per invocation. If the user wants a second feature, start the skill again from Step 0. +- **Honor CLAUDE.md and existing skill outputs.** The PRD that `/init-prd` produces is the source of truth for Step 5; don't rewrite it during implementation. diff --git a/tag-entries/SKILL.md b/tag-entries/SKILL.md new file mode 100644 index 0000000..f76a732 --- /dev/null +++ b/tag-entries/SKILL.md @@ -0,0 +1,117 @@ +--- +name: tag-entries +description: Retroactively tag existing entries in `./docs/JOURNAL.md` (the same file `init-entry` writes to) by reading each untagged entry, deciding whether a coherent theme is present, and inserting `[name](tag://name)` links directly under the timestamp heading. Use when the user invokes /tag-entries or asks to "tag the journal", "go back and tag entries", "add tags to old entries", or similar. Conservative by default — entries without a clear theme stay untagged. +--- + +# Tag existing journal entries + +A one-shot pass over `./docs/JOURNAL.md` that adds tag lines under entry headers where a coherent theme is obvious. Tags are conditional: most entries should stay untagged. The aim is to make a future grep across the journal useful, not to label every entry. + +The journal is append-only for *body content*. Adding a tag line under an existing header is allowed because tags are metadata the entry was eligible for at write time but didn't get. Never edit body prose, reorder entries, or change timestamps. + +## Format reminder + +Each entry looks like: + +``` +## YYYY-MM-DD HH:MM + +[github](tag://github) [postgres](tag://postgres) + + +``` + +- Tag line sits **directly under** the timestamp heading — no blank line between header and tags, then one blank line before the body. +- Tags are markdown links with the `tag://` URI scheme: `[name](tag://name)`. Multiple tags are space-separated on one line. +- Label and URI value are usually the same word; differ only with a real reason (e.g. `[GitHub Actions](tag://github)`). + +## Step 1 — Locate the journal + +From the current working directory: + +1. If `./docs/JOURNAL.md` exists → continue. +2. Otherwise → stop and tell the invoker no journal exists yet. Do not create one. This skill only retro-tags; `/init-entry` is the entry point for new journals. + +## Step 2 — Parse entries and existing tags + +Read the file. Walk it from top to bottom and extract: + +- Each entry: header line (`## YYYY-MM-DD HH:MM`), the optional tag line if present, and the body up to the next `## ` header or EOF. +- The set of tag URIs already in use anywhere in the file (e.g. `postgres`, `github`, `incident-2026-04`). This is the **existing tag vocabulary**. + +Skip entries that already have a tag line. Their author already made the call. Don't second-guess, don't add to them, don't reorder them. + +## Step 3 — Propose tags for each untagged entry + +For each untagged entry, decide whether a coherent theme is present. The bar is high: + +**Tag when** the entry is centrally about a specific named thing that could plausibly recur — a tool (`postgres`, `github`), a subsystem (`auth`, `worker-pool`), a recurring concern (`flaky-tests`, `oncall`), a discrete incident (`incident-2026-04-29`), or a project / workstream that spans multiple entries. + +**Don't tag when** the entry is a passing thought, a one-off observation, a meta note about the journal itself, or touches several topics without a center of gravity. "It mentions Postgres in one sentence" is not enough — the entry has to *be about* Postgres. + +**Reuse existing tags first.** If the existing vocabulary contains `postgres` and the new entry is about Postgres, use `postgres` — don't invent `pg` or `postgresql`. Only mint a new tag when nothing in the existing vocabulary fits. + +**New tag conventions:** + +- Lowercase slug, hyphens for spaces (`worker-pool`, not `worker_pool` or `workerPool`). +- Singular over plural (`migration`, not `migrations`) unless the plural is the natural name. +- Specific over generic (`postgres` beats `database`; `oauth-callback` beats `auth`) — but only if the specificity is justified by the entry's actual focus. +- 1–3 tags per entry is the typical range. If you find yourself reaching for a 4th, the entry probably doesn't have a single coherent theme and should stay untagged. + +If an entry has no clear theme, **leave it untagged**. Restraint is the point. + +## Step 4 — Show the plan and wait for confirmation + +Before writing anything, present the proposal as a compact table or list. For each entry that would change: + +- Timestamp. +- Proposed tags. +- A short (≤ 12 word) reason — what theme you saw. + +Also list, separately, any **new tags** that would be added to the vocabulary (i.e. not already in use elsewhere), so the invoker can spot inconsistent naming before it lands. + +Wait for explicit confirmation. The invoker may: + +- Approve as-is. +- Edit the proposed tags for specific entries. +- Tell you to drop tags from specific entries (leave them untagged). +- Tell you to remint a new tag using a different name. + +Apply their feedback and re-show only if the changes are non-trivial. Otherwise proceed. + +## Step 5 — Apply + +Edit `./docs/JOURNAL.md` in place. For each approved entry: + +- Insert the tag line on the line directly after the `## YYYY-MM-DD HH:MM` header. +- Ensure exactly one blank line between the tag line and the body. +- If the entry currently has a blank line between header and body, replace that blank line with the tag line followed by a blank line — net effect: tag line where the blank line was, blank line after. + +Do not touch: + +- Headers, timestamps, or entry order. +- Body prose, including whitespace inside the body. +- Entries that already had a tag line. +- The file's top-of-file preamble (the `# Journal` header and the convention note). + +Use Unix line endings, no trailing whitespace, no emojis. + +## Step 6 — Report + +One short summary: + +- Path to the file. +- How many entries were tagged, how many were left untagged on purpose. +- The list of new tags added to the vocabulary, if any. + +Stop there. + +## Hard rules + +- **Never edit body prose.** The journal is append-only for content. Tag lines are the only retro-edit allowed. +- **Never reorder, merge, split, or delete entries.** +- **Never re-tag entries that already have a tag line.** The author already decided. +- **Never invent themes.** If nothing themes-up cleanly, leave the entry untagged. Most entries should stay untagged. +- **Reuse existing tags before minting new ones.** Inconsistent vocabulary is worse than no tags. +- **No emojis. No backdating. No new headers or sections.** +- **Don't volunteer to do anything else** — don't propose an index, a tag glossary file, a TOC, or summaries. The journal stays text-only.