Skip to content

bug: CLI's shared ctx.config is resolved once from process.cwd(), ignoring the target dir/--db path for every command #2137

Description

@carlos-alm

Found during dogfooding v3.16.0

Severity: High
Command: codegraph embed <dir> (discovered here), but the root cause is shared by every command

Reproduction

# Run from a directory OTHER than the target project (e.g. a separate npm install dir,
# a CI runner's default cwd, or any MCP/multi-repo-style invocation):
cd /some/other/directory

# Target project has a .codegraphrc.json configuring the new v3.16.0 remote
# embedding provider feature:
cat /path/to/project/.codegraphrc.json
# {
#   "embeddings": { "provider": "openai" },
#   "llm": { "baseUrl": "http://my-embedding-server/v1", "apiKey": "..." }
# }

codegraph build /path/to/project
codegraph -v embed /path/to/project --db /path/to/project/.codegraph/graph.db -m minilm

Expected: routes through the remote OpenAI-compatible endpoint (embeddings.provider: "openai" in the target project's config).

Actual:

[codegraph] Building embeddings for 5086 symbols (strategy: structured)...
[codegraph] Embedding 5086 symbols...
[codegraph] Loading embedding model: Xenova/all-MiniLM-L6-v2 (384d)...

No "via remote provider" — it silently falls back to loading the local HuggingFace model, completely ignoring embeddings.provider. Additionally, embed's own validate() (src/cli/commands/embed.ts:56-68) is supposed to reject with "embeddings.provider is set to ... but no model is configured" when provider is set without embeddings.model and no --model flag — that check also never fires, because it reads the same stale/empty config.

Root cause

src/cli/index.ts:53-54 builds one shared ctx object passed to every command's validate()/execute(), using the config export from src/cli/shared/options.ts:

let _config: CodegraphConfig | undefined;
const config: CodegraphConfig = new Proxy({} as CodegraphConfig, {
  get(_t, prop: string) {
    if (_config === undefined) _config = loadConfig(process.cwd());
    return _config[prop as keyof CodegraphConfig];
  },
}) as CodegraphConfig;

This is a process-wide singleton, lazily resolved exactly once from process.cwd(), regardless of any positional dir argument or --db <path> flag a command receives. So codegraph embed /path/to/project invoked from a different cwd reads (or fails to find) .codegraphrc.json at the invoking directory, not /path/to/project.

This is the same architectural issue already partially fixed at the data-query layer in #1605 / #1881 / #2017 (via resolveConfigForDbPath() in src/db/index.ts), but those fixes only cover functions that call loadConfig() directly with a customDbPath — they don't touch the CLI dispatcher's ctx.config, which many commands (including embed's validate()/execute()) read directly instead of going through resolveConfigForDbPath().

Because ctx.config is shared process-wide, this affects every command that reads it directly rather than re-deriving config from its own customDbPath — at minimum embed (embeddings.provider/embeddings.model), and via resolveNoTests()/resolveQueryOpts() in the same file, the default -T/--no-tests behavior (config.query.excludeTests) for any command invoked with a dir/--db argument pointing elsewhere.

Suggested fix

Apply the same resolveConfigForDbPath(customDbPath) pattern used for #1881/#2017 to embed.ts's validate()/execute() (and any other command reading ctx.config directly for a value that should be project-scoped), deriving config from the command's own --db/dir argument rather than the shared cwd-pinned singleton. resolveNoTests()/resolveQueryOpts() in src/cli/shared/options.ts likely need the same treatment, since they're used broadly across commands.

Impact

This means the entire v3.16.0 headline feature (configurable remote embedding provider) silently does nothing whenever codegraph embed is invoked from outside the target project's own directory — a very common pattern (CI, multi-repo tooling, MCP-adjacent workflows, or simply passing a directory argument instead of cd-ing first).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdogfoodFound during dogfooding

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions