bantamkit-mcp 0.35.2 → 0.35.4

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
@@ -6,9 +6,13 @@ command, connect it to **Claude Code**, **Claude Desktop**, **Cursor** or **GitH
6
6
  Code**, and every later launch starts **offline**. No Python, `pip`, `uv`, `pipx` or venv.
7
7
 
8
8
  It serves the same fourteen tools, `bantamkit_status` prompt and two resource templates as the
9
- Python server on **PyPI** (`pip install "bantamkit[mcp]"`), and reads and writes the same memory
10
- store. The two are compared frame by frame: 8,130 conformance cases on 2026-09-15, with every
11
- intentional difference written down as a ruling.
9
+ Python server — [`bantamkit` on PyPI](https://pypi.org/project/bantamkit/) — and reads and
10
+ writes the same memory store. The two are compared frame by frame: 8,130 conformance cases on
11
+ 2026-09-15, with every intentional difference written down as a ruling.
12
+
13
+ **This page is the npm package's.** Every command on it runs `bantamkit-mcp` from npm. The
14
+ Python package is named where the two differ, but its own install, update and CLI commands live
15
+ on [its page](https://pypi.org/project/bantamkit/); run them here and you get nothing.
12
16
 
13
17
  ## Contents
14
18
 
@@ -31,6 +35,7 @@ intentional difference written down as a ruling.
31
35
  | [Memory stores are layered by default](#memory-stores-are-layered-by-default) | Why not to add `--store` by reflex |
32
36
  | [The asset pack](#the-asset-pack) | What ships in `assets/` and how it is found |
33
37
  | [Where the Python and Node servers differ](#where-the-python-and-node-servers-differ) | One store; concurrent writes, one hand-edit to avoid |
38
+ | [Module API](#module-api) | The one entry point, all 139 exports it reaches, and the one break in `clockIn` |
34
39
  | [Development](#development) | Build, test, conformance |
35
40
  | [Measurements and history](#measurements-and-history) | Where the measured numbers behind this page live |
36
41
 
@@ -107,7 +112,6 @@ Each host below has the same steps: **1** command, **2** file, **3** entry, **4*
107
112
  | Route | `command` | `args` |
108
113
  |---|---|---|
109
114
  | npm, install once | `/absolute/path/to/node` | `["/Users/you/.bantamkit/mcp/node_modules/bantamkit-mcp/dist/cli.js"]` |
110
- | Python venv | `/absolute/path/to/env/bin/bantamkit-mcp` | `[]` |
111
115
  | npx every launch | `npx` | `["-y", "bantamkit-mcp"]` |
112
116
 
113
117
  A JSON file cannot expand `~`, so spell both paths out. To check a recorded command, run it in
@@ -226,9 +230,12 @@ It is the only network access here — not at startup, not on `bantamkit_status`
226
230
  | `npx -y bantamkit-mcp --install <host>` from 0.34.0: a kept install at `~/.bantamkit/mcp` | `npx -y bantamkit-mcp@latest --update` patches it in place, or `npm i --prefix ~/.bantamkit/mcp bantamkit-mcp@latest`. The recorded paths do not change. Before 0.34.0, `--update` from an npx cache refuses instead |
227
231
  | `npm i -g bantamkit-mcp` | `npm i -g bantamkit-mcp@latest` |
228
232
  | `npm i --prefix <dir> bantamkit-mcp` | `npm i --prefix <dir> bantamkit-mcp@latest` |
229
- | PyPI (`pip install "bantamkit[mcp]"`) | `pip install -U "bantamkit[mcp]"` · pipx: `pipx upgrade bantamkit` · uv: `uv tool upgrade bantamkit` |
230
233
  | a checkout, via `tools/bantamkit-mcp-node` | `git pull && npm ci --prefix runtime-ts && npm run build --prefix runtime-ts` — no registry, and `runtime-ts/dist/` is build output, so a pull alone changes nothing |
231
234
 
235
+ No row above is the Python package. If what you have is **`bantamkit` from PyPI** instead, it
236
+ updates by its own route, written on [its PyPI page](https://pypi.org/project/bantamkit/); the
237
+ two sets of commands are not interchangeable.
238
+
232
239
  **Then restart the server in your host** — a running server keeps serving the code it loaded at
233
240
  startup. In Claude Code: `/mcp` → reconnect. In Claude Desktop: a full restart of the app.
234
241
 
@@ -405,6 +412,100 @@ are deliberate and each is ruled in the
405
412
  the whole store with `malformed fact file …`. Quote it: `description: '2026'`. Every fact the
406
413
  tools *write* is quoted correctly (`src/memory/factfile.ts:257`).
407
414
 
415
+ ## Module API
416
+
417
+ The package is a server first, but it is importable too: `exports` maps exactly one entry
418
+ point — `.` — to `dist/index.js` and `dist/index.d.ts`, so `import { clockIn } from
419
+ 'bantamkit-mcp'` is a supported call, and the names and arities behind that entry point are
420
+ part of what is published.
421
+
422
+ Everything in this section was measured against the **tarball**, not the source tree: `npm
423
+ pack`, extracted into a throwaway package's `node_modules`, and imported by `node`. A name in
424
+ `src/` that no entry point reaches is not API; the table below is what an importer gets.
425
+
426
+ **One entry point, and nothing beside it.** There are no subpaths, so every deep import is
427
+ refused by Node's resolver:
428
+
429
+ | specifier | result |
430
+ | --- | --- |
431
+ | `bantamkit-mcp` | 139 value exports, 26 type-only exports |
432
+ | `bantamkit-mcp/dist/cli.js` | `ERR_PACKAGE_PATH_NOT_EXPORTED` |
433
+ | `bantamkit-mcp/dist/shiftwork.js` | `ERR_PACKAGE_PATH_NOT_EXPORTED` |
434
+ | `bantamkit-mcp/package.json` | `ERR_PACKAGE_PATH_NOT_EXPORTED` |
435
+ | `bantamkit-mcp/assets/tools/memory_save.json` | `ERR_PACKAGE_PATH_NOT_EXPORTED` |
436
+
437
+ **So what you can import is the library half, not the server.** The MCP server and its
438
+ transport, both CLIs, the host-install and self-update code, the hook adapter, the document
439
+ reader, the repo map, the skill audit, the token ledger, the pricing table and the work planner
440
+ all ship inside `dist/` and none of them is reachable through an import — they are reached by
441
+ running `bantamkit-mcp` and `bantamkit-memory`. Importing the package starts no server and
442
+ opens no transport. The vendored `assets/` is not importable either, but it is *readable*:
443
+ `assetsRoot()` resolves to the `assets/` directory inside the installed package, which is how
444
+ `loadTool('memory_save')` answers from a consumer that never unpacked anything itself.
445
+
446
+ **What the one entry point exports**, grouped by the module each name comes from:
447
+
448
+ | module | what it is | exported names |
449
+ | --- | --- | --- |
450
+ | `errors.js` | The base error every other one extends | `BantamError` |
451
+ | `contract.js` | The JSON-answer contract: load it, pull JSON out of a model reply, compose the retry feedback | `ContractParseError`, `extractJson`, `loadContract`, `parseContractDocument`, `parseErrorMessage`, `REQUIRED_KEYS`, `schemaError`, `schemaRetryFeedback`, `validationErrorMessage` |
452
+ | `pyjson.js` | CPython `json` semantics over a `PyValue` tree — float repr and decode errors included | `dumpJson`, `fromJs`, `parseJson`, `PY_NONE`, `pyFloatRepr`, `PyJSONDecodeError`, `PyValueError`, `rawDecode`, `reprValue`, `toJs`<br>*types:* `DumpOptions`, `PyValue` |
453
+ | `pyjsonschema.js` | The JSON Schema validator, with CPython `jsonschema`'s error ordering and best-match | `absolutePath`, `bestMatch`, `iterErrors`, `pyEqual`, `PyJsonSchemaUnsupported`, `validate`<br>*types:* `PyValidationError` |
454
+ | `assets.js` | The vendored asset pack: schemas, skills and tool definitions | `AssetNotFound`, `assetsRoot`, `loadSchema`, `loadSkill`, `loadTool`, `loadToolAsset`<br>*types:* `Tool` |
455
+ | `memory/factfile.js` | One fact file: frontmatter, parse, format | `decodeFactBytes`, `FactParseError`, `formatFact`, `parseFactText`, `parseFrontmatter`, `pySplit`, `pyStrip`, `todayLocal`<br>*types:* `Fact`, `FactMeta`, `FactValue` |
456
+ | `memory/pyyaml.js` | The YAML subset PyYAML's safe loader and dumper accept | `constructPlain`, `PyScalar`, `resolveImplicitTag`, `safeDumpMapping`, `YamlConstructError`, `YamlEmitError`<br>*types:* `PyOrder`, `YamlScalar`, `YamlValue` |
457
+ | `memory/layers.js` | Which stores a recall reads, and where the project store is | `countFacts`, `discoverProjectStore`, `loadGrants`, `MEMORY_DIR_ENV`, `PROJECT_STORE`, `resolveProjectStore`<br>*types:* `StoreBinding` |
458
+ | `memory/component.js` | `Memory`, the save/recall/compact/dream component the server calls | `layerLabel`, `Memory`, `normalizeName`, `profileStore`<br>*types:* `CompactOutcome`, `DreamOutcome`, `MemoryOptions`, `RecallOutcome`, `SaveOutcome` |
459
+ | `memory/dream.js` | The consolidation pass: similarity, merge, supersede | `absolutise`, `blockKey`, `claimSlot`, `collapse`, `dream`, `formatFixed3`, `mergeBodies`, `mergeDescriptions`, `mergeLinks`, `PROFILE_LAYER`, `PROJECT_LAYER`, `pyIntDigits`, `sortedNames`, `splitBlocks`, `SUPERSEDED_HEADING`<br>*types:* `DateHit`, `DreamMerge`, `DreamResult`, `SimilarPair`, `Superseded` |
460
+ | `eventlog.js` | The JSONL event log | `CAP_BYTES`, `DEFAULT_RELATIVE_PATH`, `defaultPath`, `encodeRecord`, `EVENT_LOG_ENV`, `EventLog`, `formatTimestamp`, `resolvePath`, `SCHEMA_VERSION`<br>*types:* `DetailValue` |
461
+ | `memory/store.js` | `MemoryStore`: the on-disk store, its budgets and its dedupe | `BANTAMKIT_GITIGNORE_TEXT`, `DEFAULT_INDEX_BUDGET`, `DUPLICATE_JACCARD`, `ensureBantamkitGitignore`, `jaccard`, `MemoryBudgetExceeded`, `MemoryStore`, `MemoryValidationError`, `pyCompareLt`, `pyEqualValue`, `pyHashKey`, `pyText`, `RECALL_MIN_SCORE_RATIO`, `sortScored`, `tokens`, `VALID_TYPES`<br>*types:* `MemoryStoreOptions`, `SaveResult`, `StoreInternals` |
462
+ | `memory/pyfs.js` | CPython `pathlib` and `os.path` semantics over `node:fs`, error numbering included | `asPyOSError`, `cmpCodepoint`, `matchesMd`, `normcase`, `pyAppendText`, `pyCwd`, `pyDecodeUtf8`, `pyExists`, `pyExpanduser`, `pyHome`, `pyIsAbsolute`, `pyIsDir`, `pyIsFile`, `pyJoin`, `pyLexists`, `pyMkdirParents`, `pyMtimeDate`, `pyName`, `PyOSError`, `pyParent`, `pyParents`, `pyReadText`, `pyReplace`, `pyRepr`, `pyResolve`, `PyRuntimeError`, `pyScandirNames`, `pyStatIsDir`, `pySuffix`, `PyUnicodeDecodeError`, `pyUnlink`, `pyWithSuffix`, `pyWriteText`, `sortedPathNames`, `sortedPathParts`, `STRERROR_NAMES` |
463
+ | `shiftwork.js` | The shift-work checkpoint tools | `clockIn`, `clockOut`, `HISTORY_RING_SIZE`, `SCHEMA_NAME`, `status`, `TERMINAL_UNIT_STATUS`, `timestamp`<br>*types:* `ClockOptions` |
464
+ | **14 modules** | | **139 values, 26 types** |
465
+
466
+ Two things that table is easy to misread. The `py*` names are not a Python bridge — they are
467
+ this port's re-implementations of CPython semantics (`pyfs.js` alone is 36 of the 139), and
468
+ they are exported because the conformance harness and the operator tooling call them, not
469
+ because an application should. And `Function.length` is not the signature: `clockIn.length` is
470
+ `1` because two of its three parameters have defaults.
471
+
472
+ **One export changed shape.** `clockIn` took a unit id as its **second positional parameter**,
473
+ ahead of the options object:
474
+
475
+ ```ts
476
+ clockIn(checkpoint: string, unitId: string | null = null, options: ClockOptions = {}): PyValue
477
+ ```
478
+
479
+ A caller who wrote `clockIn(path, { now })` must now write `clockIn(path, null, { now })`, and
480
+ `clockIn(path, 'U3')` briefs a named ready unit. TypeScript rejects the old shape at the type;
481
+ plain JavaScript does not — the object lands in the id slot, the call asks for a unit named
482
+ `[object Object]`, and `now` is dropped, which is a wrong answer rather than an error.
483
+ `clockOut` is unchanged. The reasoning and the measurement behind it are in
484
+ [docs/porting.md → The published export surface](https://github.com/Ink01101011/bantamkit/blob/main/docs/porting.md#the-published-export-surface-and-the-one-break-in-it);
485
+ `docs/` is not in the npm tarball (`files: ["dist", "assets"]`), which is why the migration line
486
+ is repeated here.
487
+
488
+ **Rerun the whole table.** From a clone, in `runtime-ts/`:
489
+
490
+ ```sh
491
+ npm pack --pack-destination /tmp/bk
492
+ mkdir -p /tmp/bk/consumer/node_modules/bantamkit-mcp
493
+ tar -xzf /tmp/bk/bantamkit-mcp-*.tgz -C /tmp/bk/consumer/node_modules/bantamkit-mcp --strip-components=1
494
+ printf '{"name":"c","private":true,"type":"module"}' > /tmp/bk/consumer/package.json
495
+ cd /tmp/bk/consumer && node -e "import('bantamkit-mcp').then((m) => console.log(Object.keys(m).length))"
496
+ ```
497
+
498
+ It prints `139`, with no `npm install` and nothing fetched: `dist/index.js` pulls in none of the
499
+ server code, so the one runtime dependency is never reached. The 26 type-only exports are in the
500
+ same tarball — from `/tmp/bk/consumer`:
501
+
502
+ ```sh
503
+ node -e "const t=require('fs').readFileSync('node_modules/bantamkit-mcp/dist/index.d.ts','utf8');console.log([...t.matchAll(/export type \{([^}]*)\}/g)].flatMap((m) => m[1].split(',')).map((x) => x.trim()).filter(Boolean).length)"
504
+ ```
505
+
506
+ Both numbers move whenever `src/index.ts` does, which is why they are quoted with the command
507
+ that prints them rather than kept in prose.
508
+
408
509
  ## Development
409
510
 
410
511
  ```sh
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shiftwork_clock_in",
3
- "description": "Shift-work clock-in: schema-validate the checkpoint file and return the brief for the unit at plan.cursor — {unit, role, invariants, handoff, do_not, files} — to hand to the spawned agent verbatim. Structured refusals, never exceptions: result=escalate when handoff.open_questions is non-empty, result=success when every unit is done or dropped, result=error when the checkpoint fails validation. Clock-in also WRITES: on result=brief, and only then, it appends one line {event: brief, ts, unit, role} to <checkpoint>.log.jsonl — best-effort: a log it cannot write costs the brief nothing (the brief still returns, no error, only the record is lost). clock_out reads those lines back to write `briefed` on every accounting line, so the ledger is no longer one line per clock-out; a brief line carries `event` and no `status`. See docs/shiftwork.md.",
3
+ "description": "Shift-work clock-in: schema-validate the checkpoint file and return the brief for the unit at plan.cursor — {unit, role, invariants, handoff, do_not, files} — to hand to the spawned agent verbatim. The unit briefed is the one at plan.cursor, or, when unit_id is given, that unit — which must appear in shiftwork_plan's `ready`, the batch the dependency graph says may run now. A unit_id that is not ready, including one naming no unit, is refused with {\"result\": \"error\", \"reason\": \"unit <id> is not ready; ready is <a, b>\"}, and the refusals of the batch view itself (cycle, unknown dependency, unreadable checkpoint) pass through verbatim. Clock-in still never moves plan.cursor; clock_out does. Structured refusals, never exceptions: result=escalate when handoff.open_questions is non-empty, result=success when every unit is done or dropped, result=error when the checkpoint fails validation. Clock-in also WRITES: on result=brief, and only then, it appends one line {event: brief, ts, unit, role} to <checkpoint>.log.jsonl — best-effort: a log it cannot write costs the brief nothing (the brief still returns, no error, only the record is lost). clock_out reads those lines back to write `briefed` on every accounting line, so the ledger is no longer one line per clock-out; a brief line carries `event` and no `status`. See docs/shiftwork.md.",
4
4
  "surfaces": [
5
5
  "mcp"
6
6
  ],
@@ -9,6 +9,18 @@
9
9
  "checkpoint": {
10
10
  "title": "Checkpoint",
11
11
  "type": "string"
12
+ },
13
+ "unit_id": {
14
+ "anyOf": [
15
+ {
16
+ "type": "string"
17
+ },
18
+ {
19
+ "type": "null"
20
+ }
21
+ ],
22
+ "default": null,
23
+ "title": "Unit Id"
12
24
  }
13
25
  },
14
26
  "required": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shiftwork_clock_out",
3
- "description": "Shift-work clock-out: record a finished unit — set its status, advance plan.cursor, merge handoff_patch, push history_entry onto the 5-entry ring — validating the whole mutated document against the checkpoint schema BEFORE an atomic write (a failure writes nothing and returns result=error). Every success appends one accounting line (unit, role, status, ts, briefed, plus your accounting fields, e.g. tokens/duration_ms/model) to <checkpoint>.log.jsonl, beside the brief lines clock_in writes (a brief line carries `event` and no `status`). `briefed` is written by the runtime, never taken from you — a self-reported value is overwritten by the measured one: true when a brief was issued for this unit since its LAST clock-out (not ever), so a unit re-run inline after a blocked clock-out reads false. It records; it never refuses — a unit that was never clocked in still clocks out, with briefed=false. If the checkpoint's job.roles names this unit's role, accounting.model must be one of that role's model identifiers, spelled exactly \u2014 a model that is not on the list, or none reported at all, is refused before anything is written; a role job.roles omits, and a checkpoint carrying no job.roles, are unconstrained.",
3
+ "description": "Shift-work clock-out: record a finished unit — set its status, advance plan.cursor, merge handoff_patch, push history_entry onto the 5-entry ring — validating the whole mutated document against the checkpoint schema BEFORE an atomic write (a failure writes nothing and returns result=error). `unit_id` must be `plan.cursor` — which needs no brief, clocking out with briefed=false — OR a unit briefed since its own last clock-out; any other id keeps its refusal (`unit X is not the cursor unit Y`). plan.cursor then advances to the head of the ready batch recomputed on the mutated document, falling back to plan.units order over the remaining non-terminal units when the graph cannot batch (cycle or unknown dependency), and to unit_id when none remain. `handoff_patch` and `history_entry` are both required arguments: `handoff_patch` is shallow-merged into `handoff` and takes only its four keys (pass `{}` to leave it unchanged); `history_entry` needs `unit` and `outcome`; `status` is one of the five unit statuses — a value outside these shapes is refused by the schema check, nothing written. Every success appends one accounting line (unit, role, status, ts, briefed, plus your accounting fields, e.g. tokens/duration_ms/model) to <checkpoint>.log.jsonl, beside the brief lines clock_in writes (a brief line carries `event` and no `status`). `accounting` is optional: null, the default, logs a line with no cost figures at all; a non-null line must fit the accounting shape below. `briefed` is written by the runtime, never taken from you: true when a brief was issued for this unit since its LAST clock-out (not ever), so a unit re-run after a blocked clock-out reads false. If job.roles names this unit's role, accounting.model must be one of that role's model identifiers, spelled exactly, or nothing is written — see the `model` property below.",
4
4
  "surfaces": [
5
5
  "mcp"
6
6
  ],
@@ -12,21 +12,76 @@
12
12
  },
13
13
  "unit_id": {
14
14
  "title": "Unit Id",
15
- "type": "string"
15
+ "type": "string",
16
+ "description": "The unit being clocked out. Must be plan.cursor, or a unit briefed since its own last clock-out: clock_in briefs the cursor unit by default and, when its unit_id names one, any unit shiftwork_plan reports in `ready` — and that brief is what lets clock_out accept a unit the cursor is not on."
16
17
  },
17
18
  "status": {
18
19
  "title": "Status",
19
- "type": "string"
20
+ "type": "string",
21
+ "enum": [
22
+ "todo",
23
+ "in_progress",
24
+ "done",
25
+ "blocked",
26
+ "dropped"
27
+ ],
28
+ "description": "The unit's new status; the same five values the checkpoint schema allows on plan.units[].status."
20
29
  },
21
30
  "handoff_patch": {
22
- "additionalProperties": true,
23
31
  "title": "Handoff Patch",
24
- "type": "object"
32
+ "type": "object",
33
+ "additionalProperties": false,
34
+ "description": "Shallow-merged into `handoff`: only these four keys exist there, and a key outside them is refused by the schema check. Required; `{}` leaves `handoff` unchanged.",
35
+ "properties": {
36
+ "next_action": {
37
+ "type": "string",
38
+ "minLength": 1,
39
+ "description": "Imperative first move — zero re-derivation."
40
+ },
41
+ "open_questions": {
42
+ "type": "array",
43
+ "items": {
44
+ "type": "string",
45
+ "minLength": 1
46
+ },
47
+ "description": "The autonomy switch: empty = driver keeps cycling, non-empty = STOP and ask."
48
+ },
49
+ "do_not": {
50
+ "type": "array",
51
+ "items": {
52
+ "type": "string",
53
+ "minLength": 1
54
+ },
55
+ "description": "Negative space — near-mistakes past sessions made."
56
+ },
57
+ "notes": {
58
+ "type": "string",
59
+ "description": "Free-form prose for the next session, one string; the empty string clears it."
60
+ }
61
+ }
25
62
  },
26
63
  "history_entry": {
27
- "additionalProperties": true,
28
64
  "title": "History Entry",
29
- "type": "object"
65
+ "type": "object",
66
+ "required": [
67
+ "unit",
68
+ "outcome"
69
+ ],
70
+ "additionalProperties": true,
71
+ "description": "Pushed onto the 5-entry `history` ring; `unit` and `outcome` are required, `notes` optional, other keys pass through.",
72
+ "properties": {
73
+ "unit": {
74
+ "type": "string",
75
+ "minLength": 1
76
+ },
77
+ "outcome": {
78
+ "type": "string",
79
+ "minLength": 1
80
+ },
81
+ "notes": {
82
+ "type": "string"
83
+ }
84
+ }
30
85
  },
31
86
  "accounting": {
32
87
  "anyOf": [
@@ -37,7 +92,7 @@
37
92
  "tokens": {
38
93
  "type": "integer",
39
94
  "minimum": 0,
40
- "description": "Tokens the unit consumed as the harness's subagent counter reports them: every class it reports (input, output, cache creation) summed, EXCLUDING cache reads. Required, because a unit whose cost is unknown cannot be compared with any other; a rounded self-estimate is allowed only if `note` says it is one."
95
+ "description": "Tokens the unit consumed as the harness's subagent counter reports them: every class it reports (input, output, cache creation) summed, EXCLUDING cache reads. In a non-null accounting line it is required — a line without it is refused before anything is written; only `accounting: null`, the default, is exempt, and that logs no cost at all. A rounded self-estimate is allowed only if `note` says it is one; a unit whose cost is unknown cannot be compared with any other."
41
96
  },
42
97
  "cache_read_tokens": {
43
98
  "type": "integer",
@@ -47,7 +102,7 @@
47
102
  "duration_ms": {
48
103
  "type": "integer",
49
104
  "minimum": 0,
50
- "description": "Wall-clock from spawning the subagent to its final message, in whole milliseconds. Required, and the only duration key with a defined meaning: older lines carry `duration`, `duration_min` or `duration_s`, which this schema neither reads nor renames."
105
+ "description": "Wall-clock from spawning the subagent to its final message, in whole milliseconds. In a non-null line it is required, and it is the only duration key with a defined meaning: older lines carry `duration`, `duration_min` or `duration_s`, which this schema neither reads nor renames."
51
106
  },
52
107
  "model": {
53
108
  "type": "string",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shiftwork_plan",
3
- "description": "Shift-work plan: read-only batch view of a checkpoint — which units its `depends_on` graph permits to run in parallel. Answers `{\"result\": \"plan\", \"batches\", \"ready\", \"sequence\", \"width\", \"cursor\"}`: `ready` is `batches[0]`, the units dispatchable right now, and `width` is the largest batch length, the widest fan-out. Units with status `done` or `dropped` are satisfied — dropped from the graph, and edges pointing at them treated as already resolved; `todo`, `in_progress` and `blocked` stay in it. Every unit has priority 0, because the checkpoint schema has no priority field, so order inside a batch is `plan.units` order. Same refusal sentences as `work_plan` for a duplicate id, an unknown dependency or a cycle, and the same refusals `shiftwork_status` gives for a checkpoint it cannot read. It reports what the dependency graph permits; it does not move the cursor, which is echoed unchanged so the single-pointer contract and the batch view can be read side by side. An orchestrator stays responsible for what it actually dispatches: `depends_on` encodes logical order, not file contention. Never mutates.",
3
+ "description": "Shift-work plan: read-only batch view of a checkpoint — which units its `depends_on` graph permits to run in parallel. Answers `{\"result\": \"plan\", \"batches\", \"ready\", \"sequence\", \"width\", \"cursor\"}`: `ready` is `batches[0]`, the units whose dependencies are all satisfied, and `width` is the largest batch length, the widest fan-out. A `ready` member beyond the cursor is clockable, but only in that order: shiftwork_clock_in briefs any unit in `ready` when `unit_id` names it, and shiftwork_clock_out then accepts that unit because it was briefed — an id that is neither the cursor nor briefed since its own last clock-out is still refused. Units with status `done` or `dropped` are satisfied — dropped from the graph, and edges pointing at them treated as already resolved; `todo`, `in_progress` and `blocked` stay in it. Every unit has priority 0, because the checkpoint schema has no priority field, so order inside a batch is `plan.units` order. Same refusal sentences as `work_plan` for a duplicate id, an unknown dependency or a cycle, and the same refusals `shiftwork_status` gives for a checkpoint it cannot read. It reports what the dependency graph permits; it does not move the cursor, which is echoed unchanged so the single-pointer contract and the batch view can be read side by side. An orchestrator stays responsible for what it actually dispatches: `depends_on` encodes logical order, not file contention. Never mutates.",
4
4
  "surfaces": [
5
5
  "mcp"
6
6
  ],
package/dist/cli.d.ts CHANGED
@@ -6,11 +6,15 @@ export interface Options {
6
6
  store: string | null;
7
7
  start: string | null;
8
8
  assetsRoot: boolean;
9
+ hook: boolean;
9
10
  mcpReport: boolean;
10
11
  statusline: boolean;
11
12
  update: boolean;
12
13
  install: Host | null;
13
14
  force: boolean;
15
+ installHooks: boolean;
16
+ removeHooks: boolean;
17
+ yes: boolean;
14
18
  }
15
19
  /** `_parse_args`, arm for arm, including the mutually exclusive group and `-h`. */
16
20
  export declare function parseArgs(argv: readonly string[]): Options;