@warren-dean/code-kit 0.4.1 → 0.5.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +18 -0
- package/README.md +26 -25
- package/bin/code-kit.mjs +69 -14
- package/hooks/guard-bash.mjs +79 -29
- package/hooks/lane-audit.mjs +17 -6
- package/hooks/lib/config.mjs +30 -5
- package/hooks/lib/diff.mjs +5 -1
- package/hooks/lib/next.mjs +13 -2
- package/hooks/lib/plan.mjs +18 -7
- package/hooks/lib/registry.mjs +37 -3
- package/hooks/lib/reviews.mjs +39 -0
- package/hooks/lib/rules.mjs +66 -4
- package/hooks/lib/shell.mjs +94 -0
- package/hooks/lib/trace.mjs +4 -2
- package/hooks/lib/verify.mjs +74 -3
- package/hooks/mod/register.test.ts +5 -3
- package/hooks/mod/view.mjs +13 -4
- package/package.json +1 -1
- package/skills/init/SKILL.md +10 -2
- package/skills/review/SKILL.md +9 -1
- package/templates/agent.md +1 -1
|
@@ -336,7 +336,9 @@ test('ACT-1 and BAND-3 approving with the prefilled reason grants it for the lan
|
|
|
336
336
|
const confirm = await pane($, APPROVE);
|
|
337
337
|
const reason = 'Approve dayjs for the web lane: npm install dayjs';
|
|
338
338
|
expect((await confirm.find({ key: 'approve-reason' }))?.props.value).toBe(reason);
|
|
339
|
-
expect(
|
|
339
|
+
expect(
|
|
340
|
+
await confirm.find({ type: 'Text', text: /until the install is committed \(at most 7 days\)/ }),
|
|
341
|
+
).toBeDefined();
|
|
340
342
|
await press($, 'approve-confirm', APPROVE);
|
|
341
343
|
expect(w.acts).toEqual([
|
|
342
344
|
['approve', 'dep-dayjs', '--lane', 'web', '--reason', reason, '--via', 'pane'],
|
|
@@ -558,7 +560,7 @@ test('CARD-1 a refused write is drawn as a card, its text a press away', async (
|
|
|
558
560
|
test('CARD-1 a refusal an approval would allow has Approve…, which opens the confirmation', async ($, on) => {
|
|
559
561
|
const w = project();
|
|
560
562
|
w.refusal =
|
|
561
|
-
'Blocked: a new dependency (dayjs) needs a person\'s approval. Write a short change request for the lead.\n ! echo "<what you are approving>" > .claude/approvals/web/dep-dayjs\n(a person runs it; it allows installing it for the web lane,
|
|
563
|
+
'Blocked: a new dependency (dayjs) needs a person\'s approval. Write a short change request for the lead.\n ! echo "<what you are approving>" > .claude/approvals/web/dep-dayjs\n(a person runs it; it allows installing it for the web lane, until the install is committed, at most 7 days)\nCommand: npm install dayjs';
|
|
562
564
|
await start($, on, w);
|
|
563
565
|
await $.tool.call({ tool: 'Bash', tool_use_id: 'tu-2', command: 'npm install dayjs' } as any);
|
|
564
566
|
const ui = await toolResult($, 'tu-2', 'Bash');
|
|
@@ -622,7 +624,7 @@ test('CARD-4 outside a code-kit project, /approvals and /verify-branch say so',
|
|
|
622
624
|
});
|
|
623
625
|
|
|
624
626
|
const install =
|
|
625
|
-
'Blocked: a new dependency (dayjs) needs a person\'s approval. Write a short change request for the lead.\n ! echo "<what you are approving>" > .claude/approvals/dep-dayjs\n(a person runs it; it allows installing it for any agent,
|
|
627
|
+
'Blocked: a new dependency (dayjs) needs a person\'s approval. Write a short change request for the lead.\n ! echo "<what you are approving>" > .claude/approvals/dep-dayjs\n(a person runs it; it allows installing it for any agent, until the install is committed, at most 7 days)\nCommand: npm install dayjs';
|
|
626
628
|
|
|
627
629
|
test('CARD-1 a group holding a refusal unfolds, so its row can be the card', async ($, on) => {
|
|
628
630
|
const w = project();
|
package/hooks/mod/view.mjs
CHANGED
|
@@ -34,6 +34,7 @@ export function readyFrom(next) {
|
|
|
34
34
|
* One lane's row (PANE-2): the story it's on, its branch and its state.
|
|
35
35
|
* building a branch for the story and, if it has commits, the lane's agent still at work on it;
|
|
36
36
|
* in review commits on the branch (status's `review`) and the agent no longer at work;
|
|
37
|
+
* sent back a review sent the branch back, and the lane hasn't committed its fix yet;
|
|
37
38
|
* blocked the lane's next story waits on another;
|
|
38
39
|
* idle none of these.
|
|
39
40
|
* `status` can't tell a finished branch from one still being built, so the agent at work decides.
|
|
@@ -41,7 +42,9 @@ export function readyFrom(next) {
|
|
|
41
42
|
function laneRow(name, agent, stories, atWork) {
|
|
42
43
|
const own = stories.filter((s) => s.lane === name);
|
|
43
44
|
const active = Boolean(agent && atWork.has(agent));
|
|
44
|
-
const story = own.find((s) => ['in progress', 'review'].includes(s.state));
|
|
45
|
+
const story = own.find((s) => ['in progress', 'review', 'sent back'].includes(s.state));
|
|
46
|
+
if (story?.state === 'sent back')
|
|
47
|
+
return { name, agent, active, story, branch: story.branch, state: 'sent back' };
|
|
45
48
|
if (story) {
|
|
46
49
|
const state = story.state === 'review' && !active ? 'in review' : 'building';
|
|
47
50
|
return { name, agent, active, story, branch: story.branch, state };
|
|
@@ -77,7 +80,13 @@ export function projectState(check, status, next = null, atWork = new Set()) {
|
|
|
77
80
|
return { kind: 'ok', lanes, ready, planGaps: status ? (status.problems ?? []).length : null };
|
|
78
81
|
}
|
|
79
82
|
|
|
80
|
-
const STATE_COLOR = {
|
|
83
|
+
const STATE_COLOR = {
|
|
84
|
+
building: 'blue',
|
|
85
|
+
'in review': 'yellow',
|
|
86
|
+
'sent back': 'magenta',
|
|
87
|
+
blocked: 'red',
|
|
88
|
+
idle: undefined,
|
|
89
|
+
};
|
|
81
90
|
|
|
82
91
|
/** The Lanes pane's body, drawn with the elements `$.ui.resolve(e)` returned. */
|
|
83
92
|
export function lanesPane(state, { Box, Text }) {
|
|
@@ -263,7 +272,7 @@ export function approvePane(
|
|
|
263
272
|
Text({ bold: true, children: [`Approve ${request.names.join(', ')}`] }),
|
|
264
273
|
Text({
|
|
265
274
|
children: [
|
|
266
|
-
`For ${request.lane ? `the ${request.lane} lane` : 'any agent'}, for 60 minutes. Asked for: ${request.what}`,
|
|
275
|
+
`For ${request.lane ? `the ${request.lane} lane` : 'any agent'}, ${request.names.every((n) => n.startsWith('dep-')) ? 'until the install is committed (at most 7 days)' : 'for 60 minutes'}. Asked for: ${request.what}`,
|
|
267
276
|
],
|
|
268
277
|
}),
|
|
269
278
|
Input({
|
|
@@ -348,7 +357,7 @@ export function refusalCard(raw) {
|
|
|
348
357
|
kind: 'dependency',
|
|
349
358
|
title: `Install refused: ${m[1]}`,
|
|
350
359
|
why: "A new dependency needs a person's approval.",
|
|
351
|
-
todo: 'A person approves it, here or with the `!` command,
|
|
360
|
+
todo: 'A person approves it, here or with the `!` command, until the install is committed (at most 7 days).',
|
|
352
361
|
request: request(command ?? `install ${m[1]}`),
|
|
353
362
|
raw: text,
|
|
354
363
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@warren-dean/code-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Keeps AI coding agents inside your project's rules: lanes, layer rules, protected paths and proof before finish, enforced by Claude Code hooks and in CI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Warren Langeveldt",
|
package/skills/init/SKILL.md
CHANGED
|
@@ -31,7 +31,7 @@ Say which one applies and why before you start.
|
|
|
31
31
|
- **Design-first rules:** screens designed before they are built, and where approvals are listed.
|
|
32
32
|
- **Commands:** test, lint, typecheck, database and deploy commands, and which must never run locally.
|
|
33
33
|
2. **The repository.** Map the real layout: `git ls-files | cut -d/ -f1-3 | sort | uniq -c`, package manifests (`package.json`, `pyproject.toml`, `go.mod`, `Cargo.toml`), workspace files, existing lint, format and test scripts, CI workflows and CODEOWNERS.
|
|
34
|
-
3. **Existing kit.** If the project has `.claude/hooks`, agents or skills, read them. The kit replaces local hooks; agents and skills are kept and adapted, not rewritten.
|
|
34
|
+
3. **Existing kit.** If the project has `.claude/hooks`, agents or skills, read them. The kit replaces local hooks; agents and skills are kept and adapted, not rewritten. On an update (section 4), skills that came from the docs' recipes follow those recipes as they are now.
|
|
35
35
|
4. **Other tools.** Run `adapters --config .claude/code-kit.draft.json` once the draft exists. It lists the tools code-kit has an adapter for (such as Context Graph, found by its `.ctx/` folder), what each adds to the rules, and the setup each needs. Don't copy an adapter's paths into the config; they're added automatically while the tool is present.
|
|
36
36
|
|
|
37
37
|
**Brownfield, in addition:**
|
|
@@ -97,6 +97,8 @@ Draft, alongside the config:
|
|
|
97
97
|
- Drop the spec-check lines if the config has no `docs.specs`.
|
|
98
98
|
- **CLAUDE.md:** add the section from `${CLAUDE_PLUGIN_ROOT}/templates/claude-md.md`.
|
|
99
99
|
- **Project skills** (recipes like "new migration" or "new endpoint"): list the recipes the docs describe step by step, and offer to write them. Don't write them unasked, and never write an empty one.
|
|
100
|
+
- Write each as `.claude/skills/<name>/SKILL.md`, saying in its description which recipe in which doc it comes from, so an update can find it again.
|
|
101
|
+
- List it under **Skills to use** on every lane agent whose paths the recipe touches, not only one: a recipe that adds a migration and the endpoint that uses it belongs to both lanes. A recipe only the lead follows goes in the CLAUDE.md section instead.
|
|
100
102
|
|
|
101
103
|
Show the person:
|
|
102
104
|
|
|
@@ -144,10 +146,16 @@ Re-run whenever the build has moved on: new apps or packages, folders that moved
|
|
|
144
146
|
|
|
145
147
|
4. **Also propose:**
|
|
146
148
|
- agent files for new lanes, and updates to agents whose paths changed;
|
|
149
|
+
- **project skills**, by reading the recipes in the docs again and comparing them with `.claude/skills/`:
|
|
150
|
+
- a new skill for each recipe the docs now describe that has none;
|
|
151
|
+
- an update to each skill whose recipe changed, showing what changed;
|
|
152
|
+
- removing a skill whose recipe is gone from the docs (ask first: the person may want it kept);
|
|
153
|
+
- the matching **Skills to use** changes on every lane agent whose paths each recipe touches;
|
|
154
|
+
- skills the person wrote themselves, with no recipe behind them, stay as they are;
|
|
147
155
|
- a smaller baseline when `baseline` shows violations were fixed. It only ever shrinks.
|
|
148
156
|
5. **On approval:**
|
|
149
157
|
1. Replace `.claude/code-kit.json` with the draft and delete the draft.
|
|
150
|
-
2. Write or update the agents.
|
|
158
|
+
2. Write or update the agents, and write, update or remove the project skills as approved.
|
|
151
159
|
3. Run `baseline --write` if it shrank.
|
|
152
160
|
4. Run `check` and `unowned`.
|
|
153
161
|
5. Commit on a branch.
|
package/skills/review/SKILL.md
CHANGED
|
@@ -50,8 +50,16 @@ Read the story, every requirement it cites, and the full diff (`git diff <base>.
|
|
|
50
50
|
|
|
51
51
|
Report the table, `verify`'s result and one of:
|
|
52
52
|
|
|
53
|
+
- **Files any agent may write** (`anyActor`, such as a lockfile or recorded decisions) have no owning lane, but they have a reviewer: `trace <file>` names it (the lead unless the config names a lane). Review those changes as that reviewer would, or ask that lane's agent.
|
|
53
54
|
- **Merge.** Everything passes. The lead merges the branch into its own branch (`git merge --no-ff <branch>`), sets the story's `**Status:**` to `done` in the plan, and commits. `main` and the other protected branches are merged by a person, through a pull request, after `verify` passes in CI. When the branch is merged, remove its worktree.
|
|
54
|
-
- **Send back.** List each problem, where it is, and what "fixed" looks like.
|
|
55
|
+
- **Send back.** List each problem, where it is, and what "fixed" looks like. Record the send-back, so `status` and `next` read the branch as waiting for its fix rather than finished:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
node "${CLAUDE_PLUGIN_ROOT}/bin/code-kit.mjs" sent-back <branch> --reason "<the problems, in one line>"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Send the list to the same lane agent if it's still running (SendMessage), or dispatch the story again with the list added to the brief. Set the story's status to `in progress`.
|
|
62
|
+
|
|
55
63
|
- **Ask the person.** When the right answer is a product or scope decision (the spec is silent, or the lane found a real conflict), put the question to the person with your recommendation. Don't merge around it.
|
|
56
64
|
|
|
57
65
|
Keep the report short: the verdict first, then the table, then the problems.
|
package/templates/agent.md
CHANGED
|
@@ -9,7 +9,7 @@ You are the {{lane}} engineer for this repository.
|
|
|
9
9
|
**Owned paths:** {{lane paths from .claude/code-kit.json}}
|
|
10
10
|
**Layers:** {{the layers in these paths, and what each may depend on}}
|
|
11
11
|
**Specs:** {{the spec files this lane reads}}
|
|
12
|
-
**Skills to use:** spec-check{{, project
|
|
12
|
+
**Skills to use:** spec-check{{, every project skill whose recipe touches this lane's paths}}
|
|
13
13
|
**Prove your work with:** `{{the command that proves this lane's work}}`
|
|
14
14
|
|
|
15
15
|
Lane rules:
|