Skip to content

feat: add per-type subcommand helpers (tool.data/classification/procedureSubcommand) - #51

Merged
beogip merged 1 commit into
mainfrom
beogip/add-issue-41
Jul 29, 2026
Merged

feat: add per-type subcommand helpers (tool.data/classification/procedureSubcommand)#51
beogip merged 1 commit into
mainfrom
beogip/add-issue-41

Conversation

@beogip

@beogip beogip commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Summary

Adds three chainable instance methods on the Tool class as a pure type-safe construction layer in front of an unchanged runtime:

Method Composes via Fixed fields
dataSubcommand dataTypeOutput(output) ok, message
classificationSubcommand classificationTypeOutput(classification, rest) ok, message, classification
procedureSubcommand procedureTypeOutput(output) ok, message, instructions

Each one composes its output schema through the corresponding existing output helper and delegates to this.subcommand(...), so validateRegistration is inherited for free — reserved-name collisions (schema/help), duplicate names, and missing schemas keep throwing exactly as before. No dispatch, validation, or process-lifecycle code was touched.

subcommand() is now marked @deprecated, naming the next breaking change as its removal point. The low-level API and the three standalone output helpers stay exported but are no longer documented — every tool output must be one of the three tool types.

Closes #41

Naming

The methods ship as dataSubcommand / classificationSubcommand / procedureSubcommand — without a Type infix.

The infix was originally planned for symmetry with the dataTypeOutput / classificationTypeOutput / procedureTypeOutput helpers renamed in #48. That symmetry stopped being worth paying for once this PR undocumented those helpers: the Type would have been noise on the only documented API, kept for consistency with an API that is no longer in the README. The tool type still reads as the adjective — procedureSubcommand is "register a Procedure-type subcommand".

The exported types keep the infixDataTypeSubcommandSpec, ClassificationTypeSubcommandSpec, ProcedureTypeSubcommandSpec, DataTypeSchema, ClassificationTypeSchema, ProcedureTypeSchema are unchanged. There Type is genuinely semantic ("the schema of the Data type") and keeps them aligned with the *TypeOutput helpers that construct them.

Note: issue #41's title still reads tool.l1/l2/l3Subcommand, from before the #48 vocabulary rename. The shipped names are the ones above.

Verification

Gate Result
bun test 153 pass, 0 fail
bunx tsc --noEmit clean
bunx biome check . clean¹
Example output (AC-13) byte-identical to pre-change

¹ One pre-existing info notice: biome.json pins schema 2.4.15 while the local CLI is 2.4.10. Unrelated to this diff.

Compile-time acceptance criteria are enforced by @ts-expect-error fixtures in tests/fixtures/dummy-tool.ts, whose failure mode is Unused '@ts-expect-error' directive under tsc --noEmit. Both were confirmed to assert the intended diagnostic rather than being satisfied by an unrelated error:

  • AC-8 — procedure handler omitting instructionsTS2322: Property 'instructions' is missing ... required in type 'HandlerReturn<ProcedureTypeSchema<...>>'
  • AC-7 — classification output missing classificationTS2322: Type '{ score: z.ZodNumber; }' is not assignable to type 'ClassificationShape' ... Property 'classification' is missing

All 13 acceptance criteria in the plan are covered. Edge cases 1–6 have direct tests; 7 and 8 were explicitly decided out of scope during discovery.

Review

Reviewed with code-reviewer + code-attacker. Verdict: PASS — zero P0, zero P1 attributable to this change.

The attacker surfaced one genuine P1 — an author-declared message/instructions in an output shape silently overrides the fixed envelope field, because all three helpers spread ...fields last. It was independently confirmed to reproduce through the legacy tool.subcommand + standalone-helper path, so it already ships on main and is not introduced here. It lives in src/output-helpers.ts, which this PR does not modify. Filed separately as #50 so this change stays additive and in scope.

Two non-blocking P2s, both pre-existing: the excess-property-check gap on handler returns (object literals returned from contextually-typed arrow functions skip TypeScript's excess-property check — note this is not caused by the T | Promise<T> union, so a fix targeting that would be a no-op), and no async-handler test through the new methods (verified working manually).

Files changed

README.md                                          |  49 ++--
docs/specs/issue-41-...-subcommand-helpers.md      | 257 +++++++++++++++++++
examples/README.md                                 |   2 +-
examples/changeset.ts                              |  32 +--
src/index.ts                                       |   7 +
src/tool-class.ts                                  | 147 ++++++++++-
src/types.ts                                       | 135 +++++++++-
tests/cli.test.ts                                  |  78 ++++++
tests/fixtures/dummy-tool.ts                       |  92 +++++++
tests/index.test.ts                                | 279 +++++++++++++++++++++
10 files changed, 1018 insertions(+), 60 deletions(-)

Plan: docs/specs/issue-41-feat-add-per-type-subcommand-helpers.md — level full, depth medium.

Add three chainable instance methods on the Tool class — dataSubcommand,
classificationSubcommand and procedureSubcommand — as a pure type-safe
construction layer in front of an unchanged runtime. Each composes its
output schema via the corresponding output helper and delegates to
subcommand(), inheriting validateRegistration for reserved-name and
duplicate-name rejection.

Mark subcommand() @deprecated, naming the next breaking change as its
removal point. The low-level API and the standalone output helpers stay
exported but are no longer documented — every tool output must be one of
the three tool types.

Rewrite the README, examples/changeset.ts and examples/README.md against
the new API. Example output is byte-identical to before the change.
@beogip
beogip merged commit 72ae2cd into main Jul 29, 2026
6 checks passed
@beogip
beogip deleted the beogip/add-issue-41 branch July 29, 2026 17:26
beogip pushed a commit that referenced this pull request Jul 29, 2026
# [0.3.0](v0.2.0...v0.3.0) (2026-07-29)

### Features

* add per-type subcommand helpers ([#41](#41)) ([#51](#51)) ([72ae2cd](72ae2cd))
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 0.3.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: add per-layer subcommand helpers (tool.l1/l2/l3Subcommand)

1 participant