Compare commits
15 Commits
718419f6d0
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c0f813b792 | ||
|
|
8f231d49e7 | ||
|
|
545c213f88 | ||
|
|
04f24bb16d | ||
|
|
26585a86fc | ||
|
|
cd32841847 | ||
|
|
1bc7711f38 | ||
|
|
27aaabf305 | ||
|
|
a42d6b76f4 | ||
|
|
9ac5444628 | ||
|
|
94031f5a66 | ||
|
|
55f9935c63 | ||
|
|
91b9845c1b | ||
|
|
30edba08bc | ||
|
|
3d754c6e0e |
@@ -26,7 +26,7 @@ Copy the skills into a specific project (`<repo>/.agents/skills` and `<repo>/.cl
|
||||
make project TARGET=/path/to/repo
|
||||
```
|
||||
|
||||
Both commands also enable the caveman and ponytail plugins in the target's `.claude/settings.json` so their modes auto-activate on session start; existing settings are preserved.
|
||||
Both commands also patch the target's `.claude/settings.json`: the caveman and ponytail plugins are enabled so their modes auto-activate on session start, and the `autotune` reminder hook is registered so the agent points you at `/autotune` at most once an hour. Existing settings are preserved.
|
||||
|
||||
Restart your agent afterwards so it loads the new skills.
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
#!/usr/bin/env node
|
||||
// Enable the caveman and ponytail plugins in a Claude Code settings.json so
|
||||
// their SessionStart hooks auto-activate both modes. Merges non-destructively:
|
||||
// existing marketplaces and enabled plugins are preserved, unparseable files
|
||||
// are left untouched.
|
||||
// their SessionStart hooks auto-activate both modes, and register the autotune
|
||||
// reminder hook. Merges non-destructively: existing marketplaces, plugins and
|
||||
// hooks are preserved, unparseable files are left untouched.
|
||||
"use strict";
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const settingsPath = process.argv[2];
|
||||
if (!settingsPath) {
|
||||
@@ -38,5 +39,15 @@ for (const [name, entry] of Object.entries(MARKETPLACES)) {
|
||||
}
|
||||
data.enabledPlugins = Object.assign(data.enabledPlugins || {}, PLUGINS);
|
||||
|
||||
const reminder = path.join(path.dirname(settingsPath), "skills", "autotune", "hooks", "reminder.sh");
|
||||
data.hooks = data.hooks || {};
|
||||
data.hooks.UserPromptSubmit = data.hooks.UserPromptSubmit || [];
|
||||
const registered = JSON.stringify(data.hooks.UserPromptSubmit).includes("autotune/hooks/reminder.sh");
|
||||
if (!registered) {
|
||||
data.hooks.UserPromptSubmit.push({
|
||||
hooks: [{ type: "command", command: `bash ${JSON.stringify(reminder)}` }],
|
||||
});
|
||||
}
|
||||
|
||||
fs.writeFileSync(settingsPath, JSON.stringify(data, null, 2) + "\n");
|
||||
console.log(`skills: enabled caveman + ponytail plugins in ${settingsPath}`);
|
||||
console.log(`skills: enabled caveman + ponytail plugins and the autotune reminder in ${settingsPath}`);
|
||||
|
||||
38
skills/active-listening/SKILL.md
Normal file
38
skills/active-listening/SKILL.md
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
name: active-listening
|
||||
description: >
|
||||
Gather every requirement before acting. Trigger at the start of any task
|
||||
whose scope, constraints, or success criteria are not fully pinned down, and
|
||||
whenever another skill needs an operator-only fact — ask focused questions
|
||||
until nothing needed is still open, then reflect the understanding back.
|
||||
Portable across projects.
|
||||
---
|
||||
|
||||
Do not act on a half-understood request. Listen actively until you could state
|
||||
the task, its constraints, and its definition of done without guessing.
|
||||
|
||||
## Procedure
|
||||
|
||||
1. **Surface the unknowns.** List every open question the task leaves: ambiguous
|
||||
scope, unstated constraints, success criteria, edge cases, which files or
|
||||
systems are in play, what "done" means, and any decision only the operator
|
||||
can make.
|
||||
2. **Derive what you can first.** Remove from that list every question you can
|
||||
answer yourself from the code, the logs, the git history, or a sensible
|
||||
default. Active listening is not offloading your own investigation onto the
|
||||
operator.
|
||||
3. **Ask, focused.** Put the genuinely-open questions to the operator — grouped,
|
||||
concrete, answerable, with a recommended default where one exists. Prefer a
|
||||
few high-leverage questions over a long interrogation. Use the host's
|
||||
structured question mechanism when one exists.
|
||||
4. **Listen and integrate.** Fold each answer in. A new answer often opens a new
|
||||
question — keep going.
|
||||
5. **Loop until closed.** Repeat until no needed information is still open.
|
||||
6. **Reflect back.** Restate the task, its constraints, and its definition of
|
||||
done in your own words for a final confirmation before acting.
|
||||
|
||||
## Rules
|
||||
|
||||
- Never invent an answer to an open question to avoid asking.
|
||||
- Never ask what you can determine yourself.
|
||||
- One genuinely-blocking unknown is enough reason to ask; do not proceed past it.
|
||||
30
skills/autoskill/SKILL.md
Normal file
30
skills/autoskill/SKILL.md
Normal file
@@ -0,0 +1,30 @@
|
||||
---
|
||||
name: autoskill
|
||||
description: >
|
||||
Detect operator repetition and turn it into a skill. Trigger when the
|
||||
operator gives an instruction, correction, or prompt pattern for roughly
|
||||
the third time in a conversation (or says they keep repeating themselves),
|
||||
and the pattern is general enough to reuse. Portable across projects.
|
||||
---
|
||||
|
||||
When you notice the operator repeating the same kind of instruction,
|
||||
correction, or prompt pattern (about three occurrences, exact wording may
|
||||
vary), do the following:
|
||||
|
||||
1. Finish the current task first; never interrupt work in progress.
|
||||
2. Then propose, in one short paragraph: the repeated pattern you observed,
|
||||
a skill name suggestion, the trigger phrasing, and what the skill would
|
||||
do. Ask whether to create it.
|
||||
3. On approval, write the skill:
|
||||
- a conversation shortcut or expansion: add it to the `shortcuts` skill
|
||||
table instead of creating a new skill.
|
||||
- project-specific behavior: create it in the project's own skills
|
||||
directory (e.g. `skills/<name>/SKILL.md` in the repository).
|
||||
- portable behavior: create `skills/<name>/SKILL.md` in the operator's
|
||||
skills repository.
|
||||
Follow the local naming conventions and keep the skill a thin,
|
||||
single-purpose instruction; route to an authoritative doc when one
|
||||
exists instead of duplicating it.
|
||||
4. Do not propose a skill for one-off work, secrets, or anything whose
|
||||
repetition is already covered by an existing skill; when an existing
|
||||
skill almost fits, propose extending it instead.
|
||||
92
skills/autotune/SKILL.md
Normal file
92
skills/autotune/SKILL.md
Normal file
@@ -0,0 +1,92 @@
|
||||
---
|
||||
name: autotune
|
||||
description: >
|
||||
Generate new agent skills and optimize existing ones so the agent works more
|
||||
efficiently: fewer input and output tokens, fewer errors, more focus, and
|
||||
learnings that persist across sessions. Every proposal is confirmed with the
|
||||
operator through single-select (radio button) questions. Trigger ONLY on an
|
||||
explicit operator request (/autotune, "autotune", "tune the skills"); never
|
||||
run it on your own initiative. Portable across projects.
|
||||
---
|
||||
|
||||
Autotune turns observed friction into durable skills. It runs on demand only,
|
||||
it changes nothing without an explicit answer from the operator, and every
|
||||
skill it writes or rewrites lands in the operator's skills repository.
|
||||
|
||||
## Trigger discipline
|
||||
|
||||
- Run **only** when the operator asks: `/autotune`, "autotune", "tune the
|
||||
skills", or an equivalent instruction.
|
||||
- The 60-minute reminder hook is a hint for the operator, not a trigger. When it
|
||||
fires, print the hint and continue the operator's actual request.
|
||||
- Never propose, write, or edit a skill as a side effect of unrelated work.
|
||||
|
||||
## Step 1: collect evidence
|
||||
|
||||
Before asking anything, gather concrete friction from the current session and,
|
||||
where readable, from the project's memory and agent instruction files:
|
||||
|
||||
- **Token waste**: repeated file re-reads, whole-file reads where a range was
|
||||
enough, long tool output pasted back, re-running a command instead of
|
||||
grepping a saved log, verbose report prose.
|
||||
- **Errors**: rejected tool calls, permission prompts, retried commands,
|
||||
corrections the operator had to give twice.
|
||||
- **Focus loss**: work that drifted beyond the request, unrequested tests or
|
||||
deploys, questions that stalled an autonomous run.
|
||||
- **Lost learnings**: facts re-derived this session that a memory file or a
|
||||
skill should already have carried.
|
||||
|
||||
List each finding as one line: `symptom -> cost -> candidate fix`. Skip
|
||||
anything that happened once and is not a pattern.
|
||||
|
||||
## Step 2: ask, do not decide
|
||||
|
||||
Use `AskUserQuestion` with `multiSelect: false` so every question renders as
|
||||
radio buttons. Never assume an answer, never batch several decisions into one
|
||||
option, and never write a file before the answers are in.
|
||||
|
||||
Ask in this order, at most four questions per call:
|
||||
|
||||
1. **Scope** - "What should autotune do this run?" Options: `Create a new
|
||||
skill`, `Optimize an existing skill`, `Both`, plus the ranked findings if
|
||||
more than one candidate exists.
|
||||
2. **Target** - which finding becomes a skill, or which existing skill gets
|
||||
optimized. One option per candidate, each with its measured cost in the
|
||||
description.
|
||||
3. **Shape** - for a new skill: `Standalone skill`, `Row in the shortcuts
|
||||
skill`, `Extend an existing skill`, `Memory entry instead of a skill`.
|
||||
For an optimization: `Tighten wording only`, `Change the trigger`, `Add
|
||||
rules`, `Split into two skills`, `Merge into another skill`.
|
||||
4. **Placement** - `Operator skills repository (portable)` or
|
||||
`This project's skills directory (project-specific)`.
|
||||
|
||||
Present the drafted skill text (name, trigger phrasing, body outline) and ask
|
||||
for a final `Write it` / `Revise it` / `Discard` confirmation before touching
|
||||
disk. Rewrites of an existing skill additionally show a before/after diff of
|
||||
the changed lines in that confirmation.
|
||||
|
||||
## Step 3: write
|
||||
|
||||
- Portable skills go to `skills/<name>/SKILL.md` in the operator's skills
|
||||
repository; project-specific ones to that project's skills directory.
|
||||
- Frontmatter carries `name` and a `description` that states what the skill
|
||||
does **and** when to trigger it - the description is the only part loaded into
|
||||
every session, so it decides whether the skill ever fires.
|
||||
- Body is thin and single-purpose: the smallest instruction that changes
|
||||
behaviour. Route to an authoritative doc instead of duplicating it.
|
||||
- Optimizing an existing skill means the file gets shorter or sharper, not
|
||||
longer. Delete rules the agent already follows, merge duplicated ones, and
|
||||
cut every sentence that does not change an action.
|
||||
- Report what changed in at most three lines, then refresh the reminder stamp
|
||||
so the next hint is an hour out: `date +%s > ~/.claude/.autotune-stamp`.
|
||||
|
||||
## Rules
|
||||
|
||||
- One skill per run unless the operator picked `Both`; a broad rewrite of the
|
||||
whole collection is not what autotune is for.
|
||||
- Never write a skill for one-off work, for secrets, or for behaviour an
|
||||
existing skill already covers - propose extending that skill instead.
|
||||
- A skill that saves tokens but loses correctness is a regression; validation,
|
||||
error handling, and security steps are never the thing that gets trimmed.
|
||||
- Efficiency claims stay honest: name the observed friction the skill removes,
|
||||
never a guessed percentage.
|
||||
32
skills/autotune/hooks/reminder.sh
Executable file
32
skills/autotune/hooks/reminder.sh
Executable file
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env bash
|
||||
# UserPromptSubmit hook: emit the autotune hint at most once per interval.
|
||||
# Stdout lands in the agent's context, which is why the payload tells the agent
|
||||
# to show the hint and keep going instead of running autotune by itself.
|
||||
set -euo pipefail
|
||||
|
||||
interval="${AUTOTUNE_INTERVAL_SECONDS:-3600}"
|
||||
stamp="${AUTOTUNE_STAMP:-${HOME}/.claude/.autotune-stamp}"
|
||||
|
||||
now="$(date +%s)"
|
||||
last=0
|
||||
if [[ -f "${stamp}" ]]; then
|
||||
read -r last <"${stamp}" || true
|
||||
[[ "${last}" =~ ^[0-9]+$ ]] || last=0
|
||||
fi
|
||||
|
||||
if ((now - last < interval)); then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname "${stamp}")"
|
||||
printf '%s\n' "${now}" >"${stamp}"
|
||||
|
||||
cat <<'HINT'
|
||||
AUTOTUNE HINT: over an hour since the last hint. Show the operator this notice
|
||||
verbatim as a short block, then continue with their request. Do NOT run
|
||||
autotune, do not propose skills, do not ask questions about it.
|
||||
|
||||
💡 /autotune - generate a new skill or optimize an existing one
|
||||
(fewer tokens, fewer errors, more focus, learnings that persist).
|
||||
Runs only when you trigger it.
|
||||
HINT
|
||||
33
skills/comments-clean/SKILL.md
Normal file
33
skills/comments-clean/SKILL.md
Normal file
@@ -0,0 +1,33 @@
|
||||
---
|
||||
name: comments-clean
|
||||
description: >
|
||||
Purge explanatory comments from staged and unstaged changes. Run
|
||||
AUTOMATICALLY, without being asked, every time the operator requests a
|
||||
commit or stage in any wording (commit, commit no-verify, stage next,
|
||||
socon, sobu, socobu, "commite ..."), BEFORE executing that request; also
|
||||
on /comments-clean or when the operator complains about comments.
|
||||
Explanatory comments are bad practice: they narrate code instead of
|
||||
letting it speak. Portable across projects.
|
||||
---
|
||||
|
||||
When the operator asks to commit or stage, run this audit first and only
|
||||
then perform the requested git action; never ask whether to run it.
|
||||
|
||||
Audit every staged and unstaged change (`git diff` plus `git diff --cached`,
|
||||
added lines only) and delete every explanatory comment:
|
||||
|
||||
1. Delete: restated code, step narration, section banners, "Note that",
|
||||
rationale prose, apology comments, and any comment whose information the
|
||||
code, task name, or commit message already carries. When in doubt,
|
||||
delete.
|
||||
2. Keep only: parameter/exit-code docs, nocheck/noqa/shellcheck directives,
|
||||
and project-sanctioned exception markers that name a concrete trip-wire.
|
||||
A relabeled narration is not an exception; delete it instead.
|
||||
3. Re-run the project's comment lint if one exists and re-stage the files
|
||||
that were already staged.
|
||||
4. Penalty: if this audit finds anything you wrote yourself, admit it in
|
||||
the report (file and count, no excuses), lower your confidence numbers
|
||||
for the affected bundle, and write the incident to persistent agent
|
||||
memory (extend the existing minimal-comments feedback entry with the
|
||||
date and the pattern you slipped on) so the calibration survives the
|
||||
session.
|
||||
20
skills/commit-next/SKILL.md
Normal file
20
skills/commit-next/SKILL.md
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: commit-next
|
||||
description: >
|
||||
Commit the staged bundle with --no-verify, then stage the next logical
|
||||
bundle and brief the operator on it. Trigger on /commit-next or when the
|
||||
operator asks to commit and stage the next bundle in one step. Portable
|
||||
across projects.
|
||||
---
|
||||
|
||||
1. Run the commit-no-verify skill on the currently staged changes
|
||||
(comments-clean audit first, then `git commit --no-verify`, exact
|
||||
staging index only).
|
||||
2. Group the remaining uncommitted changes into logical bundles (one
|
||||
concern per bundle: same failure class, same mechanism, or same role)
|
||||
and stage the next one; never mix concerns to save a commit.
|
||||
3. For the newly staged bundle run the explain skill (what the changes do
|
||||
and how the pieces interact, file:line-anchored) followed by the
|
||||
confidence skill (fix confidence, break risk, evidence, residuals).
|
||||
4. Close with what remains unstaged and wait for the operator's commit
|
||||
decision; never auto-commit the newly staged bundle.
|
||||
15
skills/commit-no-verify/SKILL.md
Normal file
15
skills/commit-no-verify/SKILL.md
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
name: commit-no-verify
|
||||
description: >
|
||||
Commit the currently staged changes with --no-verify (skip the pre-commit
|
||||
hooks). Trigger on /commit-no-verify or when the operator asks for a
|
||||
no-verify commit in any wording. Invoking this skill IS the explicit
|
||||
per-commit authorization that --no-verify requires; it never carries over
|
||||
to later commits. Portable across projects.
|
||||
---
|
||||
|
||||
1. Run the comments-clean audit over the staged and unstaged changes first.
|
||||
2. Commit exactly the staging index with `git commit --no-verify` and a
|
||||
Conventional-Commits message derived from the staged diff; never add
|
||||
unstaged files, never push.
|
||||
3. Report the created commit hash and what remains uncommitted.
|
||||
29
skills/confidence/SKILL.md
Normal file
29
skills/confidence/SKILL.md
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
name: confidence
|
||||
description: >
|
||||
Report calibrated confidence unprompted. Trigger whenever you stage
|
||||
changes, declare a fix or bundle ready, or summarize completed work the
|
||||
operator may commit or deploy — the operator must never have to ask "how
|
||||
sure are you?". Portable across projects.
|
||||
---
|
||||
|
||||
Every stage summary, fix report, or ready-to-commit bundle ends with a
|
||||
confidence block:
|
||||
|
||||
1. **Fix confidence** (percent): probability the change does what it
|
||||
claims. **Break risk** (percent): probability it breaks anything else.
|
||||
Two separate numbers, never one blended figure.
|
||||
2. **Evidence**: the strongest proof lines only — what was executed,
|
||||
reproduced, enumerated, or read from authoritative source. Distinguish
|
||||
proven (ran it, saw it) from derived (reasoned statically).
|
||||
3. **Residuals**: name exactly what keeps the number below 100 and what
|
||||
would close the gap (live deploy, missing artifact, unexercised path).
|
||||
Never a bare percentage without its residuals.
|
||||
|
||||
Calibration rules:
|
||||
- Number the claim you verified, not the effort you spent; discount after
|
||||
any error the operator caught in the same area.
|
||||
- Distinguish "fixes the observed failure" from "makes the job green" when
|
||||
hidden layers may follow, and give both numbers.
|
||||
- Static analysis alone caps at the low nineties; only executed evidence
|
||||
(test, reproduction, live probe) justifies more.
|
||||
52
skills/dialectic/SKILL.md
Normal file
52
skills/dialectic/SKILL.md
Normal file
@@ -0,0 +1,52 @@
|
||||
---
|
||||
name: dialectic
|
||||
description: >
|
||||
Reach a defensible conclusion through a dialectical process — thesis,
|
||||
adversarial antithesis, synthesis — iterated until one thesis survives every
|
||||
attack at ~99% confidence. Trigger for any consequential root-cause, design,
|
||||
or decision where being wrong is expensive and the answer is not yet certain.
|
||||
Deep-inspects every accessible source. Portable across projects.
|
||||
---
|
||||
|
||||
Reach the truth by dialectic, not by first guess. Do not stop at a plausible
|
||||
answer; drive the loop until a thesis survives every attack at ~99% confidence.
|
||||
|
||||
## Before you start: listen
|
||||
|
||||
Invoke the `active-listening` skill first. Do not begin the dialectic until you
|
||||
have gathered every requirement, constraint, and success criterion the task
|
||||
needs. A dialectic run on the wrong question wastes the whole loop. Return to
|
||||
`active-listening` mid-loop whenever an objection turns on a fact only the
|
||||
operator holds.
|
||||
|
||||
## The loop
|
||||
|
||||
1. **Thesis.** Form the strongest initial claim you can, grounded in evidence,
|
||||
not assumption. Deep-inspect every source you can reach: the source code and
|
||||
git history; the running containers (exec, logs, inspect through whatever
|
||||
make or CLI helpers the project exposes); CI logs and artifacts; tests; docs.
|
||||
Cite `file:line` and command output — never a bare assertion.
|
||||
2. **Antithesis.** Spawn MULTIPLE independent skeptics (parallel subagents or a
|
||||
workflow) whose only job is to REFUTE the thesis. Each re-inspects the
|
||||
sources itself and defaults to "refuted" on any concrete hole. Give diverse
|
||||
skeptics diverse lenses: correctness, an alternate mechanism, does-it-
|
||||
reproduce, does-the-fix-break-something-else.
|
||||
3. **Synthesis.** Fold every surviving objection back into a refined thesis. A
|
||||
skeptic that found a real hole replaces or corrects the thesis; a skeptic
|
||||
that failed to refute raises confidence.
|
||||
4. **Iterate.** Repeat thesis → antithesis → synthesis until a full skeptic
|
||||
round finds no new hole and the thesis holds at ~99%. If confidence stalls
|
||||
below 99%, name the exact residual and the evidence that would close it, then
|
||||
go get that evidence (more inspection, or `active-listening` for an
|
||||
operator-only fact) and loop again.
|
||||
|
||||
## Rules
|
||||
|
||||
- Executed evidence beats reasoning: run it, exec into it, read the log. A
|
||||
thesis backed only by static reasoning caps in the low nineties.
|
||||
- The antithesis MUST be independent — never let the thesis-author be its own
|
||||
sole skeptic.
|
||||
- Diversity over redundancy: skeptics with different lenses catch failure modes
|
||||
identical skeptics miss.
|
||||
- Report the final thesis with its evidence AND the losing objections, so the
|
||||
operator sees why it survived.
|
||||
25
skills/explain/SKILL.md
Normal file
25
skills/explain/SKILL.md
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
name: explain
|
||||
description: >
|
||||
Explain code and its interactions. Trigger when the operator asks what a
|
||||
piece of code does, how a mechanism works, why components interact the way
|
||||
they do, or invokes /explain on a file, function, role, or module.
|
||||
Portable across projects.
|
||||
---
|
||||
|
||||
Explain the named code so an engineer who has never seen it can follow it:
|
||||
|
||||
1. Read the code and every collaborator it touches (callers, callees,
|
||||
templates, configs) before explaining; never explain from the name alone.
|
||||
2. Lead with the purpose in one sentence, then walk the flow in execution
|
||||
order: inputs, transformations, side effects, outputs. Name the real
|
||||
files and line references.
|
||||
3. Make the interactions explicit: which component induces, triggers, or
|
||||
consumes which, and where the single source of truth lives. Point out
|
||||
trip-wires (non-obvious ordering, escaping, caching, permission edges)
|
||||
as part of the flow, not as an appendix.
|
||||
4. Afterwards ask whether to persist a Mermaid diagram of the explained
|
||||
mechanism under a `## Schema` heading in the README.md next to the
|
||||
affected code (create the heading if missing, replace its previous
|
||||
diagram if present). On approval, keep the diagram to the explained
|
||||
scope: components as nodes, induction/data flow as labeled edges.
|
||||
25
skills/help-skills/SKILL.md
Normal file
25
skills/help-skills/SKILL.md
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
name: help-skills
|
||||
description: >
|
||||
List and explain every skill available in the current session. Trigger on
|
||||
/help-skills, "welche skills gibt es", "skill übersicht", or when the
|
||||
operator asks what skills exist or what a skill does. Portable across
|
||||
projects.
|
||||
---
|
||||
|
||||
Produce a one-shot overview of every skill the session can invoke:
|
||||
|
||||
1. Sources, in this order: the session's available-skills listing, the
|
||||
project's own skills (`skills/` and `.claude/skills/` in the repo), the
|
||||
user-global skills (`~/.claude/skills/`), and plugin-provided skills.
|
||||
Deduplicate by name; note when a project skill shadows a global one.
|
||||
2. Output one table per origin (project, global, plugin): skill name,
|
||||
trigger (slash command or phrasing), one-line purpose in your own words
|
||||
— compress the description, do not paste it.
|
||||
3. When the operator names a specific skill or topic, explain only the
|
||||
matching skills in more depth: what they do, when they fire on their
|
||||
own, and how they compose (e.g. commit-next → commit-no-verify →
|
||||
comments-clean).
|
||||
4. End with the activation hint: project installers or `make install` in
|
||||
the skills repository refresh the live copies; a restart loads renamed
|
||||
or new skills.
|
||||
108
skills/no-defaults/SKILL.md
Normal file
108
skills/no-defaults/SKILL.md
Normal file
@@ -0,0 +1,108 @@
|
||||
---
|
||||
name: no-defaults
|
||||
description: >
|
||||
Forbid default values for parameters, settings, and environment lookups in
|
||||
every language. Load and apply AUTOMATICALLY on every task that writes,
|
||||
edits, generates, refactors, or reviews code, config, templates, schemas,
|
||||
or infrastructure — never wait to be asked. A default silently substitutes
|
||||
a guess for a value the caller failed to supply, so a misconfiguration
|
||||
becomes wrong behaviour instead of a loud failure. Demand strict, explicit
|
||||
values and fail loudly when one is missing. `false`, `null` and `""` are the
|
||||
only free defaults; every other default needs a written justification.
|
||||
Portable across projects.
|
||||
---
|
||||
|
||||
A default value is a guess, written at the point where the caller's intent is
|
||||
least known, and it turns a missing value into a plausible wrong answer instead
|
||||
of an error. Two callers then read the same call differently: one means "use the
|
||||
default", the other simply forgot. That ambiguity is the defect this rule exists
|
||||
to prevent.
|
||||
|
||||
Never write a substantive default. Require the value. When it is absent, fail
|
||||
immediately, loudly, and with a message that names the missing thing.
|
||||
|
||||
## The rule
|
||||
|
||||
1. Every parameter, option, setting, environment lookup, column, and template
|
||||
variable MUST be supplied explicitly by its caller.
|
||||
2. A missing value MUST abort at the earliest possible moment — argument
|
||||
parsing, config load, container start — never at the point of use, and never
|
||||
by proceeding with a substitute.
|
||||
3. The error MUST name the missing key and where to set it.
|
||||
4. Optionality, when a value genuinely may be absent, MUST be modelled as an
|
||||
explicit named case that the call site handles, never as a silent
|
||||
substitution. `None`/`null` handled in a visible branch is fine; a fallback
|
||||
baked into the accessor is not.
|
||||
|
||||
## The empty defaults are free
|
||||
|
||||
`false`, `null` (`None`, `nil`, `undefined`), and the empty string `""` MAY be
|
||||
used as defaults without justification. They are the one class that carries no
|
||||
guess: they denote absence itself rather than inventing a value the caller might
|
||||
have meant. `${VAR:-}`, `d.get(k)`, `a ?? null`, and `def f(a=None)` are fine.
|
||||
|
||||
Every other default value — a number, a non-empty string, a non-empty list or
|
||||
map, an enum member, a path, a timeout, a retry count — MUST carry an inline
|
||||
comment stating why that specific value is correct for every caller that omits
|
||||
it. No comment, no default. Use whatever exception-comment form the project
|
||||
sanctions; where none exists, a plain comment on or directly above the default.
|
||||
|
||||
The comment is the point: it forces you to name the assumption, so a reviewer
|
||||
can disagree with it. "Sensible", "standard", and "usual" name nothing — if the
|
||||
justification does not survive being written down, the default does not survive
|
||||
either.
|
||||
|
||||
## Banned constructs, by language
|
||||
|
||||
Each `Never` below means the default is a substantive value. The same construct
|
||||
with `false`, `null`, or `""` is permitted, and with any other value is permitted
|
||||
only when justified in a comment.
|
||||
|
||||
| Language | Never | Instead |
|
||||
|---|---|---|
|
||||
| Bash | `${VAR:-x}`, `${VAR:=x}`, `: "${VAR:=x}"` | `: "${VAR:?set VAR in <file>}"` |
|
||||
| Python | `def f(a=1)`, `d.get(k, x)`, `getattr(o, n, x)`, `os.environ.get(k, x)`, `argparse(default=)` | required args, `d[k]`, `os.environ[k]`, `add_argument(required=True)` |
|
||||
| Jinja / Ansible | `\| default(x)`, `lookup('env', k) \| default('')` | `\| mandatory`, `assert` on the var |
|
||||
| JS / TS | `function f(a = 1)`, `const {a = 1} = o`, `a \|\| x`, `a ?? x` | required params, explicit `if (a === undefined) throw` |
|
||||
| Go | relying on zero values | constructor that validates every field |
|
||||
| Rust | `unwrap_or`, `unwrap_or_default`, `#[serde(default)]` | `ok_or(Error::MissingX)?`, `#[serde(deny_unknown_fields)]` |
|
||||
| Java / C# | optional parameters, `??`, `getOrDefault` | required constructor args, explicit null check that throws |
|
||||
| SQL | `DEFAULT` on a column | `NOT NULL` and supply it in every insert |
|
||||
| Make | `VAR ?= x` | `VAR := $(or $(VAR),$(error VAR is required))` |
|
||||
| Docker / Compose | `${VAR:-x}`, `ENV` placeholders standing in for config | `${VAR:?VAR is required}` |
|
||||
| Terraform / Helm | `variable { default = }`, `default` in `values.yaml` | no default; fail on the missing input |
|
||||
|
||||
## How to apply
|
||||
|
||||
Before writing any parameter, ask: what happens if the caller omits it? If the
|
||||
answer is anything other than "it fails and says so", rewrite it.
|
||||
|
||||
While editing, treat every `default`, `?:`, `??`, `||`, `:-`, `:=`, `or`,
|
||||
`fallback`, and `getOrElse` in a value-resolution position as a finding unless
|
||||
its substitute is `false`, `null` or `""`, or a comment already justifies it.
|
||||
Grep your own diff for them before presenting it.
|
||||
|
||||
Pushing a default one layer up is not a fix: a caller that passes a constant it
|
||||
invented has only moved the guess. Follow the value to its true owner —
|
||||
the operator, the inventory, the deployment config — and require it there.
|
||||
|
||||
Removing an existing default is a behaviour change. Do not do it silently as a
|
||||
drive-by: name it, and let the operator decide whether it lands in this change
|
||||
or its own.
|
||||
|
||||
## What does not count as a justification
|
||||
|
||||
"It is conventional", "every other project does it", "the value is obvious",
|
||||
and "it keeps the signature short" justify nothing — they assert that someone
|
||||
else made the choice, which is the ambiguity this rule exists to remove. A valid
|
||||
justification names why this value is right for a caller who says nothing.
|
||||
|
||||
An operator asking for a specific default, in this conversation, for this value,
|
||||
settles it without a comment.
|
||||
|
||||
## Penalty
|
||||
|
||||
If this audit finds an unjustified default you wrote yourself: say so in the
|
||||
report, with file and count and no excuses; lower the confidence numbers for the affected
|
||||
bundle; and write the incident to persistent agent memory, recording the date
|
||||
and the construct you reached for, so the calibration survives the session.
|
||||
60
skills/observe/SKILL.md
Normal file
60
skills/observe/SKILL.md
Normal file
@@ -0,0 +1,60 @@
|
||||
---
|
||||
name: observe
|
||||
description: >
|
||||
Watch a named target (a CI run, a pipeline, a build, a log) on a fixed
|
||||
cadence and apply the triage skill whenever new failures appear. Trigger on
|
||||
/observe <target> or when the operator asks to keep monitoring something and
|
||||
fix regressions as they surface. Portable across projects.
|
||||
---
|
||||
|
||||
Keep a target under continuous watch and act only when it degrades. The loop is
|
||||
the watcher; triage is the response. Do not re-investigate failures already
|
||||
resolved this session, and do not idle-poll work the harness will notify you
|
||||
about.
|
||||
|
||||
## Before you start: fix the target
|
||||
|
||||
The operator names one concrete target: a CI run id or url, a pipeline, a
|
||||
branch's checks, a build, or a log file. Restate it in one line and record the
|
||||
current failure set as the baseline (the failures already known or already
|
||||
fixed this session). Everything the watch reports is diffed against that
|
||||
baseline, so a stale baseline re-triages solved failures.
|
||||
|
||||
## The loop
|
||||
|
||||
Drive this with `/loop` in dynamic mode (no fixed interval unless the operator
|
||||
gave one): after each observation, schedule the next wake-up yourself and pass
|
||||
the same `/observe` prompt back so the next firing repeats the watch.
|
||||
|
||||
1. **Poll the target.** Pull its current state (job list, check conclusions, the
|
||||
log's tail). Enumerate the failures exactly as the `triage` skill's step 1
|
||||
does: failure, cancelled, timed-out; a downstream aggregate that fails only
|
||||
because an upstream did is not its own failure.
|
||||
2. **Diff against the baseline.** A failure is *new* only if it is not in the
|
||||
baseline and was not already triaged this session. If nothing is new, log one
|
||||
short line ("still green" / "N known failures, no change") and reschedule.
|
||||
3. **Triage each new failure.** Invoke the `triage` skill on the new set only —
|
||||
which applies `dialectic` per failing job to a ~99% root cause and a real
|
||||
fix. Never mask (no retry-until-pass, no disabled check, no soft-skip). Fold
|
||||
each newly fixed failure into the baseline so it is not re-triaged.
|
||||
4. **Reschedule.** Pick the next delay from what you are waiting on: an active
|
||||
run that changes every few minutes gets a check matched to its cadence; a
|
||||
quiet or long-running target gets a long fallback (1200s+). Keep watching
|
||||
until the target reaches a terminal state (run completed, pipeline finished)
|
||||
AND every failure it produced has a verified fix, then stop the loop.
|
||||
|
||||
## Rules
|
||||
|
||||
- The loop is a fallback timer, not a busy-wait: if the harness will notify you
|
||||
when tracked work finishes (a background task, a spawned agent), wait for that
|
||||
notification instead of polling for it; reserve the timed wake-up for external
|
||||
state the harness cannot see (a remote CI run, a deploy queue).
|
||||
- Executed evidence beats reasoning: read the actual failing log line and the
|
||||
artifact, not the job title — triage and dialectic carry this through.
|
||||
- One baseline, updated in place: dedup every observation against all failures
|
||||
seen so far, or a flaky-then-fixed failure reappears every cycle and the watch
|
||||
never converges.
|
||||
- Report only on change: a new failure and its triage verdict, or a terminal
|
||||
"target is green, watch closed". Silence between changes is correct.
|
||||
- Never invent results for a run still in progress; report what the poll
|
||||
actually returned.
|
||||
66
skills/plan/SKILL.md
Normal file
66
skills/plan/SKILL.md
Normal file
@@ -0,0 +1,66 @@
|
||||
---
|
||||
name: plan
|
||||
description: >
|
||||
Turn a request into an objective-oriented plan and hand it to the robot skill
|
||||
for autonomous execution. Trigger on /plan or when the operator asks for a
|
||||
plan, a breakdown, or a strategy for a task before any work starts. Portable
|
||||
across projects.
|
||||
---
|
||||
|
||||
Produce a plan whose every item is an outcome with a check, then execute it
|
||||
autonomously. A plan that lists activities instead of objectives cannot be
|
||||
verified, and an unverifiable plan cannot be handed to a robot.
|
||||
|
||||
## Procedure
|
||||
|
||||
1. **Interview first.** Open every plan with the `active-listening` skill: ask
|
||||
until scope, constraints, success criteria, and the operator-only facts are
|
||||
all pinned, then reflect the understanding back. This is the only point in
|
||||
the run where questions are allowed, so leave nothing open here.
|
||||
2. **Pin the objective.** State the end state in one sentence, as a condition
|
||||
that is either true or false, never as an activity. Name the command or
|
||||
observation that proves it.
|
||||
3. **Inspect before decomposing.** Read the code, config, tests, and history the
|
||||
objective touches. Every plan item must rest on something you have seen, not
|
||||
on an assumption about how the project works.
|
||||
4. **Decompose into sub-objectives.** Each item gets: the outcome it reaches,
|
||||
the verification that proves it, and the items it depends on. Split an item
|
||||
whenever it needs more than one verification. Order by dependency and mark
|
||||
the items that are independent, so they can run in parallel.
|
||||
5. **Resolve the open decisions now.** Any root cause, design choice, or
|
||||
trade-off the plan rests on gets settled before execution, escalating to the
|
||||
`dialectic` skill where being wrong is expensive. The robot does not ask, so
|
||||
an unresolved decision becomes a guess at runtime.
|
||||
6. **Record the plan as a todo list.** Write the sub-objectives into the
|
||||
harness's own todo tracking, one entry per item, phrased as the outcome. Keep
|
||||
a plan file only when the operator asks for one.
|
||||
7. **Present it and wait.** Show the objective, the ordered sub-objectives with
|
||||
their verifications, what is deliberately out of scope, and the assumptions
|
||||
the plan rests on. Stop here: the plan is the deliverable of this step.
|
||||
8. **Implement it statically, in full.** On the operator's go, write every code,
|
||||
config, test, and doc change the plan calls for, across all sub-objectives,
|
||||
before running anything that needs live infrastructure. Verify statically:
|
||||
read the diff, run the linters, the unit tests, and whatever dry-run or
|
||||
syntax check the project offers. This step ends only when the whole plan
|
||||
exists on disk, with no sub-objective left unwritten.
|
||||
9. **Then iterate under `robot`.** With the static implementation complete,
|
||||
invoke the `robot` skill with the objective as its goal for the dynamic part:
|
||||
run, deploy, observe, fix, repeat until every verification passes. It drives
|
||||
the loop without check-ins, verifies each outcome instead of assuming it, and
|
||||
reports the items that fall outside its clearance for the operator to run.
|
||||
|
||||
## Rules
|
||||
|
||||
- One objective per item, in outcome form: "endpoint returns 200 for an expired
|
||||
token", not "fix the auth middleware".
|
||||
- No item without a verification. If you cannot name what proves it, the item is
|
||||
still a wish, not a plan.
|
||||
- Plan only what the request covers. Speculative future-proofing, refactors
|
||||
nobody asked for, and abstractions with one caller belong in the out-of-scope
|
||||
list, not in the plan.
|
||||
- Static before dynamic: never start the robot loop on a half-written plan. A
|
||||
deploy that fails on code you had not written yet costs a full cycle and
|
||||
proves nothing.
|
||||
- Re-plan on contradicted evidence: when execution disproves an assumption the
|
||||
plan rests on, revise the plan instead of forcing the original steps through.
|
||||
- Cite `file:line` for every claim about the current state of the code.
|
||||
108
skills/refactor/SKILL.md
Normal file
108
skills/refactor/SKILL.md
Normal file
@@ -0,0 +1,108 @@
|
||||
---
|
||||
name: refactor
|
||||
description: >
|
||||
Refactor every file that is currently uncommitted in the git working tree -
|
||||
staged, unstaged and untracked - up to the project's coding standards,
|
||||
without changing behaviour. Trigger on /refactor or when the operator asks to
|
||||
clean up, tidy, or polish the current changes before committing. Portable
|
||||
across projects.
|
||||
---
|
||||
|
||||
The unit of work is the uncommitted change set, not the repository. Behaviour
|
||||
stays identical: a refactor that changes what the code does is a bug, not a
|
||||
cleanup.
|
||||
|
||||
## Step 1: fix the file set
|
||||
|
||||
Read the set from git, never from memory of what was edited:
|
||||
|
||||
```bash
|
||||
git status --porcelain
|
||||
```
|
||||
|
||||
Take modified, added, renamed and untracked files. Drop deleted files, binaries,
|
||||
lockfiles, vendored trees and generated output. Restate the resulting list in
|
||||
one line before touching anything; if it is empty, say so and stop.
|
||||
|
||||
Never `git stash` and never revert a working-tree file to compare - other agents
|
||||
and the operator share this tree. Read the committed version with
|
||||
`git show HEAD:<path>` into a temporary file instead.
|
||||
|
||||
## Step 2: get a safety net first
|
||||
|
||||
A refactor is only provably behaviour-preserving if something failed before it
|
||||
and passes after it. Before editing, check what covers the change set:
|
||||
|
||||
- **Coverage exists**: run it now and record the green baseline. A suite that
|
||||
was already red gets reported, not refactored around.
|
||||
- **No coverage and the code is testable logic**: write the characterization
|
||||
tests first, against the current behaviour - including the behaviour you
|
||||
consider wrong, so the refactor cannot silently change it. Keep them small
|
||||
and framework-free unless the project already has a framework. Fix the
|
||||
captured wrongness in a separate change, never in this one.
|
||||
- **Not sensibly testable**: pure glue, declarative config, a thin wrapper over
|
||||
a service the test would have to mock into meaninglessness, or a project with
|
||||
no test setup at all. Say so in one line and refactor without a net; do not
|
||||
invent a test harness the project never asked for.
|
||||
|
||||
The new tests are part of the deliverable and must still pass unchanged after
|
||||
the refactor. A test that had to be edited to stay green means the behaviour
|
||||
moved: revert the refactor, not the test.
|
||||
|
||||
## Step 3: refactor each file
|
||||
|
||||
Read the whole file first, then apply the standards that the surrounding code
|
||||
and the project's own config already establish (formatter config, linter rules,
|
||||
existing idioms) over any generic preference of your own.
|
||||
|
||||
Four principles govern the result:
|
||||
|
||||
- **SRP**: one file, one responsibility; one function, one reason to change. A
|
||||
unit that needs an "and" to describe it is two units.
|
||||
- **SPOT**: every fact - a constant, a path, a schema, a rule - lives in exactly
|
||||
one place and everything else references it.
|
||||
- **KISS**: the simplest construct that works. No abstraction without a second
|
||||
caller today, no configuration for a value that never varies.
|
||||
- **DRY**: the same logic is written once. A literal repeated three times is a
|
||||
missing constant.
|
||||
|
||||
Concretely:
|
||||
|
||||
- **File size**: no file containing program code exceeds 250 lines. Split the
|
||||
overflow along the SRP seam - it is the symptom, not the cause. Description
|
||||
and markup languages are exempt (Markdown, YAML, JSON, HTML, translation and
|
||||
data files), as is the rare file the language or framework genuinely forbids
|
||||
splitting: name that reason in the report, "it was easier" does not qualify.
|
||||
- **Duplication**: fold repeated logic into the helper that already exists;
|
||||
create a new one only when two or more call sites need it now.
|
||||
- **Dead weight**: unused imports, variables, parameters, branches that cannot
|
||||
be reached, flags with a single value, abstractions with one implementation.
|
||||
- **Naming**: names that state what a thing is, matching the file's convention.
|
||||
- **Shape**: split a function that needs a scroll to read; collapse nesting by
|
||||
returning early; replace magic values with a named constant at its use site.
|
||||
- **Errors**: never widen a caught exception, never swallow one, never remove a
|
||||
validation or a security check to make the code shorter.
|
||||
- **Comments**: invoke the `comments-clean` skill rather than reimplementing its
|
||||
rules here.
|
||||
|
||||
Do not reformat regions you have no substantive fix for - cosmetic churn buries
|
||||
the real change in the diff. If a file's problem is architectural and cannot be
|
||||
fixed inside the change set, report it instead of half-doing it.
|
||||
|
||||
## Step 4: keep behaviour provable
|
||||
|
||||
- Re-run the step 2 net and require the same green result as the baseline.
|
||||
- Never fold a bug fix, a feature, or a dependency change into a refactor.
|
||||
Found a real bug? Name it in the report and leave it for a separate change.
|
||||
- Run the cheap checks the project already has for the touched files (formatter,
|
||||
linter, the module's own unit tests). Do not start a full suite, a build, or a
|
||||
deploy on your own initiative - propose it and let the operator trigger it.
|
||||
- State plainly what was verified and what was not. An unverified refactor is
|
||||
reported as unverified.
|
||||
|
||||
## Step 5: report
|
||||
|
||||
One line per file: what changed and why it is safe. Then one line naming what
|
||||
was deliberately left alone (architectural findings, bugs, files skipped) and
|
||||
the exact command the operator can run to validate. Do not commit unless the
|
||||
operator asked for a commit.
|
||||
38
skills/robot/SKILL.md
Normal file
38
skills/robot/SKILL.md
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
name: robot
|
||||
description: >
|
||||
Work fully autonomously to a goal. Trigger when the operator wants hands-off,
|
||||
uninterrupted execution — the agent drives the task to completion without
|
||||
check-ins, never stops before the goal is reached, and uses only commands it
|
||||
is already cleared to run. Portable across projects.
|
||||
---
|
||||
|
||||
Operate as an autonomous robot: given a goal, reach it without hand-holding.
|
||||
|
||||
## Contract
|
||||
|
||||
- **No questions once the goal is clear.** Clarify only genuinely-blocking
|
||||
unknowns up front (via the `active-listening` skill), then proceed without
|
||||
further check-ins. Pick the cleanest option and continue; never pause to
|
||||
confirm a choice you can make yourself.
|
||||
- **Never stop before the goal.** Do not end the run, hand back, or declare done
|
||||
while any part of the goal is unmet — premature termination is forbidden. A
|
||||
blocker is something to solve or route around, not a reason to quit; if one
|
||||
step is genuinely, externally impossible, surface exactly why and keep working
|
||||
on everything else until nothing solvable remains.
|
||||
- **Stay inside your clearance.** Use ONLY commands you are pre-authorized to
|
||||
run. Never invoke anything the agent or tool settings mark as denied or as
|
||||
requiring per-invocation approval (ask) — no commit, push, deploy, or other
|
||||
approval-gated action unless the operator has already cleared it for this run.
|
||||
When a needed action is out of clearance, do all the cleared work and report
|
||||
the single blocked step for the operator to run.
|
||||
- **Verify, do not assume.** Reaching the goal means proving it: run it, read the
|
||||
output, confirm the end state. Never report success on a step you did not
|
||||
actually verify.
|
||||
|
||||
## Rules
|
||||
|
||||
- Escalate an uncertain root cause or design decision to the `dialectic` skill
|
||||
rather than guessing; uncertainty is never a reason to stop.
|
||||
- Sustain the loop across long, multi-step work — the run is finished only when
|
||||
the whole goal is verifiably met.
|
||||
95
skills/test-fix/SKILL.md
Normal file
95
skills/test-fix/SKILL.md
Normal file
@@ -0,0 +1,95 @@
|
||||
---
|
||||
name: test-fix
|
||||
description: >
|
||||
Run the project's Makefile test targets in a loop - run, diagnose, fix, re-run -
|
||||
until every selected target is green or the loop is provably stuck. Trigger on
|
||||
/test-fix or when the operator asks to make the tests pass, fix the failing
|
||||
tests, or get the suite green. Portable across projects.
|
||||
---
|
||||
|
||||
Same target discovery and selection as the `test` skill - invoke it for step 1
|
||||
and step 2 rather than reimplementing the grep and the selection list. This
|
||||
skill owns what happens after the first red result: the fix loop.
|
||||
|
||||
The contract is narrow: the code becomes correct, the tests stay honest.
|
||||
|
||||
## The loop
|
||||
|
||||
One iteration is: run -> read the failure -> one root cause -> one fix ->
|
||||
re-run. Never batch several speculative fixes into one iteration; a green run
|
||||
after three simultaneous changes proves nothing about which one mattered.
|
||||
|
||||
### Run
|
||||
|
||||
Run the failing target alone, not the whole selection - the fastest command
|
||||
that reproduces the failure. Widen back to the full selection only when that
|
||||
target is green.
|
||||
|
||||
### Diagnose
|
||||
|
||||
Read the actual error output before touching a file. Apply the `triage` skill
|
||||
to reach a verified root cause; for a failure whose cause is genuinely unclear
|
||||
after one look, apply `dialectic` rather than guessing twice.
|
||||
|
||||
Decide explicitly which side is wrong, and say so in one line before editing:
|
||||
|
||||
- **Code is wrong**: the test states the intended behaviour. Fix the code.
|
||||
- **Test is wrong**: the test encodes an outdated or incorrect expectation.
|
||||
Fixing it is allowed *only* with a stated reason for why the old expectation
|
||||
was wrong - never because the code disagrees with it.
|
||||
- **Environment is wrong**: missing dependency, stale container, absent env var,
|
||||
unbuilt artefact. Fix the environment or report it; do not patch code around
|
||||
a broken environment.
|
||||
|
||||
### Fix
|
||||
|
||||
Smallest change that addresses the root cause. Follow the project's existing
|
||||
idioms and the `no-defaults` and `comments-clean` rules.
|
||||
|
||||
Forbidden, in every iteration, without explicit operator approval:
|
||||
|
||||
- deleting, skipping, `xfail`-ing, or commenting out a failing test
|
||||
- loosening an assertion to whatever the code currently produces
|
||||
- catching or swallowing the exception the test was written to surface
|
||||
- adding retries, sleeps, or reruns to paper over a flaky failure
|
||||
- disabling a linter rule, a type check, or a test target from the selection
|
||||
|
||||
Any of these is a report item, not a fix. A suite that is green because the
|
||||
failing test no longer runs is a regression disguised as success.
|
||||
|
||||
### Re-run
|
||||
|
||||
Re-run the same target. Then, once it passes, re-run every target that was
|
||||
already green - a fix that breaks a neighbouring suite is not a fix. Only
|
||||
after the full selection is green is the loop done.
|
||||
|
||||
## Stopping
|
||||
|
||||
Stop and report, without asking for permission to stop:
|
||||
|
||||
- **Green**: full selection passes. Report and stop.
|
||||
- **No progress**: the same failure survives 3 iterations, or the failure count
|
||||
stops falling across 3 iterations. Report the root cause reached so far, what
|
||||
was tried, and why each attempt failed.
|
||||
- **Fix exceeds the mandate**: the real fix is an API change, a dependency bump,
|
||||
a schema migration, or a redesign. Name it, show the minimal diff it would
|
||||
need, and let the operator decide.
|
||||
- **Oscillation**: fixing A re-breaks B and vice versa. Report both with the
|
||||
conflict between them - that is a design problem, not a test problem.
|
||||
- **Flaky**: a target passes and fails without any change between runs. Report
|
||||
it as flaky with both outputs; never "fix" it by rerunning until green.
|
||||
|
||||
Never loop silently. Emit one line per iteration: target, failure, hypothesis,
|
||||
change made.
|
||||
|
||||
## Report
|
||||
|
||||
- Per iteration: what failed, the root cause, the fix, the result.
|
||||
- Final state of every selected target, pass or fail, with the verbatim output
|
||||
of anything still failing.
|
||||
- Everything deliberately not done: tests judged wrong but left alone,
|
||||
environment issues, out-of-mandate fixes.
|
||||
- The exact command reproducing the final state.
|
||||
|
||||
State calibrated confidence in the fixes per the `confidence` skill. Do not
|
||||
commit unless the operator asked for a commit.
|
||||
77
skills/test/SKILL.md
Normal file
77
skills/test/SKILL.md
Normal file
@@ -0,0 +1,77 @@
|
||||
---
|
||||
name: test
|
||||
description: >
|
||||
Discover the test targets of the project's Makefile, let the operator pick
|
||||
which ones to run, then run them and report. Trigger on /test or when the
|
||||
operator asks to run the tests, the test suite, or a specific test target
|
||||
without naming the exact command. Portable across projects.
|
||||
---
|
||||
|
||||
The Makefile is the source of truth for how this project runs tests. Never
|
||||
invent a command (`pytest`, `npm test`, `go test`) while a Makefile target
|
||||
exists - the target carries the project's env vars, build dependencies and
|
||||
container setup.
|
||||
|
||||
## Step 1: find the targets
|
||||
|
||||
From the repository root:
|
||||
|
||||
```bash
|
||||
grep -nE '^test[A-Za-z0-9_.-]*:' Makefile
|
||||
```
|
||||
|
||||
Read the matched rules plus their prerequisites, so the selection list can say
|
||||
what each one actually does (delegated script, container build, sub-targets).
|
||||
|
||||
Edge cases, handled explicitly rather than guessed around:
|
||||
|
||||
- **No Makefile**: say so in one line, name the test runner the project does
|
||||
use (from `pyproject.toml`, `package.json`, `tox.ini`, CI workflow), and ask
|
||||
before running anything.
|
||||
- **No `test*` target**: report the targets that do exist and stop.
|
||||
- **Included makefiles** (`include foo.mk`): grep those too.
|
||||
- **Aggregate targets**: a target whose recipe is only other test targets (e.g.
|
||||
`test: test-unit test-integration`) is the "run everything" entry - mark it as
|
||||
such in the list, do not expand it into its parts silently.
|
||||
|
||||
## Step 2: offer the selection
|
||||
|
||||
Ask with `AskUserQuestion`, `multiSelect: true`, one question. Options are the
|
||||
discovered targets, each with a one-line description of what it runs. Order the
|
||||
aggregate/full target first and label it as the complete suite.
|
||||
|
||||
The tool takes at most 4 options. With more targets than that:
|
||||
|
||||
- print the **complete** discovered list as text first, one line per target, so
|
||||
nothing is hidden, then
|
||||
- offer the 4 most useful entries (aggregate first, then the ones matching the
|
||||
operator's stated intent or the files currently uncommitted), and note that
|
||||
"Other" accepts any target name from the printed list.
|
||||
|
||||
Never silently truncate. If the operator already named a target in their
|
||||
request, skip the question and run it.
|
||||
|
||||
## Step 3: run
|
||||
|
||||
Run the selected targets in the order the operator listed them, one `make`
|
||||
invocation per target, each as its own command so a failure is attributable:
|
||||
|
||||
```bash
|
||||
make <target>
|
||||
```
|
||||
|
||||
Do not add `-k`, do not reorder, do not substitute a faster equivalent. If a
|
||||
target needs a long timeout (container builds, e2e), set it on the Bash call
|
||||
rather than backgrounding blindly.
|
||||
|
||||
Stop after the first failing target unless the operator asked for all of them -
|
||||
a later suite running against a broken build produces noise, not information.
|
||||
|
||||
## Step 4: report
|
||||
|
||||
Per target: pass/fail plus the failing test names and the exact error output,
|
||||
quoted verbatim. Never paraphrase a failure. Then one line with the exact
|
||||
command to reproduce the failure alone.
|
||||
|
||||
Do not fix what failed unless the operator asks - report first. If the operator
|
||||
does ask for a fix, apply the `triage` skill to it.
|
||||
57
skills/triage/SKILL.md
Normal file
57
skills/triage/SKILL.md
Normal file
@@ -0,0 +1,57 @@
|
||||
---
|
||||
name: triage
|
||||
description: >
|
||||
Triage a failing CI run, pipeline, or job set to a verified root cause and a
|
||||
real fix. Trigger when a build, test, or deploy pipeline has one or more failed
|
||||
jobs. Applies the dialectic skill to every failing job independently. Portable
|
||||
across projects.
|
||||
---
|
||||
|
||||
Drive a red pipeline to green by root cause, not by guesswork. Treat every
|
||||
failing job as its own investigation, and do not declare a fix until that job's
|
||||
root cause is proven.
|
||||
|
||||
## Procedure
|
||||
|
||||
1. **Enumerate every failing job.** Pull the run's job list and isolate each job
|
||||
whose conclusion is failure, cancelled, or timed-out. A downstream aggregate
|
||||
job that fails only because an upstream job did is not a separate root cause —
|
||||
note it and move on.
|
||||
2. **Cluster the failures by error message.** Group the failing jobs by their
|
||||
actual failing line, not by job name. Jobs whose messages differ in anything
|
||||
but the interpolated identifiers (host, app, port, path) belong in separate
|
||||
clusters.
|
||||
3. **Download artifacts per cluster.** For each cluster, fetch every artifact of
|
||||
one representative job: reports, rescue diagnostics, inventories, container
|
||||
logs. Pull a second member's artifacts only when the representative's
|
||||
evidence does not explain the whole cluster. The job log usually names an
|
||||
artifact path and nothing more; the assertion text, the server-side
|
||||
exception and the state dumps are inside the artifact. Do this even when the
|
||||
log looks conclusive, because a log that explains the symptom rarely
|
||||
explains the cause.
|
||||
4. **Dialectic per cluster.** For EACH cluster, invoke the `dialectic` skill:
|
||||
form a thesis about the root cause from evidence (the job log, its artifacts,
|
||||
the code at the run's commit, git history), attack it with independent
|
||||
skeptics, and iterate to a ~99% thesis. The clusters are independent, so run
|
||||
their investigations in parallel where the tooling allows.
|
||||
5. **Distinguish shared vs hidden causes.** A cluster is a hypothesis, not a
|
||||
proof: if the dialectic shows one cluster splitting into two causes, split it.
|
||||
When a job hides a second failure behind the first, keep going until the job
|
||||
is actually green, not just past the first error.
|
||||
6. **Fix at the root.** Apply the real fix in the repository for each proven root
|
||||
cause. Never mask a failure — no retry-until-pass, no disabling the check, no
|
||||
soft-skip. If a failure is genuinely external (upstream outage, flaky infra),
|
||||
confirm that with evidence and surface it honestly instead of fixing around
|
||||
it.
|
||||
7. **Follow the run.** While the run is still in progress, re-check it
|
||||
periodically and triage each newly failed job as it appears. The run is done
|
||||
only when it has finished and every failure has a verified fix.
|
||||
|
||||
## Rules
|
||||
|
||||
- Executed evidence beats reasoning: read the actual failing log line and the
|
||||
artifact, not the job title.
|
||||
- Classify each failure: a real defect (fix it), a flake or infra hiccup (prove
|
||||
it is transient before dismissing), or already-fixed-by-a-later-commit (verify
|
||||
the fix is present in the run's commit before claiming it).
|
||||
- Report a per-job verdict: root cause, the evidence that proves it, and the fix.
|
||||
92
tests/test_autotune.py
Normal file
92
tests/test_autotune.py
Normal file
@@ -0,0 +1,92 @@
|
||||
"""Validate the autotune reminder hook and its settings registration."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
SKILL = REPO_ROOT / "skills" / "autotune" / "SKILL.md"
|
||||
HOOK = REPO_ROOT / "skills" / "autotune" / "hooks" / "reminder.sh"
|
||||
PATCHER = REPO_ROOT / "scripts" / "enable-plugins.js"
|
||||
|
||||
|
||||
def _run_hook(stamp: Path, interval: str = "3600") -> subprocess.CompletedProcess:
|
||||
return subprocess.run(
|
||||
["bash", str(HOOK)],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
env={
|
||||
"PATH": "/usr/bin:/bin",
|
||||
"HOME": str(stamp.parent),
|
||||
"AUTOTUNE_STAMP": str(stamp),
|
||||
"AUTOTUNE_INTERVAL_SECONDS": interval,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class TestAutotuneSkill(unittest.TestCase):
|
||||
def test_skill_is_trigger_only(self):
|
||||
text = SKILL.read_text(encoding="utf-8")
|
||||
self.assertIn("name: autotune", text)
|
||||
self.assertIn("multiSelect: false", text)
|
||||
|
||||
|
||||
class TestAutotuneHook(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tmp = tempfile.TemporaryDirectory()
|
||||
self.addCleanup(self.tmp.cleanup)
|
||||
self.stamp = Path(self.tmp.name) / ".claude" / ".autotune-stamp"
|
||||
|
||||
def test_first_run_hints_and_stamps(self):
|
||||
result = _run_hook(self.stamp)
|
||||
self.assertIn("/autotune", result.stdout)
|
||||
self.assertTrue(self.stamp.is_file())
|
||||
|
||||
def test_second_run_is_silent_within_interval(self):
|
||||
_run_hook(self.stamp)
|
||||
self.assertEqual(_run_hook(self.stamp).stdout, "")
|
||||
|
||||
def test_hint_returns_after_the_interval(self):
|
||||
_run_hook(self.stamp)
|
||||
self.assertIn("/autotune", _run_hook(self.stamp, interval="0").stdout)
|
||||
|
||||
def test_corrupt_stamp_does_not_crash(self):
|
||||
self.stamp.parent.mkdir(parents=True, exist_ok=True)
|
||||
self.stamp.write_text("not-a-timestamp\n", encoding="utf-8")
|
||||
self.assertIn("/autotune", _run_hook(self.stamp).stdout)
|
||||
|
||||
|
||||
@unittest.skipUnless(shutil.which("node"), "node not installed")
|
||||
class TestSettingsRegistration(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tmp = tempfile.TemporaryDirectory()
|
||||
self.addCleanup(self.tmp.cleanup)
|
||||
self.settings = Path(self.tmp.name) / "settings.json"
|
||||
|
||||
def _patch(self) -> dict:
|
||||
subprocess.run(["node", str(PATCHER), str(self.settings)], check=True, capture_output=True)
|
||||
return json.loads(self.settings.read_text(encoding="utf-8"))
|
||||
|
||||
def test_hook_registered_once(self):
|
||||
self._patch()
|
||||
data = self._patch()
|
||||
entries = json.dumps(data["hooks"]["UserPromptSubmit"])
|
||||
self.assertEqual(entries.count("autotune/hooks/reminder.sh"), 1)
|
||||
|
||||
def test_existing_hooks_preserved(self):
|
||||
self.settings.write_text(
|
||||
json.dumps({"hooks": {"UserPromptSubmit": [{"hooks": [{"type": "command", "command": "true"}]}]}}),
|
||||
encoding="utf-8",
|
||||
)
|
||||
data = self._patch()
|
||||
self.assertEqual(len(data["hooks"]["UserPromptSubmit"]), 2)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user