feat(core): extract config into opt-in middleware with layered resolution#155
feat(core): extract config into opt-in middleware with layered resolution#155zrosenbauer wants to merge 7 commits intomainfrom
Conversation
…tion (#154) Move config loading out of the core runtime into `@kidd-cli/core/config` middleware. Config is no longer baked into CommandContext — it is added via module augmentation when the middleware is imported. The new middleware supports: - Single-mode: loads config from cwd (existing behavior) - Layered mode: global > project > local resolution with deep merge Co-Authored-By: Claude <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: 2a5d3ba The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Merging this PR will not alter performance
Comparing Footnotes
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughRemoved the built-in CLI-level config surface: the TConfig generic and all Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/core/src/test/types.ts (1)
13-24:⚠️ Potential issue | 🟠 Major
createTestContext()/runHandler()no longer have a safe path for config-aware handlers.After importing
@kidd-cli/core/config,CommandContextis augmented withconfig, but Lines 13-24 and 80-83 no longer let tests seed that field.packages/core/src/test/context.ts, Lines 24-49, still builds the context throughcreateContext(), andpackages/core/src/context/create-context.ts, Lines 15-51, do not attachconfig. That means handler tests can typecheck againstctx.configand then readundefinedat runtime. Please keep a config/decorated override path or add a middleware-aware test helper before removing the old config option.Also applies to: 31-45, 80-83
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/core/src/test/types.ts` around lines 13 - 24, The TestContextOptions interface (in types.ts) and the test helpers createTestContext() / runHandler() lack a way to seed a config property, so tests that import `@kidd-cli/core/config` get ctx.config === undefined at runtime; update TestContextOptions to include an optional readonly config?: ConfigType (or a generic decorated field), and adjust createTestContext(), runHandler(), and the test context builder in packages/core/src/test/context.ts to pass that config value through into the object created by createContext() (or add a middleware-aware override that attaches config before returning the context) so handlers can safely read ctx.config during tests; reference the symbols TestContextOptions, createTestContext, runHandler, and createContext when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/core/src/middleware/config/config.ts`:
- Around line 24-25: The code returns early in the "no configs found" and
"merged-validation failure" paths without populating the layered-mode provenance
field, so ensure ctx.raw.configLayers is always assigned before any early
return: set ctx.raw.configLayers to the appropriate metadata (e.g., an empty
array or the partial layer list you computed) in the "no configs found" path and
right before returning on merged-validation failure so callers still receive
provenance; update the same behavior wherever configLayers may be skipped (the
other early-return branches that check layered mode) and keep the symbol
ctx.raw.configLayers as the single source of truth.
- Around line 65-69: The current checks conflate loadError with “no config” and
silently return an empty config; instead, change the logic around client.load()
so that loadError (or any validation/merged-schema error) is surfaced (throw or
propagate the error) rather than falling back to Object.freeze({}), and only
fall back to an empty config when there is no error and the result is genuinely
absent. Update the branches that use the pattern (loadError || !result) —
including the places that call client.load(), inspect loadError and result
separately, throw or pass the load/validation error up (or call the existing
error-handling path), and only call decorateContext(ctx, 'config',
Object.freeze({})) when result is undefined/null with no loadError.
- Around line 129-135: The code tries to defineProperty('configLayers') on a
frozen ctx.raw (created by createContext()), causing a TypeError; fix by either
adding configLayers to ctx.raw before createContext() freezes it, or stop
mutating ctx.raw and instead attach the metadata to an extensible object (for
example ctx.configLayers or ctx._configLayers) so you do not call
Object.defineProperty on a non-extensible object; update the logic around
createContext()/ctx.raw and the property access sites to read from the new
location (ctx.configLayers or the pre-freeze assignment) so JSDoc promise is
satisfied.
In `@packages/core/src/middleware/config/types.ts`:
- Line 36: Replace the inline import('zod').ZodTypeAny usage with a proper
top-level type import: add "import type { ZodTypeAny } from 'zod';" at the top
of the file and change the generic constraint in ConfigMiddlewareOptions<TSchema
extends import('zod').ZodTypeAny> to ConfigMiddlewareOptions<TSchema extends
ZodTypeAny>; also update any other occurrences in this file that use
import('zod').ZodTypeAny (the two additional spots flagged) to use the imported
ZodTypeAny instead.
- Around line 69-79: The current declaration exposes CommandContext.config as
DeepReadonly<Record<string, unknown>> which conflicts with consumer
redeclarations using ConfigType<typeof configSchema>; change the exported
augmentation surface to a registry pattern instead: add and export an interface
named ConfigContextRegistry (or similar) in the config types module and update
the CommandContext declaration to type config as
ConfigContextRegistry['config']; update the docs to instruct consumers to
augment ConfigContextRegistry (using ConfigType<typeof configSchema>) rather
than redeclaring CommandContext.config so consumer-specific typed configs will
merge cleanly with the library declaration (refer to symbols: CommandContext,
ConfigType, ConfigContextRegistry, configSchema).
In `@packages/core/src/types/utility.ts`:
- Around line 59-61: The inline import in the InferSchema conditional type
should be replaced with a type-only import to satisfy consistent-type-imports:
add a top-level type-only import "import type { ZodType } from 'zod';" and
update the conditional to use ZodType directly (i.e. change "TSchema extends
import('zod').ZodType<infer TOutput>" to "TSchema extends ZodType<infer
TOutput>"). Keep the fallback AnyRecord and ensure the import is type-only so no
runtime require is introduced.
---
Outside diff comments:
In `@packages/core/src/test/types.ts`:
- Around line 13-24: The TestContextOptions interface (in types.ts) and the test
helpers createTestContext() / runHandler() lack a way to seed a config property,
so tests that import `@kidd-cli/core/config` get ctx.config === undefined at
runtime; update TestContextOptions to include an optional readonly config?:
ConfigType (or a generic decorated field), and adjust createTestContext(),
runHandler(), and the test context builder in packages/core/src/test/context.ts
to pass that config value through into the object created by createContext() (or
add a middleware-aware override that attaches config before returning the
context) so handlers can safely read ctx.config during tests; reference the
symbols TestContextOptions, createTestContext, runHandler, and createContext
when making the change.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 7ca997a0-39be-4381-a43e-05cc9a927c73
⛔ Files ignored due to path filters (1)
.changeset/config-middleware.mdis excluded by!.changeset/**
📒 Files selected for processing (25)
packages/core/package.jsonpackages/core/src/cli.test.tspackages/core/src/cli.tspackages/core/src/command.tspackages/core/src/context/create-context.test.tspackages/core/src/context/create-context.tspackages/core/src/context/types.tspackages/core/src/index.tspackages/core/src/middleware/config/config.tspackages/core/src/middleware/config/index.tspackages/core/src/middleware/config/types.tspackages/core/src/runtime/runtime.test.tspackages/core/src/runtime/runtime.tspackages/core/src/runtime/types.tspackages/core/src/test/command.tspackages/core/src/test/context.test.tspackages/core/src/test/context.tspackages/core/src/test/handler.tspackages/core/src/test/middleware.tspackages/core/src/test/types.tspackages/core/src/types/cli.tspackages/core/src/types/command.tspackages/core/src/types/index.tspackages/core/src/types/utility.tspackages/core/tsdown.config.ts
💤 Files with no reviewable changes (6)
- packages/core/src/cli.test.ts
- packages/core/src/index.ts
- packages/core/src/test/context.test.ts
- packages/core/src/test/command.ts
- packages/core/src/types/index.ts
- packages/core/src/runtime/runtime.test.ts
- Fix frozen ctx.raw: use decorateContext for configLayers instead of
Object.defineProperty on frozen object
- Surface config load/validation errors via ctx.fail() instead of
silently falling back to empty config
- Always attach configLayers on all code paths in layered mode
- Replace import('zod') type annotations with proper top-level imports
- Add ConfigRegistry pattern for type-safe module augmentation
Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/core/src/middleware/config/config.ts`:
- Around line 159-161: The code in loadLayer currently swallows per-layer load
errors by returning a null config when loadError is truthy; update loadLayer to
emit a visible warning before returning: detect when loadError is present and
call ctx.log.warn(...) including contextual info (entry.name, loadError.message
or loadError.stack and any result.filePath if available) so users see which
layer failed, then keep the existing return shape ({ config: null, filePath:
null, format: null, name: entry.name }); keep loadSingle() behavior unchanged
but ensure loadLayer logs the error rather than silently dropping it.
- Around line 134-138: The function currently calls ctx.fail(`Config validation
failed: ${validationError.message}`) but then continues executing
decorateContext(ctx, 'config', Object.freeze(validated as Record<string,
unknown>)); stop further execution when validation fails by returning
immediately after ctx.fail; modify the control flow in the block that checks
validationError (the branch that calls ctx.fail) so it returns (or throws) right
after ctx.fail to prevent decorateContext from running with an invalid or absent
validated value.
In `@packages/core/src/middleware/config/types.ts`:
- Around line 68-81: The JSDoc example for ConfigType is misleading because it
shows "interface ConfigRegistry extends ConfigType<...>" even though ConfigType
is a type alias; update the example to show adding a named property to the
registry instead (e.g., add a `config` property typed as ConfigType<typeof
configSchema>) so consumers clearly see how to augment the ConfigRegistry;
update the comment around the exported ConfigType type alias and the example to
reference ConfigType, ConfigRegistry, and configSchema accordingly.
- Around line 109-113: The declaration marks CommandContext.configLayers as
always present but decorateContext(..., 'configLayers', ...) is only called in
loadLayered (config.ts), so in single mode ctx.configLayers can be undefined;
fix by either making the type optional—change the CommandContext declaration to
readonly configLayers?: readonly ConfigLayer[]—or ensure loadSingle path also
attaches an empty array (call decorateContext(ctx, 'configLayers', []) in the
non-layered initialization), and update any callers accordingly; reference
symbols: CommandContext, configLayers, decorateContext, loadLayered, loadSingle
(or the non-layered init path) to locate the change.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 191b8371-0309-4d90-aa9f-c1cd16efd04c
📒 Files selected for processing (4)
packages/core/src/middleware/config/config.tspackages/core/src/middleware/config/index.tspackages/core/src/middleware/config/types.tspackages/core/src/types/utility.ts
Replace eager config decoration with a lazy ConfigHandle on ctx.config.
Config is no longer loaded during the middleware pass by default — instead,
handlers call ctx.config.load() on demand. Results are cached after the
first successful load.
- Add ConfigHandle<T> with load() returning Result<ConfigLoadCallResult<T>>
- Support three load modes: single cwd, layered merge, named layer
- Add eager option to pre-load during middleware pass
- Remove ctx.configLayers (layer metadata now in load({ layers: true }) result)
- Add async headers support to http() middleware for config interop
- Update advanced example and all documentation
Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 6
♻️ Duplicate comments (1)
packages/core/src/middleware/config/types.ts (1)
133-140:⚠️ Potential issue | 🟠 MajorUse a named registry property in the augmentation example.
With
ResolvedConfig = ConfigRegistry[keyof ConfigRegistry], the currentinterface ConfigRegistry extends ConfigType<typeof configSchema> {}example makesResolvedConfigcollapse to a union of field value types instead of the config object. Consumers need to add a property such asconfig: ConfigType<typeof configSchema>.Suggested doc fix
* import type { ConfigType } from '@kidd-cli/core/config' * * declare module '@kidd-cli/core/config' { - * interface ConfigRegistry extends ConfigType<typeof configSchema> {} + * interface ConfigRegistry { + * config: ConfigType<typeof configSchema> + * } * }Also applies to: 156-158
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/core/src/middleware/config/types.ts` around lines 133 - 140, The augmentation example causes ResolvedConfig = ConfigRegistry[keyof ConfigRegistry] to become a union of value types; change the example to use a named registry property so the lookup yields the config object (e.g., ensure the example declares interface ConfigRegistry with a property like "config: ConfigType<typeof configSchema>" instead of extending ConfigType directly); update both places where the example appears (the block using ConfigRegistry and the later duplicate at lines ~156-158) and keep references to ConfigRegistry, ConfigType, ResolvedConfig, and configSchema so consumers can augment by adding a "config" property that maps to ConfigType<typeof configSchema>.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/concepts/configuration.md`:
- Around line 69-74: Add the missing `layers` middleware option to the options
table and explicitly document whether layered behavior is configured at
middleware creation or per-load; update the table entries (alongside `schema`,
`eager`, `name`) to include `layers` with its Type (`boolean`), Default, and
Description, and then make the examples consistent by either showing
`middleware({ layers: true })` or `load({ layers: true })` (pick one canonical
API) so the docs consistently present the supported API shape for `layers` and
the usage pattern referenced by `load({ layers: true })`.
In `@examples/advanced/src/commands/deploy/preview.ts`:
- Around line 17-20: The current destructuring const [error, { config }] = await
ctx.config.load() can throw because the second element may be null; instead
first await ctx.config.load() into [error, result], check if error is truthy and
return via ctx.fail(error.message), then safely extract result.config (or const
{ config } = result) for use; update the code surrounding ctx.config.load(), the
error check, and subsequent uses of config to use the safely extracted value.
In `@examples/advanced/src/commands/deploy/production.ts`:
- Around line 13-16: The code destructures the second element of the result of
ctx.config.load() into { config } before checking for error, which throws when
the payload is null; change to first assign the array result (e.g., const result
= await ctx.config.load() or const [error, payload] = ...), check if error and
return, then safely destructure payload to extract config (e.g., const { config
} = payload); apply the same pattern to the preview deploy command where the
same destructuring occurs.
In `@packages/core/src/middleware/config/config.ts`:
- Around line 189-190: The code currently constructs the config client with
schema and uses resolveLayer(client.load, ...) which lets resolveLayer drop
loadError and causes per-layer validation to block composition; instead, first
load raw layer objects without schema validation: create or use a loader that
does not validate (e.g., call createConfigClient without schema or use a
client.load variant that returns raw layer objects), call resolveLayer for all
layerDirs to get pre-validation ConfigLayer.config values, merge those raw
layers, then run validation once against the composed config (e.g., via
client.validate or by creating a schema-enabled client and validating the merged
result). Apply the same change to the other occurrence (lines 278-282) so all
layers are resolved pre-validation and only the merged config is validated once.
- Around line 154-156: The code returns ok({ config: Object.freeze({}) }) when
no config file was loaded, which violates the promised validated
ConfigLoadCallResult<TConfig>; instead return an error result so callers don't
receive an unvalidated empty config. In the function that handles
loading/validating the config (the branch that currently does return ok({
config: Object.freeze({}) })), replace that success return with an err(...)
describing "no config file found/loaded" (include context like attempted paths
or loader info) and ensure the other two identical branches (the ones at the
other noted locations) are changed the same way; do not skip schema validation
by fabricating an empty validated config.
- Around line 91-125: The current module-level cached variable unjustly memoizes
the first successful load regardless of callOptions; replace the single cached:
Result<...> | null with a cache keyed by the requested load mode (e.g. a
Map<string, Result<ConfigLoadCallResult<unknown>>>), compute a cacheKey inside
load() from callOptions (undefined -> "single", {layers:true} -> "layers",
{layer: 'project'|'global'|'local'} -> `layer:${name}`), check that cache for an
existing entry before calling match(...), and only store the result into the
cache map after verifying the call succeeded (the same [resultError] === null
check). Update all references to cached to use the keyed cache and preserve
existing use of loadNamedLayer, loadLayered, and loadSingle.
---
Duplicate comments:
In `@packages/core/src/middleware/config/types.ts`:
- Around line 133-140: The augmentation example causes ResolvedConfig =
ConfigRegistry[keyof ConfigRegistry] to become a union of value types; change
the example to use a named registry property so the lookup yields the config
object (e.g., ensure the example declares interface ConfigRegistry with a
property like "config: ConfigType<typeof configSchema>" instead of extending
ConfigType directly); update both places where the example appears (the block
using ConfigRegistry and the later duplicate at lines ~156-158) and keep
references to ConfigRegistry, ConfigType, ResolvedConfig, and configSchema so
consumers can augment by adding a "config" property that maps to
ConfigType<typeof configSchema>.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 28f3ee9d-0eff-4a76-86ce-f301020ae8a2
📒 Files selected for processing (20)
docs/concepts/configuration.mddocs/concepts/context.mddocs/guides/build-a-cli.mddocs/guides/testing-your-cli.mddocs/quick-start.mddocs/reference/bootstrap.mdexamples/advanced/README.mdexamples/advanced/src/commands/deploy/preview.tsexamples/advanced/src/commands/deploy/production.tsexamples/advanced/src/commands/status.tsexamples/advanced/src/index.tspackages/core/src/middleware/config/config.test.tspackages/core/src/middleware/config/config.tspackages/core/src/middleware/config/index.tspackages/core/src/middleware/config/types.tspackages/core/src/middleware/http/http.test.tspackages/core/src/middleware/http/http.tspackages/core/src/middleware/http/types.tspackages/core/src/stories/importer.tspackages/core/src/stories/viewer/stories-screen.tsx
- Fix null destructuring bug in example commands (preview, production) - Simplify match clauses for layer names using P.union - Validate empty config against schema (applies defaults, catches missing fields) - Load raw layers without per-layer schema validation in layered mode - Remove Object.freeze calls (type system enforces immutability) - Document layers and dirs middleware options in configuration.md Co-Authored-By: Claude <noreply@anthropic.com>
…/throw
Replace Result tuple return from load() with a simpler API:
- load() returns ConfigLoadCallResult or null on error
- load({ exitOnError: true }) calls ctx.fail() on error, guarantees non-null
Uses overloaded signatures so TypeScript narrows the return type based
on the exitOnError option.
Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
packages/core/src/middleware/config/config.ts (1)
96-98:⚠️ Potential issue | 🟠 MajorCache by effective load mode, not by first success.
The handle memoizes a single successful result for every later
load()call. Afterload(), a laterload({ layers: true })can never exposelayers, and afterload({ layer: 'project' }),load({ layer: 'global' })still returns project data. The cache needs to be keyed by the effective resolution mode instead of a single slot.Suggested fix
- let cached: ConfigLoadCallResult<unknown> | null = null + const cache = new Map<string, ConfigLoadCallResult<unknown>>() async function load( callOptions?: ConfigLoadCallOptions ): Promise<ConfigLoadCallResult<unknown> | null> { - if (cached !== null) { - return cached + const cacheKey = getCacheKey(callOptions) + const cached = cache.get(cacheKey) + if (cached) { + return cached } @@ - cached = result + cache.set(cacheKey, result) return result }Also applies to: 110-132
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/core/src/middleware/config/config.ts` around lines 96 - 98, The current implementation stores a single cached ConfigLoadCallResult in the variable cached which causes subsequent load() calls with different effective modes (e.g., load({ layers: true }) or load({ layer: 'global' })) to return the first-success result; change cached from a single slot to a keyed cache (e.g., a Map<string, ConfigLoadCallResult<unknown>>) keyed by the effective resolution mode derived from the load options (such as layers boolean and layer name) inside the handle/load logic so each distinct effective mode has its own cached result; update all reads/writes that reference cached to compute the key (stringify or canonicalize the relevant options) and read/write the Map, and ensure ConfigLoadCallResult typing is preserved.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/concepts/configuration.md`:
- Around line 79-107: The docs incorrectly show ctx.config.load() returning a
[error, result] tuple; update both examples to reflect that ConfigHandle.load()
returns the load result object or null: use const result = await
ctx.config.load() and check if (!result) then call ctx.fail('Failed to load
config') (and similarly for the layers example), keeping subsequent references
to result.config and result.layers unchanged.
In `@packages/core/src/middleware/config/config.test.ts`:
- Around line 49-59: createTestContext currently hardcodes meta.dirs.global to
".my-cli", causing layered resolution (via resolveGlobalPath) to read the real
host global config and make tests environment-dependent; change the test to
either (a) set meta.dirs.global to a test-specific temporary directory created
for the test (and ensure cleanup) when constructing createContext in
createTestContext, or (b) mock/stub resolveGlobalPath at the module boundary so
it returns a controlled test path; update all occurrences (including the other
block around lines 262-283) to use the temp-dir or mocked resolveGlobalPath
approach and reference createTestContext, createContext, and resolveGlobalPath
when making the change.
In `@packages/core/src/middleware/config/types.ts`:
- Around line 149-151: The current ResolvedConfig type collapses to a union
because it uses indexed access (ConfigRegistry[keyof ConfigRegistry]); change it
to preserve the object shape by using the registry type or a mapped type instead
(e.g. make ResolvedConfig = ConfigRegistry or ResolvedConfig = { [K in keyof
ConfigRegistry]: ConfigRegistry[K] }) so that ctx.config.load().config is typed
as the full config object. Update the type definition that references
ConfigRegistry/ConfigType<typeof configSchema> (the ResolvedConfig alias) to use
the mapped/object form rather than keyof-indexed access.
---
Duplicate comments:
In `@packages/core/src/middleware/config/config.ts`:
- Around line 96-98: The current implementation stores a single cached
ConfigLoadCallResult in the variable cached which causes subsequent load() calls
with different effective modes (e.g., load({ layers: true }) or load({ layer:
'global' })) to return the first-success result; change cached from a single
slot to a keyed cache (e.g., a Map<string, ConfigLoadCallResult<unknown>>) keyed
by the effective resolution mode derived from the load options (such as layers
boolean and layer name) inside the handle/load logic so each distinct effective
mode has its own cached result; update all reads/writes that reference cached to
compute the key (stringify or canonicalize the relevant options) and read/write
the Map, and ensure ConfigLoadCallResult typing is preserved.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: b7009cf1-7bd8-4538-bc7a-a6cb8650663a
📒 Files selected for processing (8)
docs/concepts/configuration.mdexamples/advanced/src/commands/deploy/preview.tsexamples/advanced/src/commands/deploy/production.tsexamples/advanced/src/commands/status.tsexamples/advanced/src/index.tspackages/core/src/middleware/config/config.test.tspackages/core/src/middleware/config/config.tspackages/core/src/middleware/config/types.ts
- Fix docs to show config/null API instead of old Result tuple pattern - Fix ResolvedConfig type — use ConfigRegistry directly instead of indexed access which collapsed to value union Co-Authored-By: Claude <noreply@anthropic.com>
The advanced example commands use exitOnError which calls ctx.fail() when no config file exists. Add acme.config.json so integration tests have valid config data. Co-Authored-By: Claude <noreply@anthropic.com>
Summary
Closes #154
@kidd-cli/core/config)ctx.config,CliConfig,CliConfigOptions,ConfigType, andTConfiggeneric from core typesconfig()middleware factory with single-mode (cwd) and layered-mode (global > project > local deep merge)ctx.configonly when middleware is imported — keeping builds lean for CLIs that don't need config./configentrypoint to package.json and tsdown configBreaking change
ctx.configis no longer available by default. CLI authors must use the config middleware:For layered resolution (global > project > local):
Test plan
pnpm typecheckpasses@kidd-cli/cli(downstream) typechecks clean@kidd-cli/coredoes NOT pull in c12