chief-clancy 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +426 -0
- package/bin/install.js +169 -0
- package/package.json +42 -0
- package/registry/boards.json +47 -0
- package/src/agents/arch-agent.md +72 -0
- package/src/agents/concerns-agent.md +89 -0
- package/src/agents/design-agent.md +130 -0
- package/src/agents/quality-agent.md +161 -0
- package/src/agents/tech-agent.md +92 -0
- package/src/commands/doctor.md +7 -0
- package/src/commands/help.md +50 -0
- package/src/commands/init.md +7 -0
- package/src/commands/logs.md +7 -0
- package/src/commands/map-codebase.md +16 -0
- package/src/commands/once.md +13 -0
- package/src/commands/review.md +9 -0
- package/src/commands/run.md +11 -0
- package/src/commands/settings.md +7 -0
- package/src/commands/status.md +9 -0
- package/src/commands/uninstall.md +5 -0
- package/src/commands/update-docs.md +9 -0
- package/src/commands/update.md +9 -0
- package/src/templates/.env.example.github +43 -0
- package/src/templates/.env.example.jira +53 -0
- package/src/templates/.env.example.linear +43 -0
- package/src/templates/CLAUDE.md +69 -0
- package/src/templates/scripts/clancy-afk.sh +111 -0
- package/src/templates/scripts/clancy-once-github.sh +225 -0
- package/src/templates/scripts/clancy-once-linear.sh +252 -0
- package/src/templates/scripts/clancy-once.sh +259 -0
- package/src/workflows/doctor.md +115 -0
- package/src/workflows/init.md +423 -0
- package/src/workflows/logs.md +82 -0
- package/src/workflows/map-codebase.md +124 -0
- package/src/workflows/once.md +80 -0
- package/src/workflows/review.md +165 -0
- package/src/workflows/run.md +119 -0
- package/src/workflows/scaffold.md +289 -0
- package/src/workflows/settings.md +344 -0
- package/src/workflows/status.md +120 -0
- package/src/workflows/uninstall.md +75 -0
- package/src/workflows/update-docs.md +89 -0
- package/src/workflows/update.md +67 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# /clancy:once
|
|
2
|
+
|
|
3
|
+
Pick up exactly one ticket from your Kanban board, implement it, commit, squash-merge, and stop.
|
|
4
|
+
|
|
5
|
+
Good for:
|
|
6
|
+
- Your first run — watch Clancy work before going AFK
|
|
7
|
+
- Testing after changing .clancy/docs/ or CLAUDE.md
|
|
8
|
+
- Debugging a specific ticket
|
|
9
|
+
- When you only have time for one ticket
|
|
10
|
+
|
|
11
|
+
@.claude/clancy/workflows/once.md
|
|
12
|
+
|
|
13
|
+
Run one ticket as documented in the workflow above.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# /clancy:review
|
|
2
|
+
|
|
3
|
+
Fetch the next ticket from the board and score it — returns a confidence score (0–100%) and actionable recommendations before you run.
|
|
4
|
+
|
|
5
|
+
Does not implement anything. Read-only from Clancy's perspective, though it invokes Claude for analysis.
|
|
6
|
+
|
|
7
|
+
@.claude/clancy/workflows/review.md
|
|
8
|
+
|
|
9
|
+
Score the ticket as documented in the workflow above, including the full 7-criterion rubric and confidence bands.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# /clancy:run
|
|
2
|
+
|
|
3
|
+
Run Clancy in loop mode — processes tickets from your Kanban board until the queue is empty or MAX_ITERATIONS is reached.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
/clancy:run — uses MAX_ITERATIONS from .clancy/.env (default 5)
|
|
7
|
+
/clancy:run 20 — overrides MAX_ITERATIONS to 20 for this session only
|
|
8
|
+
|
|
9
|
+
@.claude/clancy/workflows/run.md
|
|
10
|
+
|
|
11
|
+
Run the loop as documented in the workflow above. The numeric argument (if provided) is session-only and never written to .clancy/.env.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# /clancy:status
|
|
2
|
+
|
|
3
|
+
Show the next tickets Clancy would pick up — read-only board check, no side effects.
|
|
4
|
+
|
|
5
|
+
Fetches up to 3 tickets from the queue using the same query as /clancy:once, so what you see here is exactly what Clancy would run next.
|
|
6
|
+
|
|
7
|
+
@.claude/clancy/workflows/status.md
|
|
8
|
+
|
|
9
|
+
Show status as documented in the workflow above. Strictly read-only — no git operations, no file writes, no ticket claiming.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# /clancy:update-docs
|
|
2
|
+
|
|
3
|
+
Incrementally refresh .clancy/docs/ — re-runs only the agents covering areas that have changed since the last scan.
|
|
4
|
+
|
|
5
|
+
Faster than a full map-codebase when you've made targeted changes (new dependency, design system update, new test strategy, etc.).
|
|
6
|
+
|
|
7
|
+
@.claude/clancy/workflows/update-docs.md
|
|
8
|
+
|
|
9
|
+
Run the incremental update as documented in the workflow above.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# /clancy:update
|
|
2
|
+
|
|
3
|
+
Update Clancy itself to the latest version via npx and show what changed.
|
|
4
|
+
|
|
5
|
+
This re-runs the installer, which copies the latest command files from the npm package into your .claude/commands/clancy/ directory. Your .clancy/ project folder (scripts, docs, progress log) is never touched.
|
|
6
|
+
|
|
7
|
+
@.claude/clancy/workflows/update.md
|
|
8
|
+
|
|
9
|
+
Run the update as documented in the workflow above.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Clancy — GitHub Issues configuration
|
|
2
|
+
# Copy this file to .env and fill in your values.
|
|
3
|
+
# Never commit .env to version control.
|
|
4
|
+
|
|
5
|
+
# ─── GitHub Issues ────────────────────────────────────────────────────────────
|
|
6
|
+
GITHUB_TOKEN=ghp_your-personal-access-token
|
|
7
|
+
GITHUB_REPO=owner/repo-name
|
|
8
|
+
|
|
9
|
+
# Optional: only pick up issues with this label (in addition to 'clancy').
|
|
10
|
+
# Useful for mixed backlogs where not every issue is suitable for autonomous implementation.
|
|
11
|
+
# Create the label in GitHub first, then add it to any issue you want Clancy to pick up.
|
|
12
|
+
# CLANCY_LABEL=clancy
|
|
13
|
+
|
|
14
|
+
# ─── Git ──────────────────────────────────────────────────────────────────────
|
|
15
|
+
# Base integration branch. Clancy branches from here when an issue has no milestone.
|
|
16
|
+
# When an issue has a milestone, Clancy auto-creates milestone/{slug} from this branch.
|
|
17
|
+
CLANCY_BASE_BRANCH=main
|
|
18
|
+
|
|
19
|
+
# ─── Loop ─────────────────────────────────────────────────────────────────────
|
|
20
|
+
# Max tickets to process per /clancy:run session (default: 20)
|
|
21
|
+
MAX_ITERATIONS=20
|
|
22
|
+
|
|
23
|
+
# ─── Model ────────────────────────────────────────────────────────────────────
|
|
24
|
+
# Claude model used for each ticket session. Leave unset to use the default.
|
|
25
|
+
# Options: claude-opus-4-6 | claude-sonnet-4-6 | claude-haiku-4-5
|
|
26
|
+
# CLANCY_MODEL=claude-sonnet-4-6
|
|
27
|
+
|
|
28
|
+
# ─── Optional: Figma MCP ──────────────────────────────────────────────────────
|
|
29
|
+
# Fetch design specs from Figma when a ticket has a Figma URL in its description
|
|
30
|
+
# FIGMA_API_KEY=your-figma-api-key
|
|
31
|
+
|
|
32
|
+
# ─── Optional: Playwright visual checks ───────────────────────────────────────
|
|
33
|
+
# Run a visual check after implementing UI tickets
|
|
34
|
+
# PLAYWRIGHT_ENABLED=true
|
|
35
|
+
# PLAYWRIGHT_DEV_COMMAND="yarn dev"
|
|
36
|
+
# PLAYWRIGHT_DEV_PORT=5173
|
|
37
|
+
# PLAYWRIGHT_STORYBOOK_COMMAND="yarn storybook"
|
|
38
|
+
# PLAYWRIGHT_STORYBOOK_PORT=6006
|
|
39
|
+
# PLAYWRIGHT_STARTUP_WAIT=15
|
|
40
|
+
|
|
41
|
+
# ─── Optional: Notifications ──────────────────────────────────────────────────
|
|
42
|
+
# Webhook URL for Slack or Teams notifications on ticket completion
|
|
43
|
+
# CLANCY_NOTIFY_WEBHOOK=https://hooks.slack.com/services/your/webhook/url
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Clancy — Jira configuration
|
|
2
|
+
# Copy this file to .env and fill in your values.
|
|
3
|
+
# Never commit .env to version control.
|
|
4
|
+
|
|
5
|
+
# ─── Jira ─────────────────────────────────────────────────────────────────────
|
|
6
|
+
JIRA_BASE_URL=https://your-org.atlassian.net
|
|
7
|
+
JIRA_USER=your-email@example.com
|
|
8
|
+
JIRA_API_TOKEN=your-api-token-from-id.atlassian.com
|
|
9
|
+
JIRA_PROJECT_KEY=PROJ
|
|
10
|
+
|
|
11
|
+
# Status name for "ready to be picked up" (default: To Do)
|
|
12
|
+
# Must be quoted if the status name contains spaces (e.g. "Selected for Development")
|
|
13
|
+
CLANCY_JQL_STATUS="To Do"
|
|
14
|
+
|
|
15
|
+
# Set to any non-empty value to filter by open sprints (requires Jira Software)
|
|
16
|
+
# Remove or leave empty if your project doesn't use sprints
|
|
17
|
+
# CLANCY_JQL_SPRINT=true
|
|
18
|
+
|
|
19
|
+
# Optional: only pick up tickets with this label. Recommended for mixed backlogs
|
|
20
|
+
# where not every ticket is suitable for autonomous implementation (e.g. non-code tasks).
|
|
21
|
+
# Create the label in Jira first, then add it to any ticket you want Clancy to pick up.
|
|
22
|
+
# CLANCY_LABEL="clancy"
|
|
23
|
+
|
|
24
|
+
# ─── Git ──────────────────────────────────────────────────────────────────────
|
|
25
|
+
# Base integration branch. Clancy branches from here when a ticket has no parent epic.
|
|
26
|
+
# When a ticket has a parent epic, Clancy auto-creates epic/{key} from this branch.
|
|
27
|
+
CLANCY_BASE_BRANCH=main
|
|
28
|
+
|
|
29
|
+
# ─── Loop ─────────────────────────────────────────────────────────────────────
|
|
30
|
+
# Max tickets to process per /clancy:run session (default: 5)
|
|
31
|
+
MAX_ITERATIONS=5
|
|
32
|
+
|
|
33
|
+
# ─── Model ────────────────────────────────────────────────────────────────────
|
|
34
|
+
# Claude model used for each ticket session. Leave unset to use the default.
|
|
35
|
+
# Options: claude-opus-4-6 | claude-sonnet-4-6 | claude-haiku-4-5
|
|
36
|
+
# CLANCY_MODEL=claude-sonnet-4-6
|
|
37
|
+
|
|
38
|
+
# ─── Optional: Figma MCP ──────────────────────────────────────────────────────
|
|
39
|
+
# Fetch design specs from Figma when a ticket has a Figma URL in its description
|
|
40
|
+
# FIGMA_API_KEY=your-figma-api-key
|
|
41
|
+
|
|
42
|
+
# ─── Optional: Playwright visual checks ───────────────────────────────────────
|
|
43
|
+
# Run a visual check after implementing UI tickets
|
|
44
|
+
# PLAYWRIGHT_ENABLED=true
|
|
45
|
+
# PLAYWRIGHT_DEV_COMMAND="yarn dev"
|
|
46
|
+
# PLAYWRIGHT_DEV_PORT=5173
|
|
47
|
+
# PLAYWRIGHT_STORYBOOK_COMMAND="yarn storybook"
|
|
48
|
+
# PLAYWRIGHT_STORYBOOK_PORT=6006
|
|
49
|
+
# PLAYWRIGHT_STARTUP_WAIT=15
|
|
50
|
+
|
|
51
|
+
# ─── Optional: Notifications ──────────────────────────────────────────────────
|
|
52
|
+
# Webhook URL for Slack or Teams notifications on ticket completion
|
|
53
|
+
# CLANCY_NOTIFY_WEBHOOK=https://hooks.slack.com/services/your/webhook/url
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Clancy — Linear configuration
|
|
2
|
+
# Copy this file to .env and fill in your values.
|
|
3
|
+
# Never commit .env to version control.
|
|
4
|
+
|
|
5
|
+
# ─── Linear ───────────────────────────────────────────────────────────────────
|
|
6
|
+
LINEAR_API_KEY=lin_api_your-personal-api-key
|
|
7
|
+
LINEAR_TEAM_ID=your-team-uuid
|
|
8
|
+
|
|
9
|
+
# Optional: only pick up issues with this label. Recommended for mixed backlogs
|
|
10
|
+
# where not every issue is suitable for autonomous implementation (e.g. non-code tasks).
|
|
11
|
+
# Create the label in Linear first, then add it to any issue you want Clancy to pick up.
|
|
12
|
+
# CLANCY_LABEL=clancy
|
|
13
|
+
|
|
14
|
+
# ─── Git ──────────────────────────────────────────────────────────────────────
|
|
15
|
+
# Base integration branch. Clancy branches from here when an issue has no parent.
|
|
16
|
+
# When an issue has a parent, Clancy auto-creates epic/{key} from this branch.
|
|
17
|
+
CLANCY_BASE_BRANCH=main
|
|
18
|
+
|
|
19
|
+
# ─── Loop ─────────────────────────────────────────────────────────────────────
|
|
20
|
+
# Max tickets to process per /clancy:run session (default: 20)
|
|
21
|
+
MAX_ITERATIONS=20
|
|
22
|
+
|
|
23
|
+
# ─── Model ────────────────────────────────────────────────────────────────────
|
|
24
|
+
# Claude model used for each ticket session. Leave unset to use the default.
|
|
25
|
+
# Options: claude-opus-4-6 | claude-sonnet-4-6 | claude-haiku-4-5
|
|
26
|
+
# CLANCY_MODEL=claude-sonnet-4-6
|
|
27
|
+
|
|
28
|
+
# ─── Optional: Figma MCP ──────────────────────────────────────────────────────
|
|
29
|
+
# Fetch design specs from Figma when a ticket has a Figma URL in its description
|
|
30
|
+
# FIGMA_API_KEY=your-figma-api-key
|
|
31
|
+
|
|
32
|
+
# ─── Optional: Playwright visual checks ───────────────────────────────────────
|
|
33
|
+
# Run a visual check after implementing UI tickets
|
|
34
|
+
# PLAYWRIGHT_ENABLED=true
|
|
35
|
+
# PLAYWRIGHT_DEV_COMMAND="yarn dev"
|
|
36
|
+
# PLAYWRIGHT_DEV_PORT=5173
|
|
37
|
+
# PLAYWRIGHT_STORYBOOK_COMMAND="yarn storybook"
|
|
38
|
+
# PLAYWRIGHT_STORYBOOK_PORT=6006
|
|
39
|
+
# PLAYWRIGHT_STARTUP_WAIT=15
|
|
40
|
+
|
|
41
|
+
# ─── Optional: Notifications ──────────────────────────────────────────────────
|
|
42
|
+
# Webhook URL for Slack or Teams notifications on ticket completion
|
|
43
|
+
# CLANCY_NOTIFY_WEBHOOK=https://hooks.slack.com/services/your/webhook/url
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
<!-- clancy:start -->
|
|
2
|
+
## Clancy
|
|
3
|
+
|
|
4
|
+
This project uses Clancy for autonomous ticket-driven development.
|
|
5
|
+
|
|
6
|
+
### Docs
|
|
7
|
+
Before every run, read all docs in `.clancy/docs/`:
|
|
8
|
+
- STACK.md — tech stack and dependencies
|
|
9
|
+
- INTEGRATIONS.md — external services and APIs
|
|
10
|
+
- ARCHITECTURE.md — system design and data flow
|
|
11
|
+
- CONVENTIONS.md — code style and patterns
|
|
12
|
+
- TESTING.md — test approach and coverage expectations
|
|
13
|
+
- GIT.md — branching, commit format, merge strategy
|
|
14
|
+
- DESIGN-SYSTEM.md — tokens, components, visual conventions
|
|
15
|
+
- ACCESSIBILITY.md — WCAG requirements and ARIA patterns
|
|
16
|
+
- DEFINITION-OF-DONE.md — checklist before marking a ticket complete
|
|
17
|
+
- CONCERNS.md — known risks, tech debt, things to avoid
|
|
18
|
+
|
|
19
|
+
### Executability check
|
|
20
|
+
|
|
21
|
+
Before any git operation, branch creation, or code change — assess whether this ticket can be implemented entirely as a code change committed to this repo.
|
|
22
|
+
|
|
23
|
+
**Skip the ticket** if it primarily requires any of the following — Clancy cannot do these:
|
|
24
|
+
- **External system admin:** work in Google Analytics, Salesforce, HubSpot, the AWS console, app store dashboards, or any external platform not accessible through code
|
|
25
|
+
- **Human process steps:** getting sign-off or approval, sending emails to customers, coordinating with people, scheduling meetings, making announcements to users
|
|
26
|
+
- **Non-repo production ops:** deploying to production, rotating secrets in prod, scaling infrastructure — unless the task is purely about editing CI/CD config files that live in this repo
|
|
27
|
+
- **Non-code deliverables:** writing runbooks, updating Confluence or wikis, creating presentations, documenting in external tools
|
|
28
|
+
|
|
29
|
+
When in doubt: "Is the primary deliverable a code change committed to this repo?" — if yes, implement it; if no, skip it.
|
|
30
|
+
|
|
31
|
+
**If skipping, do all four of these in order:**
|
|
32
|
+
1. Output this exact line: `⚠ Skipping [TICKET-KEY]: {one-line reason}`
|
|
33
|
+
2. Output this exact line: `Ticket skipped — update it to be codebase-only work, then re-run.`
|
|
34
|
+
3. Append to `.clancy/progress.txt`: `YYYY-MM-DD HH:MM | TICKET-KEY | SKIPPED | {reason}`
|
|
35
|
+
4. Stop. No branches, no file changes, no git operations.
|
|
36
|
+
|
|
37
|
+
**If the ticket is codebase work**, proceed to implementation normally.
|
|
38
|
+
|
|
39
|
+
### Git workflow
|
|
40
|
+
- Read GIT.md before every run — follow its conventions exactly
|
|
41
|
+
- Default (if GIT.md is silent): one feature branch per ticket `feature/{ticket-key-lowercase}`, squash merge into target branch, conventional commits `feat(TICKET-123): summary`
|
|
42
|
+
- Target branch is auto-detected from the ticket: if it has a parent epic, Clancy branches from and merges into `epic/{epic-key}` (created from `CLANCY_BASE_BRANCH` if it doesn't exist); otherwise branches from `CLANCY_BASE_BRANCH` directly
|
|
43
|
+
- Delete ticket branch locally after merge — never push deletes
|
|
44
|
+
|
|
45
|
+
### Progress
|
|
46
|
+
Log completed tickets to `.clancy/progress.txt`:
|
|
47
|
+
`YYYY-MM-DD HH:MM | TICKET-KEY | Summary | DONE`
|
|
48
|
+
|
|
49
|
+
### Design context
|
|
50
|
+
When a ticket description contains a Figma URL, fetch design context before implementing.
|
|
51
|
+
Use the three-tier approach in order:
|
|
52
|
+
1. Figma MCP: get_metadata → targeted get_design_context → get_screenshot (3 calls, best quality)
|
|
53
|
+
2. Figma REST API image export: parse file key + node ID from URL, fetch rendered PNG as vision input
|
|
54
|
+
3. Ticket image attachment: use any image attached to the ticket description as vision input
|
|
55
|
+
Fetch once only — never return to Figma mid-session.
|
|
56
|
+
Map all token values to CSS custom properties. Never use raw SVGs or hardcoded colours.
|
|
57
|
+
Figma URL must come from the ticket description only — never from CLAUDE.md or other docs.
|
|
58
|
+
|
|
59
|
+
### Visual checks
|
|
60
|
+
After implementing a UI ticket, run a visual check before committing.
|
|
61
|
+
Read .clancy/docs/PLAYWRIGHT.md to determine which server to use (Storybook or dev server).
|
|
62
|
+
Apply the decision rule against this ticket's description — route/page/screen/layout → dev server,
|
|
63
|
+
component/atom/molecule/organism/variant/story → Storybook, ambiguous → dev server default.
|
|
64
|
+
Start the server using health check polling (not sleep). Navigate to the relevant route or story.
|
|
65
|
+
Screenshot, assess visually, check console. Fix before committing if anything looks wrong.
|
|
66
|
+
If a Figma design was fetched for this ticket, compare the screenshot directly against it — check layout, spacing, colours, typography, and component fidelity. Treat the Figma design as the source of truth.
|
|
67
|
+
Kill by PID then sweep the port unconditionally — never leave a port open.
|
|
68
|
+
Log result: YYYY-MM-DD HH:MM | TICKET-KEY | PLAYWRIGHT_PASS|FAIL | server-used
|
|
69
|
+
<!-- clancy:end -->
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Strict mode: exit on error (-e), undefined variables (-u), pipe failures (-o pipefail).
|
|
3
|
+
# This means any command that fails will stop the script immediately rather than silently continuing.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
# ─── WHAT THIS SCRIPT DOES ─────────────────────────────────────────────────────
|
|
7
|
+
#
|
|
8
|
+
# Loop runner for Clancy. Calls clancy-once.sh repeatedly until:
|
|
9
|
+
# - No more tickets are found ("No tickets found", "All done", etc.)
|
|
10
|
+
# - A preflight check fails (output line starting with ✗)
|
|
11
|
+
# - MAX_ITERATIONS is reached
|
|
12
|
+
# - The user presses Ctrl+C
|
|
13
|
+
#
|
|
14
|
+
# This script does not know about boards. All board logic lives in clancy-once.sh,
|
|
15
|
+
# which is always the runtime filename regardless of which board is configured.
|
|
16
|
+
# /clancy:init copies the correct board variant as clancy-once.sh during setup.
|
|
17
|
+
#
|
|
18
|
+
# ───────────────────────────────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
# ─── PREFLIGHT ─────────────────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
command -v claude >/dev/null 2>&1 || {
|
|
23
|
+
echo "✗ claude CLI not found."
|
|
24
|
+
echo " Install it: https://claude.ai/code"
|
|
25
|
+
exit 0
|
|
26
|
+
}
|
|
27
|
+
command -v jq >/dev/null 2>&1 || {
|
|
28
|
+
echo "✗ jq not found."
|
|
29
|
+
echo " Install: brew install jq (mac) | apt install jq (linux)"
|
|
30
|
+
exit 0
|
|
31
|
+
}
|
|
32
|
+
command -v curl >/dev/null 2>&1 || {
|
|
33
|
+
echo "✗ curl not found. Install curl for your OS."
|
|
34
|
+
exit 0
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
[ -f .clancy/.env ] || {
|
|
38
|
+
echo "✗ .clancy/.env not found."
|
|
39
|
+
echo " Copy .clancy/.env.example to .clancy/.env and fill in your credentials."
|
|
40
|
+
exit 0
|
|
41
|
+
}
|
|
42
|
+
# shellcheck source=/dev/null
|
|
43
|
+
source .clancy/.env
|
|
44
|
+
|
|
45
|
+
git rev-parse --git-dir >/dev/null 2>&1 || {
|
|
46
|
+
echo "✗ Not a git repository."
|
|
47
|
+
echo " Clancy must be run from the root of a git project."
|
|
48
|
+
exit 0
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
# ─── END PREFLIGHT ─────────────────────────────────────────────────────────────
|
|
52
|
+
|
|
53
|
+
MAX_ITERATIONS=${MAX_ITERATIONS:-5}
|
|
54
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
55
|
+
|
|
56
|
+
# clancy-once.sh is always the runtime filename regardless of board.
|
|
57
|
+
# /clancy:init copies the correct board variant as clancy-once.sh.
|
|
58
|
+
ONCE_SCRIPT="$SCRIPT_DIR/clancy-once.sh"
|
|
59
|
+
|
|
60
|
+
if [ ! -f "$ONCE_SCRIPT" ]; then
|
|
61
|
+
echo "✗ Script not found: $ONCE_SCRIPT"
|
|
62
|
+
echo " Run /clancy:init to scaffold scripts."
|
|
63
|
+
exit 0
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
echo "Starting Clancy — will process up to $MAX_ITERATIONS ticket(s). Ctrl+C to stop early."
|
|
67
|
+
echo ""
|
|
68
|
+
|
|
69
|
+
i=0
|
|
70
|
+
while [ "$i" -lt "$MAX_ITERATIONS" ]; do
|
|
71
|
+
i=$((i + 1))
|
|
72
|
+
echo ""
|
|
73
|
+
echo "=== Iteration $i of $MAX_ITERATIONS ==="
|
|
74
|
+
|
|
75
|
+
# Run clancy-once.sh and stream its output live via tee.
|
|
76
|
+
# tee writes to both stdout (visible to user) and a temp file (for stop-condition checks).
|
|
77
|
+
# Without tee, output would be buffered in a variable and hidden during implementation.
|
|
78
|
+
TMPFILE=$(mktemp)
|
|
79
|
+
bash "$ONCE_SCRIPT" 2>&1 | tee "$TMPFILE"
|
|
80
|
+
OUTPUT=$(cat "$TMPFILE")
|
|
81
|
+
rm -f "$TMPFILE"
|
|
82
|
+
|
|
83
|
+
# Stop if no tickets remain
|
|
84
|
+
if echo "$OUTPUT" | grep -qE "No tickets found|No issues found|All done"; then
|
|
85
|
+
echo ""
|
|
86
|
+
echo "✓ Clancy finished — no more tickets."
|
|
87
|
+
exit 0
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
# Stop if Claude skipped the ticket (not implementable from the codebase).
|
|
91
|
+
# Re-running would just fetch and skip the same ticket again — stop and let
|
|
92
|
+
# the user update the ticket or remove it from the queue before continuing.
|
|
93
|
+
if echo "$OUTPUT" | grep -q "Ticket skipped"; then
|
|
94
|
+
echo ""
|
|
95
|
+
echo "⚠ Clancy stopped — ticket was skipped (not implementable from the codebase)."
|
|
96
|
+
echo " Update the ticket to focus on codebase work, then re-run."
|
|
97
|
+
exit 0
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
# Stop if a preflight check failed (lines starting with ✗)
|
|
101
|
+
if echo "$OUTPUT" | grep -qE "^✗ "; then
|
|
102
|
+
echo ""
|
|
103
|
+
echo "✗ Clancy stopped — preflight check failed. See output above."
|
|
104
|
+
exit 0
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
sleep 2
|
|
108
|
+
done
|
|
109
|
+
|
|
110
|
+
echo ""
|
|
111
|
+
echo "Reached max iterations ($MAX_ITERATIONS). Run clancy-afk.sh again to continue."
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Strict mode: exit on error (-e), undefined variables (-u), pipe failures (-o pipefail).
|
|
3
|
+
# This means any command that fails will stop the script immediately rather than silently continuing.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
# ─── WHAT THIS SCRIPT DOES ─────────────────────────────────────────────────────
|
|
7
|
+
#
|
|
8
|
+
# Board: GitHub Issues
|
|
9
|
+
#
|
|
10
|
+
# 1. Preflight — checks all required tools, credentials, and repo reachability
|
|
11
|
+
# 2. Fetch — pulls the next open issue with the 'clancy' label assigned to you
|
|
12
|
+
# 3. Branch — creates a feature branch from the issue's milestone branch (or base branch)
|
|
13
|
+
# 4. Implement — passes the issue to Claude Code, which reads .clancy/docs/ and implements it
|
|
14
|
+
# 5. Merge — squash-merges the feature branch back into the target branch
|
|
15
|
+
# 6. Close — marks the GitHub issue as closed via the API
|
|
16
|
+
# 7. Log — appends a completion entry to .clancy/progress.txt
|
|
17
|
+
#
|
|
18
|
+
# This script is run once per issue. The loop is handled by clancy-afk.sh.
|
|
19
|
+
#
|
|
20
|
+
# NOTE: GitHub's /issues endpoint returns pull requests too. This script filters
|
|
21
|
+
# them out by checking for the presence of the 'pull_request' key in each result.
|
|
22
|
+
#
|
|
23
|
+
# NOTE: Failures use exit 0, not exit 1. This is intentional — clancy-afk.sh
|
|
24
|
+
# detects stop conditions by reading script output rather than exit codes, so a
|
|
25
|
+
# non-zero exit would be treated as an unexpected crash rather than a clean stop.
|
|
26
|
+
#
|
|
27
|
+
# ───────────────────────────────────────────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
# ─── PREFLIGHT ─────────────────────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
command -v claude >/dev/null 2>&1 || {
|
|
32
|
+
echo "✗ claude CLI not found."
|
|
33
|
+
echo " Install it: https://claude.ai/code"
|
|
34
|
+
exit 0
|
|
35
|
+
}
|
|
36
|
+
command -v jq >/dev/null 2>&1 || {
|
|
37
|
+
echo "✗ jq not found."
|
|
38
|
+
echo " Install: brew install jq (mac) | apt install jq (linux)"
|
|
39
|
+
exit 0
|
|
40
|
+
}
|
|
41
|
+
command -v curl >/dev/null 2>&1 || {
|
|
42
|
+
echo "✗ curl not found. Install curl for your OS."
|
|
43
|
+
exit 0
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
[ -f .clancy/.env ] || {
|
|
47
|
+
echo "✗ .clancy/.env not found."
|
|
48
|
+
echo " Copy .clancy/.env.example to .clancy/.env and fill in your credentials."
|
|
49
|
+
echo " Then run: /clancy:init"
|
|
50
|
+
exit 0
|
|
51
|
+
}
|
|
52
|
+
# shellcheck source=/dev/null
|
|
53
|
+
source .clancy/.env
|
|
54
|
+
|
|
55
|
+
git rev-parse --git-dir >/dev/null 2>&1 || {
|
|
56
|
+
echo "✗ Not a git repository."
|
|
57
|
+
echo " Clancy must be run from the root of a git project."
|
|
58
|
+
exit 0
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if ! git diff --quiet || ! git diff --cached --quiet; then
|
|
62
|
+
echo "⚠ Working directory has uncommitted changes."
|
|
63
|
+
echo " Consider stashing or committing first to avoid confusion."
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
[ -n "${GITHUB_TOKEN:-}" ] || { echo "✗ GITHUB_TOKEN is not set in .clancy/.env"; exit 0; }
|
|
67
|
+
[ -n "${GITHUB_REPO:-}" ] || { echo "✗ GITHUB_REPO is not set in .clancy/.env"; exit 0; }
|
|
68
|
+
|
|
69
|
+
# Validate GITHUB_REPO format — must be owner/repo with safe characters only
|
|
70
|
+
if ! echo "$GITHUB_REPO" | grep -qE '^[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+$'; then
|
|
71
|
+
echo "✗ GITHUB_REPO format is invalid. Expected owner/repo (e.g. acme/my-app). Check GITHUB_REPO in .clancy/.env."
|
|
72
|
+
exit 0
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
PING=$(curl -s -o /dev/null -w "%{http_code}" \
|
|
76
|
+
-H "Authorization: Bearer $GITHUB_TOKEN" \
|
|
77
|
+
-H "X-GitHub-Api-Version: 2022-11-28" \
|
|
78
|
+
"https://api.github.com/repos/$GITHUB_REPO")
|
|
79
|
+
|
|
80
|
+
case "$PING" in
|
|
81
|
+
200) ;;
|
|
82
|
+
401) echo "✗ GitHub authentication failed. Check GITHUB_TOKEN in .clancy/.env."; exit 0 ;;
|
|
83
|
+
403) echo "✗ GitHub access denied. Your token may lack the repo scope."; exit 0 ;;
|
|
84
|
+
404) echo "✗ GitHub repo '$GITHUB_REPO' not found. Check GITHUB_REPO in .clancy/.env."; exit 0 ;;
|
|
85
|
+
000) echo "✗ Could not reach GitHub. Check your network."; exit 0 ;;
|
|
86
|
+
*) echo "✗ GitHub returned unexpected status $PING. Check your config."; exit 0 ;;
|
|
87
|
+
esac
|
|
88
|
+
|
|
89
|
+
if [ "${PLAYWRIGHT_ENABLED:-}" = "true" ]; then
|
|
90
|
+
if lsof -ti:"${PLAYWRIGHT_DEV_PORT:-5173}" >/dev/null 2>&1; then
|
|
91
|
+
echo "⚠ Port ${PLAYWRIGHT_DEV_PORT:-5173} is already in use."
|
|
92
|
+
echo " If visual checks fail, stop whatever is using the port first."
|
|
93
|
+
fi
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
echo "✓ Preflight passed. Starting Clancy..."
|
|
97
|
+
|
|
98
|
+
# ─── END PREFLIGHT ─────────────────────────────────────────────────────────────
|
|
99
|
+
|
|
100
|
+
# ─── FETCH ISSUE ───────────────────────────────────────────────────────────────
|
|
101
|
+
|
|
102
|
+
# Fetch open issues assigned to the authenticated user with the 'clancy' label.
|
|
103
|
+
# GitHub's issues endpoint returns PRs too — filter them out by checking for pull_request key.
|
|
104
|
+
# per_page=3 so we can find one real issue even if the first result(s) are PRs.
|
|
105
|
+
RESPONSE=$(curl -s \
|
|
106
|
+
-H "Authorization: Bearer $GITHUB_TOKEN" \
|
|
107
|
+
-H "X-GitHub-Api-Version: 2022-11-28" \
|
|
108
|
+
"https://api.github.com/repos/$GITHUB_REPO/issues?state=open&assignee=@me&labels=clancy&per_page=3")
|
|
109
|
+
|
|
110
|
+
# Verify response is an array before parsing (guards against error objects on rate limit / transient failure)
|
|
111
|
+
if ! echo "$RESPONSE" | jq -e 'type == "array"' >/dev/null 2>&1; then
|
|
112
|
+
ERR_MSG=$(echo "$RESPONSE" | jq -r '.message // "Unexpected response"' 2>/dev/null || echo "Unexpected response")
|
|
113
|
+
echo "✗ GitHub API error: $ERR_MSG. Check GITHUB_TOKEN in .clancy/.env."
|
|
114
|
+
exit 0
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
# Filter out PRs and take first real issue
|
|
118
|
+
ISSUE=$(echo "$RESPONSE" | jq 'map(select(has("pull_request") | not)) | .[0]')
|
|
119
|
+
|
|
120
|
+
if [ "$(echo "$ISSUE" | jq 'type')" = '"null"' ] || [ -z "$(echo "$ISSUE" | jq -r '.number // empty')" ]; then
|
|
121
|
+
echo "No issues found. All done!"
|
|
122
|
+
exit 0
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
ISSUE_NUMBER=$(echo "$ISSUE" | jq -r '.number')
|
|
126
|
+
TITLE=$(echo "$ISSUE" | jq -r '.title')
|
|
127
|
+
BODY=$(echo "$ISSUE" | jq -r '.body // "No description"')
|
|
128
|
+
MILESTONE=$(echo "$ISSUE" | jq -r '.milestone.title // "none"')
|
|
129
|
+
|
|
130
|
+
BASE_BRANCH="${CLANCY_BASE_BRANCH:-main}"
|
|
131
|
+
TICKET_BRANCH="feature/issue-${ISSUE_NUMBER}"
|
|
132
|
+
|
|
133
|
+
# GitHub has no native epic concept — use milestone as the grouping signal.
|
|
134
|
+
# If the issue has a milestone, branch from milestone/{slug} (creating it from
|
|
135
|
+
# BASE_BRANCH if it doesn't exist yet). Otherwise branch from BASE_BRANCH directly.
|
|
136
|
+
if [ "$MILESTONE" != "none" ]; then
|
|
137
|
+
MILESTONE_SLUG=$(echo "$MILESTONE" | tr '[:upper:]' '[:lower:]' | tr ' ' '-' | tr -cd '[:alnum:]-')
|
|
138
|
+
TARGET_BRANCH="milestone/${MILESTONE_SLUG}"
|
|
139
|
+
git show-ref --verify --quiet "refs/heads/$TARGET_BRANCH" \
|
|
140
|
+
|| git checkout -b "$TARGET_BRANCH" "$BASE_BRANCH"
|
|
141
|
+
else
|
|
142
|
+
TARGET_BRANCH="$BASE_BRANCH"
|
|
143
|
+
fi
|
|
144
|
+
|
|
145
|
+
# ─── IMPLEMENT ─────────────────────────────────────────────────────────────────
|
|
146
|
+
|
|
147
|
+
echo "Picking up: [#${ISSUE_NUMBER}] $TITLE"
|
|
148
|
+
echo "Milestone: $MILESTONE | Target branch: $TARGET_BRANCH"
|
|
149
|
+
|
|
150
|
+
git checkout "$TARGET_BRANCH"
|
|
151
|
+
# -B creates the branch if it doesn't exist, or resets it to HEAD if it does.
|
|
152
|
+
# This handles retries cleanly without failing on an already-existing branch.
|
|
153
|
+
git checkout -B "$TICKET_BRANCH"
|
|
154
|
+
|
|
155
|
+
PROMPT="You are implementing GitHub Issue #${ISSUE_NUMBER}.
|
|
156
|
+
|
|
157
|
+
Title: $TITLE
|
|
158
|
+
Milestone: $MILESTONE
|
|
159
|
+
|
|
160
|
+
Description:
|
|
161
|
+
$BODY
|
|
162
|
+
|
|
163
|
+
Step 0 — Executability check (do this before any git or file operation):
|
|
164
|
+
Read the issue title and description above. Can this issue be implemented entirely
|
|
165
|
+
as a code change committed to this repo? Consult the 'Executability check' section of
|
|
166
|
+
CLAUDE.md for the full list of skip conditions.
|
|
167
|
+
|
|
168
|
+
If you must SKIP this issue:
|
|
169
|
+
1. Output: ⚠ Skipping [#${ISSUE_NUMBER}]: {one-line reason}
|
|
170
|
+
2. Output: Ticket skipped — update it to be codebase-only work, then re-run.
|
|
171
|
+
3. Append to .clancy/progress.txt: YYYY-MM-DD HH:MM | #${ISSUE_NUMBER} | SKIPPED | {reason}
|
|
172
|
+
4. Stop — no branches, no file changes, no git operations.
|
|
173
|
+
|
|
174
|
+
If the issue IS implementable, continue:
|
|
175
|
+
1. Read ALL docs in .clancy/docs/ — especially GIT.md for branching and commit conventions
|
|
176
|
+
2. Follow the conventions in GIT.md exactly
|
|
177
|
+
3. Implement the issue fully
|
|
178
|
+
4. Commit your work following the conventions in GIT.md
|
|
179
|
+
5. When done, confirm you are finished."
|
|
180
|
+
|
|
181
|
+
CLAUDE_ARGS=(--dangerously-skip-permissions)
|
|
182
|
+
[ -n "${CLANCY_MODEL:-}" ] && CLAUDE_ARGS+=(--model "$CLANCY_MODEL")
|
|
183
|
+
echo "$PROMPT" | claude "${CLAUDE_ARGS[@]}"
|
|
184
|
+
|
|
185
|
+
# ─── MERGE, CLOSE & LOG ────────────────────────────────────────────────────────
|
|
186
|
+
|
|
187
|
+
# Squash all commits from the feature branch into a single commit on the target branch.
|
|
188
|
+
git checkout "$TARGET_BRANCH"
|
|
189
|
+
git merge --squash "$TICKET_BRANCH"
|
|
190
|
+
if git diff --cached --quiet; then
|
|
191
|
+
echo "⚠ No changes staged after squash merge. Claude may not have committed any work."
|
|
192
|
+
else
|
|
193
|
+
git commit -m "feat(#${ISSUE_NUMBER}): $TITLE"
|
|
194
|
+
fi
|
|
195
|
+
|
|
196
|
+
# Delete ticket branch locally
|
|
197
|
+
git branch -d "$TICKET_BRANCH"
|
|
198
|
+
|
|
199
|
+
# Close the issue — warn but don't fail if this doesn't go through
|
|
200
|
+
CLOSE_HTTP=$(curl -s -o /dev/null -w "%{http_code}" -X PATCH \
|
|
201
|
+
-H "Authorization: Bearer $GITHUB_TOKEN" \
|
|
202
|
+
-H "X-GitHub-Api-Version: 2022-11-28" \
|
|
203
|
+
-H "Content-Type: application/json" \
|
|
204
|
+
"https://api.github.com/repos/$GITHUB_REPO/issues/${ISSUE_NUMBER}" \
|
|
205
|
+
-d '{"state": "closed"}')
|
|
206
|
+
[ "$CLOSE_HTTP" = "200" ] || echo "⚠ Could not close issue #${ISSUE_NUMBER} (HTTP $CLOSE_HTTP). Close it manually on GitHub."
|
|
207
|
+
|
|
208
|
+
# Log progress
|
|
209
|
+
echo "$(date '+%Y-%m-%d %H:%M') | #${ISSUE_NUMBER} | $TITLE | DONE" >> .clancy/progress.txt
|
|
210
|
+
|
|
211
|
+
echo "✓ #${ISSUE_NUMBER} complete."
|
|
212
|
+
|
|
213
|
+
# Send completion notification if webhook is configured
|
|
214
|
+
if [ -n "${CLANCY_NOTIFY_WEBHOOK:-}" ]; then
|
|
215
|
+
NOTIFY_MSG="✓ Clancy completed [#${ISSUE_NUMBER}] $TITLE"
|
|
216
|
+
if echo "$CLANCY_NOTIFY_WEBHOOK" | grep -q "hooks.slack.com"; then
|
|
217
|
+
curl -s -X POST "$CLANCY_NOTIFY_WEBHOOK" \
|
|
218
|
+
-H "Content-Type: application/json" \
|
|
219
|
+
-d "$(jq -n --arg text "$NOTIFY_MSG" '{"text": $text}')" >/dev/null 2>&1 || true
|
|
220
|
+
else
|
|
221
|
+
curl -s -X POST "$CLANCY_NOTIFY_WEBHOOK" \
|
|
222
|
+
-H "Content-Type: application/json" \
|
|
223
|
+
-d "$(jq -n --arg text "$NOTIFY_MSG" '{"type":"message","attachments":[{"contentType":"application/vnd.microsoft.card.adaptive","content":{"type":"AdaptiveCard","body":[{"type":"TextBlock","text":$text}]}}]}')" >/dev/null 2>&1 || true
|
|
224
|
+
fi
|
|
225
|
+
fi
|