feat: migrate storage to project-local .clix/#21
Conversation
Consolidate all project-specific data into .clix/ directory:
- Move sessions from XDG_STATE_HOME/clix/sessions/ to .clix/sessions/
- Move auth credentials from XDG_STATE_HOME/clix/ to .clix/credentials.json
- Move Firebase tokens into unified credentials.json structure
- Keep global config in XDG_CONFIG_HOME/clix/config.json
Unified credentials structure:
version: 1
clix?: { Auth0 tokens }
firebase?: { Firebase OAuth tokens }
This enables per-project credential management while maintaining
global user preferences, improving multi-workspace workflows.
Co-Authored-By: Claude (global.anthropic.claude-haiku-4-5-20251001-v1:0) <noreply@anthropic.com>
WalkthroughMove credential storage to a project-local Changes
Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 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.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fe9e078f0e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Use clearClixCredentials() instead of delete() in LogoutUI to only clear Clix auth tokens while preserving Firebase OAuth tokens. Co-Authored-By: Claude (global.anthropic.claude-haiku-4-5-20251001-v1:0) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
src/lib/services/session-store.ts (1)
42-53:⚠️ Potential issue | 🟠 MajorEnsure project-local sessions are gitignored (or keep the XDG state path).
Project-local session storage can be committed accidentally, and this path bypasses any gitignore enforcement. If the migration is intentional, add an automatic
.clix/.gitignore(or root.gitignoreentry) when using the default path; otherwise keep the XDG state directory.🛡️ Suggested guard to prevent accidental commits
async function ensureSessionsDir(): Promise<string> { const dir = resolveSessionsDir(); await fs.mkdir(dir, { recursive: true }); + if (!process.env.CLIX_SESSION_DIR) { + const clixDir = path.dirname(dir); + const gitignorePath = path.join(clixDir, '.gitignore'); + await fs + .writeFile(gitignorePath, '*\n!.gitignore\n', { flag: 'wx' }) + .catch(() => undefined); + } return dir; }As per coding guidelines: Do not commit API keys or user data. Store local config in
$XDG_CONFIG_HOME/clix/config.jsonand sessions in$XDG_STATE_HOME/clix/sessions/.src/lib/services/transfer-service.ts (1)
41-58:⚠️ Potential issue | 🟠 MajorSession markdown under .clix needs gitignore protection.
Conversation history is user data; writing it under
.clixwithout ensuring ignore rules risks accidental commits. Consider creating a.clix/.gitignorewhen using the default path, or keep session artifacts in the XDG state directory.🛡️ Suggested guard to prevent accidental commits
async function saveSessionHistory(history: ConversationMessage[]): Promise<string> { const clixDir = join(process.cwd(), '.clix'); // Ensure .clix directory exists try { await mkdir(clixDir, { recursive: true }); } catch (_error) { // Directory might already exist, ignore error } + await writeFile(join(clixDir, '.gitignore'), '*\n!.gitignore\n', { + encoding: 'utf-8', + flag: 'wx', + }).catch(() => undefined);As per coding guidelines: Do not commit API keys or user data. Store local config in
$XDG_CONFIG_HOME/clix/config.jsonand sessions in$XDG_STATE_HOME/clix/sessions/.src/lib/auth/credentials.ts (1)
24-47:⚠️ Potential issue | 🟠 MajorKeep credentials in XDG user scope, not project/.clix.
Storing credentials under the project directory risks accidental commits and conflicts with the established two-config architecture. Please keep credentials in the user-level XDG state/config location and only keep project metadata in
.clix/(or namespace per-project under XDG if isolation is needed).💡 Possible fix (align with XDG state home)
+import { homedir } from 'node:os'; import { chmod, mkdir, readFile, rm, stat, writeFile } from 'node:fs/promises'; import { join } from 'node:path'; constructor(customStateDir?: string) { - this.stateDirPath = customStateDir ?? join(process.cwd(), '.clix'); + const xdgStateHome = + process.env.XDG_STATE_HOME ?? join(homedir(), '.local', 'state'); + this.stateDirPath = customStateDir ?? join(xdgStateHome, 'clix'); this.credentialsFilePath = join(this.stateDirPath, 'credentials.json'); }As per coding guidelines: Do not commit API keys or user data. Store local config in
$XDG_CONFIG_HOME/clix/config.jsonand sessions in$XDG_STATE_HOME/clix/sessions/.
Based on learnings: Clix CLI uses a two-config architecture: (1) User-level config at$XDG_CONFIG_HOME/clix/config.jsonfor credentials and global preferences, (2) Project-local config at.clix/config.jsoncin the project root for project-specific metadata (which Clix project/org the directory is linked to).
🤖 Fix all issues with AI agents
In `@AGENTS.md`:
- Around line 260-265: The documentation is inconsistent: AGENTS.md documents
global config at `$XDG_CONFIG_HOME/clix/config.json` (default
`~/.config/clix/config.json`) and project-local storage at `.clix/sessions/` and
`.clix/credentials.json`, but README.md and llms.txt still reference the old
`~/.clix/config.json` path and do not mention the project-local `.clix/` layout;
update README.md and llms.txt to match AGENTS.md by changing any
`~/.clix/config.json` references to `~/.config/clix/config.json` (or
`$XDG_CONFIG_HOME/clix/config.json`) and add concise entries documenting the
project-local storage locations `.clix/sessions/` and `.clix/credentials.json`
so all three files (AGENTS.md, README.md, llms.txt) consistently describe global
vs project-local config paths.
In `@src/lib/auth/credentials.ts`:
- Around line 377-397: The current checks allow empty/partial FirebaseTokens to
be treated as valid: update hasFirebaseTokens() to verify the token object
contains required, non-empty fields (e.g., access_token and expiry_date, and
optionally refresh_token) rather than only non-null, and update
isFirebaseExpired(tokens: FirebaseTokens) to treat a missing or falsy
expiry_date as expired (return true) so tokens without expiry info will be
refreshed; refer to the methods hasFirebaseTokens and isFirebaseExpired to
locate and change the logic accordingly.
🧹 Nitpick comments (1)
src/lib/services/firebase/oauth/token-store.ts (1)
18-77: Consider module-level functions instead of a class forTokenStore.Project conventions prefer plain objects/interfaces; exporting functions would align better and avoid class instantiation.
As per coding guidelines: Prefer plain objects with interfaces over classes. Use ES module exports for encapsulation.
…lidation - Update README.md and llms.txt to reflect project-local storage paths - Fix config path reference from ~/.clix/config.json to ~/.config/clix/config.json - Update session path references from ~/.local/state/clix to .clix/sessions - Improve hasFirebaseTokens() to check for actual token content - Improve isFirebaseExpired() to treat missing access_token or expiry_date as expired Addresses PR review feedback about documentation inconsistency and empty/partial Firebase token handling. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/lib/auth/credentials.ts`:
- Around line 45-47: The constructor for the credentials manager currently sets
stateDirPath using process.cwd(), causing per-subdirectory credential stores;
change it to resolve the project root instead (reuse the existing
ProjectConfigManager/project-root resolver used elsewhere) and set
this.stateDirPath = join(resolvedProjectRoot, '.clix') (fall back to
process.cwd() only if resolver returns undefined). Update usage of
credentialsFilePath accordingly; ensure you import and call the same resolver
that ProjectConfigManager/ensureGitignore() uses so credentials are always
stored relative to the actual project root.
🧹 Nitpick comments (1)
llms.txt (1)
1285-1287: Add the project-local.clix/config.jsoncto the storage paths summary.
Right now it only mentions sessions and credentials under.clix/, which can leave out the project metadata file.Based on learnings: Clix CLI uses a two-config architecture: (1) User-level config at
$XDG_CONFIG_HOME/clix/config.jsonfor credentials and global preferences, (2) Project-local config at.clix/config.jsoncin the project root for project-specific metadata (which Clix project/org the directory is linked to). The project-local config is automatically gitignored and follows patterns from tools like VSCode, IntelliJ, and Claude Code.
Add findProjectRoot() utility that walks up directories to find project root markers (package.json, .git, .clix, etc.) instead of using process.cwd() directly. This ensures credentials and sessions are stored relative to the actual project root, not the current working directory. Applied to: - CredentialsManager: credentials stored at project_root/.clix/ - session-store: sessions stored at project_root/.clix/sessions/ - transfer-service: session files saved to project_root/.clix/ This prevents inconsistent behavior when users run clix from subdirectories. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/lib/auth/credentials.ts (1)
25-48:⚠️ Potential issue | 🟠 MajorPotential policy conflict: credentials now stored inside project tree.
Storing credentials in
project/.clix/credentials.jsonincreases the risk of accidental commit and conflicts with the documented split between user‑level credentials and project‑level metadata. If this change is intentional, please update the policy/docs and ensure.clix/is always gitignored (and possibly excluded from tooling that scans repo files). Otherwise, keep credentials in the user‑level XDG config/state paths.
As per coding guidelines: “Do not commit API keys or user data. Store local config in$XDG_CONFIG_HOME/clix/config.jsonand sessions in$XDG_STATE_HOME/clix/sessions/.”
Based on learnings: “Clix CLI uses a two-config architecture: (1) User-level config at$XDG_CONFIG_HOME/clix/config.jsonfor credentials and global preferences, (2) Project-local config at.clix/config.jsoncfor project-specific metadata.”
🧹 Nitpick comments (1)
src/lib/utils/path.ts (1)
24-42: Minor: Redundantprocess.cwd()call.The fallback at line 41 recalculates
startDir ?? process.cwd()even though the same value was already computed at line 25. Consider capturing the original starting directory to avoid the redundant call.♻️ Suggested simplification
export function findProjectRoot(startDir?: string): string { - let currentDir = startDir ?? process.cwd(); + const originalDir = startDir ?? process.cwd(); + let currentDir = originalDir; const { root } = parse(currentDir); while (currentDir !== root) { // Check for any project markers for (const marker of PROJECT_MARKERS) { const markerPath = join(currentDir, marker); if (existsSync(markerPath)) { return currentDir; } } // Move up one directory currentDir = dirname(currentDir); } // No markers found, return the original starting directory - return startDir ?? process.cwd(); + return originalDir; }
Summary
Consolidates session and credential storage into project-local
.clix/directory for improved multi-workspace management. Sessions move from XDG_STATE_HOME to.clix/sessions/, and both Clix Auth0 and Firebase OAuth tokens are unified in.clix/credentials.json.Details
Storage Changes:
~/.local/state/clix/sessions/→project/.clix/sessions/~/.local/state/clix/credentials.json→project/.clix/credentials.json~/.config/clix/firebase-tokens.json→project/.clix/credentials.json(unified)~/.config/clix/config.jsonUnified Credentials Structure:
API Changes:
CredentialsManager.getClixCredentials(),saveClixCredentials()CredentialsManager.getFirebaseTokens(),saveFirebaseTokens()How to Validate
bun run check && bun test && bun run build && bun test tests/e2e/.clix/sessions/.clix/credentials.jsonunderfirebasekeyPre-Merge Checklist
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation
Refactor
Bug Fixes