Skip to content

Commit bf6de9c

Browse files
Merge remote-tracking branch 'origin/main' into docs/rspress-website-consolidated
2 parents bd09399 + 1c82c31 commit bf6de9c

10 files changed

Lines changed: 487 additions & 23 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"agent-bundle": minor
3+
---
4+
5+
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)

‎docs/diagnostics.md‎

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -195,16 +195,27 @@ development-only fallback can never produce a release artifact, so
195195
| `AB4011` | warning | `package.json` is unusable — unparsable, not a JSON object, or symlinked outside the project root. |
196196
| `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. |
197197

198-
## Migration nudges (`AB4730`–`AB4736`)
198+
## Migration nudges and convention claims (`AB4730`–`AB4738`)
199199

200200
The entry conventions and the framework-owned stdio lifecycle shell (RFC #50)
201201
replaced patterns consumers previously wrote by hand. When `validate`,
202202
`inspect`, `build`, or `dev` prepares project source and finds one of those
203203
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.
208219

209220
### `AB4730` — self-connecting stdio MCP entry
210221

@@ -276,6 +287,52 @@ Recover: move the document under `src/skills/`, `src/commands/`, or
276287
`src/rules/`. Explicit `skills` paths remain valid anywhere. Published
277288
artifact paths remain `skills/`, `commands/`, and `rules/`.
278289

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+
279336
## Prebuilt payloads (`AB4740`–`AB4750`)
280337

281338
The `payload` block and `{ prebuilt: ... }` entries (see

‎docs/entry-conventions.md‎

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ entries carry `provenance.kind: 'conventional'` in the normalized model.
7777
| `src/mcp/<server-id>.ts` | Stdio entry for the declared MCP server `<server-id>` that names no `entry`, `command`, or `url`. | Declare `entry` explicitly |
7878
| `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` |
7979
| `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 |
8282
| `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 `_` |
8383
| `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 `_` |
8484
| `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.
8787
Route and package entry conventions match `.ts` and `.tsx` files exactly;
8888
the state convention is specifically `src/state.ts`.
8989

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+
90120
### Config beside a route-generated MCP server
91121

92122
A `mcp.servers.<server>` block whose `<server>` the route graph compiles in

‎packages/agent-bundle/README.md‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,13 @@ agent-bundle also owns the npm-facing package build: `bin` entries become self-e
8787
`dist/bin/<name>.js` bundles (shebang, executable bit, generated `main(argv)` envelope) and the
8888
optional `lib` entry becomes `dist/<stem>.js` with declarations (resolving `typescript` from the
8989
project). The `src/cli.ts`, `src/index.ts`, and `src/mcp/<server-id>.ts` conventions fill these in
90-
when the config is silent; config always wins and `bin: false` / `lib: false` opt out. MCP server
90+
when the config is silent; config always wins and `bin: false` / `lib: false` opt out. An explicit
91+
`scripts`, `hooks`, `lib`, or `mcp` entry claims the module it references out of conventional route
92+
discovery, but a `bin` entry does not claim a `src/scripts/<name>.ts` module: the same file
93+
ships as both `dist/bin/<name>.js` and the artifact `scripts/<name>.mjs` (the module must export
94+
`main` or be self-executing: a `default`-only plain script is `AB4738`, and a rendered `.tsx` script
95+
must export both its default component and `main` or it is `AB4737`; prefix a path segment with `_`
96+
for a bin-only module). MCP server
9197
entries that default-export a server factory are wrapped in the framework stdio lifecycle shell
9298
(console-to-stderr guard with raw stdout restored for protocol frames, SIGINT 130 / SIGTERM 143,
9399
stdin-EOF exit 0, bounded shutdown, heartbeat), also available directly from

0 commit comments

Comments
 (0)