chief-clancy 0.5.12 → 0.7.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/README.md +15 -8
- package/dist/bundle/clancy-afk.js +6 -2
- package/dist/bundle/clancy-once.js +71 -48
- package/dist/installer/hook-installer/hook-installer.d.ts +2 -0
- package/dist/installer/hook-installer/hook-installer.d.ts.map +1 -1
- package/dist/installer/hook-installer/hook-installer.js +36 -1
- package/dist/installer/hook-installer/hook-installer.js.map +1 -1
- package/dist/installer/install.js +16 -1
- package/dist/installer/install.js.map +1 -1
- package/dist/schemas/env.d.ts +36 -0
- package/dist/schemas/env.d.ts.map +1 -1
- package/dist/schemas/env.js +11 -0
- package/dist/schemas/env.js.map +1 -1
- package/dist/schemas/github-issues.d.ts +6 -0
- package/dist/schemas/github-issues.d.ts.map +1 -1
- package/dist/schemas/github-issues.js +3 -0
- package/dist/schemas/github-issues.js.map +1 -1
- package/dist/schemas/jira.d.ts +21 -0
- package/dist/schemas/jira.d.ts.map +1 -1
- package/dist/schemas/jira.js +18 -0
- package/dist/schemas/jira.js.map +1 -1
- package/dist/schemas/linear.d.ts +41 -0
- package/dist/schemas/linear.d.ts.map +1 -1
- package/dist/schemas/linear.js +34 -0
- package/dist/schemas/linear.js.map +1 -1
- package/dist/scripts/afk/afk.d.ts.map +1 -1
- package/dist/scripts/afk/afk.js +28 -0
- package/dist/scripts/afk/afk.js.map +1 -1
- package/dist/scripts/afk/report/report.d.ts +47 -0
- package/dist/scripts/afk/report/report.d.ts.map +1 -0
- package/dist/scripts/afk/report/report.js +194 -0
- package/dist/scripts/afk/report/report.js.map +1 -0
- package/dist/scripts/board/github/github.d.ts +33 -3
- package/dist/scripts/board/github/github.d.ts.map +1 -1
- package/dist/scripts/board/github/github.js +112 -27
- package/dist/scripts/board/github/github.js.map +1 -1
- package/dist/scripts/board/jira/jira.d.ts +36 -4
- package/dist/scripts/board/jira/jira.d.ts.map +1 -1
- package/dist/scripts/board/jira/jira.js +138 -65
- package/dist/scripts/board/jira/jira.js.map +1 -1
- package/dist/scripts/board/linear/linear.d.ts +32 -5
- package/dist/scripts/board/linear/linear.d.ts.map +1 -1
- package/dist/scripts/board/linear/linear.js +135 -17
- package/dist/scripts/board/linear/linear.js.map +1 -1
- package/dist/scripts/once/board-ops/board-ops.d.ts.map +1 -1
- package/dist/scripts/once/board-ops/board-ops.js +1 -1
- package/dist/scripts/once/board-ops/board-ops.js.map +1 -1
- package/dist/scripts/once/cost/cost.d.ts +10 -0
- package/dist/scripts/once/cost/cost.d.ts.map +1 -0
- package/dist/scripts/once/cost/cost.js +23 -0
- package/dist/scripts/once/cost/cost.js.map +1 -0
- package/dist/scripts/once/deliver/deliver.d.ts.map +1 -1
- package/dist/scripts/once/deliver/deliver.js +18 -1
- package/dist/scripts/once/deliver/deliver.js.map +1 -1
- package/dist/scripts/once/fetch-ticket/fetch-ticket.d.ts +19 -1
- package/dist/scripts/once/fetch-ticket/fetch-ticket.d.ts.map +1 -1
- package/dist/scripts/once/fetch-ticket/fetch-ticket.js +93 -29
- package/dist/scripts/once/fetch-ticket/fetch-ticket.js.map +1 -1
- package/dist/scripts/once/lock/lock.d.ts +17 -0
- package/dist/scripts/once/lock/lock.d.ts.map +1 -0
- package/dist/scripts/once/lock/lock.js +70 -0
- package/dist/scripts/once/lock/lock.js.map +1 -0
- package/dist/scripts/once/once.d.ts.map +1 -1
- package/dist/scripts/once/once.js +100 -4
- package/dist/scripts/once/once.js.map +1 -1
- package/dist/scripts/once/resume/resume.d.ts +24 -0
- package/dist/scripts/once/resume/resume.d.ts.map +1 -0
- package/dist/scripts/once/resume/resume.js +159 -0
- package/dist/scripts/once/resume/resume.js.map +1 -0
- package/dist/scripts/shared/format/format.d.ts.map +1 -1
- package/dist/scripts/shared/format/format.js +6 -1
- package/dist/scripts/shared/format/format.js.map +1 -1
- package/dist/scripts/shared/progress/progress.d.ts +18 -2
- package/dist/scripts/shared/progress/progress.d.ts.map +1 -1
- package/dist/scripts/shared/progress/progress.js +31 -4
- package/dist/scripts/shared/progress/progress.js.map +1 -1
- package/dist/scripts/shared/pull-request/pr-body/pr-body.d.ts +2 -1
- package/dist/scripts/shared/pull-request/pr-body/pr-body.d.ts.map +1 -1
- package/dist/scripts/shared/pull-request/pr-body/pr-body.js +10 -1
- package/dist/scripts/shared/pull-request/pr-body/pr-body.js.map +1 -1
- package/dist/types/remote.d.ts +1 -1
- package/dist/types/remote.d.ts.map +1 -1
- package/hooks/clancy-branch-guard.js +129 -0
- package/hooks/clancy-check-update.js +43 -0
- package/hooks/clancy-context-monitor.js +134 -46
- package/hooks/clancy-post-compact.js +53 -0
- package/hooks/package.json +3 -0
- package/package.json +3 -2
- package/src/agents/devils-advocate.md +53 -0
- package/src/agents/verification-gate.md +128 -0
- package/src/roles/planner/workflows/approve-plan.md +2 -2
- package/src/roles/reviewer/workflows/logs.md +9 -6
- package/src/roles/setup/commands/help.md +7 -0
- package/src/roles/setup/workflows/init.md +111 -6
- package/src/roles/setup/workflows/scaffold.md +57 -0
- package/src/roles/setup/workflows/settings.md +145 -0
- package/src/roles/setup/workflows/update.md +18 -0
- package/src/roles/strategist/commands/approve-brief.md +20 -0
- package/src/roles/strategist/commands/brief.md +27 -0
- package/src/roles/strategist/workflows/approve-brief.md +763 -0
- package/src/roles/strategist/workflows/brief.md +732 -0
- package/src/templates/CLAUDE.md +8 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Autonomous, board-driven development for Claude Code.**
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/chief-clancy) [](./LICENSE) [](https://www.npmjs.com/package/chief-clancy) [](./LICENSE) [](docs/TESTING.md) [](https://github.com/Pushedskydiver/clancy/stargazers)
|
|
6
6
|
|
|
7
7
|
> [!WARNING]
|
|
8
8
|
> Clancy is in early development. Expect bugs, breaking changes, and rough edges. If you hit an issue, please [open a bug report](https://github.com/Pushedskydiver/clancy/issues/new).
|
|
@@ -25,14 +25,16 @@ Named after Chief Clancy Wiggum (The Simpsons) — built on the [Ralph technique
|
|
|
25
25
|
|
|
26
26
|
## What it does
|
|
27
27
|
|
|
28
|
-
Clancy does
|
|
28
|
+
Clancy does six things:
|
|
29
29
|
|
|
30
30
|
1. **Scaffolds itself** into a project — scripts, docs, CLAUDE.md, .clancy/.env
|
|
31
31
|
2. **Scans your codebase** with 5 parallel specialist agents and writes 10 structured docs that Claude reads before every run
|
|
32
|
-
3. **
|
|
33
|
-
4. **
|
|
32
|
+
3. **Writes strategy briefs** — interviews you (or runs a devil's advocate AI-grill in AFK mode), decomposes work into vertical slices with HITL/AFK classification, and creates board tickets with blocking dependencies
|
|
33
|
+
4. **Plans tickets** — fetches backlog tickets, explores the codebase, and generates structured implementation plans posted as comments for human review
|
|
34
|
+
5. **Runs autonomously** — picking one ticket per loop from your Kanban board (skipping blocked candidates), implementing it, committing, and creating a PR (targeting an epic branch for parented tickets, or the base branch for standalone tickets)
|
|
35
|
+
6. **Verifies before delivery** — runs lint, test, and typecheck via verification gates with self-healing retry, guards against force push and destructive resets, and recovers from crashes via lock file detection
|
|
34
36
|
|
|
35
|
-
One ticket per run. Fresh context window every iteration. No context rot.
|
|
37
|
+
Brief → approve → plan → implement. One ticket per run. Fresh context window every iteration. No context rot.
|
|
36
38
|
|
|
37
39
|
---
|
|
38
40
|
|
|
@@ -149,6 +151,8 @@ npx chief-clancy
|
|
|
149
151
|
|
|
150
152
|
| Command | Description |
|
|
151
153
|
| ---------------------- | ------------------------------------------------------------------------ |
|
|
154
|
+
| `/clancy:brief` ² | Grill phase → strategy brief → vertical slice decomposition → tickets |
|
|
155
|
+
| `/clancy:approve-brief` ² | Review and approve a strategy brief, then create board tickets |
|
|
152
156
|
| `/clancy:plan` ¹ | Refine backlog tickets into structured implementation plans |
|
|
153
157
|
| `/clancy:plan 3` ¹ | Plan up to 3 tickets in batch mode |
|
|
154
158
|
| `/clancy:approve-plan` ¹ | Promote an approved plan to the ticket description |
|
|
@@ -169,6 +173,7 @@ npx chief-clancy
|
|
|
169
173
|
| `/clancy:help` | Command reference |
|
|
170
174
|
|
|
171
175
|
¹ Planner is an optional role — see [Roles](#roles) below.
|
|
176
|
+
² Strategist is an optional role — see [Roles](#roles) below.
|
|
172
177
|
|
|
173
178
|
---
|
|
174
179
|
|
|
@@ -182,12 +187,13 @@ Clancy organises its commands into **roles** — each role handles a different s
|
|
|
182
187
|
| **Reviewer** | Score ticket readiness and track progress | `/clancy:review`, `/clancy:status`, `/clancy:logs` |
|
|
183
188
|
| **Setup** | Configure and maintain Clancy | `/clancy:init`, `/clancy:settings`, `/clancy:doctor`, etc. |
|
|
184
189
|
| **Planner** *(optional)* | Refine backlog tickets into structured implementation plans | `/clancy:plan`, `/clancy:approve-plan` |
|
|
190
|
+
| **Strategist** *(optional)* | Grill → brief → vertical slices → board tickets | `/clancy:brief`, `/clancy:approve-brief` |
|
|
185
191
|
|
|
186
|
-
**Core roles** (Implementer, Reviewer, Setup) are always installed. **Optional roles** (Planner) are opt-in — add them to `CLANCY_ROLES` in `.clancy/.env` and re-run the installer:
|
|
192
|
+
**Core roles** (Implementer, Reviewer, Setup) are always installed. **Optional roles** (Planner, Strategist) are opt-in — add them to `CLANCY_ROLES` in `.clancy/.env` and re-run the installer:
|
|
187
193
|
|
|
188
194
|
```bash
|
|
189
|
-
# Enable
|
|
190
|
-
echo 'CLANCY_ROLES="planner"' >> .clancy/.env
|
|
195
|
+
# Enable optional roles
|
|
196
|
+
echo 'CLANCY_ROLES="planner,strategist"' >> .clancy/.env
|
|
191
197
|
npx chief-clancy@latest --local # or --global
|
|
192
198
|
```
|
|
193
199
|
|
|
@@ -199,6 +205,7 @@ Each role has detailed documentation:
|
|
|
199
205
|
- [Reviewer](docs/roles/REVIEWER.md) — scores ticket readiness, tracks progress
|
|
200
206
|
- [Setup & Maintenance](docs/roles/SETUP.md) — init wizard, settings, diagnostics, codebase mapping
|
|
201
207
|
- [Planner](docs/roles/PLANNER.md) — refines backlog tickets into structured implementation plans
|
|
208
|
+
- [Strategist](docs/roles/STRATEGIST.md) — grill phase, strategy briefs, vertical slice decomposition, board ticket creation
|
|
202
209
|
|
|
203
210
|
For a visual overview of how roles, commands, and flows connect, see the [Visual Architecture](docs/VISUAL-ARCHITECTURE.md) diagrams.
|
|
204
211
|
|
|
@@ -1,2 +1,6 @@
|
|
|
1
|
-
import{spawnSync as
|
|
2
|
-
|
|
1
|
+
import{spawnSync as J}from"node:child_process";import{existsSync as X}from"node:fs";import{dirname as Z,join as q,resolve as z}from"node:path";import{setTimeout as G}from"node:timers/promises";import{fileURLToPath as M}from"node:url";function $(t){let o=Math.floor(t/1e3);if(o<60)return`${o}s`;let r=Math.floor(o/60),i=o%60;if(r<60)return i>0?`${r}m ${i}s`:`${r}m`;let u=Math.floor(r/60),c=r%60;return c>0?`${u}h ${c}m`:`${u}h`}function L(t){return t.includes("hooks.slack.com")}function O(t){return JSON.stringify({text:t})}function _(t){return JSON.stringify({type:"message",attachments:[{contentType:"application/vnd.microsoft.card.adaptive",content:{$schema:"http://adaptivecards.io/schemas/adaptive-card.json",type:"AdaptiveCard",version:"1.4",body:[{type:"TextBlock",text:t,wrap:!0}]}}]})}async function C(t,o){let r=L(t)?O(o):_(o);try{let i=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:r});i.ok||console.warn(`\u26A0 Notification failed: HTTP ${i.status}`)}catch{console.warn("\u26A0 Notification failed: could not reach webhook")}}var p=t=>`\x1B[2m${t}\x1B[0m`,x=t=>`\x1B[1m${t}\x1B[0m`;var D=t=>`\x1B[32m${t}\x1B[0m`,w=t=>`\x1B[31m${t}\x1B[0m`;import{mkdirSync as j,readFileSync as B,writeFileSync as W}from"node:fs";import{join as E}from"node:path";import{appendFileSync as rt,mkdirSync as it,readFileSync as U}from"node:fs";import{dirname as at,join as K}from"node:path";function R(t){let o=K(t,".clancy","progress.txt"),r;try{r=U(o,"utf8")}catch{return[]}let i=[];for(let u of r.split(`
|
|
2
|
+
`)){let c=u.trim();if(!c)continue;let s=c.split(" | ");if(s.length<4)continue;let l=s[0];if(s[1]==="BRIEF"||s[1]==="APPROVE_BRIEF"){i.push({timestamp:l,key:s[2],summary:s.slice(3).join(" | "),status:s[1]});continue}let f=s[1],a,h,y,k=[];for(let g=2;g<s.length;g++){let m=s[g],S=m.match(/^pr:(\d+)$/),n=m.match(/^parent:(.+)$/);S?h=parseInt(S[1],10):n?y=n[1]:g>=3&&!a&&m===m.toUpperCase()&&m.length>1?a=m:k.push(m)}a&&(a==="APPROVE"&&(a="APPROVE_PLAN"),i.push({timestamp:l,key:f,summary:k.join(" | "),status:a,...h!=null&&{prNumber:h},...y!=null&&{parent:y}}))}return i}function H(t,o){let r=E(t,".clancy","costs.log"),i;try{i=B(r,"utf8")}catch{return[]}let u=[];for(let c of i.split(`
|
|
3
|
+
`)){let s=c.trim();if(!s)continue;let l=s.split(" | ");if(l.length<4)continue;let f=l[0],a=new Date(f).getTime();Number.isNaN(a)||a<o||u.push({timestamp:f,key:l[1],duration:l[2],tokens:l[3]})}return u}function Y(t){return/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/.test(t)?new Date(t.replace(" ","T")+":00Z").getTime():NaN}var A=new Set(["DONE","PR_CREATED","PUSHED","EPIC_PR_CREATED","RESUMED"]),V=new Set(["SKIPPED","PUSH_FAILED","TIME_LIMIT"]);function F(t,o,r){let i=Math.floor(o/6e4)*6e4,c=R(t).filter(e=>{let d=Y(e.timestamp);return!Number.isNaN(d)&&d>=i}),s=H(t,o),l=new Map;for(let e of s)l.set(e.key,e);let f=new Map;for(let e of c)f.set(e.key,e);let a=[];for(let e of f.values()){let d=l.get(e.key);a.push({key:e.key,summary:e.summary,status:e.status,...e.prNumber!=null&&{prNumber:e.prNumber},...d&&{duration:d.duration,tokens:d.tokens}})}let h=a.filter(e=>A.has(e.status)),y=a.filter(e=>V.has(e.status)),k=$(r-o),g=0;for(let e of s){let d=e.tokens.match(/~(\d[\d,]*)/);d&&(g+=parseInt(d[1].replace(/,/g,""),10))}let m=new Date(o),S=`${m.getUTCFullYear()}-${String(m.getUTCMonth()+1).padStart(2,"0")}-${String(m.getUTCDate()).padStart(2,"0")}`,n=[];n.push(`# AFK Session Report \u2014 ${S}`),n.push(""),n.push("## Summary"),n.push(`- Tickets completed: ${h.length}`),n.push(`- Tickets failed: ${y.length}`),n.push(`- Total duration: ${k}`),g>0&&n.push(`- Estimated token usage: ${g.toLocaleString("en-US")}`),n.push(""),n.push("## Tickets"),a.length===0&&(n.push(""),n.push("No tickets were processed in this session."));for(let e of a){let I=A.has(e.status)?"\u2713":"\u2717";n.push(""),n.push(`### ${I} ${e.key} \u2014 ${e.summary}`),e.duration&&n.push(`- Duration: ${e.duration}`),e.tokens&&n.push(`- Tokens: ${e.tokens}`),e.prNumber!=null&&n.push(`- PR: #${e.prNumber}`),n.push(`- Status: ${e.status}`)}let T=a.filter(e=>e.prNumber!=null).map(e=>`#${e.prNumber}`),b=y.map(e=>e.key);if(T.length>0||b.length>0){n.push(""),n.push("## Next Steps"),T.length>0&&n.push(`- Review PRs ${T.join(", ")}`);for(let e of b)n.push(`- ${e} needs manual intervention`)}n.push("");let N=n.join(`
|
|
4
|
+
`);try{let e=E(t,".clancy","session-report.md");j(E(t,".clancy"),{recursive:!0}),W(e,N,"utf8")}catch{}return N}var P={noTickets:/No tickets found|No issues found|All done/,skipped:/Ticket skipped/,preflightFail:/^✗ /m};function Q(t){return P.noTickets.test(t)?{stop:!0,reason:"No more tickets \u2014 all done"}:P.skipped.test(t)?{stop:!0,reason:"Ticket was skipped \u2014 update the ticket and re-run"}:P.preflightFail.test(t)?{stop:!0,reason:"Preflight check failed"}:{stop:!1}}async function tt(t,o=5){let r=q(t,"clancy-once.js");if(!X(r)){console.error(w("\u2717 clancy-once.js not found in"),t);return}console.log(p("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510")),console.log(p("\u2502")+x(" \u{1F916} Clancy \u2014 AFK mode ")+p("\u2502")),console.log(p("\u2502")+p(` "I'm on it. Proceed to the abandoned warehouse." `)+p("\u2502")),console.log(p("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"));let i=Date.now();for(let c=1;c<=o;c++){let s=Date.now();console.log(""),console.log(x(`\u{1F501} Iteration ${c}/${o}`));let l=J("node",[r],{encoding:"utf8",stdio:["inherit","pipe","inherit"],cwd:process.cwd(),env:{...process.env,CLANCY_AFK_MODE:"1"}}),f=l.stdout??"";f&&process.stdout.write(f);let a=$(Date.now()-s);if(l.error){console.error(w(`\u2717 Failed to run clancy-once: ${l.error.message}`));return}let h=Q(f);if(h.stop){let y=$(Date.now()-i);console.log(""),console.log(p(` Iteration ${c} took ${a}`)),console.log(`
|
|
5
|
+
${h.reason}`),console.log(p(` Total: ${c} iteration${c>1?"s":""} in ${y}`)),v(i);return}console.log(p(` Iteration ${c} took ${a}`)),c<o&&await G(2e3)}let u=$(Date.now()-i);console.log(""),console.log(D(`\u{1F3C1} Completed ${o} iterations`)+p(` (${u})`)),console.log(p(` "That's some good police work."`)),console.log(p(" Run clancy-afk again to continue.")),v(i)}function v(t){try{let o=F(process.cwd(),t,Date.now());console.log(""),console.log(p("\u2500\u2500\u2500 Session Report \u2500\u2500\u2500")),console.log(o);let r=process.env.CLANCY_NOTIFY_WEBHOOK;if(r){let c=`Clancy AFK: ${o.split(`
|
|
6
|
+
`).filter(s=>s.startsWith("- Tickets")||s.startsWith("- Total")).join(". ")}. Report: .clancy/session-report.md`;C(r,c).catch(()=>{})}}catch{}}if(process.argv[1]&&M(import.meta.url)===z(process.argv[1])){let t=Z(M(import.meta.url)),o=parseInt(process.env.MAX_ITERATIONS??"5",10)||5;tt(t,o)}export{Q as checkStopCondition,tt as runAfkLoop};
|