Skip to content
Merged
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
19 changes: 17 additions & 2 deletions .agents/skills/setup/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -384,9 +384,24 @@ Ask: **"Create any missing labels from this baseline now? (yes/no)"**

Wait for response.

- **yes** → create missing labels only (do not recreate existing labels). Use sensible colors and short descriptions.
- **yes** → run `python3 CodeCannon/skills/github-agile/scripts/label-create.py bug enhancement chore documentation ready-for-qa qa-passed qa-failed`. The script applies sensible color/description defaults from a baked-in table and warns-and-continues on any name that already exists.
- **no / skip / anything else** → continue without creating labels.

#### Configured-label audit

Whether or not the greenfield baseline ran, audit the labels that configured skills will try to apply at runtime against what actually exists in the repo:

```bash
python3 CodeCannon/skills/github-agile/scripts/label-audit.py
```

The script reads `.codecannon.yaml`, collects the names referenced by `TICKET_LABELS` plus `QA_READY_LABEL` / `QA_PASSED_LABEL` / `QA_FAILED_LABEL` (skipping any that are unset), diffs against `gh label list`, and prints missing names — one per line — on stdout. A one-line diagnostic goes to stderr.

- **No stdout output** → all configured labels exist. Continue silently to the "Display the results" step below.
- **One or more names on stdout** → show them as a combined list and ask once: **"These labels are referenced by your configured skills but don't exist in the repo: \<list\>. Create them now? (yes/no)"**
- **yes** → run `python3 CodeCannon/skills/github-agile/scripts/label-create.py <name1> <name2> ...` with the missing names from the audit output.
- **no / skip** → continue, but at the end of Phase 4 print a one-line summary: "Skipped creating: \<list\>. `/submit-for-review` will warn and continue if it needs to apply a missing label; `/qa` and `/start` may degrade similarly."

After this step (or if labels were non-zero initially), run `gh label list --limit 100 --json name,color,description` again.

If `TICKET_LABELS` is unset or fewer than 5 labels exist, add a note: "`/start` works best with a clear issue-label pool (`TICKET_LABELS`), and `/qa` needs explicit QA lifecycle labels (`ready-for-qa`, `qa-passed`, `qa-failed`). Consider a lightweight priority scheme (e.g. `priority:high`, `priority:medium`, `priority:low`) if the team needs triage support. If the team runs planned iterations, set `DEFAULT_MILESTONE` in Phase 5; otherwise leave it unset so `/start` auto-detects."
Expand Down Expand Up @@ -470,4 +485,4 @@ Add a note: `/start` can be used to create well-formed GitHub issues without wri
- Never fetch more than 100 labels in a single command. `gh label list --limit 100` is the ceiling.
- Do not skip any human gate in Phase 3, Phase 4, or Phase 5 — each write requires confirmation.
- If the user skips a config value, do not ask again. Move on.
<!-- generated by CodeCannon/sync.py | skill: setup | adapter: codex | hash: c8f4433c | DO NOT EDIT — run CodeCannon/sync.py to regenerate -->
<!-- generated by CodeCannon/sync.py | skill: setup | adapter: codex | hash: a1dd504e | DO NOT EDIT — run CodeCannon/sync.py to regenerate -->
19 changes: 17 additions & 2 deletions .claude/commands/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -379,9 +379,24 @@ Ask: **"Create any missing labels from this baseline now? (yes/no)"**

Wait for response.

- **yes** → create missing labels only (do not recreate existing labels). Use sensible colors and short descriptions.
- **yes** → run `python3 CodeCannon/skills/github-agile/scripts/label-create.py bug enhancement chore documentation ready-for-qa qa-passed qa-failed`. The script applies sensible color/description defaults from a baked-in table and warns-and-continues on any name that already exists.
- **no / skip / anything else** → continue without creating labels.

#### Configured-label audit

Whether or not the greenfield baseline ran, audit the labels that configured skills will try to apply at runtime against what actually exists in the repo:

```bash
python3 CodeCannon/skills/github-agile/scripts/label-audit.py
```

The script reads `.codecannon.yaml`, collects the names referenced by `TICKET_LABELS` plus `QA_READY_LABEL` / `QA_PASSED_LABEL` / `QA_FAILED_LABEL` (skipping any that are unset), diffs against `gh label list`, and prints missing names — one per line — on stdout. A one-line diagnostic goes to stderr.

- **No stdout output** → all configured labels exist. Continue silently to the "Display the results" step below.
- **One or more names on stdout** → show them as a combined list and ask once: **"These labels are referenced by your configured skills but don't exist in the repo: \<list\>. Create them now? (yes/no)"**
- **yes** → run `python3 CodeCannon/skills/github-agile/scripts/label-create.py <name1> <name2> ...` with the missing names from the audit output.
- **no / skip** → continue, but at the end of Phase 4 print a one-line summary: "Skipped creating: \<list\>. `/submit-for-review` will warn and continue if it needs to apply a missing label; `/qa` and `/start` may degrade similarly."

After this step (or if labels were non-zero initially), run `gh label list --limit 100 --json name,color,description` again.

If `TICKET_LABELS` is unset or fewer than 5 labels exist, add a note: "`/start` works best with a clear issue-label pool (`TICKET_LABELS`), and `/qa` needs explicit QA lifecycle labels (`ready-for-qa`, `qa-passed`, `qa-failed`). Consider a lightweight priority scheme (e.g. `priority:high`, `priority:medium`, `priority:low`) if the team needs triage support. If the team runs planned iterations, set `DEFAULT_MILESTONE` in Phase 5; otherwise leave it unset so `/start` auto-detects."
Expand Down Expand Up @@ -465,4 +480,4 @@ Add a note: `/start` can be used to create well-formed GitHub issues without wri
- Never fetch more than 100 labels in a single command. `gh label list --limit 100` is the ceiling.
- Do not skip any human gate in Phase 3, Phase 4, or Phase 5 — each write requires confirmation.
- If the user skips a config value, do not ask again. Move on.
<!-- generated by CodeCannon/sync.py | skill: setup | adapter: claude | hash: 2173a91e | DO NOT EDIT — run CodeCannon/sync.py to regenerate -->
<!-- generated by CodeCannon/sync.py | skill: setup | adapter: claude | hash: a086fe27 | DO NOT EDIT — run CodeCannon/sync.py to regenerate -->
19 changes: 17 additions & 2 deletions .cursor/rules/setup.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -385,9 +385,24 @@ Ask: **"Create any missing labels from this baseline now? (yes/no)"**

Wait for response.

- **yes** → create missing labels only (do not recreate existing labels). Use sensible colors and short descriptions.
- **yes** → run `python3 CodeCannon/skills/github-agile/scripts/label-create.py bug enhancement chore documentation ready-for-qa qa-passed qa-failed`. The script applies sensible color/description defaults from a baked-in table and warns-and-continues on any name that already exists.
- **no / skip / anything else** → continue without creating labels.

#### Configured-label audit

Whether or not the greenfield baseline ran, audit the labels that configured skills will try to apply at runtime against what actually exists in the repo:

```bash
python3 CodeCannon/skills/github-agile/scripts/label-audit.py
```

The script reads `.codecannon.yaml`, collects the names referenced by `TICKET_LABELS` plus `QA_READY_LABEL` / `QA_PASSED_LABEL` / `QA_FAILED_LABEL` (skipping any that are unset), diffs against `gh label list`, and prints missing names — one per line — on stdout. A one-line diagnostic goes to stderr.

- **No stdout output** → all configured labels exist. Continue silently to the "Display the results" step below.
- **One or more names on stdout** → show them as a combined list and ask once: **"These labels are referenced by your configured skills but don't exist in the repo: \<list\>. Create them now? (yes/no)"**
- **yes** → run `python3 CodeCannon/skills/github-agile/scripts/label-create.py <name1> <name2> ...` with the missing names from the audit output.
- **no / skip** → continue, but at the end of Phase 4 print a one-line summary: "Skipped creating: \<list\>. `/submit-for-review` will warn and continue if it needs to apply a missing label; `/qa` and `/start` may degrade similarly."

After this step (or if labels were non-zero initially), run `gh label list --limit 100 --json name,color,description` again.

If `TICKET_LABELS` is unset or fewer than 5 labels exist, add a note: "`/start` works best with a clear issue-label pool (`TICKET_LABELS`), and `/qa` needs explicit QA lifecycle labels (`ready-for-qa`, `qa-passed`, `qa-failed`). Consider a lightweight priority scheme (e.g. `priority:high`, `priority:medium`, `priority:low`) if the team needs triage support. If the team runs planned iterations, set `DEFAULT_MILESTONE` in Phase 5; otherwise leave it unset so `/start` auto-detects."
Expand Down Expand Up @@ -471,4 +486,4 @@ Add a note: `/start` can be used to create well-formed GitHub issues without wri
- Never fetch more than 100 labels in a single command. `gh label list --limit 100` is the ceiling.
- Do not skip any human gate in Phase 3, Phase 4, or Phase 5 — each write requires confirmation.
- If the user skips a config value, do not ask again. Move on.
<!-- generated by CodeCannon/sync.py | skill: setup | adapter: cursor | hash: 579ad346 | DO NOT EDIT — run CodeCannon/sync.py to regenerate -->
<!-- generated by CodeCannon/sync.py | skill: setup | adapter: cursor | hash: 512323a0 | DO NOT EDIT — run CodeCannon/sync.py to regenerate -->
19 changes: 17 additions & 2 deletions .gemini/skills/setup/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -384,9 +384,24 @@ Ask: **"Create any missing labels from this baseline now? (yes/no)"**

Wait for response.

- **yes** → create missing labels only (do not recreate existing labels). Use sensible colors and short descriptions.
- **yes** → run `python3 CodeCannon/skills/github-agile/scripts/label-create.py bug enhancement chore documentation ready-for-qa qa-passed qa-failed`. The script applies sensible color/description defaults from a baked-in table and warns-and-continues on any name that already exists.
- **no / skip / anything else** → continue without creating labels.

#### Configured-label audit

Whether or not the greenfield baseline ran, audit the labels that configured skills will try to apply at runtime against what actually exists in the repo:

```bash
python3 CodeCannon/skills/github-agile/scripts/label-audit.py
```

The script reads `.codecannon.yaml`, collects the names referenced by `TICKET_LABELS` plus `QA_READY_LABEL` / `QA_PASSED_LABEL` / `QA_FAILED_LABEL` (skipping any that are unset), diffs against `gh label list`, and prints missing names — one per line — on stdout. A one-line diagnostic goes to stderr.

- **No stdout output** → all configured labels exist. Continue silently to the "Display the results" step below.
- **One or more names on stdout** → show them as a combined list and ask once: **"These labels are referenced by your configured skills but don't exist in the repo: \<list\>. Create them now? (yes/no)"**
- **yes** → run `python3 CodeCannon/skills/github-agile/scripts/label-create.py <name1> <name2> ...` with the missing names from the audit output.
- **no / skip** → continue, but at the end of Phase 4 print a one-line summary: "Skipped creating: \<list\>. `/submit-for-review` will warn and continue if it needs to apply a missing label; `/qa` and `/start` may degrade similarly."

After this step (or if labels were non-zero initially), run `gh label list --limit 100 --json name,color,description` again.

If `TICKET_LABELS` is unset or fewer than 5 labels exist, add a note: "`/start` works best with a clear issue-label pool (`TICKET_LABELS`), and `/qa` needs explicit QA lifecycle labels (`ready-for-qa`, `qa-passed`, `qa-failed`). Consider a lightweight priority scheme (e.g. `priority:high`, `priority:medium`, `priority:low`) if the team needs triage support. If the team runs planned iterations, set `DEFAULT_MILESTONE` in Phase 5; otherwise leave it unset so `/start` auto-detects."
Expand Down Expand Up @@ -470,4 +485,4 @@ Add a note: `/start` can be used to create well-formed GitHub issues without wri
- Never fetch more than 100 labels in a single command. `gh label list --limit 100` is the ceiling.
- Do not skip any human gate in Phase 3, Phase 4, or Phase 5 — each write requires confirmation.
- If the user skips a config value, do not ask again. Move on.
<!-- generated by CodeCannon/sync.py | skill: setup | adapter: gemini | hash: ba7f4709 | DO NOT EDIT — run CodeCannon/sync.py to regenerate -->
<!-- generated by CodeCannon/sync.py | skill: setup | adapter: gemini | hash: 74f978c8 | DO NOT EDIT — run CodeCannon/sync.py to regenerate -->
130 changes: 130 additions & 0 deletions skills/github-agile/scripts/label-audit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
#!/usr/bin/env python3
"""label-audit — list configured labels that don't exist in the GitHub repo.

Reads `.codecannon.yaml` from the current working directory, collects the
labels that skills will try to apply at runtime (TICKET_LABELS pool plus the
three QA labels), then queries `gh label list` and emits the missing names —
one per line — on stdout. Used by /setup Phase 4 to catch the failure mode
where a configured label doesn't exist in the repo and a downstream skill
(/submit-for-review, /qa, /start) only finds out at the worst moment.

Stdout is intentionally minimal so the calling skill can do `if stdout is
non-empty: prompt the operator`. A one-line diagnostic goes to stderr for
transparency.

Usage:
python3 CodeCannon/skills/github-agile/scripts/label-audit.py

Exit codes:
0 audit completed (stdout may be empty or list missing labels)
1 could not read .codecannon.yaml
2 gh subcommand failed
"""

import json
import subprocess
import sys
from pathlib import Path


def _dequote(value):
if len(value) >= 2 and value[0] == value[-1] and value[0] in ('"', "'"):
return value[1:-1]
return value


def read_config_values(config_path):
"""Return the {key: value} pairs nested under the top-level `config:` key.

Minimal flat-YAML reader — only covers what /setup writes: a `config:`
block of `KEY: value` lines, indent 2, optional quotes. Block scalars
(`|`) and other keys are ignored — the audit only needs string values.
"""
text = config_path.read_text()
values = {}
in_config = False
for raw in text.splitlines():
line = raw.rstrip()
if not line.strip() or line.lstrip().startswith('#'):
continue
indent = len(line) - len(line.lstrip())
stripped = line.strip()
if indent == 0:
in_config = stripped == 'config:'
continue
if not in_config or indent != 2 or ':' not in stripped:
continue
key, _, value = stripped.partition(':')
value = value.strip()
if value == '|' or not value:
continue
values[key.strip()] = _dequote(value)
return values


def collect_configured_labels(values):
"""Return the unique set of label names referenced by configured skills."""
labels = set()
pool = values.get('TICKET_LABELS', '')
for name in pool.split(','):
name = name.strip()
if name:
labels.add(name)
for key in ('QA_READY_LABEL', 'QA_PASSED_LABEL', 'QA_FAILED_LABEL'):
name = values.get(key, '').strip()
if name:
labels.add(name)
return labels


def fetch_existing_labels():
"""Return the set of label names that exist in the current GitHub repo."""
result = subprocess.run(
['gh', 'label', 'list', '--limit', '100', '--json', 'name'],
capture_output=True,
text=True,
)
if result.returncode != 0:
print(f"Error: gh label list failed: {result.stderr.strip()}", file=sys.stderr)
return None
try:
data = json.loads(result.stdout)
except json.JSONDecodeError as exc:
print(f"Error: could not parse gh label list output: {exc}", file=sys.stderr)
return None
return {entry['name'] for entry in data}


def main(argv):
config_path = Path('.codecannon.yaml')
if not config_path.is_file():
print(f"Error: {config_path} not found in current directory.", file=sys.stderr)
return 1

try:
values = read_config_values(config_path)
except OSError as exc:
print(f"Error: could not read {config_path}: {exc}", file=sys.stderr)
return 1

configured = collect_configured_labels(values)
if not configured:
print("audited 0 configured labels (none set in .codecannon.yaml)", file=sys.stderr)
return 0

existing = fetch_existing_labels()
if existing is None:
return 2

missing = sorted(configured - existing)
print(
f"audited {len(configured)} configured label(s); {len(missing)} missing",
file=sys.stderr,
)
for name in missing:
print(name)
return 0


if __name__ == '__main__':
sys.exit(main(sys.argv))
68 changes: 68 additions & 0 deletions skills/github-agile/scripts/label-create.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/usr/bin/env python3
"""label-create — create GitHub labels with sensible defaults.

Wraps `gh label create` so call-sites don't have to improvise colors and
descriptions. A baked-in table covers the names skills commonly reference
(bug, enhancement, documentation, chore, ready-for-qa, qa-passed,
qa-failed). For any other name — including operator-renamed QA labels
(e.g. QA_PASSED_LABEL: "tested") — falls back to neutral gray with no
description; the operator can refine later in the GitHub UI.

Used by /setup Phase 4 in two places: the greenfield baseline create when
the repo has zero labels, and the configured-label audit when one or more
labels referenced by .codecannon.yaml don't yet exist in the repo.

Per the warn-and-continue posture established in #182, a single label
that fails to create (typically because it already exists) prints a
one-line warning and the script moves on to the next name. Exit is 0 as
long as the loop completed; only argument errors abort.

Usage:
python3 CodeCannon/skills/github-agile/scripts/label-create.py <name>...

Exit codes:
0 loop completed (individual labels may have warned to stderr)
3 no label names provided
"""

import subprocess
import sys

# Default color and description for well-known label names. Colors are the
# GitHub defaults where they exist (bug, enhancement, documentation) and
# conventional traffic-light choices for the QA labels.
DEFAULTS = {
'bug': ('d73a4a', "Something isn't working"),
'enhancement': ('a2eeef', 'New feature or request'),
'documentation': ('0075ca', 'Improvements or additions to documentation'),
'chore': ('cccccc', 'Routine maintenance'),
'ready-for-qa': ('fbca04', 'Ready for QA review'),
'qa-passed': ('0e8a16', 'Passed QA review'),
'qa-failed': ('e11d21', 'Failed QA review'),
}

FALLBACK_COLOR = 'cccccc'


def main(argv):
if len(argv) < 2:
print(f"Usage: {argv[0]} <name>...", file=sys.stderr)
return 3

for name in argv[1:]:
color, description = DEFAULTS.get(name, (FALLBACK_COLOR, ''))
cmd = ['gh', 'label', 'create', name, '--color', color]
if description:
cmd += ['--description', description]
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
stderr = result.stderr.strip() or '(no stderr)'
print(f"Warning: failed to create label '{name}': {stderr}", file=sys.stderr)
continue
print(f"Created label: {name}")

return 0


if __name__ == '__main__':
sys.exit(main(sys.argv))
Loading
Loading