feat(hermes): MVP converter + 4 universal skills converted

Adds the conversion infrastructure for the Hermes-rollout (Nous Research):

- hermes/mapping.yaml — per-skill schema (mode: auto|manual|skip|pending,
  category, replace-rules, source override). Every skills/<name>/ has an
  explicit entry; the build fails on unmapped skills.
- scripts/build-hermes.py — Python converter. Reads mapping.yaml; for auto
  applies replace-rules to SKILL.md and copies the rest verbatim; for manual
  copies hermes/skills/<source>/ as-is; for skip / pending records to
  dist-hermes/SKIPPED.md.
- dist-hermes/ — pre-converted Hermes-flavour tree (committed; consumed by
  the recursive installer in [hermes-installer-skill]). Includes 4 universal
  skills: pulling-before-work, active-platform (with Linux+bash replacement
  rule), project-discipline, using-markitdown. Plus SKIPPED.md listing
  8 skip + 10 pending entries.
- README.md — adds "Build for Hermes" section, updates layout, mentions
  hermes/, dist-hermes/, build-hermes.py.

Mapping is the source of truth for the audit table from
.wiki/concepts/hermes-skills-rollout-design.md (Q1-Q8). Pending entries
preserve the audit decision (intended mode/category) for the follow-up
tasks: hermes-flavour-mcp-setups, hermes-installer-skill,
hermes-mvp-coverage. Security carry-forward (extraheader-pattern,
POSIX-absolute paths, semver bumps) — infrastructure ready (replace-rules +
manual mode); concrete content lands in hermes-flavour-mcp-setups.

Smoke-tests: idempotent re-run produces no diff; replace-rules verified on
active-platform (Linux+bash); strict-mapping fail-fast verified on a
synthetic unmapped skill (exit 1).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-07 00:00:00 +03:00
parent 065270de42
commit 6b36b312fa
10 changed files with 912 additions and 4 deletions

28
dist-hermes/SKIPPED.md Normal file
View File

@@ -0,0 +1,28 @@
# Skipped Skills
Auto-generated by `scripts/build-hermes.py` from `hermes/mapping.yaml`.
Do not edit by hand — edit the mapping and re-run the build.
## Skipped (not applicable to Hermes)
- **caveman** — Hermes runs on glm-5.1 (cheap local model); token-compression motive disappears.
- **caveman-commit** — Caveman family — cheap-model context, no compression motive.
- **caveman-compress** — Caveman family — cheap-model context, no compression motive.
- **caveman-help** — Help-card for the caveman family which itself is skipped.
- **caveman-review** — Caveman family — cheap-model context, no compression motive.
- **find-skills** — Hermes has built-in skills_list() / progressive disclosure.
- **setup-interns** — Hermes itself is a cheap-intern model — the delegation tier collapses.
- **using-interns** — Hermes itself is a cheap-intern model — the delegation tier collapses.
## Pending (deferred to follow-up tasks)
- **project-bootstrap** — Pending hermes-mvp-coverage. Orchestrator — adapts last; CLAUDE.md trigger-lines drop (Hermes auto-discovers). → intended: `mode: auto, category: software-development`
- **recommend-dont-menu** — Added 2026-05-06 after the original audit. Cross-agent applicability claimed (response-style rule) — Hermes-side audit not yet done. → intended: `mode: auto, category: productivity`
- **setup-context7** — Pending hermes-flavour-mcp-setups: rewrite as yaml-edit ~/.hermes/config.yaml. → intended: `mode: manual, category: mcp`
- **setup-projects-meta** — Pending hermes-flavour-mcp-setups: rewrite as yaml-edit ~/.hermes/config.yaml plus pre-check + extraheader fallback. → intended: `mode: manual, category: mcp`
- **setup-tasks** — Pending hermes-mvp-coverage. → intended: `mode: auto, category: productivity`
- **setup-wiki** — Pending hermes-mvp-coverage. Hermes ships research/llm-wiki — our schema is preserved via override-precedence. → intended: `mode: auto, category: research`
- **using-context7** — Pending hermes-mvp-coverage. → intended: `mode: auto, category: mcp`
- **using-projects-meta** — Pending hermes-mvp-coverage. → intended: `mode: auto, category: mcp`
- **using-tasks** — Pending hermes-mvp-coverage. → intended: `mode: auto, category: productivity`
- **using-wiki** — Pending hermes-mvp-coverage. Hermes ships research/llm-wiki — our schema is preserved via override-precedence. → intended: `mode: auto, category: research`

View File

@@ -0,0 +1,81 @@
---
name: using-markitdown
version: 1.0.0
description: Use when capturing external content into a markdown-based knowledge base, wiki `raw/` directory, or any pipeline that must preserve the source's full text — for web pages, PDFs, DOCX/PPTX/XLSX, EPUB, CSV/JSON/XML, ZIP archives, images (with OCR/EXIF), audio (with transcription), or YouTube URLs. Also use when WebFetch returned an LLM-summarized version but the raw content is what's needed.
---
# using-markitdown
> Convert almost any URI to plain markdown using Microsoft's `markitdown` MCP server. Returns **raw textual content**, not an LLM summary.
## Tool
```
mcp__markitdown__convert_to_markdown(uri: string) → markdown string
```
`uri` accepts: `http://`, `https://`, `file://`, `data:`.
## Local files — Docker-mount caveat (READ FIRST)
The markitdown MCP usually runs in a **Docker container** with a single host directory bind-mounted. The container does **not** see your full host filesystem. `file://` URIs must point to the **in-container path**, not the host path.
1. Open `~/.claude.json` and find `mcpServers.markitdown.args`. Look for the `-v` flag — e.g. `-v C:\Users\vitya:/workdir` means host `C:\Users\vitya` is mounted at `/workdir` inside the container.
2. Translate the host path to the container path before forming the URI.
3. Forward slashes only inside the container path.
**Example.** Host file at `C:\Users\vitya\modular\heart-and-mask\.wiki\raw\foo.html` with mount `C:\Users\vitya:/workdir`:
```
file:///workdir/modular/heart-and-mask/.wiki/raw/foo.html
```
**Symptom of getting this wrong:** `[Errno 2] No such file or directory: '/c:/Users/...'` — the container literally tried to open the host-shaped path. The fix is path translation, not URL encoding.
**If the file falls outside the mount:** either copy it into the mounted tree, or extend the mount in `~/.claude.json` (a Claude restart is required for MCP changes to take effect — MCP servers are spawned at session start).
**Filenames.** Non-ASCII filenames (Cyrillic, etc.) inside `file://` URIs are flaky across the URL-encode → urllib → Docker → host-FS chain. Rename to Latin kebab-case **before** calling markitdown.
## When to use
- Filling a wiki's `raw/` directory from a URL or local PDF/DOCX.
- Datasheets, papers, blog posts, GitHub READMEs, Obsidian Web Clipper outputs, KiCad netlist exports — anything where the source text matters and lossy summarization would break later ingest steps.
- Any time the next step is "save the source verbatim before summarizing".
## When NOT to use
- You only need a *summary* or an *answer about* a page → use **WebFetch** (cheaper, runs through a small model, returns prose).
- The URI is GitHub/PR/issue/release content → use `gh` CLI (richer metadata, structured output).
- The URI is private/authenticated (GDocs, Confluence, Jira, Slack, Notion, `share.google/*` sign-in walls) → markitdown receives the **public-facing fallback page** (sign-in screen, cookie banner) and returns *that* as markdown. Verify the result is real content before saving.
- **The URI is a browser-rendered web page the user is already viewing** → ask the user to capture it via **Obsidian Web Clipper** (browser extension, runs Readability extraction client-side) and drop the resulting `.md` into `raw/`. Web Clipper output is dramatically cleaner than markitdown's HTML pass — no nav chrome, no sidebar history, no cookie banners — plus it carries YAML frontmatter (title / source URL / date) out of the box. Reserves markitdown for things browsers can't easily save (PDF, DOCX, PPTX, XLSX, EPUB, file:// resources). Note: rename the resulting file to Latin kebab-case before ingest (Web Clipper preserves the page `<title>` verbatim, often non-ASCII).
## Pattern: ingest a remote source into a wiki
```
1. mcp__markitdown__convert_to_markdown(uri="https://example.com/foo.pdf")
2. Inspect the head of the result. If it looks like a sign-in/cookie/consent page, abort — ask the user for an alternative (manual save, paste, authenticated MCP).
3. Write the result to .wiki/raw/<slug>.md (kebab-case, Latin only).
4. Register the new file in .wiki/raw/README.md.
5. Hand off to the wiki ingest workflow (creates sources/<slug>.md summary + entity/concept updates).
```
## Common gotchas
| Symptom | Cause | Fix |
|---|---|---|
| Output is a Google/Microsoft sign-in page in some random language | URI behind auth wall | Ask user to export the content manually (Save as PDF, copy-paste) and put it in `raw/` |
| Output is mostly nav/cookie banner text | Site is JS-rendered or anti-bot | Try the cached or print URL; or ask user for HTML export |
| Output lacks images / diagrams | Markdown is text-only by design | Save the original asset separately under `raw/assets/`; reference it from the `sources/` summary |
| Tool not available in session | MCP server not loaded | Confirm `mcp__markitdown__convert_to_markdown` appears via ToolSearch; load with `select:mcp__markitdown__convert_to_markdown` |
| Huge output (book-length) | Whole document converted in one call | Save raw, then summarize *from the saved file* — do not hold the entire markdown in working context |
## Quick contrast with WebFetch and Web Clipper
| | markitdown | WebFetch | Obsidian Web Clipper |
|---|---|---|---|
| Returns | raw markdown of the source | LLM answer about the source | Readability-extracted markdown with YAML frontmatter |
| Use for | PDF / DOCX / non-browser-friendly | one-shot Q&A | any browser-viewable web page |
| Auth-aware | no | no | **yes** (uses the user's logged-in browser session) |
| Chrome / nav stripped | partial (still verbose) | n/a (model picks signal) | **yes** (clean) |
| Handles PDFs / DOCX | yes | text-only HTML extraction | no (web pages only) |
| Who triggers it | Claude | Claude | the user (manual click) |

View File

@@ -0,0 +1,85 @@
---
name: active-platform
description: Switches command, path, and example generation to the user's currently active development platform — Windows / Linux / macOS. Use this whenever you produce shell commands, install steps, README quick-start sections, or any output the user will paste into a terminal — even if they don't say "shell" or "command" explicitly. The default active platform is Windows / PowerShell because that's the user's primary workstation. Recognize and obey trigger phrases like "мы на винде", "мы на линуксе", "мы на маке", "we're on Windows", "we're on Linux", "we're on macOS", and close variants — they switch the active platform for the rest of the session. Also activate this skill anytime the user mentions a different machine, a remote box, or asks "how would I run this on X".
---
# active-platform
> Keep generated commands in the shell the user actually has open. The user works on multiple machines (Windows primary, Linux/Mac secondary). Translating shell idioms in their head wastes attention; this skill removes that friction.
## Default active platform
**Linux + bash.** Use this until something in the session tells you to switch. Reason: Hermes runs on Linux factory machines. This default is global, not per-project.
## Trigger phrases
Recognize anywhere in user input — beginning, middle, in passing — and update the active platform immediately. Match liberally: case-insensitive, mixed Cyrillic/Latin, missing punctuation should still trigger.
| Switch to → | Russian | English |
|---|---|---|
| **Windows** | "мы на винде", "я на винде", "мы на windows", "переключись на винду", "сейчас под виндой" | "we're on Windows", "I'm on Windows", "switch to Windows", "on a Windows box" |
| **Linux** | "мы на линуксе", "я на линуксе", "мы на linux", "переключись на линукс", "сейчас под линуксом" | "we're on Linux", "I'm on Linux", "switch to Linux", "on a Linux box" |
| **macOS** | "мы на маке", "я на маке", "мы на макоси", "переключись на мак", "сейчас под маком" | "we're on macOS", "we're on a Mac", "I'm on a Mac", "switch to macOS" |
After matching, briefly confirm the switch in one short line ("ок, теперь под линукс" / "got it, switching to Linux") so the user knows the change took. Don't lecture — one line is enough.
## What to apply the active platform to
This rule governs **what you show the user** — chat command snippets, README quick-start sections, install instructions, any line they'll copy and paste. It does **not** govern your own tool calls: those follow the harness's `Shell:` line, which is what actually runs in this environment. The two are decoupled on purpose — the harness might be running git-bash on a Windows machine, but you should still hand the user PowerShell commands because that's their daily shell.
## Per-platform conventions
### Windows / PowerShell
- **Shell**: PowerShell. Show `pwsh` (PowerShell 7) when offering install instructions; assume `powershell` (5.1) is the floor for compatibility notes.
- **Chaining**: `;` for sequential. PS 5.1 has no `&&`/`||` — use `if ($?) { B }` for "B only if A succeeded". Don't write `A && B` for Windows users on the assumption it works.
- **Paths**: backslashes in user-facing examples (`C:\Users\…`, `~\.claude\skills\`). Forward slashes only inside code that runs in bash/git-bash.
- **Env vars**: `$env:NAME = "value"` for set, `$env:NAME` for read. Not `export`.
- **Common idioms**: `Invoke-WebRequest` (`iwr`), `Test-Path`, `New-Item`, `Get-ChildItem` (`ls`), `Get-Content` (`cat`), `Remove-Item` (`rm`).
- **Install scripts**: in repos with both variants, prefer the `.ps1`. Example: `pwsh scripts/install.ps1`, not `bash scripts/install.sh`.
- **Stop-parsing**: for arguments containing `-`/`@` that PS would mis-parse, use `--%`.
### Linux / bash
- **Shell**: bash. POSIX-friendly syntax.
- **Chaining**: `&&`, `||`, `|` work as expected.
- **Paths**: forward slashes (`~/.claude/skills/`, `/var/log/...`).
- **Env vars**: `export NAME=value`.
- **Install scripts**: `bash scripts/install.sh`.
- **Coreutils**: GNU flavor — `sed -i 's/x/y/'`, `readlink -f`, `cp -a`.
### macOS / zsh
zsh is the default shell since Catalina. Mostly the same as Linux, with a few divergences worth flagging when relevant:
- **Package manager**: `brew install …` (Homebrew).
- **BSD coreutils**: `sed -i '' 's/x/y/'` (the empty `''` after `-i` is required), `greadlink` instead of `readlink -f`, no `cp -a`.
- **Paths**: `~/Library/...` rather than XDG-style.
## Cross-platform docs (READMEs in repos that target multiple OSes)
When writing user-facing docs in a repo that is *explicitly* cross-platform — like this `claude-skills` repo — show both PowerShell and bash variants. List the active platform's variant first, the other second. Tag each fenced block clearly:
````markdown
**Windows (PowerShell):**
```powershell
pwsh scripts/install.ps1
```
**Linux / macOS (bash):**
```bash
bash scripts/install.sh
```
````
This is a docs-level decision, independent of the active platform — both blocks ship together.
## Per-question overrides (don't change the session)
If the user asks about a *specific other machine* in a single question — e.g. "how would I run this on the prod box, which is Ubuntu", "что это будет на маке" — answer that one in the named platform's shell, but do **not** flip the session-wide active platform. The trigger phrases above are deliberately phrased as "we're on …" / "мы на …" because they imply *the workstation we're now using together*, not "tell me what this looks like elsewhere".
## Ambiguity policy
- No signal in the conversation → use the default (Windows).
- Conflicting signals (e.g., user said "we're on Linux" earlier, but now asks an OS-specific question that contradicts) → trust the most recent explicit trigger; if still unclear, ask one short question.
- Unknown platform names ("BSD?", "WSL?") → treat WSL as Linux; for anything genuinely unfamiliar, ask.

View File

@@ -0,0 +1,29 @@
# project-discipline
Policy skill that codifies four cross-project discipline rules so the same
guarantees that hold in a tightly-maintained repo apply everywhere.
## When it triggers
- **Session start** — when `CLAUDE.md` contains the line `follow project discipline` (added by `project-bootstrap` v1.5.0+).
- **In-chat** — when the user says "use project discipline", "соблюди дисциплину", "проектные правила", or close variants.
## The four rules
1. **Project conventions over skill defaults.** `CLAUDE.md` / `.wiki/CLAUDE.md` / `.tasks/` override any other skill's defaults. Specs go to `.wiki/concepts/`, not `docs/superpowers/specs/`. Tasks to `.tasks/`, not `docs/superpowers/plans/`.
2. **Master-only.** All work on `master` (or `main`). No feature branches without explicit user approval.
3. **Semver discipline.** Bump `version:` in `SKILL.md` / `package.json` / `pyproject.toml` on every edit per MAJOR / MINOR / PATCH; record in commit message; rebuild `dist/` artifacts after.
4. **Commit yes, push no.** Every session starts asking before push. User can grant auto-push within session ("разреши автопуш"); session end resets to ask-mode. Force / delete / non-ff push always asks.
## Prerequisites
None. The skill is a textual policy document; it takes no actions and has no
external dependencies. Activate it by adding `follow project discipline` to
`CLAUDE.md` (or use `project-bootstrap` v1.5.0+ which adds it automatically).
## Related
- `project-bootstrap` (v1.5.0+) — adds the trigger line to new and existing projects' `CLAUDE.md`.
- `pulling-before-work` — companion skill activated by the canonical template; pulls origin once at session start (`git pull --ff-only`).
- `using-tasks` / `using-wiki` — the format conventions Rule 1 routes work into.
- `.wiki/concepts/project-discipline-design.md` (in `claude-skills`) — full design rationale.

View File

@@ -0,0 +1,139 @@
---
name: project-discipline
version: 0.1.1
description: >
Codifies five cross-project discipline rules: (1) project CLAUDE.md /
.wiki/CLAUDE.md / .tasks/ override defaults from any other skill (specs
go to .wiki/concepts/, not docs/superpowers/specs/; tasks to .tasks/,
not docs/superpowers/plans/); (2) all work on master/main, no feature
branches without explicit user approval; (3) bump semver on every edit
of versioned artifacts (SKILL.md frontmatter, package.json,
pyproject.toml) per MAJOR/MINOR/PATCH rules, recorded in commit message;
(4) commit freely, never push without explicit per-session user approval
— grant via "разреши автопуш" / "allow auto-push", revoke via "отзови"
/ "revoke", session end resets to ask-mode; force/delete/non-ff push
always asks; (5) transit-zone / brainstorm workspaces — artifacts
go to .brainstorm/ or global wiki only via explicit user direction,
never auto-promote by analogy. Activated by "follow project discipline"
trigger in CLAUDE.md (added by project-bootstrap v1.5.0+).
---
# project-discipline
> Four cross-project rules. Read at session start. Apply before any other skill's defaults touch paths, branches, versions, or remote pushes.
## When this runs
**At session start** — when `CLAUDE.md` contains the line `follow project discipline`. The skill is a policy document; the agent reads it and applies the four rules to all subsequent work in the session.
**On explicit reference** — when the user says "use project discipline", "соблюди дисциплину", "проектные правила", "что у меня по правилам?", or close variants asking about/applying the rules.
The skill itself takes no actions and has no external side-effects. It instructs the agent how to behave.
## Rule 1 — Project conventions override skill defaults
Before applying defaults from any other skill (superpowers, frontend-design, mcp-builder, etc.), read in this order:
1. `CLAUDE.md` in the project root.
2. `.wiki/CLAUDE.md` (if it exists).
3. `.tasks/STATUS.md` (if it exists).
Any path, format, or workflow explicitly stated in those files **overrides the skill default**.
Concrete consequences:
- **Specs / design documents** go to `.wiki/concepts/<topic>-design.md`, **not** `docs/superpowers/specs/`.
- **Task tracking / implementation plans** go to `.tasks/<slug>.md` plus a board entry in `.tasks/STATUS.md` (the `using-tasks` format), **not** `docs/superpowers/plans/` or any inline-in-chat plan format.
- **Frontmatter, naming conventions, log format** — as described in the project's `.wiki/CLAUDE.md`.
If no convention is stated explicitly — fall back to the skill default.
## Rule 2 — Master-only
All work happens on the repo's main integration branch — usually `master`, but if a project uses `main`, treat `main` as equivalent.
- No `git checkout -b feature/foo` for solo work.
- Sync with remote: `git pull --ff-only` or `git pull --rebase`. **No merge commits** for solo work.
- If a task genuinely requires isolation (large experiment, risky refactor with rollback potential, multi-day work with intermediate WIP commits) — **ask** the user: "this needs its own branch, ok?" — and wait for explicit approval. Without approval, work continues on master.
If the agent finds itself on a non-main branch (after a manual `git checkout`) or in detached HEAD — report it and ask whether to return to master before working.
## Rule 3 — Versioning discipline
When editing any artifact with a semver field, **bump the version before committing** per:
- **MAJOR** (`X+1.0.0`) — breaks the contract. Renames, removed triggers, layout changes, removed public functions, breaking API change.
- **MINOR** (`X.Y+1.0`) — adds capability without breaking. New trigger, new optional step, new public function.
- **PATCH** (`X.Y.Z+1`) — wording / clarity / typo fixes with no behavior change.
The bump is recorded in the commit message: `feat(<artifact>): … [vX.Y.Z]` or whatever convention the project uses (see Rule 1).
**Applies to:** `skills/<name>/SKILL.md` (`version:` in frontmatter), `package.json` (`"version":`), `pyproject.toml` (`version =`), `Cargo.toml` (`version =`), and any other semver field in any other manifest.
**If the artifact is packaged** as `dist/<name>.skill`, `dist/*.tgz`, etc. — **rebuild** the package in the same or the next commit. Forgotten dist artifacts are a common cause of deploying stale binaries.
**First edit of an unversioned artifact** that COULD have a semver field (a new skill without `version:`, a new `package.json` without `"version":`) — **add** `version: 0.1.0` (or its equivalent) before committing; do not bump anything.
**Does not apply to:** artifacts with no semver field and no potential for one (wiki concept pages, README.md, shell scripts without a public interface).
## Rule 4 — Commit yes, push no (session-scoped)
**Every session starts in ask-before-push mode.** On every `git push`, ask:
> Готов push'нуть в `<remote>/<branch>` (N коммитов: <subjects>). Ок?
(or its English equivalent if the user is communicating in English) and wait for explicit `yes` / `да` / `push` / equivalent. Without confirmation — do not push.
**Granting auto-push within a session.** When the user says:
- "разреши автопуш" / "allow auto-push" / "автопуш ок" / equivalent
— push without further confirmation until the end of the session or until revoked.
**Revoking auto-push within a session.** When the user says:
- "отзови автопуш" / "revoke auto-push" / "снова спрашивай" / equivalent
— return to ask-before-push mode.
**Session end resets to ask-mode.** The next session starts asking again, regardless of what was granted in the previous one. This is intentional: a grant is given for the current context (user nearby, consciously decided pushes are safe), and should not survive a context switch.
**Always ask, even with active grant:**
- `git push --force` / `--force-with-lease` (history rewrite);
- `git push origin --delete <branch>` (branch deletion);
- push to a remote/branch other than the current tracked upstream (`git push other-remote ...`, `git push origin other-branch`);
- push to the main branch that would require non-fast-forward (i.e. would need force).
A grant covers ordinary fast-forward push to the configured upstream. Anything else is a separate class of operation and needs its own decision.
**What counts as "push":** only `git push` family commands. Local commits, `git stash push`, etc. are not push; the grant does not apply.
## Rule 5 — Transit-zone / brainstorm workspaces
Some workspaces are **transit zones** — discussion areas with no `.tasks/`, where brainstorm artifacts are explicitly NOT auto-promoted to project wikis.
**Default destination for brainstorm artifacts:**
- **In-progress brainstorm outputs** → `.brainstorm/<topic>.md` (or whatever the workspace's README/CLAUDE.md declares)
- **Mature, cross-cutting outputs** → `~/projects/.wiki/concepts/<topic>-design.md` via `mcp__projects-meta__knowledge_ingest`**only** when user explicitly directs this
**Agent must NOT auto-promote** brainstorm artifacts to global wikis by analogy with Rule 1. Convergence-moment (move from workspace to permanent wiki) is a user decision, not an automatic action.
**Example:** `~/projects/.meeting-room/` is a transit zone. Its CLAUDE.md explicitly states "no `.tasks/`, transit zone, artifacts go to `.brainstorm/` or global wiki via user command." Rule 1's "project conventions override" applies, but the override is explicit in the workspace contract — auto-promotion by analogy would violate that contract.
**When in doubt:** ask the user "this goes to `.brainstorm/`, or should I promote to shared wiki?" rather than assuming.
## Out of scope
The skill **does not**:
- modify `CLAUDE.md` (that's `project-bootstrap`'s job);
- enforce rules via git hooks / pre-commit / CI (this is agent discipline, not tooling);
- manage `settings.json` permissions (that's `update-config`);
- check the existence of `.wiki/` / `.tasks/` (that's `setup-wiki` / `setup-tasks` / `project-bootstrap`); if a project doesn't have them, Rule 1 simply finds no overrides and falls back to skill defaults.
## Why this exists
In a tightly-disciplined repo (`claude-skills`) the four rules already hold by accident — the agent reads `.wiki/CLAUDE.md`, knows specs go to `.wiki/concepts/`, knows to bump `version:`, knows not to push without confirmation. In **other** projects of the same user, that discipline does not transfer: the agent uses `superpowers`' default `docs/superpowers/specs/`, branches on a whim, forgets `version:` bumps, and pushes without asking. This skill makes the discipline explicit and portable.
Full design rationale (why one skill instead of four, why a skill instead of inline `CLAUDE.md` lines, scope of each rule, push-permission mechanism choice) lives in `.wiki/concepts/project-discipline-design.md` (in this repo; in other projects bootstrapped from this repo, the design lives in `claude-skills`).

View File

@@ -0,0 +1,29 @@
# pulling-before-work
Policy skill that pulls the current branch from `origin` once at session start
and on explicit re-sync requests. Designed to remove the "edited on stale base"
footgun without trampling dirty work-trees or auto-merging.
## When it triggers
- **Session start** — when `CLAUDE.md` contains the line `pull remote before work` (added by `project-bootstrap` v1.4.0+).
- **In-chat** — when the user says `sync`, `resync`, `pull`, `обнови репо`, `git pull please`, or close variants.
Stays silent in non-git folders. Prints one informational line and exits in:
no `origin` remote, no upstream tracking, dirty work-tree, detached HEAD.
## What it does
`git pull --ff-only` against the configured upstream — never auto-merges, never
auto-rebases, never stashes, never commits, never pushes. On divergence it prints
a warning with manual-resolution hints and exits.
## Prerequisites
None. The skill is a no-op outside git repos and folders without an `origin`
remote, so it's safe to leave activated everywhere.
## Related
- `project-bootstrap` (v1.4.0+) — adds the trigger line to new and existing projects' `CLAUDE.md`.
- `.wiki/concepts/pulling-before-work-design.md` (in projects bootstrapped from this repo: this design lives in `claude-skills`) — full design rationale.

View File

@@ -0,0 +1,153 @@
---
name: pulling-before-work
version: 1.0.0
description: >
Pulls the current branch from origin once at session start and on explicit
re-sync requests. Use when CLAUDE.md contains the trigger line "pull remote
before work", or when the user says "sync", "resync", "pull", "обнови репо",
"git pull please", or close variants asking to refresh from the remote.
Runs `git pull --ff-only` — never auto-merges or rebases. Stays silent in
non-git folders. Prints one informational line and exits when there is no
origin remote, no upstream tracking, the working tree is dirty, or HEAD is
detached. Does not stash, commit, or push. Activated by `project-bootstrap`
v1.4.0+ via the canonical CLAUDE.md template.
---
# pulling-before-work
> Pull from `origin` once when work starts. Don't auto-merge. Don't trample dirty work-trees. Don't ask twice in the same session unless asked.
## When this runs
**At session start** — once, when the skill is activated by the `pull remote before work` line in `CLAUDE.md`. The cycle below runs immediately.
**On explicit re-sync** — when the user says any of: `sync`, `resync`, `pull`, `обнови репо`, `pull please`, `git pull`, `подтяни`, `pull from origin`. Re-runs the full cycle. There is no per-session counter; the user is always allowed to ask.
**Never** before each commit, before each tool call, on every message, or in any other implicit cadence. Mode-3 ("start + on-demand") was the explicit design choice — see `.wiki/concepts/pulling-before-work-design.md`.
## The pull cycle
Run these checks in order. Print at most one line of chat output per run.
### 1. Inside a git work-tree?
```bash
git rev-parse --is-inside-work-tree 2>/dev/null
```
If the command fails or prints anything other than `true`**exit silently, no chat output.** This is the not-a-git-repo case; the skill must not be noisy in random folders.
### 2. Has an `origin` remote?
```bash
git remote get-url origin 2>/dev/null
```
If the command fails (no such remote) → print one line and exit:
```
no origin remote — skip pull
```
### 3. Is the working tree clean?
```bash
git status --porcelain
```
If the output is non-empty → print one line and exit:
```
working tree dirty — skipping pull. commit/stash, потом скажи "sync"
```
Never stash automatically. Stash-pop conflicts are exactly the friction this skill exists to remove.
### 4. Is HEAD attached?
```bash
git symbolic-ref -q HEAD
```
If the command fails (empty output, exit 1) → detached HEAD. Print:
```
detached HEAD — skip pull
```
### 5. Does the current branch have an upstream?
```bash
git rev-parse --abbrev-ref --symbolic-full-name '@{u}' 2>/dev/null
```
Capture the upstream name (e.g. `origin/master`). If the command fails → no upstream tracking. Print:
```
no upstream tracking for <branch> — skip pull
```
(Where `<branch>` is `git rev-parse --abbrev-ref HEAD`.)
### 6. Pull, fast-forward only
```bash
git pull --ff-only
```
(No args — uses the configured upstream captured above.)
Classify by exit code and stdout:
| Result | Print |
|---|---|
| Already up to date | `✅ already up to date with <upstream>` |
| Fast-forward, N commits | `✅ pulled N commits from <upstream>` |
| Non-fast-forward / diverged (exit non-zero with "diverged" or "non-fast-forward" in output) | `⚠️ diverged from <upstream> — resolve manually (git pull --rebase or merge); skill never auto-merges/rebases` |
### Out of scope
The skill never:
- commits, stashes, or pushes
- recurses into submodules
- pulls from non-`origin` remotes
- pulls on detached HEAD
- runs auto-merge or auto-rebase
- runs more than once per session unless the user asks
## Recovery hints
If the skill skipped because of a dirty tree:
```powershell
# Windows / PowerShell
git status # see what's dirty
git add . ; git commit -m "wip"
# then ask the agent: "sync"
```
```bash
# Linux / macOS
git status
git add . && git commit -m "wip"
# then say "sync"
```
If the skill reported `diverged`:
```bash
# Option A: rebase your local commits on top of origin
git pull --rebase
# Option B: explicit merge (creates a merge commit)
git pull --no-ff
```
The skill stays out of these decisions on purpose — both options have valid use cases and the user owns the choice.
## Why this exists
Stale local branches are a silent footgun: edits land on top of yesterday's `origin`, the divergence shows up at push time, and by then there's a chunk of work to rebase or merge on the wrong base. One pull at start covers the common case; an explicit re-sync trigger handles long sessions where someone pushed mid-flight.
Full design rationale (mode choice, dirty-tree skip vs stash, `--ff-only` vs auto-merge, the upstream-check) lives in `.wiki/concepts/pulling-before-work-design.md`.