@evertonferreira96/agents 0.1.1 → 0.1.2
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 +432 -10
- package/VERSION +1 -1
- package/bin/chew.mjs +133 -0
- package/package.json +3 -2
- package/scripts/bootstrap.mjs +1 -1
package/README.md
CHANGED
|
@@ -1,15 +1,437 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
1
3
|
# agents
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
**Agents, rules, hooks and skills for Claude Code, installed into any project with one command.**
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/@evertonferreira96/agents)
|
|
8
|
+
[](package.json)
|
|
9
|
+
[](docs/README.md)
|
|
10
|
+
[](AGENTS.md)
|
|
11
|
+
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Contents
|
|
17
|
+
|
|
18
|
+
- [What it is](#what-it-is)
|
|
19
|
+
- [Quick start](#quick-start)
|
|
20
|
+
- [How it works](#how-it-works)
|
|
21
|
+
- [The big picture](#the-big-picture)
|
|
22
|
+
- [What `setup:pull` does, step by step](#what-setuppull-does-step-by-step)
|
|
23
|
+
- [How each file is decided](#how-each-file-is-decided)
|
|
24
|
+
- [What lands in your project](#what-lands-in-your-project)
|
|
25
|
+
- [Working in a configured project](#working-in-a-configured-project)
|
|
26
|
+
- [Command reference](#command-reference)
|
|
27
|
+
- [Release pipeline](#release-pipeline)
|
|
28
|
+
- [Maintaining this repo](#maintaining-this-repo)
|
|
29
|
+
- [Troubleshooting](#troubleshooting)
|
|
30
|
+
- [Further reading](#further-reading)
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## What it is
|
|
35
|
+
|
|
36
|
+
A complete working setup for Claude Code, kept in one repo and shipped as the npm package `@evertonferreira96/agents`. Install it once per machine, run one command in a project, and the project gets the same team of agents, the same guardrails and the same architecture playbooks as every other project.
|
|
37
|
+
|
|
38
|
+
| Piece | What you get |
|
|
39
|
+
|---|---|
|
|
40
|
+
| **Agents** (10) | An orchestrator flow plus specialists: `quick`, `product`, `architect`, `api`, `web`, `app`, `infra`, `ui`, `verifier`, `security` |
|
|
41
|
+
| **Skills** (37) | Workflow (orchestrate, triage, brief, review, grilling, handoff, retro), architecture (`arch-api`, `arch-web`, `arch-app`, `arch-db`), quality (tdd, diagnosing-bugs, ponytail), design and motion |
|
|
42
|
+
| **Rules** (9) | Loaded always or by path glob: git, tickets, api, web, mobile, db, ui, graphify, delivery diagram |
|
|
43
|
+
| **Hooks** (8) | Deterministic guards that block what rules can only ask for: `git push`, `git add -A`, staging `.env`, the orchestrator editing code, lint and tests on Stop |
|
|
44
|
+
| **AGENTS.md** | The shared base that Cursor and Codex read too |
|
|
45
|
+
| **Updates** | A manifest of every installed file, so updates refresh what you did not touch and never overwrite what you changed |
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Quick start
|
|
50
|
+
|
|
51
|
+
**Requirements:** Node.js 18 or newer, npm, and Claude Code.
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# 1. once per machine
|
|
55
|
+
npm install -g @evertonferreira96/agents
|
|
56
|
+
|
|
57
|
+
# 2. in the root of any project, new or already configured
|
|
58
|
+
cd path/to/your-project
|
|
59
|
+
chew agents setup:pull
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
3. Fill in the project's purpose and commands in the generated `CLAUDE.md`.
|
|
63
|
+
4. Open Claude Code in the project and describe the task.
|
|
64
|
+
|
|
65
|
+
To update later, run `chew agents setup:pull` again. Every run downloads the latest published version, so the global install does not need to be current.
|
|
66
|
+
|
|
67
|
+
<details>
|
|
68
|
+
<summary>Without a global install</summary>
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npx @evertonferreira96/agents init # first time
|
|
72
|
+
npx @evertonferreira96/agents@latest update # later
|
|
73
|
+
npx @evertonferreira96/agents check # drift report
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
</details>
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## How it works
|
|
81
|
+
|
|
82
|
+
### The big picture
|
|
83
|
+
|
|
84
|
+
The config lives in this repo. Every push to `main` that changes a shipped file is checked, versioned and published. On your machine, `chew` pulls the newest published version into the project you are in.
|
|
85
|
+
|
|
86
|
+
```mermaid
|
|
87
|
+
flowchart LR
|
|
88
|
+
subgraph REPO["This repo on GitHub"]
|
|
89
|
+
SRC[".claude/ agents, rules, skills, hooks<br/>AGENTS.md, templates, scripts"]
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
subgraph CI["GitHub Actions"]
|
|
93
|
+
CHECK["check.yml<br/>parse, catalog, tests"]
|
|
94
|
+
REL["release.yml<br/>version, publish, tag, release"]
|
|
95
|
+
CHECK --> REL
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
subgraph REG["Registries"]
|
|
99
|
+
NPM[("npmjs.com<br/>public")]
|
|
100
|
+
GHP[("GitHub Packages<br/>private")]
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
subgraph DEV["Your machine"]
|
|
104
|
+
CHEW["chew<br/>global CLI"]
|
|
105
|
+
PROJ["your-project/<br/>.claude/ AGENTS.md CLAUDE.md"]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
SRC -- "git push main" --> CHECK
|
|
109
|
+
REL -- "npm publish" --> NPM
|
|
110
|
+
REL -- "npm publish" --> GHP
|
|
111
|
+
NPM -- "latest tarball" --> CHEW
|
|
112
|
+
CHEW -- "chew agents setup:pull" --> PROJ
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### What `setup:pull` does, step by step
|
|
116
|
+
|
|
117
|
+
```mermaid
|
|
118
|
+
sequenceDiagram
|
|
119
|
+
autonumber
|
|
120
|
+
actor You
|
|
121
|
+
participant Chew as chew (global)
|
|
122
|
+
participant Npm as npmjs.com
|
|
123
|
+
participant Tmp as Temp folder
|
|
124
|
+
participant Engine as Install engine (downloaded)
|
|
125
|
+
participant Proj as Your project
|
|
126
|
+
|
|
127
|
+
You->>Chew: chew agents setup:pull
|
|
128
|
+
alt registry reachable
|
|
129
|
+
Chew->>Npm: npm pack @evertonferreira96/agents@latest
|
|
130
|
+
Npm-->>Chew: agents-X.Y.Z.tgz
|
|
131
|
+
Chew->>Tmp: extract the package
|
|
132
|
+
Chew->>Engine: load scripts/bootstrap.mjs from version X.Y.Z
|
|
133
|
+
else offline or blocked
|
|
134
|
+
Chew-->>You: warning, using the version installed with chew
|
|
135
|
+
end
|
|
136
|
+
Engine->>Proj: read .claude/config-manifest.json (if any)
|
|
137
|
+
Engine->>Proj: add, update or remove files, one decision per file
|
|
138
|
+
Engine->>Proj: write manifest, config-source.json, CLAUDE.md (once), .gitignore lines
|
|
139
|
+
Chew->>Tmp: delete the temp folder
|
|
140
|
+
Chew-->>You: report: added, updated, removed, kept
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
1. **Download.** `chew` asks npm for the latest version, always against the public registry (also for the `@evertonferreira96` scope), so a user `.npmrc` that points to another feed does not interfere.
|
|
144
|
+
2. **Extract.** The tarball is unpacked into a temporary folder.
|
|
145
|
+
3. **Use the new engine.** The install runs with the `bootstrap.mjs` of the downloaded version, not the one installed with `chew`. An old global `chew` still applies the newest config and only prints a hint to update itself.
|
|
146
|
+
4. **Decide file by file.** Each shipped file is compared with the project copy and with the manifest of the last install (next section).
|
|
147
|
+
5. **Record.** `.claude/config-manifest.json` stores the hash of every installed file; `.claude/config-source.json` stores the package and version.
|
|
148
|
+
6. **First run only.** `CLAUDE.md` is created from `templates/CLAUDE.project.md`, and the `.gitignore` entries are appended.
|
|
149
|
+
7. **Clean up and report.** The temp folder is deleted and the result is printed.
|
|
150
|
+
|
|
151
|
+
### How each file is decided
|
|
152
|
+
|
|
153
|
+
The manifest is what makes updates safe. It records the hash each file had when the config installed it, so the engine can tell a file that is only **outdated** (safe to refresh) from one you **edited** (kept).
|
|
154
|
+
|
|
155
|
+
```mermaid
|
|
156
|
+
flowchart TD
|
|
157
|
+
A["File shipped by the latest version"] --> B{"Exists in<br/>the project?"}
|
|
158
|
+
B -- no --> ADD["added"]
|
|
159
|
+
B -- yes --> C{"Same content?"}
|
|
160
|
+
C -- yes --> SAME["unchanged"]
|
|
161
|
+
C -- no --> D{"Project copy matches<br/>the manifest hash?"}
|
|
162
|
+
D -- "yes: untouched,<br/>only outdated" --> UPD["updated"]
|
|
163
|
+
D -- "no: edited here" --> F{"run with<br/>force flag?"}
|
|
164
|
+
F -- yes --> UPD
|
|
165
|
+
F -- no --> KEPT["kept<br/>listed as edited"]
|
|
166
|
+
|
|
167
|
+
O["File in the manifest that the<br/>latest version no longer ships"] --> G{"Untouched?"}
|
|
168
|
+
G -- yes --> DEL["removed"]
|
|
169
|
+
G -- no --> OBS["kept<br/>listed as obsolete"]
|
|
170
|
+
|
|
171
|
+
classDef add fill:#dcfce7,stroke:#16a34a,color:#14532d
|
|
172
|
+
classDef upd fill:#dbeafe,stroke:#2563eb,color:#1e3a8a
|
|
173
|
+
classDef keep fill:#fef3c7,stroke:#d97706,color:#78350f
|
|
174
|
+
classDef del fill:#fee2e2,stroke:#dc2626,color:#7f1d1d
|
|
175
|
+
classDef same fill:#f3f4f6,stroke:#9ca3af,color:#374151
|
|
176
|
+
class ADD add
|
|
177
|
+
class UPD upd
|
|
178
|
+
class KEPT,OBS keep
|
|
179
|
+
class DEL del
|
|
180
|
+
class SAME same
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
**Never touched, not even with `--force`:** `CLAUDE.md`, `.claude/settings.local.json` and `.claude/agent-memory/**`.
|
|
184
|
+
|
|
185
|
+
| Situation | Result |
|
|
186
|
+
|---|---|
|
|
187
|
+
| New project, nothing installed | Everything added, `CLAUDE.md` created, `.gitignore` extended |
|
|
188
|
+
| Configured project, nothing edited | New files added, changed files updated, removed files deleted |
|
|
189
|
+
| You edited a shipped file | Kept and listed; `--force` replaces it with the config version |
|
|
190
|
+
| Project configured before the manifest existed | Differing files count as edited; review them, then run `--force` once |
|
|
191
|
+
|
|
192
|
+
### What lands in your project
|
|
193
|
+
|
|
194
|
+
```text
|
|
195
|
+
your-project/
|
|
196
|
+
├── AGENTS.md shared base (Claude Code, Cursor, Codex)
|
|
197
|
+
├── CLAUDE.md created once from the template, then yours
|
|
198
|
+
├── .worktreeinclude files copied into each task worktree
|
|
199
|
+
├── .gitignore + worktrees, handoffs, local settings, graphify-out
|
|
200
|
+
├── skills-lock.json origin and hash of every vendored skill
|
|
201
|
+
├── docs/
|
|
202
|
+
│ ├── plans/README.md plan template, one plan per task
|
|
203
|
+
│ ├── adr/README.md ADR template
|
|
204
|
+
│ └── brief-template.md
|
|
205
|
+
├── scripts/ bootstrap, check-catalog, skills-update
|
|
206
|
+
└── .claude/
|
|
207
|
+
├── agents/ 10 agents
|
|
208
|
+
├── skills/ 37 skills
|
|
209
|
+
├── rules/ 9 rules
|
|
210
|
+
├── hooks/ 8 hooks
|
|
211
|
+
├── settings.json hook wiring, worktree base, context limits
|
|
212
|
+
├── config-manifest.json hash of every installed file (commit it)
|
|
213
|
+
└── config-source.json installed package and version
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Working in a configured project
|
|
219
|
+
|
|
220
|
+
Open Claude Code and describe the task, paste the ticket or paste the error. The main session acts as the orchestrator: it sizes the work, plans it with you and delegates it. It never edits code itself (a hook enforces this).
|
|
221
|
+
|
|
222
|
+
```mermaid
|
|
223
|
+
flowchart TD
|
|
224
|
+
REQ["Task, ticket or error"] --> RAW{"Raw report?"}
|
|
225
|
+
RAW -- yes --> TRI["triage<br/>classify, reproduce, brief"]
|
|
226
|
+
RAW -- no --> CLS{"Size"}
|
|
227
|
+
TRI --> CLS
|
|
228
|
+
|
|
229
|
+
CLS -- "S: one file,<br/>one-sentence diff" --> QK["quick agent"]
|
|
230
|
+
QK --> STOP["Stop hook<br/>lint and tests"]
|
|
231
|
+
|
|
232
|
+
CLS -- "M or L" --> WT["Task worktree<br/>.claude/worktrees/slug"]
|
|
233
|
+
WT --> GRL["Grill with you<br/>L always, M on open decisions"]
|
|
234
|
+
GRL --> PRD["product agent<br/>scope, AC, test seams, tickets"]
|
|
235
|
+
PRD -- "L or architecture trigger" --> ARC["architect agent"]
|
|
236
|
+
PRD -- M --> GATE
|
|
237
|
+
ARC --> GATE{"You approve<br/>the tickets"}
|
|
238
|
+
GATE --> IMP["One implementer per ticket<br/>api, web, app, infra, ui<br/>test-first with tdd"]
|
|
239
|
+
IMP --> INT["Integrate"]
|
|
240
|
+
INT --> VER["verifier agent<br/>Spec axis, then Standards axis"]
|
|
241
|
+
VER -- "auth, secrets, PII, payments" --> SEC["security agent"]
|
|
242
|
+
VER --> CMT["Commit and report"]
|
|
243
|
+
SEC --> CMT
|
|
244
|
+
CMT -- L --> RET["retro<br/>lessons become hooks or rules"]
|
|
245
|
+
|
|
246
|
+
classDef you fill:#fef3c7,stroke:#d97706,color:#78350f
|
|
247
|
+
classDef agent fill:#dbeafe,stroke:#2563eb,color:#1e3a8a
|
|
248
|
+
classDef guard fill:#dcfce7,stroke:#16a34a,color:#14532d
|
|
249
|
+
class GRL,GATE you
|
|
250
|
+
class QK,PRD,ARC,IMP,VER,SEC agent
|
|
251
|
+
class STOP guard
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
<sub>Amber: steps with you. Blue: agents. Green: hooks.</sub>
|
|
255
|
+
|
|
256
|
+
| Agent | Does | Mode |
|
|
257
|
+
|---|---|---|
|
|
258
|
+
| `quick` | S work: one file, one-sentence diff | writes |
|
|
259
|
+
| `product` | Plan: scope, stories, acceptance criteria, test seams, tickets | read-only |
|
|
260
|
+
| `architect` | Boundaries, contracts, failure modes; verdict and ADR proposal | read-only |
|
|
261
|
+
| `api` | Server side: APIs, domain, persistence, workers (`arch-api`, `arch-db`, `tdd`) | writes |
|
|
262
|
+
| `web` | Next.js app (`arch-web`, `tdd`) | writes |
|
|
263
|
+
| `app` | React Native / Expo app (`arch-app`, `tdd`) | writes |
|
|
264
|
+
| `infra` | Terraform, CI, deploy, observability; never applies | writes |
|
|
265
|
+
| `ui` | Design system, tokens, components, motion, accessibility | writes |
|
|
266
|
+
| `verifier` | Fresh-context check of the diff against the plan; runs lint, build, tests | no edits |
|
|
267
|
+
| `security` | Auth, secrets, PII, payments, public routes; severity report | read-only |
|
|
268
|
+
|
|
269
|
+
**Guardrails enforced by hooks**
|
|
270
|
+
|
|
271
|
+
| Hook | Effect |
|
|
272
|
+
|---|---|
|
|
273
|
+
| `guard-bash` | Blocks `git push`, `git add -A` / `.`, staging `.env*`, `--no-verify`, `reset --hard`, `clean -f`, PR and issue writes |
|
|
274
|
+
| `orchestrator-guard` | The main session can edit plans, ADRs and docs, never code |
|
|
275
|
+
| `readonly-guard` | Read-only agents can only write their own memory |
|
|
276
|
+
| `stop-verify` | Lint and tests on Stop when the tree changed |
|
|
277
|
+
| `context-budget` | Warns at 60k, 90k and 110k tokens; compaction at 120k |
|
|
278
|
+
| `post-compact` | Re-injects the active plan after compaction |
|
|
279
|
+
| `graphify-update` | Refreshes the code graph after edits, when one exists |
|
|
280
|
+
| `config-update-check` | One line at session start when a local-clone install has a newer version |
|
|
281
|
+
|
|
282
|
+
The full flow, with every section of the playbook, is in [`.claude/skills/orchestrate/SKILL.md`](.claude/skills/orchestrate/SKILL.md). A five-minute walkthrough is in [docs/quickstart.md](docs/quickstart.md).
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## Command reference
|
|
287
|
+
|
|
288
|
+
### `chew` (global)
|
|
289
|
+
|
|
290
|
+
| Command | What it does |
|
|
291
|
+
|---|---|
|
|
292
|
+
| `chew agents setup:pull [dir]` | Download the latest version and install or refresh it in `dir` (default: current folder) |
|
|
293
|
+
| `chew agents setup:check [dir]` | Compare the project with the latest version; exit code 1 on drift |
|
|
294
|
+
| `chew --version` | Version of the installed `chew` |
|
|
295
|
+
|
|
296
|
+
| Flag | Effect |
|
|
297
|
+
|---|---|
|
|
298
|
+
| `--force` | Also overwrite files edited in the project (protected files excepted) |
|
|
299
|
+
| `--dry-run` | Show what would change, write nothing |
|
|
300
|
+
| `--local` | Use the version installed with `chew` instead of downloading |
|
|
301
|
+
|
|
302
|
+
### `agents` (same package, via `npx` or global)
|
|
303
|
+
|
|
304
|
+
| Command | What it does |
|
|
305
|
+
|---|---|
|
|
306
|
+
| `agents init [dir]` | Copy what is missing; never overwrites |
|
|
307
|
+
| `agents update [dir]` | Same rules as `setup:pull`, using this installed version |
|
|
308
|
+
| `agents check [dir]` | Report missing, outdated, edited and obsolete files; exit 1 on drift |
|
|
309
|
+
|
|
310
|
+
### Legacy, from a local clone
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
node <agents-repo>/scripts/bootstrap.mjs <project> # init
|
|
314
|
+
node <agents-repo>/scripts/bootstrap.mjs <project> --check # drift report
|
|
315
|
+
node scripts/bootstrap.mjs --sync # inside the project: pull the clone, then update
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Release pipeline
|
|
321
|
+
|
|
322
|
+
Every push to `main` that changes a shipped file (`.claude/**`, `AGENTS.md`, `bin/`, `scripts/`, `templates/`, `package.json`, `VERSION`, the workflow itself) publishes a new version. Pushes that change only files the package does not ship (this README, the CHANGELOG, most of `docs/`) do not.
|
|
323
|
+
|
|
324
|
+
```mermaid
|
|
325
|
+
flowchart LR
|
|
326
|
+
PUSH["push to main"] --> CHK["check.yml<br/>hooks and scripts parse<br/>JSON, catalog, em-dash scan<br/>npm test, npm pack"]
|
|
327
|
+
CHK --> VER["release-version.mjs<br/>picks X.Y.Z"]
|
|
328
|
+
VER --> PUB["npm publish<br/>npmjs.com"]
|
|
329
|
+
PUB --> TAG["git tag vX.Y.Z"]
|
|
330
|
+
TAG --> GHR["GitHub release<br/>notes from commits"]
|
|
331
|
+
GHR --> GPK["GitHub Packages<br/>same version"]
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
**Versioning**
|
|
335
|
+
|
|
336
|
+
| You want | Do |
|
|
337
|
+
|---|---|
|
|
338
|
+
| A patch | Nothing. The pipeline publishes the next free patch after the highest published version |
|
|
339
|
+
| A minor or major | Set the new base in `package.json` and `VERSION` (they must match) and push |
|
|
340
|
+
|
|
341
|
+
The bumped version exists only in the published package and the tag; the repo keeps the base.
|
|
342
|
+
|
|
343
|
+
**Credentials**
|
|
344
|
+
|
|
345
|
+
| Target | Auth |
|
|
346
|
+
|---|---|
|
|
347
|
+
| npmjs.com | npm trusted publishing (OIDC, no secret), or the `NPM_TOKEN` repository secret until it is configured |
|
|
348
|
+
| GitHub Packages | The workflow's own `GITHUB_TOKEN`; nothing to set up |
|
|
349
|
+
|
|
350
|
+
`.npmrc` pins the public registry and reads the token only from the `NPM_TOKEN` environment variable. Never write a token into it: the file is committed.
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
## Maintaining this repo
|
|
355
|
+
|
|
356
|
+
```bash
|
|
357
|
+
npm test # CLI round trip: init, check, update, force, obsolete files, chew from a tarball
|
|
358
|
+
npm run check # skills on disk vs catalog vs lock vs agents vs hooks
|
|
359
|
+
node scripts/skills-update.mjs # vendored skills that changed upstream
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
- Every change that ships gets an entry in [CHANGELOG.md](CHANGELOG.md) in the same commit.
|
|
363
|
+
- Decisions about the config itself go to [docs/adr/](docs/adr/).
|
|
364
|
+
- Adding a skill: [docs/skills.md](docs/skills.md), section "Adding a skill"; `npm run check` lists what is missing.
|
|
365
|
+
- CI rejects em dashes in our own prose (`AGENTS.md`, docs, agents, rules, own skills).
|
|
366
|
+
|
|
367
|
+
```text
|
|
368
|
+
agents/
|
|
369
|
+
├── .claude/ the config that ships (agents, hooks, rules, skills, settings.json)
|
|
370
|
+
├── .github/workflows/ check.yml (CI) and release.yml (publish)
|
|
371
|
+
├── bin/ chew.mjs (global CLI), agents.mjs (package CLI)
|
|
372
|
+
├── scripts/ bootstrap.mjs (install engine), release-version.mjs, tests, catalog and skills checks
|
|
373
|
+
├── templates/ CLAUDE.project.md for new projects
|
|
374
|
+
├── docs/ quickstart, catalog, ADRs, plan and brief templates
|
|
375
|
+
├── AGENTS.md shared base
|
|
376
|
+
└── CHANGELOG.md
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
## Troubleshooting
|
|
382
|
+
|
|
383
|
+
<details>
|
|
384
|
+
<summary><b><code>chew: command not found</code> after the global install</b></summary>
|
|
385
|
+
|
|
386
|
+
The npm global bin folder is not on your `PATH`. `npm prefix -g` shows the prefix; on Windows the binaries live in `%AppData%\npm`, on macOS and Linux in `<prefix>/bin`. Add it to `PATH` and open a new terminal.
|
|
387
|
+
|
|
388
|
+
</details>
|
|
389
|
+
|
|
390
|
+
<details>
|
|
391
|
+
<summary><b>The global install fails with E401 or asks for another registry</b></summary>
|
|
392
|
+
|
|
393
|
+
Your user `.npmrc` points to a private feed (for example Azure Artifacts). Install from the public registry:
|
|
394
|
+
|
|
395
|
+
```bash
|
|
396
|
+
npm install -g @evertonferreira96/agents --registry https://registry.npmjs.org/
|
|
397
|
+
# or, once:
|
|
398
|
+
npm config set @evertonferreira96:registry https://registry.npmjs.org/
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
`chew` itself already pins the public registry when it downloads updates.
|
|
402
|
+
|
|
403
|
+
</details>
|
|
404
|
+
|
|
405
|
+
<details>
|
|
406
|
+
<summary><b>A file I changed was not updated</b></summary>
|
|
407
|
+
|
|
408
|
+
By design: files edited in the project are kept and listed as `edited`. `chew agents setup:check` shows them. To adopt the config version, run `chew agents setup:pull --force` (try `--dry-run` first).
|
|
409
|
+
|
|
410
|
+
</details>
|
|
411
|
+
|
|
412
|
+
<details>
|
|
413
|
+
<summary><b>"no manifest from a previous install"</b></summary>
|
|
414
|
+
|
|
415
|
+
The project was configured by copy before the manifest existed, so every differing file looks edited. Review the list, then run `chew agents setup:pull --force` once. From then on, updates are automatic.
|
|
416
|
+
|
|
417
|
+
</details>
|
|
418
|
+
|
|
419
|
+
<details>
|
|
420
|
+
<summary><b>No internet, or npm is blocked</b></summary>
|
|
421
|
+
|
|
422
|
+
`setup:pull` warns and installs the version that came with `chew`. Use `--local` to do that on purpose.
|
|
423
|
+
|
|
424
|
+
</details>
|
|
4
425
|
|
|
5
|
-
|
|
6
|
-
- Roster: `.claude/agents/` (quick, product, architect, api, web, app, infra, ui, verifier, security)
|
|
7
|
-
- Deterministic guards: `.claude/hooks/` wired in `.claude/settings.json`
|
|
8
|
-
- `skills-lock.json` records origin, hash and adaptation of every vendored skill; `node scripts/skills-update.mjs` checks them against upstream; catalog in [docs/skills.md](docs/skills.md)
|
|
9
|
-
- Decisions about this config: [docs/adr/](docs/adr/); history: [CHANGELOG.md](CHANGELOG.md)
|
|
10
|
-
- Put it in a project: `npx @evertonferreira96/agents init` in the project root (or `npx github:EvertonFerreira96/agents init` straight from the repo); keep it current with `npx @evertonferreira96/agents@latest update` and look for drift with `npx @evertonferreira96/agents check`. A local clone still works: `node scripts/bootstrap.mjs <project-root>`. First task: [docs/quickstart.md](docs/quickstart.md)
|
|
11
|
-
- Release: every push to `main` that changes what the package ships runs `.github/workflows/release.yml`: the full check, then `npm publish`, the `vX.Y.Z` tag and the GitHub release. Patches are automatic; for a minor or major, bump `version` in `package.json` and `VERSION` together. Add the CHANGELOG entry in the same commit
|
|
426
|
+
---
|
|
12
427
|
|
|
13
|
-
##
|
|
428
|
+
## Further reading
|
|
14
429
|
|
|
15
|
-
|
|
430
|
+
| Document | Covers |
|
|
431
|
+
|---|---|
|
|
432
|
+
| [docs/quickstart.md](docs/quickstart.md) | First task in five minutes |
|
|
433
|
+
| [docs/README.md](docs/README.md) | Every file in the config, bootstrap details, common questions |
|
|
434
|
+
| [docs/skills.md](docs/skills.md) | Skill catalog: invocation and who uses each one |
|
|
435
|
+
| [docs/adr/](docs/adr/) | Why the config is the way it is |
|
|
436
|
+
| [AGENTS.md](AGENTS.md) | The shared base every agent reads |
|
|
437
|
+
| [CHANGELOG.md](CHANGELOG.md) | What changed, by version |
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.1.
|
|
1
|
+
0.1.2
|
package/bin/chew.mjs
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// chew: the global CLI. Install once with `npm install -g @evertonferreira96/agents`, then, in any project:
|
|
3
|
+
// chew agents setup:pull [dir] download the latest published config and install it (fresh project: everything;
|
|
4
|
+
// configured project: refresh what was not edited there, add new files, drop obsolete ones)
|
|
5
|
+
// chew agents setup:check [dir] compare the project with the latest published config; exit 1 on drift
|
|
6
|
+
// Flags: --force (also overwrite files edited in the project), --dry-run (show, write nothing),
|
|
7
|
+
// --local (use the version installed with chew instead of downloading the latest).
|
|
8
|
+
// The install runs with the engine of the downloaded version, so an old global chew still applies the newest setup.
|
|
9
|
+
// Test hook: CHEW_TARBALL=<path to a .tgz from `npm pack`> replaces the registry download.
|
|
10
|
+
import { spawnSync } from "node:child_process";
|
|
11
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
12
|
+
import { tmpdir } from "node:os";
|
|
13
|
+
import { dirname, join, resolve, sep } from "node:path";
|
|
14
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
15
|
+
import { gunzipSync } from "node:zlib";
|
|
16
|
+
|
|
17
|
+
const ROOT = fileURLToPath(new URL("..", import.meta.url));
|
|
18
|
+
const SELF = JSON.parse(readFileSync(join(ROOT, "package.json"), "utf8"));
|
|
19
|
+
const REGISTRY = SELF.publishConfig?.registry || "https://registry.npmjs.org/";
|
|
20
|
+
const SCOPE = SELF.name.startsWith("@") ? SELF.name.split("/")[0] : null;
|
|
21
|
+
|
|
22
|
+
const HELP = `chew ${SELF.version}
|
|
23
|
+
|
|
24
|
+
Usage: chew agents <command> [dir] [--force] [--dry-run] [--local]
|
|
25
|
+
|
|
26
|
+
Commands:
|
|
27
|
+
setup:pull download the latest ${SELF.name} and install it into the project
|
|
28
|
+
(fresh project: everything; configured project: refresh what was not edited there)
|
|
29
|
+
setup:check compare the project with the latest published config (exit 1 on drift)
|
|
30
|
+
|
|
31
|
+
Flags:
|
|
32
|
+
--force also overwrite files edited in the project
|
|
33
|
+
(CLAUDE.md, .claude/settings.local.json and .claude/agent-memory/** are never touched)
|
|
34
|
+
--dry-run show what would change without writing
|
|
35
|
+
--local use the version installed with chew (${SELF.version}) instead of downloading
|
|
36
|
+
|
|
37
|
+
dir defaults to the current directory.`;
|
|
38
|
+
|
|
39
|
+
// npm pinned to the public registry, also for the package scope, so a user npmrc pointing elsewhere (Azure feed) does not matter.
|
|
40
|
+
function npm(args, cwd) {
|
|
41
|
+
const full = [...args, "--registry", REGISTRY, ...(SCOPE ? [`--${SCOPE}:registry=${REGISTRY}`] : [])];
|
|
42
|
+
const win = process.platform === "win32";
|
|
43
|
+
const r = spawnSync(win ? "npm.cmd" : "npm", full, { cwd, encoding: "utf8", shell: win });
|
|
44
|
+
if (r.status !== 0) throw new Error((r.stderr || r.stdout || r.error?.message || "npm failed").trim().split("\n").slice(-3).join(" | "));
|
|
45
|
+
return r.stdout;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Minimal reader for an npm tarball (ustar, GNU long names, pax paths); strips the leading "package/" and keeps file modes.
|
|
49
|
+
function untar(gz, dest) {
|
|
50
|
+
const buf = gunzipSync(gz);
|
|
51
|
+
const root = resolve(dest);
|
|
52
|
+
let off = 0, longName = null;
|
|
53
|
+
while (off + 512 <= buf.length) {
|
|
54
|
+
const h = buf.subarray(off, off + 512);
|
|
55
|
+
if (h.every((b) => b === 0)) break;
|
|
56
|
+
const field = (start, len) => h.subarray(start, start + len).toString("utf8").replace(/\0[\s\S]*$/, "");
|
|
57
|
+
let name = field(0, 100);
|
|
58
|
+
const prefix = field(345, 155);
|
|
59
|
+
if (prefix) name = `${prefix}/${name}`;
|
|
60
|
+
const size = parseInt(field(124, 12).trim() || "0", 8);
|
|
61
|
+
const mode = parseInt(field(100, 8).trim() || "644", 8) & 0o777;
|
|
62
|
+
const type = String.fromCharCode(h[156] || 48);
|
|
63
|
+
off += 512;
|
|
64
|
+
const data = buf.subarray(off, off + size);
|
|
65
|
+
off += Math.ceil(size / 512) * 512;
|
|
66
|
+
if (type === "L") { longName = data.toString("utf8").replace(/\0[\s\S]*$/, ""); continue; }
|
|
67
|
+
if (type === "x") { const m = /\d+ path=([^\n]+)\n/.exec(data.toString("utf8")); if (m) longName = m[1]; continue; }
|
|
68
|
+
if (longName) { name = longName; longName = null; }
|
|
69
|
+
if (type !== "0" && type !== "7") continue; // directories are created on demand; npm tarballs carry no links
|
|
70
|
+
const out = resolve(root, name.replace(/^package\//, ""));
|
|
71
|
+
if (!out.startsWith(root + sep)) throw new Error(`unsafe path in tarball: ${name}`);
|
|
72
|
+
mkdirSync(dirname(out), { recursive: true });
|
|
73
|
+
writeFileSync(out, data, { mode: mode || 0o644 });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Directory with the config to install: the latest published version, or the one installed with chew.
|
|
78
|
+
function fetchConfig(local) {
|
|
79
|
+
const installed = { dir: ROOT, version: SELF.version, cleanup() {} };
|
|
80
|
+
if (local) return installed;
|
|
81
|
+
const tmp = mkdtempSync(join(tmpdir(), "chew-"));
|
|
82
|
+
const cleanup = () => rmSync(tmp, { recursive: true, force: true });
|
|
83
|
+
try {
|
|
84
|
+
let tarball = process.env.CHEW_TARBALL;
|
|
85
|
+
if (!tarball) {
|
|
86
|
+
const packed = JSON.parse(npm(["pack", `${SELF.name}@latest`, "--pack-destination", ".", "--json"], tmp));
|
|
87
|
+
tarball = join(tmp, packed[0].filename);
|
|
88
|
+
}
|
|
89
|
+
const dir = join(tmp, "package");
|
|
90
|
+
untar(readFileSync(tarball), dir);
|
|
91
|
+
const version = JSON.parse(readFileSync(join(dir, "package.json"), "utf8")).version;
|
|
92
|
+
return { dir, version, cleanup };
|
|
93
|
+
} catch (e) {
|
|
94
|
+
cleanup();
|
|
95
|
+
console.warn(`could not download the latest ${SELF.name} (${e.message}); using the installed ${SELF.version}`);
|
|
96
|
+
return installed;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const newer = (a, b) => { const x = a.split(".").map(Number), y = b.split(".").map(Number); for (let i = 0; i < 3; i++) if (x[i] !== y[i]) return x[i] > y[i]; return false; };
|
|
101
|
+
|
|
102
|
+
async function main(argv) {
|
|
103
|
+
const flags = new Set(argv.filter((a) => a.startsWith("-")));
|
|
104
|
+
const [ns, command, dir] = argv.filter((a) => !a.startsWith("-"));
|
|
105
|
+
if (flags.has("--version") || flags.has("-v")) { console.log(SELF.version); return 0; }
|
|
106
|
+
if (!ns || ns === "help" || flags.has("--help") || flags.has("-h")) { console.log(HELP); return ns ? 0 : 2; }
|
|
107
|
+
if (ns !== "agents" || !["setup:pull", "setup:check"].includes(command)) {
|
|
108
|
+
console.error(`unknown command: chew ${[ns, command].filter(Boolean).join(" ")}\n\n${HELP}`);
|
|
109
|
+
return 2;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const target = resolve(dir ?? process.cwd());
|
|
113
|
+
if (!existsSync(target)) throw new Error(`target is not a directory: ${target}`);
|
|
114
|
+
try { if (JSON.parse(readFileSync(join(target, "package.json"), "utf8")).name === SELF.name) throw new Error("this is the config repo itself; run chew inside a project"); }
|
|
115
|
+
catch (e) { if (e.message.startsWith("this is the config repo")) throw e; }
|
|
116
|
+
|
|
117
|
+
const config = fetchConfig(flags.has("--local"));
|
|
118
|
+
try {
|
|
119
|
+
const engine = await import(pathToFileURL(join(config.dir, "scripts", "bootstrap.mjs")).href);
|
|
120
|
+
const mode = command === "setup:check" ? "check" : "update";
|
|
121
|
+
const r = engine.install({ src: config.dir, target, mode, force: flags.has("--force"), dryRun: flags.has("--dry-run") });
|
|
122
|
+
engine.report(r);
|
|
123
|
+
if (mode === "update" && !flags.has("--dry-run") && r.written.some((w) => w.startsWith("CLAUDE.md"))) {
|
|
124
|
+
console.log("\nnext: fill in purpose and commands in CLAUDE.md; optional: `npx impeccable install` (for /ui) and `/graphify .`");
|
|
125
|
+
}
|
|
126
|
+
if (newer(config.version, SELF.version)) console.log(`\nchew itself is ${SELF.version} (latest ${config.version}); update it with: npm install -g ${SELF.name}@latest`);
|
|
127
|
+
return mode === "check" && engine.drift(r) ? 1 : 0;
|
|
128
|
+
} finally {
|
|
129
|
+
config.cleanup();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
main(process.argv.slice(2)).then((code) => { process.exitCode = code; }, (e) => { console.error(e.message); process.exitCode = 2; });
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evertonferreira96/agents",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Personal agents, rules, hooks and skills for Claude Code (portable to Cursor and Codex through AGENTS.md), installable into any project.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"agents": "bin/agents.mjs"
|
|
7
|
+
"agents": "bin/agents.mjs",
|
|
8
|
+
"chew": "bin/chew.mjs"
|
|
8
9
|
},
|
|
9
10
|
"files": [
|
|
10
11
|
"bin/",
|
package/scripts/bootstrap.mjs
CHANGED
|
@@ -168,7 +168,7 @@ function legacy(argv) {
|
|
|
168
168
|
const info = readJson(join(process.cwd(), SOURCE_FILE));
|
|
169
169
|
if (!info) { console.error(`--sync needs ${SOURCE_FILE} (written by a previous install)`); return 2; }
|
|
170
170
|
if (!info.source || !existsSync(info.source)) {
|
|
171
|
-
console.log(`This project was installed from the ${info.package || "config"} package. Update it with: npx ${info.package || "<package>"}@latest update`);
|
|
171
|
+
console.log(`This project was installed from the ${info.package || "config"} package. Update it with: chew agents setup:pull (global CLI) or npx ${info.package || "<package>"}@latest update`);
|
|
172
172
|
return 0;
|
|
173
173
|
}
|
|
174
174
|
src = info.source; target = process.cwd();
|