cherry-works 0.4.0 → 0.6.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 CHANGED
@@ -114,20 +114,26 @@ Each kind comes up at a different time:
114
114
  |---|---|
115
115
  | `guide` | a touched file matches one of its `globs`, or on every turn when it has no globs |
116
116
  | `sensor` | the `signal` it names is raised, and the harness runs its `run` command |
117
- | `skill` | one of its `triggers` matches the request, or it is called by name as `/skill-<id>` |
118
- | `playbook` | one of its `triggers` matches; its body is a sequence of skills |
119
- | `agent` | it is spawned by identity, with only the `tools` it lists |
117
+ | `skill` | one of its `triggers` matches the request, or it is called by name as `/<id>` |
118
+ | `playbook` | one of its `triggers` matches, or it is called as `/<id>`; its body is a sequence of skills |
119
+ | `agent` | it is spawned by its id, with only the `tools` it lists |
120
120
  | `posture` | always, wherever the host can be told what to `allow` and `deny` |
121
121
  | `corpus` | a primitive's `rationale` cites it, to give the reasoning behind a rule |
122
122
  | `mixin` | never on its own; its body is lent to the primitives that pull it in |
123
- | `mcp` | a primitive's body names it as `mcp:<id>`: a place outside the repository, reached through `cw mcp serve` |
123
+ | `mcp` | a primitive's body names it as `[[<id>]]`: an mcp origin outside the repository, reached through `cw mcp serve` |
124
124
 
125
- `cw kinds <kind>` gives the exact fields each kind takes. An identity is
126
- `kind:id`, and it names exactly one primitive across the whole charter. If two
127
- files claim the same identity, the build is refused and both files are named.
125
+ A skill or a playbook that is only a step of another can say
126
+ `disable-user-invocation: true`: it is then not offered as a `/` command, and
127
+ the agent still opens it when its triggers match or a playbook sends it there.
128
+
129
+ `cw kinds <kind>` gives the exact fields each kind takes. An id names exactly
130
+ one primitive across the whole charter, whatever its kind. If two files claim
131
+ the same id, the build is refused and both files are named. A primitive is
132
+ named by its id alone: bare in a header (`rationale: type-safety`), a test or a
133
+ command, and as `[[<id>]]` in a body, which the build turns into a link.
128
134
  Every primitive is a folder holding its `index.md`, and any other file in that
129
135
  folder is one of its assets, reached from the body as `./<file>`. An id is at
130
- most 40 characters, and may be grouped with `/`: `mcp:mfbs/billing` is kept at
136
+ most 40 characters, and may be grouped with `/`: the mcp `mfbs/billing` is kept at
131
137
  `.cw/charter/mcp/mfbs/billing/index.md`.
132
138
 
133
139
  `cw build` compiles every primitive to `.cw/out/<kind>/<id>/index.md`, with its
@@ -145,16 +151,19 @@ agent reads it.
145
151
  | `cw list [--kind] [--min]` | List what the charter holds |
146
152
  | `cw kinds [kind]` | List the kinds, or the fields one of them requires |
147
153
  | `cw add <kind> <id> [--header k=v …]` | Write a new primitive |
148
- | `cw edit <identity>` | Open a primitive's file in your editor |
149
- | `cw remove <identity> [--yes]` | Delete a primitive this repository authored |
150
- | `cw explain <identity>` | Show which file declares an identity, what it pulls in and which tests name it |
154
+ | `cw edit <id>` | Open a primitive's file in your editor |
155
+ | `cw remove <id> [--yes]` | Delete a primitive this repository authored |
156
+ | `cw explain <id>` | Show which file declares an id, what it pulls in and which tests name it |
151
157
  | `cw doctor` | Check the agents, the charter, the vendor sources and the compiled output |
158
+ | `cw context [--exact]` | Say how many tokens the charter puts into the agent's context when a session opens |
159
+ | `cw sessions [--since] [--until] [--json]` | Sum up the tokens of the sessions kept on this machine, by day and by session |
152
160
  | `cw test` | Run every case in `.cw/test/` against the charter |
161
+ | `cw eval` | Put every case in `.cw/eval/` to the real agent under promptfoo |
153
162
  | `cw suite add \| edit \| remove` | Manage test files |
154
163
  | `cw vendor add <source> [--ref] \| remove \| list` | Install, update, remove or list vendor sources |
155
164
  | `cw portal [--port]` | Open the charter in a browser on this machine |
156
- | `cw mcp auth [identity] [--status]` | Sign in, as yourself, to every place the charter reaches, or to one again |
157
- | `cw mcp serve` | Serve every place the charter reaches to your agent, over stdio |
165
+ | `cw mcp auth [id] [--status]` | Sign in, as yourself, to every mcp origin the charter reaches, or to one again |
166
+ | `cw mcp serve` | Serve every mcp origin the charter reaches to your agent, over stdio |
158
167
 
159
168
  `cw doctor` exits with a failure status when the charter has an error, when a
160
169
  vendored file was edited in this repository, or when the compiled output no
@@ -214,15 +223,95 @@ one:
214
223
  {
215
224
  "description": "The TypeScript guide comes up on source files.",
216
225
  "cases": [
217
- { "do": { "touchFile": "src/one.ts" }, "expect": { "activate": "guide:no-any" } },
218
- { "when": "Stop", "expect": { "run": "sensor:lint" } },
226
+ { "do": { "touchFile": "src/one.ts" }, "expect": { "activate": "no-any" } },
227
+ { "when": "Stop", "expect": { "run": "lint" } },
219
228
  { "do": { "touchFile": ".env" }, "expect": { "allow": false } }
220
229
  ]
221
230
  }
222
231
  ```
223
232
 
224
233
  `cw test` resolves each case against the charter and reports the ones that no
225
- longer hold.
234
+ longer hold. It runs no agent and costs nothing, so it fits in CI.
235
+
236
+ ## Evaluating against the real agent
237
+
238
+ `cw test` proves what the charter says; `cw eval` proves what the agent does
239
+ with it. A file in `.cw/eval/` holds prompts and what is expected of each:
240
+
241
+ ```json
242
+ {
243
+ "description": "Planning goes through the planning skill.",
244
+ "cases": [
245
+ { "prompt": "plan this feature: export a report as PDF", "expect": { "invoke": "sdd-plan", "maxTokens": 200000 } },
246
+ { "prompt": "add a function that totals an invoice", "expect": { "follow": "no-single-word-naming" } }
247
+ ]
248
+ }
249
+ ```
250
+
251
+ Each case expects at least one of:
252
+
253
+ | Expectation | Passes when |
254
+ |---|---|
255
+ | `invoke: <id>` | the agent invoked that skill or playbook |
256
+ | `follow: <id>` | a model judge finds the agent followed that guide |
257
+ | `maxTokens: <n>` | the case used no more than that many tokens |
258
+
259
+ ```bash
260
+ cw eval
261
+ ```
262
+
263
+ Every case is handed to [promptfoo](https://www.promptfoo.dev), which puts its
264
+ prompt to Claude Code run headless, in a worktree of your last commit with the
265
+ charter built, so your working tree is never touched. You never write a
266
+ promptfoo configuration. The first run installs promptfoo and the Claude Agent
267
+ SDK under `~/.cherry-works/promptfoo/`, and later runs reuse them.
268
+
269
+ The report gives each case's outcome and tokens, and the run's total. A case
270
+ that misses what it expects fails the run, with what the agent did instead.
271
+ It runs on whatever Claude Code is signed in with, or on `ANTHROPIC_API_KEY`
272
+ when set, and spends real tokens: commit before you run it, and keep the cases
273
+ few.
274
+
275
+ ## What the charter costs
276
+
277
+ Everything the charter loads when a session opens — always-on guides, every
278
+ skill's description, every subagent's — is paid for on every turn.
279
+
280
+ ```bash
281
+ cw context # each primitive's tokens, the largest first, estimated
282
+ cw context --exact # counted through the agent's own command line
283
+ ```
284
+
285
+ A guide with `globs` is listed apart: it is loaded only when a file it names is
286
+ touched. `cw build` prints the total in one line, the portal shows the same
287
+ list, and `cw doctor` warns once the total passes `mainContextCeiling` in
288
+ `.cw/settings.json` (20,000 tokens unless you set it).
289
+
290
+ Once a session runs, the engine's own Stop hook counts what it has used, its
291
+ subagents included, each time the agent stops. It keeps one line per stop
292
+ under `~/.cherry-works/` (or the folder `sessionAnalysisFolder` names), never in
293
+ the repository, and tells you in one line each time a session crosses another
294
+ `sessionContextMark` tokens (100,000 unless you set it). The model is never
295
+ sent that line.
296
+
297
+ ```bash
298
+ cw sessions # the last 31 days, by day and by session
299
+ cw sessions --since 2026-10-01 # 31 days from that day at most
300
+ cw sessions --since 2026-10-01 --until 2026-10-05 --json
301
+ ```
302
+
303
+ To put a price on it, ask your agent what last week cost: the built-in
304
+ `cw-session-cost` skill reads the same summary, asks whether to use prices you
305
+ type or the provider's published ones, and answers per session and per day,
306
+ naming the prices it used.
307
+
308
+ ```json
309
+ {
310
+ "agents": ["claude"],
311
+ "mainContextCeiling": 20000,
312
+ "sessionContextMark": 100000
313
+ }
314
+ ```
226
315
 
227
316
  ## Vendor sources
228
317
 
@@ -235,7 +324,7 @@ there, test it, and push. Only its `.cw/charter/` folder is installed, under
235
324
  `.cw/vendor/<name>/`, pinned to the ref you chose and committed. A repository
236
325
  without that folder is refused, and so is one that has vendors of its own:
237
326
  vendoring is one level deep. Vendored primitives are read-only. To differ from
238
- one, write your own primitive under a new identity.
327
+ one, write your own primitive under a new id.
239
328
 
240
329
  Run `cw vendor add` again with a new `--ref` to update a source, and
241
330
  `cw vendor remove <name>` to take it away. Run `cw build` after either.
@@ -244,12 +333,13 @@ Run `cw vendor add` again with a new `--ref` to update a source, and
244
333
 
245
334
  | Charter | What it gives your agent | Install |
246
335
  | --- | --- | --- |
336
+ | [sdd-charter](https://github.com/tamnguyenvt/sdd-charter) | Spec-driven development: a spec set with a status per story and a roadmap of their dependencies, and six commands (`/sdd-init`, `/sdd-plan`, `/sdd-implement`, `/sdd-bug-fix`, `/sdd-status`, `/sdd-report`) that take each story from request to merge, stopping for your yes at each step. | `cw vendor add git@github.com:tamnguyenvt/sdd-charter.git --ref v0.1.7` |
247
337
  | [hexagonal-architecture-charter](https://github.com/tamnguyenvt/hexagonal-architecture-charter) | Sets up a hexagonal (ports and adapters) architecture in a folder you choose, then keeps the agent following it. | `cw vendor add git@github.com:tamnguyenvt/hexagonal-architecture-charter.git --ref v0.1.0` |
248
338
 
249
- ## Places outside the repository
339
+ ## MCP origins outside the repository
250
340
 
251
341
  Much of what an agent needs to know is kept elsewhere: another service's code,
252
- an issue tracker, a wiki. An `mcp` primitive declares one such place, an MCP
342
+ an issue tracker, a wiki. An `mcp` primitive declares one such mcp origin, an MCP
253
343
  server, and the tools of it your agent may use:
254
344
 
255
345
  ```markdown
@@ -266,31 +356,38 @@ tools: ["get_file_contents", "search_code", "list_pull_requests"]
266
356
  The billing service. Look here before changing anything that charges a customer.
267
357
  ```
268
358
 
269
- A place is either an `endpoint` reached over HTTP, or a `command` with its
359
+ An mcp origin is either an `endpoint` reached over HTTP, or a `command` with its
270
360
  `args` that `cw` starts as a local process; a local one takes a token only, in
271
361
  the environment variable its `tokenEnv` names. Any primitive points the agent
272
- at a place by naming it in its body as `mcp:mfbs/billing`.
362
+ at an mcp origin by naming it in its body as `[[mfbs/billing]]`.
273
363
 
274
364
  ```bash
275
365
  cw build # adds one entry, cw mcp serve, to your agent's .mcp.json
276
- cw mcp auth # sign in to every place you are not signed in to yet
366
+ cw mcp auth # sign in to every mcp origin you are not signed in to yet
277
367
  cw mcp auth --status
278
368
  ```
279
369
 
280
- Every developer signs in as themselves, by browser where the place offers OAuth
370
+ Every developer signs in as themselves, by browser where the mcp origin offers OAuth
281
371
  or with a token typed at a hidden prompt. Credentials are kept in the operating
282
372
  system's credential store, never in the repository, and renewed without asking
283
- where the place allows it. `cw mcp serve` is the one MCP server your agent is
284
- given: it serves the tools each place declares and forwards every call under
285
- your own credential. A place that is down or not signed in to loses only its own
373
+ where the mcp origin allows it. `cw mcp serve` is the one MCP server your agent is
374
+ given: it serves the tools each mcp origin declares and forwards every call under
375
+ your own credential. An mcp origin that is down or not signed in to loses only its own
286
376
  tools.
287
377
 
288
- A subagent reaches only the places it lists under `tools`: `mcp:<id>` for every
289
- tool of a place, or `mcp:<id>:<tool>` for one of them.
378
+ The build asks each mcp origin for its tools once and keeps them beside the
379
+ charter, and turns your agent's own tool search on: a session opens with each
380
+ tool's name and no schema, and the agent looks up the one it needs. `cw mcp
381
+ serve` lists the kept tools without reaching any mcp origin, and reaches one
382
+ only for a call. A tool an `mcp` declares that its mcp origin does not list
383
+ stops the build.
384
+
385
+ A subagent reaches only the mcp origins it lists under `tools`: `[[<id>]]` for every
386
+ tool of an mcp origin, or `[[<id>]]:<tool>` for one of them.
290
387
 
291
388
  ## Your agent can write rules too
292
389
 
293
- Every governed repository gets a built-in skill, `skill:cw-author`, in the
390
+ Every governed repository gets a built-in skill, `cw-author`, in the
294
391
  agent's hands from the moment `cw init` finishes. It teaches
295
392
  the agent to ask `cw kinds` what a kind requires and to write primitives with
296
393
  `cw add`, instead of copying fields from memory.
package/dist/cw.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // package.json
4
4
  var package_default = {
5
5
  name: "cherry-works",
6
- version: "0.4.0",
6
+ version: "0.6.0",
7
7
  description: "The charter engine: resolves a charter and compiles it into what a coding agent reads.",
8
8
  keywords: [
9
9
  "claude-code",
@@ -42,11 +42,9 @@ var package_default = {
42
42
  scripts: {
43
43
  build: "tsup && tailwindcss -i src/driver/portal/page/styles.css -o dist/portal/styles.css --minify",
44
44
  typecheck: "tsc --noEmit && tsc --noEmit -p src/driver/portal/page",
45
- test: 'pnpm run lint:deps && pnpm run build && node --import tsx --test "test/**/*.test.ts"',
46
- cw: "node --import tsx main.ts",
45
+ test: 'pnpm run lint:deps && pnpm run build && node --import tsx --import ./scripts/moduleLoader.mjs --test "test/**/*.test.ts"',
46
+ cw: "node --import tsx --import ./scripts/moduleLoader.mjs main.ts",
47
47
  "lint:deps": "depcruise src main.ts",
48
- "specs:link": "node --import tsx scripts/link-specs.ts",
49
- "specs:check": "node --import tsx scripts/link-specs.ts --check",
50
48
  release: "node --import tsx scripts/release.ts",
51
49
  prepublishOnly: `node -e "console.error('Publish with \\"pnpm release <version>\\", which checks the package first.'); process.exit(1)"`
52
50
  },
@@ -56,6 +54,9 @@ var package_default = {
56
54
  "@codemirror/language": "^6.12.4",
57
55
  "@codemirror/state": "^6.7.4",
58
56
  "@codemirror/view": "^6.43.11",
57
+ "@hono/node-server": "^2.1.1",
58
+ "@hono/zod-openapi": "^1.6.3",
59
+ "@modelcontextprotocol/sdk": "^1.31.0",
59
60
  "@tailwindcss/cli": "^4.3.3",
60
61
  "@tanstack/react-query": "^5.103.2",
61
62
  "@types/node": "^22.10.2",
@@ -68,9 +69,14 @@ var package_default = {
68
69
  clsx: "^2.1.1",
69
70
  cmdk: "^1.1.1",
70
71
  "dependency-cruiser": "^18.2.0",
72
+ hono: "^4.13.7",
73
+ "js-tiktoken": "^1.0.21",
71
74
  "lucide-react": "^1.47.0",
72
75
  marked: "^18.0.13",
76
+ picomatch: "^4.0.7",
73
77
  playwright: "^1.63.0",
78
+ promptfoo: "0.123.1",
79
+ prompts: "^2.4.2",
74
80
  "radix-ui": "^1.6.7",
75
81
  react: "^19.3.0",
76
82
  "react-dom": "^19.3.0",
@@ -80,23 +86,15 @@ var package_default = {
80
86
  tsup: "^8.3.5",
81
87
  tsx: "^4.19.2",
82
88
  "tw-animate-css": "^1.4.0",
83
- typescript: "^5.7.2"
89
+ typescript: "^5.7.2",
90
+ yaml: "^2.9.0",
91
+ yargs: "^18.1.0",
92
+ zod: "^4.6.1"
84
93
  },
85
94
  pnpm: {
86
95
  onlyBuiltDependencies: [
87
96
  "esbuild"
88
97
  ]
89
- },
90
- dependencies: {
91
- "@hono/node-server": "^2.1.1",
92
- "@hono/zod-openapi": "^1.6.3",
93
- "@modelcontextprotocol/sdk": "^1.31.0",
94
- hono: "^4.13.7",
95
- picomatch: "^4.0.7",
96
- prompts: "^2.4.2",
97
- yaml: "^2.9.0",
98
- yargs: "^18.1.0",
99
- zod: "^4.6.1"
100
98
  }
101
99
  };
102
100