Skip to content

fix(packages/core): add DirsConfig and fix auth dir mismatch#87

Merged
zrosenbauer merged 6 commits intomainfrom
fix/auth
Mar 18, 2026
Merged

fix(packages/core): add DirsConfig and fix auth dir mismatch#87
zrosenbauer merged 6 commits intomainfrom
fix/auth

Conversation

@zrosenbauer
Copy link
Member

Summary

  • Bug fix: login() / logout() hardcoded the store directory to .<cliName>, while credential() respected auth.file({ dirName }) overrides. Tokens written during login were invisible to credential lookups when custom dir names were configured.
  • New feature: Adds DirsConfig on cli() with separate local and global directory names (both default to .<name>), flowing through ctx.meta.dirs into the auth subsystem.
  • Auth-level override: auth({ dirName: '.custom' }) overrides both local and global dirs for auth operations.

API

cli({
  name: 'jog',
  version: '1.0.0',
  dirs: {
    local: '.joggr',   // project-root/.joggr/
    global: '.joggr',  // ~/.joggr/
  },
})

Files changed

  • types/cli.ts — new DirsConfig / ResolvedDirs types
  • cli.tsresolveDirs() helper, passes dirs to runtime
  • context/create-context.ts / context/types.tsdirs on Meta
  • middleware/auth/auth.tsresolveAuthDirs(), passes dirs to context + chain
  • middleware/auth/context.tslogin() / logout() use dirs.global
  • middleware/auth/chain.ts — strategy chain receives dirs
  • middleware/auth/strategies/file.ts — separate localDirName / globalDirName
  • middleware/auth/types.tsdirName override on AuthOptions
  • 19 test files updated

Test plan

  • pnpm typecheck passes (0 errors)
  • pnpm lint passes (0 errors, 0 warnings)
  • pnpm format passes
  • pnpm test --filter=@kidd-cli/core passes (741/741 tests)

…login/credential

Auth `login()` and `logout()` hardcoded the store directory to `.<cliName>`,
while `credential()` (passive reads) respected `auth.file({ dirName })` overrides.
This caused tokens written during login to be invisible to credential lookups when
custom dir names were configured.

Introduces `DirsConfig` on `cli()` with separate `local` and `global` directory
names that flow through `ctx.meta.dirs` into the auth subsystem. Auth-level
`dirName` overrides are also supported via `auth({ dirName })`.

- Add `DirsConfig` / `ResolvedDirs` types to `packages/core/src/types/cli.ts`
- Flow `dirs` through runtime → context → auth middleware
- Fix `resolveFromFile` to accept separate `localDirName` / `globalDirName`
- Fix `login()` / `logout()` to use `dirs.global` from context
- Add auth-level `dirName` override in `auth({ dirName })`
- Update all 27 affected test files

Co-Authored-By: Claude <noreply@anthropic.com>
@changeset-bot
Copy link

changeset-bot bot commented Mar 18, 2026

🦋 Changeset detected

Latest commit: 884dcf3

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@kidd-cli/core Minor
@kidd-cli/cli Patch

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

zrosenbauer and others added 3 commits March 18, 2026 15:54
Co-Authored-By: Claude <noreply@anthropic.com>
…lic type exports

Replace `auth({ dirName })` with `auth({ dirs })` to match the DirsConfig
pattern used by `cli()`. Auth dirs now partially merge onto ctx.meta.dirs
instead of replacing both fields with a single string.

Remove DirsConfig and ResolvedDirs from the public API surface — they are
inferrable from the cli() parameter type and don't need explicit export.

Co-Authored-By: Claude <noreply@anthropic.com>
- cli.test.ts: default dirs, custom dirs, partial global/local overrides
- auth.test.ts: auth-level dirs override smoke tests
- file.test.ts: separate local/global dir resolution, short-circuit behavior
- chain.test.ts: dirs passthrough to resolveFromFile

Co-Authored-By: Claude <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Mar 18, 2026

Warning

Rate limit exceeded

@zrosenbauer has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 51 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: f6842725-e1e6-4918-a6ee-27e0bc87f43a

📥 Commits

Reviewing files that changed from the base of the PR and between 4e3e9c6 and 884dcf3.

📒 Files selected for processing (4)
  • packages/core/src/cli.test.ts
  • packages/core/src/cli.ts
  • packages/core/src/context/create-context.ts
  • packages/core/src/middleware/auth/context.ts
📝 Walkthrough

Walkthrough

This PR adds configurable directory support for file-backed stores. Two new public types, DirsConfig and ResolvedDirs, are exported. A resolveDirs(name, dirs) utility derives default dirs as .<name> and merges optional overrides. The dirs field is threaded into CLI bootstrap, runtime, context meta, and test fixtures. Auth middleware accepts an optional per-auth dirs override (merged via resolveAuthDirs), and file-backed credential resolution is changed to a local-then-global lookup. Several function signatures and call sites were updated to pass explicit localDirName/globalDirName or a dirs: ResolvedDirs argument.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 73.08% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title directly describes the main changes: adding DirsConfig types and fixing the auth directory mismatch bug where login/logout and credential lookups used different directories.
Description check ✅ Passed The description clearly explains the bug fix, new feature, and API changes with concrete examples and comprehensive file listing, directly corresponding to the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/auth
📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/cli.ts`:
- Around line 344-356: resolveDirs currently treats only null/undefined as
missing and accepts empty strings, causing config/auth to be written to
project/home roots; update resolveDirs to validate DirsConfig using Zod so
global/local must be non-empty strings: create a Zod schema (e.g., z.object({
global: z.string().min(1).optional(), local: z.string().min(1).optional() }))
and parse/ safeParse the incoming dirs; when a field is missing or fails
validation (empty string), fall back to defaultDir for that field; reference the
resolveDirs function and types DirsConfig/ResolvedDirs when applying this
change.

In `@packages/core/src/context/create-context.ts`:
- Around line 55-57: The code is aliasing options.meta.dirs into ctxMeta (and
thus ctx.meta.dirs), which allows mutations to bleed across contexts because
createRuntime() reuses the same dirs object for every createContext() call; to
fix, stop referencing the runtime dirs by reference and create a fresh copy for
ctxMeta.dirs when building the Meta object in create-context.ts (i.e., copy
options.meta.dirs into a new object instead of assigning it directly) so
downstream mutations don't affect other contexts; check createContext(),
ctxMeta, Meta, and usages of ctx.meta.dirs to ensure no shared mutable object is
returned.

In `@packages/core/src/middleware/auth/auth.ts`:
- Around line 250-267: The resolveAuthDirs function uses an if/else with two
branches; replace that conditional with a ts-pattern match expression (using
match from 'ts-pattern') to return { global: dirName, local: dirName } when
dirName is defined and metaDirs otherwise, keeping the same return type; update
imports to include match from 'ts-pattern' and ensure the pattern checks for
dirName being undefined vs defined so behavior and types are preserved in
resolveAuthDirs.

In `@packages/core/src/middleware/auth/context.ts`:
- Around line 99-100: The login/logout behavior currently only writes/removes
from the global store (createStore with dirs.global and DEFAULT_AUTH_FILENAME),
while file-based resolution (auth/strategies/file.ts) prefers the local store
first, leaving a stale local credential active; update the logic in the
functions that call createStore/save/remove (the login() and logout() flows in
context.ts around DEFAULT_AUTH_FILENAME) to either (A) resolve which store is
effective (check local then global like file.ts) and persist/remove the
credential in that effective scope, or (B) perform the operation in both scopes
(createStore for dirs.local and dirs.global) so local credentials are
overwritten/cleared as well; ensure credential() and authenticated() continue to
read using the same resolution order as the strategy implementation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3fa170bb-b523-41a9-8b35-d34599cb59e3

📥 Commits

Reviewing files that changed from the base of the PR and between b47dee5 and 0e0628f.

⛔ Files ignored due to path filters (1)
  • .changeset/fix-auth-dirs.md is excluded by !.changeset/**
📒 Files selected for processing (27)
  • packages/core/src/cli.ts
  • packages/core/src/compose.test.ts
  • packages/core/src/context/create-context.test.ts
  • packages/core/src/context/create-context.ts
  • packages/core/src/context/decorate.test.ts
  • packages/core/src/context/types.ts
  • packages/core/src/index.ts
  • packages/core/src/middleware/auth/auth-http-chain.test.ts
  • packages/core/src/middleware/auth/auth.test.ts
  • packages/core/src/middleware/auth/auth.ts
  • packages/core/src/middleware/auth/chain.test.ts
  • packages/core/src/middleware/auth/chain.ts
  • packages/core/src/middleware/auth/context.test.ts
  • packages/core/src/middleware/auth/context.ts
  • packages/core/src/middleware/auth/strategies/file.test.ts
  • packages/core/src/middleware/auth/strategies/file.ts
  • packages/core/src/middleware/auth/types.ts
  • packages/core/src/middleware/http/http.test.ts
  • packages/core/src/middleware/icons/icons.test.ts
  • packages/core/src/middleware/typed-middleware.test.ts
  • packages/core/src/runtime/runtime.test.ts
  • packages/core/src/runtime/runtime.ts
  • packages/core/src/runtime/types.ts
  • packages/core/src/test/context.ts
  • packages/core/src/test/types.ts
  • packages/core/src/types/cli.ts
  • packages/core/src/types/index.ts

Writes (login/logout) always target the global (home) directory so
credentials are user-scoped. Reads (credential/file strategy) check
local → global, allowing project-level overrides.

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
packages/core/src/middleware/auth/context.ts (1)

99-103: ⚠️ Potential issue | 🟠 Major

login() / logout() can leave the effective credential unchanged when local auth exists.

At Line 102 and Line 123, writes/deletes only target dirs.global, but credential resolution is local → global. A stale local credential can remain authoritative, so login()/logout() can report success while credential() / authenticated() still reflect local state.

Proposed fix (align persistence scope with resolution precedence)
-    const store = createStore({ dirName: dirs.global })
-    const [saveError] = store.save(DEFAULT_AUTH_FILENAME, validatedCredential)
+    const targetDirs = dirs.local === dirs.global ? [dirs.global] : [dirs.local, dirs.global]
+    const saveError = targetDirs
+      .map((dirName) => createStore({ dirName }).save(DEFAULT_AUTH_FILENAME, validatedCredential)[0])
+      .find((error): error is Error => error !== null)

...
-    const store = createStore({ dirName: dirs.global })
-    const [removeError, filePath] = store.remove(DEFAULT_AUTH_FILENAME)
+    const targetDirs = dirs.local === dirs.global ? [dirs.global] : [dirs.local, dirs.global]
+    const removeResults = targetDirs.map((dirName) =>
+      createStore({ dirName }).remove(DEFAULT_AUTH_FILENAME)
+    )
+    const removeError = removeResults
+      .map(([error]) => error)
+      .find((error): error is Error => error !== null)
+    const filePath = removeResults.find(([, resolvedPath]) => resolvedPath.length > 0)?.[1] ?? ''

Also applies to: 122-124

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/middleware/auth/context.ts` around lines 99 - 103, The
login/logout persistence currently only mutates the global store (createStore({
dirName: dirs.global }) and store.save/delete) while credential resolution
prefers local → global, so a stale local credential can remain active; update
the login/logout logic to perform the same operation in the local scope first
and then the global scope (i.e., createStore({ dirName: dirs.local }) then
createStore({ dirName: dirs.global }) and save/delete DEFAULT_AUTH_FILENAME in
both) so that credential() / authenticated() reflect the effective credential
after login/logout; ensure you reference createStore, DEFAULT_AUTH_FILENAME,
dirs.local, dirs.global, and the credential()/authenticated() code paths 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.

Duplicate comments:
In `@packages/core/src/middleware/auth/context.ts`:
- Around line 99-103: The login/logout persistence currently only mutates the
global store (createStore({ dirName: dirs.global }) and store.save/delete) while
credential resolution prefers local → global, so a stale local credential can
remain active; update the login/logout logic to perform the same operation in
the local scope first and then the global scope (i.e., createStore({ dirName:
dirs.local }) then createStore({ dirName: dirs.global }) and save/delete
DEFAULT_AUTH_FILENAME in both) so that credential() / authenticated() reflect
the effective credential after login/logout; ensure you reference createStore,
DEFAULT_AUTH_FILENAME, dirs.local, dirs.global, and the
credential()/authenticated() code paths when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 544c803e-5324-4330-add2-c9dcad93b756

📥 Commits

Reviewing files that changed from the base of the PR and between 0722944 and 4e3e9c6.

📒 Files selected for processing (1)
  • packages/core/src/middleware/auth/context.ts

- Guard against empty/whitespace-only dir strings in resolveDirs()
- Freeze dirs object in createContext() to prevent mutation
- Clarify logout() intentionally targets global only with expanded comment
- Add test for empty string dirs fallback

Co-Authored-By: Claude <noreply@anthropic.com>
@zrosenbauer zrosenbauer merged commit 4beaa57 into main Mar 18, 2026
3 checks passed
@zrosenbauer zrosenbauer deleted the fix/auth branch March 18, 2026 20:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant