refactor(blast,cli,serve): Zod-Core-Out vertical slice (#109)#110
Merged
stackbilt-admin merged 1 commit intomainfrom Apr 16, 2026
Merged
refactor(blast,cli,serve): Zod-Core-Out vertical slice (#109)#110stackbilt-admin merged 1 commit intomainfrom
stackbilt-admin merged 1 commit intomainfrom
Conversation
Prove the shared-schema pattern on blast end-to-end:
@stackbilt/blast
- Add zod@^3.24.1 as a runtime dependency
- Add BlastInputSchema and BlastOutputSchema with .describe() on every field
- Add high-level analyze(input) that composes buildGraph + blastRadius
- Extract DEFAULT_MAX_DEPTH constant; schema and blastRadius both reference it
- Make topHotFiles tie-break deterministic (secondary sort by filename)
- analyze validates seed existence with a descriptive error
CLI blast.ts
- Route argv through BlastInputSchema.parse; Zod owns the --depth rules
- Call analyze() instead of buildGraph + blastRadius directly
- Export detectTsconfigAliases for reuse in serve.ts
MCP serve.ts
- Register charter_blast tool; handler parses raw args through BlastInputSchema,
auto-detects tsconfig aliases, calls analyze, returns structured JSON
- Advertised inputSchema uses a simple raw shape (SDK's ZodRawShapeCompat
rejects .default() chains); defaults are applied inside the handler
Tests
- Structural assertions (no byte-identical snapshots)
- Defaults verified, invalid maxDepth/empty seeds rejected
- analyze agrees with low-level API on affected files
- hotFiles tie-break determinism verified
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
stackbilt-admin
pushed a commit
that referenced
this pull request
Apr 16, 2026
Addresses feedback on PR #111: - credentials.test.ts: mock node:fs so loadCredentials returns deterministic results instead of reading the developer's real ~/.charter/credentials.json. Empty/whitespace-env-var cases are now pinned to the stored-credentials fallback with stubbed data rather than being conditionally skipped. Added explicit "env wins when both sources set" test that was claimed by the describe block but never exercised. - STACKBILT_API_BASE_URL companion env var: resolveApiKey now returns a baseUrl from the env when source='env'. Fixes the silent base-URL drop for users migrating from `charter login --url https://custom ...`. - login.ts: --logout now emits the deprecation notice before clearing credentials. One-line fix; users exiting the deprecated surface see the upgrade path. - CLI README: "Authentication (optional)" gains a one-line security caveat about env-var inheritance by child processes, recommending per-invocation setting in CI over global export in shared shells. - auth-wiring.test.ts (new): mocks resolveApiKey and EngineClient; pins the useGateway decision for `run` (scaffold vs build) and asserts architect forwards the resolved apiKey + baseUrl into the client constructor. 5 tests covering env/credentials/null. All 16 auth-related tests pass (credentials: 9, login: 2, auth-wiring: 5). Full suite: 413/415 pass; the two failures are the same WSL-flaky precommit-hook integration test already flagged on PR #110 and unrelated to this diff. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 tasks
stackbilt-admin
added a commit
that referenced
this pull request
Apr 16, 2026
) * feat(cli): STACKBILT_API_KEY env var auth; deprecate charter login Additive preparation for the 1.0 package split (see charter RFC TBD). No public API removed or renamed — follows OSS update policy. Why The CLI currently solicits a Stackbilt API key via `charter login`, which is the most eyebrow-raising OSS/commercial coupling in the package: an OSS governance tool writing a bearer token to ~/.charter/credentials.json. Supporting the standard env-var path lets users authenticate the commercial commands (`run`, `architect`) without the CLI persisting anything to disk, materially improving the audit story now while the full split to @stackbilt/build is scoped and sequenced separately. What changes credentials.ts + API_KEY_ENV_VAR = 'STACKBILT_API_KEY' + resolveApiKey() → { apiKey, source: 'env' | 'credentials', baseUrl? } | null Env var wins over stored credentials; empty/whitespace env var falls through. loadCredentials(), saveCredentials(), clearCredentials() preserved unchanged. commands/architect.ts, commands/run.ts - loadCredentials() call sites → resolveApiKey(). `run`'s useGateway decision now honors the env var path identically to stored creds. commands/login.ts - printDeprecationNotice() on stderr for every invocation except --logout. Command functionality unchanged. Help text reworked: env var is the "Preferred" path, stored credentials the "Deprecated alternative." If the env var is set and no --key is given, we report the env var as the authoritative source. http-client.ts - Scaffold auth-error string now points at STACKBILT_API_KEY first, `charter login` marked deprecated. Tests + credentials.test.ts — 4 tests covering env-var precedence, trimming, empty-string fallthrough, whitespace-only fallthrough. + login.test.ts — 2 tests covering the stderr deprecation notice and env-var reporting path. All 405 existing tests still pass. Docs - packages/cli/README.md: new "Authentication (optional)" section documenting the env var and login deprecation. Root README unchanged — that rewrite ships with the package split. - CHANGELOG.md: [Unreleased] section added with Added/Deprecated/Changed subsections. Follow-ups (not in this PR) - Root README rewrite when package split lands - RFC: extract commercial surface into @stackbilt/build - 1.0 cut removes login, credentials persistence, run/architect/scaffold Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * review(cli): fix test isolation, add baseUrl env var, wiring tests Addresses feedback on PR #111: - credentials.test.ts: mock node:fs so loadCredentials returns deterministic results instead of reading the developer's real ~/.charter/credentials.json. Empty/whitespace-env-var cases are now pinned to the stored-credentials fallback with stubbed data rather than being conditionally skipped. Added explicit "env wins when both sources set" test that was claimed by the describe block but never exercised. - STACKBILT_API_BASE_URL companion env var: resolveApiKey now returns a baseUrl from the env when source='env'. Fixes the silent base-URL drop for users migrating from `charter login --url https://custom ...`. - login.ts: --logout now emits the deprecation notice before clearing credentials. One-line fix; users exiting the deprecated surface see the upgrade path. - CLI README: "Authentication (optional)" gains a one-line security caveat about env-var inheritance by child processes, recommending per-invocation setting in CI over global export in shared shells. - auth-wiring.test.ts (new): mocks resolveApiKey and EngineClient; pins the useGateway decision for `run` (scaffold vs build) and asserts architect forwards the resolved apiKey + baseUrl into the client constructor. 5 tests covering env/credentials/null. All 16 auth-related tests pass (credentials: 9, login: 2, auth-wiring: 5). Full suite: 413/415 pass; the two failures are the same WSL-flaky precommit-hook integration test already flagged on PR #110 and unrelated to this diff. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Kurt Overmier <kurt@stackbilt.dev> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
stackbilt-admin
pushed a commit
that referenced
this pull request
Apr 16, 2026
Synchronized version bump for all @stackbilt/* packages to 0.11.0. Highlights: - STACKBILT_API_KEY / STACKBILT_API_BASE_URL env-var auth (#111) - Zod-Core-Out vertical slice for @stackbilt/blast + charter_blast MCP tool (#110) - charter login deprecated; scheduled for 1.0 removal alongside @stackbilt/build split (#112) See CHANGELOG.md [0.11.0] for the full entry. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
stackbilt-admin
pushed a commit
that referenced
this pull request
Apr 17, 2026
…114) Mirror the blast Core-Out refactor (#110) on @stackbilt/surface: SurfaceInputSchema + SurfaceOutputSchema become the authoritative contract, analyze(input) composes extractSurface, and `charter_surface` joins `charter_blast` as an MCP tool in `charter serve`. Motivation: prerequisite for the #113 repo-brief RFC. The brief's Surface section consumes analyze() output; without Zod-validated schemas the brief shape would reshape on every surface primitive refactor. - @stackbilt/surface: add zod runtime dep, RouteSchema / SchemaTableSchema / SchemaColumnSchema / SurfaceInputSchema / SurfaceOutputSchema, analyze(), DEFAULT_SURFACE_EXTENSIONS / DEFAULT_SURFACE_IGNORE_DIRS exports - @stackbilt/cli: route `charter surface` argv through SurfaceInputSchema, map ZodError to CLIError; register `charter_surface` MCP tool with format: "json" | "markdown" input - Route/SchemaTable/SchemaColumn become z.infer<> aliases, structurally identical to the prior interfaces — OSS additive-only policy preserved - Tests: schema validation + analyze structural assertions via SurfaceOutputSchema.parse on fixtures Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
13 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #109.
Proves the Zod-Core-Out pattern end-to-end on
@stackbilt/blast: one Zod schema is the source of truth; the CLI and the newcharter_blastMCP tool are thin adapters over the same pureanalyze()core.What ships
@stackbilt/blastzod@^3.24.1as a runtime dep. README's "zero runtime dependencies" claim updated — Zod is now the authoritative contract at the package boundary.BlastInputSchema,BlastOutputSchema,BlastInput,BlastOutput,DEFAULT_MAX_DEPTH,analyze().analyze(input: BlastInput): BlastOutputcomposesbuildGraph+blastRadiusand is what both adapters call.topHotFilesnow uses a deterministic secondary sort (filename ascending) so tiedimporterscounts no longer depend on filesystem scan order.buildGraph,blastRadius,topHotFiles, interfaces) preserved — fully additive per the OSS update policy.CLI (
packages/cli/src/commands/blast.ts)BlastInputSchema.parse→analyze()→ human formatter. Zod owns the--depthpositive-integer rule; the manualNumber.isFinitecheck is gone.detectTsconfigAliasesis now exported soserve.tsreuses it.MCP (
packages/cli/src/commands/serve.ts)charter_blasttool. Handler parses raw JSON-RPC args throughBlastInputSchema(defaults applied there), auto-detects tsconfig aliases at scan root if the caller didn't supply them, callsanalyze, returns structured JSON.inputSchemais a simpleZodRawShape— the SDK'sZodRawShapeCompatoverload trips TS2589 on anything containing.default()chains. Defaults are documented inline in.describe()strings and applied atparsetime. Authoritative schema isBlastInputSchema; the advertised shape is a documentation surface.Decisions locked per review on #109
See the comment thread — abbreviated:
maxDepthSoT: singleDEFAULT_MAX_DEPTHconstant referenced by both the schema's.default()andblastRadius's in-function fallback. The publicblastRadiusbehavior is unchanged — the review's concern about silent drift is addressed by the shared constant rather than by removing the in-function default (which would have broken existing programmatic consumers).BlastOutputSchema.parse(result)and targeted field checks;hotFilesdeterminism is enforced at the source, not in tests.@stackbilt/blast. Zod becomes a runtime dep; the "zero deps" purity was aesthetic, and colocating schemas in blast is the only arrangement that preserves the "one schema, many adapters" promise for programmatic consumers.Known follow-ups (out of scope for this PR)
as Functioncasts on all threeregisterToolcall sites once the SDK ships typings that don't hit TS2589. Reviewer's nitpick; the new registration uses the same cast as the existing two rather than introducing a mismatched style.validate,drift,classify,surface— one vertical slice per follow-up PR.Acceptance criteria
@stackbilt/blastexports Zod schemas; existingBlastRadiusResult/BlastOptionsinterfaces still importable.charter blast <path>JSON output carries the same fields as before (keys in the same order).charter serveregisterscharter_blast;tools/listsurfaces the JSON schema withrequired: ["seeds"]and per-field descriptions.analyzevia synthetic fixtures (no child-process tests).BlastInputSchema.parse({seeds:['x']})yieldsmaxDepth === DEFAULT_MAX_DEPTH(3).BlastOutputSchema.parse(analyze(...))succeeds on a representative fixture.hotFilesordering is deterministic (filename ascending).packages/blast/package.jsonlistszodas adependency, notdevDependency.Test plan
pnpm buildcleanpnpm exec vitest run packages/blast— 27/27 pass (includes 11 new tests for schema + analyze)precommit-hook.test.ts > auto-tidies bloated CLAUDE.md) is a pre-existing WSL timing flake — passes in isolation (35s), times out under parallel IO contention. Not caused by this change.tools/listreturnscharter_blastwith correct JSON schema;tools/callwith{"seeds":["src/b.ts"]}returns structurally-identical output tocharter blast src/b.ts --format jsonagainst the same fixture.