Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions skills/concise-comments/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
name: concise-comments
description: Rules for concise code comments/docstrings, plus a pass to trim verbose ones. Use when writing or reviewing code, or when asked to reduce comment verbosity / trim over-commented code / cut docstring bloat. Referenced by /spec and /process-reviews.
---

# Concise comments & docstrings

Prevention: follow these when writing. Cleanup: run the pass below when invoked as `/concise-comments`.

## The test
Keep a comment only if a competent reader of THIS codebase couldn't infer it from the code and names. Otherwise delete it, or cut it to the one missing fact in one line.

## For / not for
For: the non-obvious *why* (constraint, invariant, footgun, edge-case gotcha) and a one-line *what* on public APIs.
Not for: design narrative, alternatives-considered, defending the approach, repro steps, byte-count walkthroughs, ticket history, or restating the code. Those go in the PR description, DECISIONS.md, the issue, or a test.

## Limits (defaults; exceed only with a stated reason)
- Inline comment: 1 line. Needing 3+ lines to justify a line of code → rename things or move the rationale out.
- Function/method docstring: 1-line summary; +≤3 lines only for genuinely subtle behaviour.
- Module docstring: ≤8 lines. Test docstring: 1 line, or none if the name says it.
- Section banner: ≤1 label line, no `---` paragraphs.

Say it once at the definition; don't re-explain at every call site.

## Delete on sight
- **Restates the code** (`# strip prefix` above `x.split(...)`).
- **Ticket-ID noise** (`DEV-1234:` on every line/attribute; the value is the fact).
- **Design essay / justification debate** (paragraphs on why this is right and alternatives wrong).
- **Worked examples & repros** (byte counts, sample output) → move to the test.
- **Docstring echoing the name** (`def test_x: """Test x."""`).
- **Emphasis theatre** (ALL-CAPS sentences, "astronomically", "provably", "THE … requirement").

Keep, as ≤1 line: a real cross-dialect/edge gotcha, a non-obvious invariant, a "looks wrong but isn't", an ordering/side-effect warning. When unsure a *why* is inferable, keep it but shorten it.

## The pass (`/concise-comments`)
1. Scope: default to the branch diff (`git diff $(git merge-base <base> HEAD) -- '*.py'`); or the path/file the user names. Only added/modified comments unless told to sweep.
2. Edit comments/docstrings ONLY — never code, string literals, test data, asserts, imports, markers.
3. Verify: run affected tests + linter (green); confirm `git diff` changed no code line; measure before/after with `scripts/count_comments.py --range <mergebase> <paths>`.
4. Report per-file and total lines before → after + ratio, and that tests/lint pass.

Scale to the ask: "trim a bit" = kill the essays; "3x" / "massively" = apply every limit hard.
87 changes: 87 additions & 0 deletions skills/concise-comments/scripts/count_comments.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#!/usr/bin/env python3
"""Count comment + docstring lines in Python files.

Comments via tokenize, docstrings via AST (module/class/function first stmt).

count_comments.py <file>... # per-file + total counts
count_comments.py --range <ref> <path>... # net added vs a git ref (working tree)
"""
from __future__ import annotations

import ast
import io
import subprocess
import sys
import tokenize


def counts(src: str | None) -> tuple[int, int]:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add solid tests for the shipped utility.

This change adds parsing, Git-reference, file, and CLI behavior without adding tests. Cover valid and malformed Python, module/class/function/async docstrings, missing files, invalid refs, deleted paths, and incomplete --range arguments.

As per coding guidelines: “Whenever you do any changes to production code ... ALWAYS add solid test coverage for it.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/concise-comments/scripts/count_comments.py` at line 18, Add
comprehensive tests for the utility centered on counts, covering valid and
malformed Python, module/class/function/async docstrings, missing files, invalid
Git references, deleted paths, and incomplete --range CLI arguments. Verify
expected counts and error behavior for each case, including both valid and
malformed inputs.

Source: Coding guidelines

if src is None:
return (0, 0)
try:
comment = sum(
1 for t in tokenize.generate_tokens(io.StringIO(src).readline)
if t.type == tokenize.COMMENT
)
except Exception:
comment = 0
doc = 0
try:
tree = ast.parse(src)
except Exception:
return (comment, 0)
kinds = (ast.Module, ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)
for node in ast.walk(tree):
if isinstance(node, kinds):
body = getattr(node, "body", [])
first = body[0] if body else None
if (
isinstance(first, ast.Expr)
and isinstance(getattr(first, "value", None), ast.Constant)
and isinstance(first.value.value, str)
):
doc += first.end_lineno - first.lineno + 1
return (comment, doc)


def git_show(ref: str, path: str) -> str | None:
r = subprocess.run(["git", "show", f"{ref}:{path}"], capture_output=True, text=True)
return r.stdout if r.returncode == 0 else None
Comment on lines +47 to +49

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- file map ---'
ast-grep outline skills/concise-comments/scripts/count_comments.py

printf '%s\n' '--- relevant source ---'
cat -n skills/concise-comments/scripts/count_comments.py | sed -n '1,140p'

printf '%s\n' '--- tracked related files ---'
git ls-files | rg '(^|/)(count_comments|test|tests)' | head -100

printf '%s\n' '--- structural references to git_show ---'
ast-grep run --lang python --pattern 'git_show($_, $_)' skills/concise-comments/scripts/count_comments.py

Repository: MotleyAI/claude-configs

Length of output: 3891


🏁 Script executed:

#!/bin/bash
set -eu

python3 - <<'PY'
import subprocess

def git_show(ref: str, path: str):
    r = subprocess.run(
        ["git", "show", f"{ref}:{path}"],
        capture_output=True,
        text=True,
    )
    return r.stdout if r.returncode == 0 else None, r.returncode, r.stderr.strip()

head = subprocess.run(
    ["git", "rev-parse", "--verify", "HEAD"],
    capture_output=True,
    text=True,
    check=True,
).stdout.strip()

cases = [
    ("invalid-ref", "HEAD_DOES_NOT_EXIST", "skills/concise-comments/scripts/count_comments.py"),
    ("missing-path", head, "skills/concise-comments/scripts/path_does_not_exist.py"),
    ("valid-path", head, "skills/concise-comments/scripts/count_comments.py"),
]
for name, ref, path in cases:
    value, code, error = git_show(ref, path)
    print(f"{name}: returncode={code}, result_is_none={value is None}, stdout_len={len(value or '')}, stderr={error!r}")
PY

Repository: MotleyAI/claude-configs

Length of output: 563


Validate the Git reference before reading the file. git_show maps both invalid references and missing paths to None, so an invalid ref produces false additions. Validate ref separately, then treat only a missing ref:path as empty content.

🧰 Tools
🪛 ast-grep (0.45.1)

[error] 47-47: Command coming from incoming request
Context: subprocess.run(["git", "show", f"{ref}:{path}"], capture_output=True, text=True)
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(subprocess-from-request)

🪛 Ruff (0.16.1)

[error] 48-48: subprocess call: check for execution of untrusted input

(S603)


[error] 48-48: Starting a process with a partial executable path

(S607)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/concise-comments/scripts/count_comments.py` around lines 47 - 49,
Update git_show to validate the Git ref separately before reading ref:path,
preserving distinct handling for invalid refs and missing paths. Return None
only when the reference is valid but the requested path is unavailable, and
ensure invalid refs do not become empty content that is interpreted as
additions.



def read(path: str) -> str | None:
try:
return open(path, encoding="utf-8").read()
except FileNotFoundError:
return None
Comment on lines +52 to +56

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make missing-file handling mode-aware.

read maps FileNotFoundError to None, so normal mode reports a misspelled path as a successful zero count. Fail for missing paths in normal mode. Allow missing working-tree paths only when --range intentionally measures deletions.

Also applies to: 77-79

🧰 Tools
🪛 ast-grep (0.45.1)

[warning] 53-53: File path is request-/variable-derived; validate and normalize to prevent path traversal.
Context: open(path, encoding="utf-8")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(open-filename-from-request)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/concise-comments/scripts/count_comments.py` around lines 52 - 56,
Update read so missing files raise an error in normal mode instead of returning
None, while allowing None for intentionally missing working-tree paths when
--range deletion analysis is active. Thread the mode or range context through
the callers of read and preserve the existing deletion-count behavior.



def main(argv: list[str]) -> int:
if argv and argv[0] == "--range":
ref = argv[1]
paths = argv[2:]
Comment on lines +59 to +62

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Validate --range arguments before indexing argv.

--range without a reference raises IndexError. --range REF also lacks a required path. Return a usage error unless both a reference and at least one path are present.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/concise-comments/scripts/count_comments.py` around lines 59 - 62,
Update main so the --range branch validates that argv contains both a reference
and at least one path before accessing argv[1] or assigning paths. Return the
existing usage-error result for missing arguments, while preserving the current
range processing when both are supplied.

tc = td = 0
for p in paths:
b = counts(git_show(ref, p))
h = counts(read(p))
dc, dd = h[0] - b[0], h[1] - b[1]
print(f"{dc + dd:+5d} comment={dc:+4d} doc={dd:+4d} {p}")
tc += dc
td += dd
print(f"{'=' * 50}\nNET ADDED total={tc + td} (comment {tc}, docstring {td})")
Comment on lines +65 to +71

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Align --range output with the skill’s report contract.

This branch prints only deltas. skills/concise-comments/SKILL.md Line 39 requires per-file and total before→after counts plus a ratio. Print those values and define the zero-baseline ratio, or change the skill requirement to match the delta-only output.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/concise-comments/scripts/count_comments.py` around lines 65 - 71,
Update the reporting logic around counts(), the per-file print, and the final
total print so --range output includes comment and docstring before→after counts
and the required ratio, matching the report contract in SKILL.md. Define and
consistently apply behavior for a zero baseline ratio; alternatively, update the
documented requirement to explicitly permit delta-only output.

return 0
if not argv:
print(__doc__)
return 1
tc = td = 0
for p in argv:
c, d = counts(read(p))
print(f"{c + d:5d} comment={c:4d} doc={d:4d} {p}")
tc += c
td += d
print(f"{'=' * 50}\nTOTAL={tc + td} (comment {tc}, docstring {td})")
return 0


if __name__ == "__main__":
raise SystemExit(main(sys.argv[1:]))
3 changes: 2 additions & 1 deletion skills/process-reviews/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ Don't dedupe across sources blindly — if multiple sources flag the same area,

For each entry, classify as VALID or INVALID using a source-appropriate signal:

- **CodeRabbit / Sonar / Codex** — READ the cited file at the cited line(s). Apply the user's global rules from `~/.claude/CLAUDE.md` (trust internal code, validate only at boundaries; imports at the top; etc.). Lean toward VALID when uncertain — false positives are easier to defend later than missed bugs now.
- **CodeRabbit / Sonar / Codex** — READ the cited file at the cited line(s). Apply the user's global rules from `~/.claude/CLAUDE.md` (trust internal code, validate only at boundaries; imports at the top; etc.). Lean toward VALID when uncertain — false positives are easier to defend later than missed bugs now. Over-verbose comments/docstrings in the diff (design essays, ticket-ID-on-every-line, code-restating comments, name-echo docstrings) are a valid maintainability finding per the `concise-comments` skill — flag them even if no bot did.
- **CI failure** — READ the failed-step log excerpt in the JSON. Classify as INVALID only when the failure is clearly unrelated to this PR's changes (network blip / runner died / well-known flaky test that the user has previously confirmed is flaky / out-of-date Action that times out before doing anything). Otherwise VALID. **A test failure that points at code this PR touched is always VALID** — don't argue your way out of it.

Codex-specific validation hazards (apply on top of the shared CodeRabbit/Sonar rules):
Expand Down Expand Up @@ -237,6 +237,7 @@ Do **not** start writing code until the user picks.
- NEVER call any "resolve" mutation on CodeRabbit threads (no `resolveReviewThread`, no UI-equivalent). Only `/replies`. This is a hard global rule from the user.
- NOSONAR suppressions MUST include the rule key and a reason. Bare `// NOSONAR` is forbidden. The rule key inside the parentheses must be alphanumeric only — never include the `python:` / `javascript:` / etc. language prefix (it makes Sonar treat the suppression as malformed).
- This skill stops at the plan. Code fixes happen only after the user picks a group.
- When you later write fixes, keep any comments/docstrings concise per the `concise-comments` skill — don't add design essays or code-restating comments while resolving findings.
- If validation makes you LESS than 80% confident an issue is invalid, classify it as VALID and put it in the plan. Better to over-fix than to silently dismiss a real bug.

## When NOT to use this skill
Expand Down
13 changes: 13 additions & 0 deletions skills/spec/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ Interview me in detail.
Cover implementation approach, edge cases, gotchas, design choices, tradeoffs, and constraints.
Skip obvious questions.
Ask one at a time and build on my answers.

**IMPORTANT — WHENEVER you ask me to make a choice, you MUST give, for the
decision, explicit PROS and CONS of each option AND a clear RECOMMENDATION of
which one you'd pick and why. This is non-negotiable. Never present options as
a bare menu. If you use a structured/multiple-choice prompt, spell out the
pros, the cons, and the recommendation inside it — do not rely on a one-line
description to carry them.**

When you think you have enough information, return a detailed, complete spec.
In the spec you write, NEVER take shortcuts or make simplifications or
extensions to the original requirements without asking me about each one first.
Expand Down Expand Up @@ -80,6 +88,11 @@ Implement the plan. Run the full non-integration test suite (per
`feedback_unit_tests_only.md`) after changes and fix any failures. Do not
declare done until every test from Step 4/5 passes.

When writing code AND tests (Steps 4 and 6), follow the `concise-comments`
skill: no design essays, ticket-ID-on-every-line, or code-restating comments;
docstrings to a line. Rationale belongs in the spec / PR description / this
issue — not in code — so verbose comments never get written in the first place.

## Step 7 — Ask me to commit, push, and PR

Once all tests pass, stop and ask me whether to commit, push, and open a PR.
Expand Down