Agent Skills: File Naming & Path Standards

File naming, path standards, and conventional commits. Use when naming files, creating branches, writing commit messages, or setting up new projects. Covers underscore vs hyphen conventions, commit format, and branch naming patterns.

UncategorizedID: tianjianjiang/smith/smith-style

Install this agent skill to your local

pnpm dlx add-skill https://github.com/tianjianjiang/smith/tree/HEAD/smith-style

Skill Files

Browse the full folder contents for smith-style.

Download Skill

Loading file tree…

smith-style/SKILL.md

Skill Metadata

Name
smith-style
Description
Naming and path standards. Use when naming a file or branch, or writing a commit message.

File Naming & Path Standards

Prerequisites: @smith-principles/SKILL.md

CRITICAL: Naming Separators

Underscore (_): Multi-word phrases as single concept

  • user_authentication, query_processor, semantic_search
  • Think: "What kind of X?" → underscore

Hyphen (-): Hierarchical/parallel relationships

  • auth-login (login is part of auth)
  • api-rest vs api-graphql (variants)
  • Dates: 2025-01-15
  • Tickets: JIRA-1234

File Patterns

Test files: test_«module»_«function»_«type».py JSON test data: «module»_«function»_cases.json Documentation: «topic».md (hyphen for hierarchy, underscore for phrases) Config: .env, pyproject.toml, AGENTS.md

Path References

Use code blocks, not Markdown links:

**Core Principles**: @smith-principles/SKILL.md - Description
**Related Skill**: @skill-name/SKILL.md - Description

Variables: $WORKSPACE_ROOT, $REPO_ROOT, $HOME

Conventional Commits

Format: type: description or type(scope): description

Types: feat, fix, docs, refactor, style, test, chore, perf, build, ci

Length limits (50/72 rule):

  • Subject: 50 chars target, 72 max
  • Body: 72 chars per line

Examples

feat(auth): add token refresh
fix: resolve CORS issues
docs: update deployment guide

Also keep in mind:

  • Scope each commit to a single logical change — avoid mixing unrelated changes ("add X and fix Y")
  • Use docs only when the commit doesn't also change code

Special prefixes (outside conventional commits):

  • #WIP — Work-in-progress checkpoint, not a conventional commit (used when the user accepts a WIP checkpoint commit)

Assisted-by attribution

Smith policy: every commit, PR body, PR review comment, and Slack message produced with AI assistance ends with an Assisted-by: trailer naming the agent and model. The trailer format follows the Linux kernel coding-assistants policy (https://docs.kernel.org/process/coding-assistants.html), which mandates it for commits; extending it to PR bodies, review comments, and Slack is a Smith convention.

Format: Assisted-by: «AGENT_NAME»:«MODEL_VERSION» [«tool»]…

  • Smith value: Assisted-by: Claude:«session-model-id» — «MODEL_VERSION» is the actual running session model id, not a frozen string.
  • Optional bracketed [«tool»] entries name specialized analysis tools only (e.g. coccinelle, sparse, semgrep); never basic dev tools (git, editors).

Deterministic source — do not hand-type the trailer. Hand-typing is where the format drifts (a parenthetical instead of the colon form). smith-style/scripts/attribution.sh is the single source: it prints Assisted-by: «AGENT_NAME»:«MODEL_VERSION» from the first model source listed in smith-style/scripts/attribution-model-sources.txt that knows the session (for Claude Code, smith-ctx-claude/scripts/attribution-model-source.sh, kept current by the attribution-model-stamp hook). Pull it, never type it. For a commit message, PR body or review comment, write the body to a draft file, append the trailer in a separate shell call with …/attribution.sh >> «file», show the file, and pass its absolute path with git commit -F «file» or gh … --body-file «file» as a command of its own. gh pr-review has no --body-file: run the script first and end a single-quoted --body '…' with the line it printed, unchanged. For an MCP message (Slack/Jira), run the script and paste its output.

One rule, checked where the agent writes. smith-style/scripts/lib/attribution.mjs holds the rule. It asserts only that the script's line did not reach a text: the line is missing, differs from what the script prints, or an Assisted-by:-shaped line differs from it. A matching line proves nothing more, so the rule makes no claim that the script ran. Agent adapters call it at the agent's own tool boundary and block; humans are not checked. A write whose text cannot be read from a declared field (a -m message, the editor, a merge or revert message git generates, a literal body where a body-file flag exists) is not allowed. Covered writes and their allowed shapes are data, smith-style/scripts/attribution-signatures.json: git commits, merges, pulls and reverts, pull request and issue comment text, and Slack drafts. The Claude Code adapter is smith-ctx-claude's attribution-guard. Registration in README "Hooks".

Never add a Signed-off-by: trailer yourself — only a human can certify the DCO, so the agent must not add one (a human may still add their own), and Assisted-by: does not replace human authorship/sign-off. It names the AI that assisted, not the human who directed the work — the commit's author field, or the account it posts under, already carries that name, so do not pair it with an "on behalf of" line (@smith-gh-pr).

Claude Code's own default trailer competes with this. The harness appends its own Co-Authored-By:/"Generated with Claude Code" text to commits and PR bodies unless settings.json's attribution.commit/attribution.pr keys are set to "" (the deprecated includeCoAuthoredBy boolean predates this and is superseded by it). Set both to empty rather than to a literal Assisted-by: string: attribution.commit/.pr take only a static string with no model-name templating, so hardcoding one there reintroduces the exact staleness problem attribution.sh exists to avoid. Emptying them removes the competing default; attribution-guard.sh (@smith-ctx-claude) is what actually enforces the correct trailer's presence.

Branch Names

Pattern: type/description — the Conventional Branch specification (https://conventionalbranch.org/), adopted verbatim instead of a smith-invented hyphen/underscore split.

  • description is one or more lowercase-and-digit segments joined by a single hyphen (-) — never an underscore, space, or uppercase letter. No consecutive, leading, or trailing hyphens. A segment MAY contain dots (.) — the spec allows this for version-like text, e.g. chore/deps-node18.20-bump.
  • type matches the Conventional Commits type of the work (feat, fix, docs, refactor, style, test, chore, perf, build, ci) — branch type MUST match commit type.
  • Prefer full words over abbreviations (cmd, cfg, auth) unless the full word exceeds 15 chars OR the abbreviation is a domain-standard term in this repo (gh = GitHub, pr = pull request, ci = continuous integration, mcp = Model Context Protocol). command, configuration, authentication all fit — spell them out.
  • Never put post-review/after-review in a branch or commit — that names the change's ORIGIN (a review round), not what the change does. Name the effect instead: fix/auth-token-expiry, not fix/auth-post-review.

Examples: feat/user-authentication, fix/plan-claude-model-detection, docs/gh-pr-attribution-wording.

Why not encode scope hierarchy in the branch name. A two-separator convention (hyphen = hierarchy, underscore = single concept) leaves the choice between the two to judgement, so names get chosen by feel and rationalized afterward instead of generated from an algorithm. The scope information also matters to no tooling that exists. release-please (https://github.com/googleapis/release-please) — the standard tool for automating versions/changelogs from Conventional Commits, should this repo ever adopt it — parses only commit messages on the default branch; it never reads branch names. Scope belongs in the commit's type(scope): description header, where release-please-class tooling actually looks; the branch name only needs to be unambiguous and human-readable, which a single separator already guarantees.

Enforced deterministically by smith-git/scripts/hooks/branch-name-guard.mjs (PreToolUse, matchers: Bash + EnterWorktree) — blocks git commands and pushes with non-conforming names, warns on EnterWorktree (cannot block, rename immediately after). Bash matcher blocks branch-creating/renaming git invocations (git branch, git checkout -b, git switch -c) and git push with non-conforming target names. EnterWorktree matcher warns when creating worktrees (tool always creates worktree-{name}, which violates the pattern) and instructs immediate rename via git branch -m <type>/description. If the name was not explicitly given by the user, still confirm it with them before the first push — the hook checks format, not what the user actually wanted named.

Older branch names follow the two-separator convention described above. This section governs new branches; do not rename already-merged branches to match retroactively.

External Communication Standards

Language matching:

  • Match the language of the source context (English PR → English reply; zh-Hant Notion → zh-Hant reply)
  • Code artifacts always English: variable names, commits, branch names, inline code comments
  • Default for user-facing explanations when context is ambiguous: zh-Hant (user preference)
  • Never switch language unprompted — if unsure, ask once

Wiki-link leakage prevention:

  • [[Page Title]] renders as broken literal text outside Notion
  • When copying to Slack/GitHub/Jira: convert to plain name or full URL

Issue format (Job Story):

  • "When «situation», I want to «motivation», so I can «expected outcome»"
  • Describe the problem and outcome, not the implementation approach

Related

  • @smith-git/SKILL.md - Branch and commit workflows
  • @smith-principles/SKILL.md - Core principles (DRY, KISS, YAGNI)
  • @smith-slack/SKILL.md - Slack message formatting and pre-send gate (owns all Slack rules)

Before You Finish

Before naming a file (branches: see Branch Names):

  1. Is it a single concept? → underscore
  2. Is it a part/variant? → hyphen
  3. Is it a date/ticket? → hyphen

Before committing:

  1. Subject ≤72 chars?
  2. Single atomic change?
  3. Type matches branch?
  4. Assisted-by: trailer present, and no AI-added Signed-off-by:?