cherry-works 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 +253 -0
- package/dist/cw.js +116 -0
- package/dist/main.js +4086 -0
- package/dist/portal/index.html +11 -0
- package/dist/portal/logo.png +0 -0
- package/dist/portal/main.js +227 -0
- package/dist/portal/styles.css +2 -0
- package/package.json +97 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tam Nguyen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# Cherry Works
|
|
2
|
+
|
|
3
|
+
A charter engine for coding agents. You write your team's standards as small
|
|
4
|
+
markdown files, and `cw` checks them, tests them and compiles them into the
|
|
5
|
+
files your coding agent actually reads.
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
.cw/charter/guide/no-any.md ──cw build──▶ CLAUDE.md
|
|
9
|
+
.cw/charter/sensor/lint.md .claude/rules/…
|
|
10
|
+
.cw/charter/posture/secrets.md .claude/settings.json (hooks, permissions)
|
|
11
|
+
… .claude/skills/…, .claude/commands/…
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Why
|
|
15
|
+
|
|
16
|
+
A coding agent follows whatever instructions it can see. Those instructions
|
|
17
|
+
usually end up as one long prompt file that nobody reviews and that falls out of
|
|
18
|
+
date. Cherry Works handles them the way you handle code:
|
|
19
|
+
|
|
20
|
+
- **Small, typed files.** Each rule is a separate primitive of one kind, such as
|
|
21
|
+
a guide, a sensor or a posture. Each kind says which fields it requires and
|
|
22
|
+
when it comes up.
|
|
23
|
+
- **One build.** `cw build` regenerates every file the agent reads from the
|
|
24
|
+
charter in one pass. Nothing is edited by hand, and a rule you delete
|
|
25
|
+
disappears from the output.
|
|
26
|
+
- **Checked and tested.** `cw doctor` names each broken file and says how to fix
|
|
27
|
+
it. `cw test` runs cases that pin down which rule must come up for which file
|
|
28
|
+
or event. It runs no command and needs no agent, so the result is the same on
|
|
29
|
+
every machine.
|
|
30
|
+
- **Shared without copying.** You can install another team's charter as a vendor
|
|
31
|
+
source. It is pinned to a version and committed with your code, and it never
|
|
32
|
+
silently overrides your own rules.
|
|
33
|
+
|
|
34
|
+
## Requirements
|
|
35
|
+
|
|
36
|
+
- Node.js 22 or later
|
|
37
|
+
- git
|
|
38
|
+
- A supported agent. Today that is [Claude Code](https://claude.com/claude-code).
|
|
39
|
+
`cw` always writes a neutral output under `.cw/out/` as well.
|
|
40
|
+
|
|
41
|
+
## Install
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm install -g cherry-works
|
|
45
|
+
cw --help
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
To run it once without installing anything, use `npx cherry-works <command>`.
|
|
49
|
+
To pin one version to a repository, so that its scripts and its CI run that
|
|
50
|
+
version whatever is installed globally, add it as a development dependency:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npm install -D cherry-works
|
|
54
|
+
npx cw --version
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`cw --version` prints the version you are running. Upgrade with
|
|
58
|
+
`npm install -g cherry-works@latest`.
|
|
59
|
+
|
|
60
|
+
## Install from source
|
|
61
|
+
|
|
62
|
+
To run `cw` from a checkout of this repository, you also need pnpm 10 or later.
|
|
63
|
+
Build it and link it globally:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
git clone https://github.com/tamnguyenvt/cherry-works.git
|
|
67
|
+
cd cherry-works
|
|
68
|
+
pnpm install
|
|
69
|
+
pnpm build
|
|
70
|
+
pnpm link --global
|
|
71
|
+
cw --help
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The link points at your checkout. After you change the sources, run
|
|
75
|
+
`pnpm build` again. To remove the link, run `pnpm uninstall --global cherry-works`.
|
|
76
|
+
|
|
77
|
+
## Quick start
|
|
78
|
+
|
|
79
|
+
Inside the git repository you want to govern:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
cw init # asks which agent to compile for, then scaffolds .cw/charter/
|
|
83
|
+
cw kinds guide # what a guide requires
|
|
84
|
+
cw add guide no-any --header description="No any in TypeScript." --header globs="src/**/*.ts"
|
|
85
|
+
cw edit guide:no-any # write the body in $VISUAL or $EDITOR
|
|
86
|
+
cw build # compile the charter for your agent
|
|
87
|
+
cw doctor # check that everything holds
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Commit `.cw/` together with what `cw build` wrote. Your agent now reads the rule
|
|
91
|
+
in every session in this repository.
|
|
92
|
+
|
|
93
|
+
## The charter
|
|
94
|
+
|
|
95
|
+
A primitive is a markdown file with YAML front matter. The folder it sits in
|
|
96
|
+
names its kind:
|
|
97
|
+
|
|
98
|
+
```markdown
|
|
99
|
+
---
|
|
100
|
+
kind: guide
|
|
101
|
+
id: no-single-word-naming
|
|
102
|
+
description: How to name a function or a variable.
|
|
103
|
+
globs: ["src/**/*.ts"]
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
Never name a function or variable with a single vague word such as `it`, `data`
|
|
107
|
+
or `merged`. Name it after what it holds or does.
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Each kind comes up at a different time:
|
|
111
|
+
|
|
112
|
+
| Kind | Comes up when |
|
|
113
|
+
|---|---|
|
|
114
|
+
| `guide` | a touched file matches one of its `globs`, or on every turn when it has no globs |
|
|
115
|
+
| `sensor` | the `signal` it names is raised, and the harness runs its `run` command |
|
|
116
|
+
| `command` | it is asked for by name, or a request matches its description |
|
|
117
|
+
| `skill` | one of its `triggers` matches the request |
|
|
118
|
+
| `playbook` | one of its `triggers` matches; its body is a sequence of commands |
|
|
119
|
+
| `agent` | it is spawned by identity, with only the `tools` it lists |
|
|
120
|
+
| `posture` | always, wherever the host can be told what to `allow` and `deny` |
|
|
121
|
+
| `corpus` | a primitive's `rationale` cites it, to give the reasoning behind a rule |
|
|
122
|
+
| `mixin` | never on its own; its body is lent to the primitives that pull it in |
|
|
123
|
+
|
|
124
|
+
`cw kinds <kind>` gives the exact fields each kind takes. An identity is
|
|
125
|
+
`kind:id`, and it names exactly one primitive across the whole charter. If two
|
|
126
|
+
files claim the same identity, the build is refused and both files are named.
|
|
127
|
+
|
|
128
|
+
## Commands
|
|
129
|
+
|
|
130
|
+
| Command | What it does |
|
|
131
|
+
|---|---|
|
|
132
|
+
| `cw init [--agent]` | Set the repository up under a charter |
|
|
133
|
+
| `cw build [--preview]` | Compile the charter, or show what a build would write |
|
|
134
|
+
| `cw list [--kind] [--min]` | List what the charter holds |
|
|
135
|
+
| `cw kinds [kind]` | List the kinds, or the fields one of them requires |
|
|
136
|
+
| `cw add <kind> <id> [--header k=v …]` | Write a new primitive |
|
|
137
|
+
| `cw edit <identity>` | Open a primitive's file in your editor |
|
|
138
|
+
| `cw remove <identity> [--yes]` | Delete a primitive this repository authored |
|
|
139
|
+
| `cw explain <identity>` | Show which file declares an identity, what it pulls in and which tests name it |
|
|
140
|
+
| `cw doctor` | Check the agents, the charter, the vendor sources and the compiled output |
|
|
141
|
+
| `cw test` | Run every case in `.cw/test/` against the charter |
|
|
142
|
+
| `cw suite add \| edit \| remove` | Manage test files |
|
|
143
|
+
| `cw vendor add <source> [--ref] \| remove \| list` | Install, update, remove or list vendor sources |
|
|
144
|
+
| `cw portal [--port]` | Open the charter in a browser on this machine |
|
|
145
|
+
|
|
146
|
+
`cw doctor` exits with a failure status when the charter has an error, when a
|
|
147
|
+
vendored file was edited in this repository, or when the compiled output no
|
|
148
|
+
longer matches the charter. That makes it a gate you can run in CI.
|
|
149
|
+
|
|
150
|
+
## The portal
|
|
151
|
+
|
|
152
|
+
If you would rather not remember commands and field names, run the portal from
|
|
153
|
+
inside a governed repository:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
cw portal # serves on port 9927, or the next free port
|
|
157
|
+
cw portal --port 8080
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
It prints an address such as `http://127.0.0.1:9927/?t=…`. Open that address in
|
|
161
|
+
your browser. The page has three tabs:
|
|
162
|
+
|
|
163
|
+

|
|
164
|
+
|
|
165
|
+
- **Repo Charter** lists every primitive of this repository by kind, with the
|
|
166
|
+
engine's own line on when that kind comes up. From here you can create a
|
|
167
|
+
primitive, then open it to edit its fields and its markdown body, or delete
|
|
168
|
+
it. The form is built from what the kind requires, so it asks only for the
|
|
169
|
+
fields that kind takes. The **?** on a row explains the primitive: when it
|
|
170
|
+
comes up, what it pulls in, what cites it and which tests name it.
|
|
171
|
+
|
|
172
|
+
| Editing a primitive | Explaining it |
|
|
173
|
+
|---|---|
|
|
174
|
+
|  |  |
|
|
175
|
+
- **Vendor** lists the vendor sources you installed and their primitives, which
|
|
176
|
+
are read-only. You can add a source by its git address and a version, or
|
|
177
|
+
remove one.
|
|
178
|
+
- **Test** lists the test files in `.cw/test/`. You can create, edit and delete
|
|
179
|
+
them, and run every case to see which pass and why a failing one failed.
|
|
180
|
+
|
|
181
|
+

|
|
182
|
+
|
|
183
|
+
The header has a search that finds any primitive in any layer, and two buttons:
|
|
184
|
+
**Build**, which shows what a build would write before it writes anything, and
|
|
185
|
+
**Doctor**, which runs the health check and offers a build when the output is
|
|
186
|
+
behind.
|
|
187
|
+
|
|
188
|
+
The portal drives the same engine as the command line and adds no behaviour of
|
|
189
|
+
its own. What it shows is what the engine answered, and everything it does has
|
|
190
|
+
a command-line equivalent. When the engine refuses a change, the portal shows
|
|
191
|
+
the refusal in the engine's words and writes nothing. It listens only on
|
|
192
|
+
`127.0.0.1`, and every request needs the token in the printed address. Stop it
|
|
193
|
+
with Ctrl+C.
|
|
194
|
+
|
|
195
|
+
## Testing a charter
|
|
196
|
+
|
|
197
|
+
A test file in `.cw/test/` describes situations and what must come up in each
|
|
198
|
+
one:
|
|
199
|
+
|
|
200
|
+
```json
|
|
201
|
+
{
|
|
202
|
+
"description": "The TypeScript guide comes up on source files.",
|
|
203
|
+
"cases": [
|
|
204
|
+
{ "do": { "touchFile": "src/one.ts" }, "expect": { "activate": "guide:no-any" } },
|
|
205
|
+
{ "when": "Stop", "expect": { "run": "sensor:lint" } },
|
|
206
|
+
{ "do": { "touchFile": ".env" }, "expect": { "allow": false } }
|
|
207
|
+
]
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
`cw test` resolves each case against the charter and reports the ones that no
|
|
212
|
+
longer hold.
|
|
213
|
+
|
|
214
|
+
## Vendor sources
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
cw vendor add git@github.com:your-org/charter-baseline.git --ref v1.2.0
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
A vendor source is a git repository with primitives laid out in folders by kind.
|
|
221
|
+
It is installed under `.cw/vendor/<name>/`, pinned to the ref you chose and
|
|
222
|
+
committed. Vendored primitives are read-only. To differ from one, write your own
|
|
223
|
+
primitive under a new identity.
|
|
224
|
+
|
|
225
|
+
## Your agent can write rules too
|
|
226
|
+
|
|
227
|
+
Every governed repository gets a built-in skill, `skill:cw-author`. It teaches
|
|
228
|
+
the agent to ask `cw kinds` what a kind requires and to write primitives with
|
|
229
|
+
`cw add`, instead of copying fields from memory.
|
|
230
|
+
|
|
231
|
+
## Development
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
pnpm install
|
|
235
|
+
pnpm exec playwright install chromium # once per machine, for the portal's browser tests
|
|
236
|
+
pnpm test # dependency rules, build, then every test
|
|
237
|
+
pnpm typecheck
|
|
238
|
+
pnpm cw <command> # run the CLI from source, without building
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
A release is `pnpm release <version>`, run from a clean `develop` level with
|
|
242
|
+
`origin/develop`. It runs the tests, installs the exact package it would publish
|
|
243
|
+
into a fresh repository and builds it there, and only then commits, tags
|
|
244
|
+
`v<version>`, publishes and pushes. `pnpm release <version> --dry-run` runs
|
|
245
|
+
every check and changes nothing. A plain `npm publish` is refused.
|
|
246
|
+
|
|
247
|
+
The engine follows a hexagonal layout. `src/hexagon/` holds the domain, the
|
|
248
|
+
application use cases and their ports. `src/driver/` holds the CLI and the
|
|
249
|
+
portal that drive it. `src/zdriven/` holds the adapters for files, git and YAML.
|
|
250
|
+
The design is written in [specs/spec.md](specs/spec.md) and
|
|
251
|
+
[specs/plan.md](specs/plan.md).
|
|
252
|
+
|
|
253
|
+
This repository is governed by its own charter under [.cw/charter/](.cw/charter/).
|
package/dist/cw.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// package.json
|
|
4
|
+
var package_default = {
|
|
5
|
+
name: "cherry-works",
|
|
6
|
+
version: "0.1.0",
|
|
7
|
+
description: "The charter engine: resolves a charter and compiles it into what a coding agent reads.",
|
|
8
|
+
keywords: [
|
|
9
|
+
"claude-code",
|
|
10
|
+
"coding-agent",
|
|
11
|
+
"charter",
|
|
12
|
+
"ai-agents",
|
|
13
|
+
"cli",
|
|
14
|
+
"guardrails"
|
|
15
|
+
],
|
|
16
|
+
homepage: "https://github.com/tamnguyenvt/cherry-works#readme",
|
|
17
|
+
bugs: {
|
|
18
|
+
url: "https://github.com/tamnguyenvt/cherry-works/issues"
|
|
19
|
+
},
|
|
20
|
+
repository: {
|
|
21
|
+
type: "git",
|
|
22
|
+
url: "git+https://github.com/tamnguyenvt/cherry-works.git"
|
|
23
|
+
},
|
|
24
|
+
license: "MIT",
|
|
25
|
+
author: "Tam Nguyen",
|
|
26
|
+
type: "module",
|
|
27
|
+
engines: {
|
|
28
|
+
node: ">=22",
|
|
29
|
+
pnpm: ">=10"
|
|
30
|
+
},
|
|
31
|
+
packageManager: "pnpm@10.2.1",
|
|
32
|
+
bin: {
|
|
33
|
+
cw: "./dist/cw.js"
|
|
34
|
+
},
|
|
35
|
+
files: [
|
|
36
|
+
"dist"
|
|
37
|
+
],
|
|
38
|
+
imports: {
|
|
39
|
+
"#hexagon/*": "./src/hexagon/*",
|
|
40
|
+
"#portal/*": "./dist/portal/*"
|
|
41
|
+
},
|
|
42
|
+
scripts: {
|
|
43
|
+
build: "tsup && tailwindcss -i src/driver/portal/page/styles.css -o dist/portal/styles.css --minify",
|
|
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",
|
|
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
|
+
release: "node --import tsx scripts/release.ts",
|
|
51
|
+
prepublishOnly: `node -e "console.error('Publish with \\"pnpm release <version>\\", which checks the package first.'); process.exit(1)"`
|
|
52
|
+
},
|
|
53
|
+
devDependencies: {
|
|
54
|
+
"@codemirror/commands": "^6.11.1",
|
|
55
|
+
"@codemirror/lang-markdown": "^6.5.2",
|
|
56
|
+
"@codemirror/language": "^6.12.4",
|
|
57
|
+
"@codemirror/state": "^6.7.4",
|
|
58
|
+
"@codemirror/view": "^6.43.11",
|
|
59
|
+
"@tailwindcss/cli": "^4.3.3",
|
|
60
|
+
"@tanstack/react-query": "^5.103.2",
|
|
61
|
+
"@types/node": "^22.10.2",
|
|
62
|
+
"@types/picomatch": "^4.0.3",
|
|
63
|
+
"@types/prompts": "^2.4.9",
|
|
64
|
+
"@types/react": "^19.3.0",
|
|
65
|
+
"@types/react-dom": "^19.3.0",
|
|
66
|
+
"@types/yargs": "^17.0.35",
|
|
67
|
+
"class-variance-authority": "^0.7.1",
|
|
68
|
+
clsx: "^2.1.1",
|
|
69
|
+
cmdk: "^1.1.1",
|
|
70
|
+
"dependency-cruiser": "^18.2.0",
|
|
71
|
+
"lucide-react": "^1.47.0",
|
|
72
|
+
marked: "^18.0.13",
|
|
73
|
+
playwright: "^1.63.0",
|
|
74
|
+
"radix-ui": "^1.6.7",
|
|
75
|
+
react: "^19.3.0",
|
|
76
|
+
"react-dom": "^19.3.0",
|
|
77
|
+
sonner: "^2.0.8",
|
|
78
|
+
"tailwind-merge": "^3.7.0",
|
|
79
|
+
tailwindcss: "^4.3.3",
|
|
80
|
+
tsup: "^8.3.5",
|
|
81
|
+
tsx: "^4.19.2",
|
|
82
|
+
"tw-animate-css": "^1.4.0",
|
|
83
|
+
typescript: "^5.7.2"
|
|
84
|
+
},
|
|
85
|
+
pnpm: {
|
|
86
|
+
onlyBuiltDependencies: [
|
|
87
|
+
"esbuild"
|
|
88
|
+
]
|
|
89
|
+
},
|
|
90
|
+
dependencies: {
|
|
91
|
+
"@hono/node-server": "^2.1.1",
|
|
92
|
+
"@hono/zod-openapi": "^1.6.3",
|
|
93
|
+
hono: "^4.13.7",
|
|
94
|
+
picomatch: "^4.0.7",
|
|
95
|
+
prompts: "^2.4.2",
|
|
96
|
+
yaml: "^2.9.0",
|
|
97
|
+
yargs: "^18.1.0",
|
|
98
|
+
zod: "^4.6.1"
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// bin.ts
|
|
103
|
+
var _a;
|
|
104
|
+
var neededMajor = Number((_a = /\d+/.exec(package_default.engines.node)) == null ? void 0 : _a[0]);
|
|
105
|
+
var runningMajor = Number(process.versions.node.split(".")[0]);
|
|
106
|
+
if (runningMajor < neededMajor) {
|
|
107
|
+
process.stderr.write(
|
|
108
|
+
`cw needs Node.js ${neededMajor} or later, and this is Node.js ${process.versions.node}.
|
|
109
|
+
Install Node.js ${neededMajor} or later, then run cw again.
|
|
110
|
+
`
|
|
111
|
+
);
|
|
112
|
+
process.exitCode = 1;
|
|
113
|
+
} else {
|
|
114
|
+
const commandEntry = "./main.js";
|
|
115
|
+
void import(commandEntry);
|
|
116
|
+
}
|