From eca22db647fc76a77cc2fb8874e6e3a552b127ab Mon Sep 17 00:00:00 2001 From: carlos-alm Date: Wed, 1 Jul 2026 21:50:53 -0600 Subject: [PATCH 1/2] fix(cli): scaffold embeddings section in config --init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit buildInitTemplate() never included an embeddings block, so a fresh global config had no model/llmProvider/provider keys — including the provider field added in #1716 for the remote embedding backend. docs check acknowledged: this is a scaffold-only addition to an existing CLI command; docs/guides/configuration.md already documents the embeddings section in full, and no README/CLAUDE.md/ROADMAP.md content is affected. Impact: 1 functions changed, 2 affected --- src/cli/commands/config.ts | 9 +++++++++ tests/integration/cli.test.ts | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/src/cli/commands/config.ts b/src/cli/commands/config.ts index 756252949..7c7217212 100644 --- a/src/cli/commands/config.ts +++ b/src/cli/commands/config.ts @@ -123,6 +123,15 @@ function buildInitTemplate(): string { apiKeyCommand: DEFAULTS.llm.apiKeyCommand, }, + // Embedding backend for `codegraph embed`/`search`. Leave provider null + // for the local bundled model, or set it to "openai" to call a remote + // OpenAI-compatible endpoint configured via llm.baseUrl/apiKeyCommand. + embeddings: { + model: DEFAULTS.embeddings.model, + llmProvider: DEFAULTS.embeddings.llmProvider, + provider: DEFAULTS.embeddings.provider, + }, + query: { defaultDepth: DEFAULTS.query.defaultDepth, defaultLimit: DEFAULTS.query.defaultLimit, diff --git a/tests/integration/cli.test.ts b/tests/integration/cli.test.ts index e109cf048..770881f22 100644 --- a/tests/integration/cli.test.ts +++ b/tests/integration/cli.test.ts @@ -330,3 +330,37 @@ describe('Registry CLI commands', () => { expect(out2).toContain('"api-2"'); }); }); + +// ─── Config CLI ─────────────────────────────────────────────────────── + +describe('Config CLI commands', () => { + let tmpHome: string; + + /** Run CLI with isolated HOME so --init doesn't touch the real global config */ + function runCfg(...args) { + return execFileSync('node', [...NODE_TS_FLAGS, CLI, ...args], { + cwd: tmpDir, + encoding: 'utf-8', + timeout: 30_000, + env: { ...process.env, HOME: tmpHome, USERPROFILE: tmpHome }, + }); + } + + beforeAll(() => { + tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-cfghome-')); + }); + + afterAll(() => { + if (tmpHome) fs.rmSync(tmpHome, { recursive: true, force: true }); + }); + + test('config --init scaffolds an embeddings section alongside llm', () => { + runCfg('config', '--init'); + const configPath = path.join(tmpHome, '.config', 'codegraph', 'config.json'); + const scaffolded = JSON.parse(fs.readFileSync(configPath, 'utf-8')); + + expect(scaffolded.embeddings).toEqual({ model: null, llmProvider: null, provider: null }); + expect(scaffolded.llm).toHaveProperty('baseUrl'); + expect(scaffolded.llm).toHaveProperty('apiKeyCommand'); + }); +}); From 0a43b4f771a2485c9623a5e893123884c0444699 Mon Sep 17 00:00:00 2001 From: carlos-alm Date: Wed, 1 Jul 2026 22:35:09 -0600 Subject: [PATCH 2/2] fix: isolate CODEGRAPH_USER_CONFIG in config --init CLI test (#1719) The new "config --init scaffolds an embeddings section" test only overrode HOME/USERPROFILE for the spawned CLI process, but getDefaultUserConfigPath() checks XDG_CONFIG_HOME and (on Windows) APPDATA ahead of the HOME-derived fallback. Both are set ambiently on GitHub Actions hosted runners (XDG_CONFIG_HOME=/home/runner/.config on ubuntu-latest, APPDATA=...\runneradmin\AppData\Roaming on windows-2022), so --init wrote the scaffolded file outside the test's tmpHome and the follow-up read failed with ENOENT. Pin the target path explicitly via CODEGRAPH_USER_CONFIG (highest priority in the resolution order) and unset XDG_CONFIG_HOME/APPDATA for the spawned process, matching the isolation pattern already used in tests/unit/config-user.test.ts. --- tests/integration/cli.test.ts | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/tests/integration/cli.test.ts b/tests/integration/cli.test.ts index 770881f22..f61b69153 100644 --- a/tests/integration/cli.test.ts +++ b/tests/integration/cli.test.ts @@ -335,19 +335,36 @@ describe('Registry CLI commands', () => { describe('Config CLI commands', () => { let tmpHome: string; - - /** Run CLI with isolated HOME so --init doesn't touch the real global config */ + let userConfigPath: string; + + /** + * Run CLI with isolated HOME *and* an explicit CODEGRAPH_USER_CONFIG so + * --init doesn't touch the real global config. HOME/USERPROFILE alone + * aren't sufficient: getDefaultUserConfigPath() prefers XDG_CONFIG_HOME + * (ambiently set on GitHub Actions ubuntu-latest runners) and, on Windows, + * APPDATA (always set) ahead of the HOME-derived fallback — so on CI the + * scaffolded file can land outside tmpHome unless the path is pinned + * explicitly, same as tests/unit/config-user.test.ts does. + */ function runCfg(...args) { return execFileSync('node', [...NODE_TS_FLAGS, CLI, ...args], { cwd: tmpDir, encoding: 'utf-8', timeout: 30_000, - env: { ...process.env, HOME: tmpHome, USERPROFILE: tmpHome }, + env: { + ...process.env, + HOME: tmpHome, + USERPROFILE: tmpHome, + CODEGRAPH_USER_CONFIG: userConfigPath, + XDG_CONFIG_HOME: undefined, + APPDATA: undefined, + }, }); } beforeAll(() => { tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-cfghome-')); + userConfigPath = path.join(tmpHome, '.config', 'codegraph', 'config.json'); }); afterAll(() => { @@ -356,8 +373,7 @@ describe('Config CLI commands', () => { test('config --init scaffolds an embeddings section alongside llm', () => { runCfg('config', '--init'); - const configPath = path.join(tmpHome, '.config', 'codegraph', 'config.json'); - const scaffolded = JSON.parse(fs.readFileSync(configPath, 'utf-8')); + const scaffolded = JSON.parse(fs.readFileSync(userConfigPath, 'utf-8')); expect(scaffolded.embeddings).toEqual({ model: null, llmProvider: null, provider: null }); expect(scaffolded.llm).toHaveProperty('baseUrl');