You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit bf6de9c
Browse filesBrowse the repository at this point in the historyBrowse files
Keep a conventional `src/scripts/<name>.ts` module in script discovery when a `bin` config entry also references it, so `agent-bundle build` emits both `dist/bin/<name>.js` and the artifact `scripts/<name>.mjs` and `agent-bundle inspect --json` lists the module under both `packageBuild.bins` and `scripts` instead of silently dropping the script. Explicit `scripts`, `hooks`, `lib`, and `mcp` entries still claim the module they reference. Report `AB4737` when a `bin` entry points at a rendered `src/scripts/<name>.tsx` script that does not export both its default Server Component and a named `main`, because the bin envelope calls `main` while the artifact script renders the component, and `AB4738` when it points at a plain `src/scripts/<name>.ts` script that exports a `default` but no `main`, because the artifact script would ship inert while the bin runs the default export. (#413)
Copy file name to clipboardExpand all lines: docs/diagnostics.md
+62-5Lines changed: 62 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -195,16 +195,27 @@ development-only fallback can never produce a release artifact, so
195
195
|`AB4011`| warning |`package.json` is unusable — unparsable, not a JSON object, or symlinked outside the project root. |
196
196
|`AB4013`| error (build) |`agent-bundle build` refuses a project with no release version: `plugin.version` is omitted and `package.json` declares no valid semantic version. |
197
197
198
-
## Migration nudges (`AB4730`–`AB4736`)
198
+
## Migration nudges and convention claims (`AB4730`–`AB4738`)
199
199
200
200
The entry conventions and the framework-owned stdio lifecycle shell (RFC #50)
201
201
replaced patterns consumers previously wrote by hand. When `validate`,
202
202
`inspect`, `build`, or `dev` prepares project source and finds one of those
203
203
pre-convention patterns, it reports a migration diagnostic. `AB4730`–`AB4735`
204
-
are **informational** nudges and never block anything. `AB4736` is an error:
205
-
the removed top-level authored-document locations are no longer discovered,
206
-
so the compiler refuses to omit them silently. The CLI prints these in human
207
-
`validate` output and includes them in every `--json` diagnostics array.
204
+
are **informational** nudges and never block anything. `AB4736`–`AB4738` are
205
+
errors: the removed top-level authored-document locations are no longer
206
+
discovered, and a conventional script whose `bin` entry would run an export
207
+
the artifact script ignores cannot ship on both surfaces, so the compiler
208
+
refuses to omit or misbuild them silently. The CLI prints these in
209
+
human `validate` output and includes them in every `--json` diagnostics array.
210
+
211
+
Which explicit config keys *claim* a conventional module out of discovery is
212
+
tabulated in `docs/entry-conventions.md` ("Which config keys claim a
213
+
conventional module"). In short: `scripts`, `hooks`, `lib`, and `mcp` entries
214
+
claim the module they reference; a `bin` entry claims every conventional
215
+
module **except** a safely named direct `src/scripts/<name>` child, which
216
+
keeps shipping as an artifact script beside the bin because the two outputs
217
+
are disjoint and both envelopes run the same `main`. That dual-surface shape
218
+
is intentional and raises no diagnostic.
208
219
209
220
### `AB4730` — self-connecting stdio MCP entry
210
221
@@ -276,6 +287,52 @@ Recover: move the document under `src/skills/`, `src/commands/`, or
276
287
`src/rules/`. Explicit `skills` paths remain valid anywhere. Published
277
288
artifact paths remain `skills/`, `commands/`, and `rules/`.
278
289
290
+
### `AB4737` — rendered script claimed as a package bin entry lacks `main` or the component
291
+
292
+
An explicit `bin` entry references a conventional rendered script
293
+
(`src/scripts/<name>.tsx` or `.jsx`) that does not export **both** an async
294
+
default Server Component and a named `main`. The component check is the
295
+
route compiler's own static scan — the default export must be an async
296
+
function, so `export default {}` does not count; a default re-exported from
297
+
another module (`export { default } from './component.tsx'`) cannot be judged
298
+
statically and is accepted (the rendered worker still verifies it at run
299
+
time). A plain `src/scripts/<name>.ts` module
300
+
ships happily on both surfaces — the npm bin envelope calls its `main(argv)`
301
+
and the artifact script is the same bundle — but a rendered script's default
302
+
export is an async Server Component the Agent renderer drives with
303
+
`{ argv, signal }` props. The bin envelope prefers a named `main` export and
304
+
only falls back to the default export, so without `main` it would call that
305
+
component as `main(argv)` and produce a bin that renders nothing; without the
306
+
default component, the bin works but `scripts/<name>.mjs` fails at run time
307
+
with no component to render. The compiler refuses either shape instead of
308
+
emitting a broken surface beside a working one. A rendered script that
309
+
exports both serves both surfaces and is not gated. The message names every
310
+
`bin` entry referencing the module and which export is missing.
311
+
312
+
Recover: export both an async default Server Component and a named
313
+
`main(argv)` from the module; point the `bin` entry at a plain module that exports `main`;
314
+
rename the script to `.ts` so one plain module ships as both the bin and the
315
+
artifact script; or prefix a path segment with `_` (`src/scripts/_name.tsx`)
316
+
to keep the module out of script discovery and bin-only.
317
+
318
+
### `AB4738` — plain script claimed as a package bin entry runs only as the bin
319
+
320
+
An explicit `bin` entry references a conventional plain script
321
+
(`src/scripts/<name>.ts`) that exports a `default` but no named `main`. Both
322
+
the bin envelope and the artifact-script envelope wrap a `main(argv)` export
323
+
and bundle a self-executing module (no `main`, no `default`) byte for byte,
324
+
so those shapes run identically on both surfaces. Only the bin envelope falls
325
+
back to invoking a default export: the artifact `scripts/<name>.mjs` would
326
+
merely define the function and exit, so a successful build would publish an
327
+
inert script beside a working bin. The detection is the same static export
328
+
scan the package build uses. The message names every `bin` entry referencing
329
+
the module.
330
+
331
+
Recover: export a named `main(argv)` so both surfaces run the same entry;
332
+
make the module self-executing (drop the default export and run at top
333
+
level); or prefix a path segment with `_` (`src/scripts/_name.ts`) to keep
334
+
the module out of script discovery and bin-only.
335
+
279
336
## Prebuilt payloads (`AB4740`–`AB4750`)
280
337
281
338
The `payload` block and `{ prebuilt: ... }` entries (see
Copy file name to clipboardExpand all lines: docs/entry-conventions.md
+32-2Lines changed: 32 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -77,8 +77,8 @@ entries carry `provenance.kind: 'conventional'` in the normalized model.
77
77
|`src/mcp/<server-id>.ts`| Stdio entry for the declared MCP server `<server-id>` that names no `entry`, `command`, or `url`. | Declare `entry` explicitly |
78
78
|`src/mcp/<server>/{tools,resources,prompts}/*.{ts,tsx}`| Generated MCP server routes; path supplies identity and each executable module supplies static `config`, schemas, and one async default Server Component. | Set `routes.servers.<server>` to `custom`, `command`, or `remote`|
79
79
|`src/mcp/<server>/apps/*.{ts,tsx}`| Browser MCP App entry compiled to self-contained HTML and registered on the generated server; static `config.resourceUri` is required. An optional `config.template` HTML shell resolves relative to the route module like its imports (`'./dashboard.html'`); the legacy project-root-relative form is accepted only while unambiguous (`AB4827` otherwise). Tools, resources, and prompts reference the App from their own static `config` with `appResourceUri('<app>')` from `agent-bundle/routes` or a shared `const` string literal instead of repeating the `ui://` literal. | Use a custom server or prefix the file with `_`|
80
-
|`src/scripts/<name>.ts`| Plain script compiled to `scripts/<name>.mjs` in every selected target artifact — the same pipeline explicit `scripts` entries use, with ordinary Node stdout/stderr semantics. A `scripts` entry that references the file claims it. Nested modules are hard errors (`AB4808`). | Prefix a path segment with `_`, or claim the file with an explicit `scripts` entry |
81
-
|`src/scripts/<name>.tsx`| Rendered script: the async default component receives `{ argv, signal }` and renders through the Agent renderer with the CLI output contract (`--json`, `--ndjson`, TTY progress, piped Markdown). Compiles to `scripts/<name>.mjs` plus a `scripts/<name>-flight.mjs` react-server worker. The extension is the explicit, visible contract — plain `.ts` scripts are never wrapped in React behavior, and explicit `scripts` config entries stay plain regardless of extension. | Rename to `.ts`, prefix a path segment with `_`, or claim the file with an explicit `scripts` entry |
80
+
|`src/scripts/<name>.ts`| Plain script compiled to `scripts/<name>.mjs` in every selected target artifact — the same pipeline explicit `scripts` entries use, with ordinary Node stdout/stderr semantics. A `scripts` entry that references the file claims it. Nested modules are hard errors (`AB4808`). A `bin` entry that references the file does **not** claim it: the module ships as both the npm bin and the artifact script (see [Which config keys claim a conventional module](#which-config-keys-claim-a-conventional-module)); export `main` or make the module self-executing, because a `default`-only module would run as the bin but ship as an inert script (`AB4738`). | Prefix a path segment with `_`, or claim the file with an explicit `scripts` entry |
81
+
|`src/scripts/<name>.tsx`| Rendered script: the async default component receives `{ argv, signal }` and renders through the Agent renderer with the CLI output contract (`--json`, `--ndjson`, TTY progress, piped Markdown). Compiles to `scripts/<name>.mjs` plus a `scripts/<name>-flight.mjs` react-server worker. The extension is the explicit, visible contract — plain `.ts` scripts are never wrapped in React behavior, and explicit `scripts` config entries stay plain regardless of extension. A `bin` entry that references a rendered script is `AB4737` unless the module exports both the default component (for the script) and a named `main` (for the bin envelope); with both, the module serves both surfaces. | Rename to `.ts`, prefix a path segment with `_`, or claim the file with an explicit `scripts` entry |
82
82
|`src/cli/**/*.{ts,tsx}`| Routed CLI commands compiled into one collision-checked command graph and one generated package executable named after `plugin.name` (superseding the `src/cli.ts` bin convention for the project). Nesting is identity: `src/cli/library/audit.ts` runs as `<bin> library audit`. Plain `.ts` commands execute directly and print one canonical JSON line; `.tsx` commands render through the dispatcher with the four output modes. |`bin: false`, `routes.cli: 'conventional'`, or prefix a path segment with `_`|
83
83
|`src/events/<family>/<event>.{ts,tsx}`, `src/events/stop.{ts,tsx}`| Semantic event route: the path is the canonical event family (`src/events/tool/after.tsx` is `tool/after`; `stop` is the one top-level family) and must be one of the admitted `canonicalAgentEvents`. The optional static `config` (`AgentEventRouteConfig`: `targets`, `tools`, `runtime: 'shared' \| 'standalone'`, `fallback`, `delivery`, `timeoutMs`) restricts hosts and selects the execution mode; the async default Server Component receives `AgentEventRouteProps` (`{ canonical, native, signal }`) and returns `Agent.*` output that the selected host adapter encodes into its native hook envelope. Application code never branches on host JSON or emits native hook documents; per-host support is a capability state (`supported`/`degraded`/`unavailable`/`prohibited`) surfaced by `inspect` and enforced at build time (`AB4817`, `AB4823`–`AB4825`). | Restrict `config.targets`, or prefix a path segment with `_`|
84
84
|`src/state.ts`| Project state definition: default-exports `defineState({ ... })`; generated MCP, routed-CLI, and rendered-script request scopes mount `(await agent()).state` and `.notices`. |`state: false`, or rename the file to `_state.ts`|
@@ -87,6 +87,36 @@ entries carry `provenance.kind: 'conventional'` in the normalized model.
87
87
Route and package entry conventions match `.ts` and `.tsx` files exactly;
88
88
the state convention is specifically `src/state.ts`.
89
89
90
+
### Which config keys claim a conventional module
91
+
92
+
An explicit config entry that references a module under a conventional route
93
+
directory *claims* it: the module belongs to that declaration and leaves
94
+
conventional discovery. Claims are decided by the module path the entry
95
+
resolves to, so nothing is ever compiled twice into one artifact output. The
96
+
exception is `bin`: a bin compiles to `dist/bin/<name>.js`, which is disjoint
97
+
from every artifact output, and the bin envelope and the artifact-script
98
+
envelope run the same `main`, so a direct `src/scripts/<name>` child a `bin`
99
+
entry references stays a conventional script and ships on both surfaces. (A
100
+
nested `src/scripts/<dir>/<name>` module or one whose stem is not a safe route
101
+
identity — which the flat scripts artifact could not ship anyway — stays
102
+
claimed, so a bin-only entry there never turns into `AB4808` or `AB4803`.)
103
+
`inspect` shows such a module under both `packageBuild.bins` and `scripts`; no
104
+
diagnostic fires, because that is the intended "same entry, npm bin + hook
105
+
target" shape. `lib` is not an executable surface, so a `lib.entry` claims
106
+
its module like every other key.
107
+
108
+
| Config key | Claims a module under | Effect on a `src/scripts/<name>.ts` module it references |
109
+
| --- | --- | --- |
110
+
|`scripts.<name>`| every route directory | Claimed: the explicit entry ships it as `scripts/<name>.mjs`; the convention no longer applies. A *different* module under `src/scripts/` sharing the configured `<name>` is `AB4809`. |
111
+
|`hooks.<event>[].handler`| every route directory | Claimed: the module is a hook handler compiled under `hooks/`, not an artifact script. |
112
+
|`mcp.servers.<id>.entry`, `mcp.servers.<id>.apps.<app>.entry` / `.template`| every route directory | Claimed: the module is the server or App entry compiled under `mcp/` or `mcp-apps/`. |
113
+
|`lib.entry`| every route directory | Claimed: the module is the library entry compiled to `dist/<stem>.js`, not an artifact script. |
114
+
|`bin.<name>`|`src/cli/**`, `src/events/**`, `src/mcp/**`, `src/providers/*`, and any `src/scripts/**` module that is nested or unsafely named — **not** a safely named direct `src/scripts/<name>` child | Not claimed: the module ships as both `dist/bin/<name>.js` and `scripts/<name>.mjs`. The module must export `main` or be self-executing: a plain `default`-only module is `AB4738`, and a rendered `src/scripts/<name>.tsx` must export both the default component and `main`, otherwise `AB4737`. |
115
+
116
+
To ship a `src/scripts/` module as a bin only, prefix a path segment with `_`
117
+
(`src/scripts/_hauler.ts`): private segments opt the module out of discovery
118
+
while the `bin` entry still references it explicitly.
119
+
90
120
### Config beside a route-generated MCP server
91
121
92
122
A `mcp.servers.<server>` block whose `<server>` the route graph compiles in
0 commit comments