@warren-dean/code-kit 0.18.0 → 0.20.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.
@@ -1676,6 +1676,42 @@ export function withUsage(ledger, who, usage) {
1676
1676
  };
1677
1677
  }
1678
1678
 
1679
+ /**
1680
+ * HUBR-2: the ledger as a report carries it: [{ story, kind, input, output, cache }], each entry's kind
1681
+ * (lead, lane, reviewer, security-reviewer or background) from who made it and `typeOf(key)`, the agent's
1682
+ * type. Tokens only: no prompt, no message.
1683
+ */
1684
+ export function usageForReport(ledger, typeOf = () => null) {
1685
+ return Object.fromEntries(
1686
+ Object.entries(ledger).map(([id, e]) => {
1687
+ const type = e.key === 'lead' ? null : typeOf(e.key);
1688
+ const kind =
1689
+ e.key === 'lead'
1690
+ ? 'lead'
1691
+ : e.lane
1692
+ ? 'lane'
1693
+ : type === 'security-reviewer'
1694
+ ? 'security-reviewer'
1695
+ : /(^|:)reviewer$/.test(type ?? '')
1696
+ ? 'reviewer'
1697
+ : 'background';
1698
+ return [
1699
+ id,
1700
+ { story: e.story ?? null, kind, input: e.input, output: e.output, cache: e.cache },
1701
+ ];
1702
+ }),
1703
+ );
1704
+ }
1705
+
1706
+ /**
1707
+ * HUBR-6: an agent's presence, by tool name and the branch its lane works on (never what it reads or
1708
+ * writes): { type, tool, branch, at }.
1709
+ */
1710
+ export function presenceOf({ agentId, type, tool, lane, lanes, at }) {
1711
+ const branch = lane ? (lanes?.find((l) => l.name === lane)?.branch ?? null) : null;
1712
+ return { type: agentId ? (type ?? null) : 'lead', tool: tool ?? null, branch, at };
1713
+ }
1714
+
1679
1715
  const total = (e) => e.input + e.output + e.cache;
1680
1716
  const median = (xs) => {
1681
1717
  const s = [...xs].sort((a, b) => a - b);
@@ -0,0 +1,57 @@
1
+ // SessionStart and SessionEnd (code-kit spec 21, HUBR-6): when the team's provider is a hosted one, its
2
+ // adapter holds the session's live link (the event stream and the team's state on disk) from start to
3
+ // end, and the session reports the project's facts once as it starts (HUBR-3). Nothing happens in a
4
+ // project whose team works through GitHub, or that has no team.
5
+ import { execFileSync, spawn } from 'node:child_process';
6
+ import { readFileSync } from 'node:fs';
7
+ import { dirname, join } from 'node:path';
8
+ import { fileURLToPath } from 'node:url';
9
+ import { providerAdapterOf } from './lib/adapters/index.mjs';
10
+
11
+ let input = {};
12
+ try {
13
+ input = JSON.parse(readFileSync(0, 'utf8') || '{}');
14
+ } catch {
15
+ // no input: nothing to start
16
+ }
17
+ const root = process.env.CLAUDE_PROJECT_DIR ?? input.cwd ?? process.cwd();
18
+ const session = String(input.session_id ?? 'session').replace(/[^\w-]/g, '');
19
+ let raw = null;
20
+ try {
21
+ raw = JSON.parse(readFileSync(join(root, '.claude', 'code-kit.json'), 'utf8'));
22
+ } catch {
23
+ // not a code-kit project
24
+ }
25
+ const hosted = providerAdapterOf(raw?.team?.provider);
26
+ if (hosted && !hosted.problem) {
27
+ const { adapter, url } = hosted;
28
+ if (process.argv[2] === 'start') {
29
+ // The link lives as long as the Claude Code process that started this hook.
30
+ let watch = process.ppid;
31
+ try {
32
+ watch =
33
+ Number(
34
+ execFileSync('ps', ['-o', 'ppid=', '-p', String(process.ppid)], {
35
+ encoding: 'utf8',
36
+ }).trim(),
37
+ ) || process.ppid;
38
+ } catch {
39
+ // the hook's own parent, then
40
+ }
41
+ adapter.session.start(root, session, watch, url);
42
+ // The project's facts as the session starts, in the background: the session doesn't wait on it.
43
+ if (raw.team.report !== false)
44
+ spawn(
45
+ process.execPath,
46
+ [
47
+ join(dirname(fileURLToPath(import.meta.url)), '..', 'bin', 'code-kit.mjs'),
48
+ 'team',
49
+ 'pulse',
50
+ '--session',
51
+ session,
52
+ ],
53
+ { cwd: root, detached: true, stdio: 'ignore' },
54
+ ).unref();
55
+ }
56
+ if (process.argv[2] === 'end') adapter.session.end(root, session);
57
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@warren-dean/code-kit",
3
- "version": "0.18.0",
3
+ "version": "0.20.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",
@@ -48,6 +48,8 @@ node "${CLAUDE_PLUGIN_ROOT}/bin/code-kit.mjs" team claim ST-<n> --lease "<glob>,
48
48
 
49
49
  The lease is the paths the story will create or change: its lane's folders for this feature, and any contract it changes. A claim someone else holds is refused with who holds it; pick another story or ask them. Stories in a lane another person owns are theirs to dispatch. Making the story's branch also claims it, and each commit on it adds what it changed to the lease; merging it ends the claim.
50
50
 
51
+ One agent per story: never send a running lane agent a second story, or a send-back. A story that goes on (sent back, or handed back at its context limit) gets a fresh agent: first run `node "${CLAUDE_PLUGIN_ROOT}/bin/code-kit.mjs" worktrees release <branch>`, which keeps the branch's commits and moves its report to the main checkout, then start the agent as below with the brief and "Read your earlier report first: `.claude/state/reports/<branch with / as _>.md` in the main checkout". A long-lived agent rereads its whole context on every step, which is most of what a build costs.
52
+
51
53
  Start each story with the Agent tool: `subagent_type` is the lane's agent, `isolation: "worktree"`, and run them in the background, with a `description` that starts with the story's id ("ST-12 booking form"): the loop reads it to know the story is in flight, and won't ask for it again while the agent works. Start every story in the batch in the same message so they run in parallel.
52
54
 
53
55
  The brief is the agent's whole context. Write it so the agent needs nothing else:
@@ -38,7 +38,7 @@ Any problem it reports means the branch goes back. Don't fix a lane's code yours
38
38
 
39
39
  ## 3. Check the work against the story
40
40
 
41
- Read the story, every requirement it cites, and the full diff (`git diff <base>...<branch>`). Read each changed file in full where the diff alone doesn't show the behaviour. Then, for each requirement:
41
+ Read the story and every requirement it cites. Then start from what's already been worked out, not the whole diff: the lane's report (`.claude/state/reports/<branch with / as _>.md`, in its worktree or the main checkout), the reviewer's findings when the background reviewer ran, and `git diff --stat <base>...<branch>`. Open the diff one file at a time (`git diff <base>...<branch> -- <file>`) for the files each requirement depends on and the files a finding names, and a changed file in full only where its diff doesn't show the behaviour. Everything you open stays in your context for the rest of the build, so open what the verdict needs. Then, for each requirement:
42
42
 
43
43
  | Requirement | Acceptance criterion | Built (where) | Test (which) | Verdict |
44
44
  | ----------- | -------------------- | ------------- | ------------ | ------- |
@@ -84,7 +84,7 @@ Report the table, `verify`'s result, the security review's findings (or that non
84
84
  node "${CLAUDE_PLUGIN_ROOT}/bin/code-kit.mjs" sent-back <branch> --reason "<the problems, in one line>"
85
85
  ```
86
86
 
87
- 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`.
87
+ Dispatch the story again to a fresh agent with the list added to the brief (`/code-kit:dispatch`, which first releases the branch's worktree), never to the agent that built it: its context is already large. Set the story's status to `in progress`.
88
88
 
89
89
  - **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.
90
90
 
@@ -12,7 +12,7 @@ The project's specs are in the folder named by `docs.specs` in `.claude/code-kit
12
12
  1. **Find the section.** The task should name its requirement IDs (`BOOK-4`, headings in the specs) or a spec section. If it doesn't, search the specs folder for the domain nouns. Read the whole section, not just the matching lines, plus every section it references.
13
13
  2. **Read the principles.** Re-read the parts of `docs.principles` that apply: layers, ports and providers, events, testing and the rules for agents. Note this lane's layer rules from `layers` in the config.
14
14
  3. **Make a checklist.** One line per requirement: behaviour, fields, error cases, permissions, states, tests.
15
- 4. **Compare with the code.** For each line, search for where it's implemented, then read those files in full.
15
+ 4. **Compare with the code.** For each line, search for where it's implemented, then read what implements it: the functions and types the requirement touches, not every file around them.
16
16
  5. **Report** as a table:
17
17
 
18
18
  | Requirement | Status (done / partial / missing / conflicts) | Where | Note |
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: team-init
3
- description: Set up a code-kit project for a team of developers, change its team setup, or make it one developer's again. Asks who the tech lead is, who owns which lanes, when a claim goes stale and where intents go, shows the team section before writing it, checks GitHub is ready (gh signed in, the shared repository, the intents' discussion category), and offers CODEOWNERS, the Project board and a hosted team provider. Use when the person says they work in a team, wants teammates to share the project, wants to change the tech lead or lane owners, wants to stop working as a team, or runs /code-kit:team-init. Not every developer works in a team: never set one up unasked.
3
+ description: Set up a code-kit project for a team of developers, change its team setup, or make it one developer's again. Asks who the tech lead is, who owns which lanes, when a claim goes stale and where intents go, shows the team section before writing it, checks GitHub is ready (gh signed in, the shared repository, the intents' discussion category), offers CODEOWNERS and the Project board, and connects a hosted team provider only when the config already names one. Use when the person says they work in a team, wants teammates to share the project, wants to change the tech lead or lane owners, wants to stop working as a team, or runs /code-kit:team-init. Not every developer works in a team: never set one up unasked.
4
4
  ---
5
5
 
6
6
  # Set up the team
@@ -25,7 +25,7 @@ Ask only what the config can't answer, with the current value as the default on
25
25
  - **Tech lead:** whose GitHub handle owns the plan, specs, contracts and principles, agrees or drops intents, and may release or reassign anyone's claim. Default: `me`.
26
26
  - **Lane owners:** for each lane, whose sessions build its stories (GitHub handles), or nobody, in which case whoever claims a story first builds it. `git log --format='%an' -- <lane paths> | sort | uniq -c` and CODEOWNERS suggest people, but ask for handles: names in commits aren't handles.
27
27
  - **Stale after:** how many working days without a push before a claim is stale (default 3).
28
- - **Intents category:** the GitHub Discussions category intents go in (default `Intents`).
28
+ - **Intents category:** the GitHub Discussions category intents go in (default `Intents`). Skip it when the config's `team.provider` names a hosted provider (`{ "<name>": "<https address>" }`) and `team.intents` isn't set: that provider keeps the intents.
29
29
  - **Back to one developer:** confirm that claims, owners and intents stop applying, and that teammates' sessions lose them too once it's merged.
30
30
 
31
31
  ## 3. Show, then write
@@ -48,7 +48,7 @@ Each is optional; ask, and do only what they want:
48
48
 
49
49
  - **CODEOWNERS** (TEAM-11): `team codeowners --write` writes `.github/CODEOWNERS` from the config, so GitHub asks the tech lead and lane owners to review. Then they require code owners' review in a ruleset on the protected branch.
50
50
  - **A Project board** (TEAM-12): with a GitHub Project's owner and number, add `"board": { "owner": "<user or organisation>", "number": <n> }` to `team` in the config; the mod then mirrors the plan onto it.
51
- - **A hosted team provider:** if a plugin for one is installed (its connect skill is in your skills list, such as `/code-kit-teams:connect`), offer it: it makes claims, leases and intents live, shows who's at work where, and checks each push for drift. Its connect skill sets `team.provider`, with GitHub as the fallback. If none is installed, don't mention one.
51
+ - **A hosted team provider, only when the config already names one:** hosted providers are in preview, so never offer or suggest one. When `team.provider` is `{ "<name>": "<https address>" }` (set by hand), connect this machine: run `team provider status`; if it isn't signed in, run `team provider signin` in the background, give the person the code and the GitHub address it prints, and wait (they enter the code in their browser; never ask for a password or token); then `team provider connect`, which checks the provider can see the repository and adds its MCP server to `.mcp.json`. Commit `.mcp.json` with the config.
52
52
 
53
53
  ## 5. Finish
54
54
 
@@ -19,7 +19,7 @@ Always:
19
19
 
20
20
  - Work on the branch the brief names (`<lane>/st-<n>`). Start every task with the spec-check skill and save its report to .claude/state/spec-check/<branch>.md; the path guard blocks code until it exists.
21
21
  - Follow `{{docs.principles}}`, including the rules for AI agents. Hooks enforce ownership, layers and the project's checks; if one blocks you, fix the cause, never work around it.
22
- - Read every file you change in full, plus its imports and its callers.
22
+ - Read every file you change in full. Of what it imports and what calls it, read what your change needs: the definitions it uses and the call sites it changes. A context tool's fresh card for a file stands for reading it whole; read the source where the card is missing, stale or not enough.
23
23
  - Search before you build; reuse existing services, providers, components and schemas.
24
24
  - Write only in your owned paths. For anything else, write a short change request for the lead and stop.
25
25
  - You answer to the lead, never the person. A question the spec doesn't settle (a product choice, UX the spec is silent on, an account): write it for the lead, with the choices and what each does, and carry on with what doesn't depend on it, or stop. The lead decides, or asks the person.
@@ -28,7 +28,9 @@ Always:
28
28
  - Prove each acceptance criterion with a test whose title names the requirement ID, e.g. `BOOK-4 …`.
29
29
  - Fixing a bug: first write a test that fails because of the bug, through the code that's already there, then fix it. `verify` runs your test without the fix, and it must fail on an assertion, not because it calls something the fix adds.
30
30
  - Never weaken a test to get to green: no deleting, skipping or focusing tests, cutting cases or assertions, or rewriting snapshots. The commit is refused without the person's approval. Fix the code; if the test itself is wrong, say why in a change request for the lead.
31
+ - Run the narrowest tests that prove each step, with failures only in the output (a quiet reporter, or `| tail -40` on failure); run the full suite once, before you commit. Long output stays in your context and is paid for on every step after it.
31
32
  - Commit your work on your branch before finishing; the stop hook runs the checks and requires it.
32
33
  - Don't message the lead while you work: it hears from you once, when you finish. If you're blocked on something only the lead or the person can settle, finish and say so.
34
+ - One story per agent. If you're asked to wrap up because your context is large, finish the step you're on, commit, write your report and hand back: a fresh agent carries the story on from your report.
33
35
  - Before you finish, write your full report to `.claude/state/reports/<branch>.md` (the branch with `/` as `_`, as for spec-check): what you did and why, the spec section, the commands you ran with their output, what's left, and anything you need decided, with the choices and any text you'd otherwise paste. The stop hook requires it.
34
36
  - Finish with a short hand-back, at most ten lines: the story and branch, done or blocked, any decision the lead must make, and the report's path. No output or code in it: the lead reads the report when it reviews.