Skip to content

feat(docs): persist the docs LLM agent choice (config + onboarding + CLI)#312

Merged
efenocchi merged 3 commits into
mainfrom
feat/docs-llm-agent-config
Jul 14, 2026
Merged

feat(docs): persist the docs LLM agent choice (config + onboarding + CLI)#312
efenocchi merged 3 commits into
mainfrom
feat/docs-llm-agent-config

Conversation

@efenocchi

@efenocchi efenocchi commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

What & why

Make the docs-generation host agent a persistent choice instead of an env var that vanishes when you close the shell. You asked: "why does it default to claude, and where do I change it so it survives a restart?"

Behaviour

  • New config field docs.llmAgent in ~/.deeplake/config.json (survives restarts, machines, sessions).
  • Resolution precedence in resolveDocLlmSpec:
    HIVEMIND_DOCS_LLM_BIN > HIVEMIND_DOCS_LLM_AGENT (env, one-off) > config docs.llmAgent > auto-detect (claude → codex → pi → cursor).
  • Asked during onboarding (runDocsOnboarding) — but only when >1 host CLI is installed AND none is pinned yet. One agent, or an existing pin → no question (zero nag). The choice persists.
  • New command hivemind docs agent [name] — show current + installed, or set (validated; warns if not on PATH; no org creds needed).

Out of scope (explicitly deferred): a persistent per-tier model override, and giving cursor/pi the fast/slow split. Sonnet stays the slow-tier default.

Verification

  • tsc clean; 20 unit tests (precedence env>config>auto, config persistence + no-clobber of embeddings, install detection, onboarding gate across all cases: >1/1/already-pinned/declined).
  • Real end-to-end (sandbox HOME): two separate processes — process A docs agent cursor, process B (fresh) docs agent reads cursor back from disk → survives restart. Unknown agent → exit 1.
  • Full suite completes with no regressions (the one unrelated red is the pre-existing tree-sitter absent bundle test, an env artifact on Node 24 that passes in CI).

Files

  • src/user-config.tsdocs.llmAgent field + get/setDocsLlmAgent
  • src/docs/refresh-llm.ts — precedence + detectAvailableAgents/knownDocsAgents
  • src/commands/docs.tsdocs agent command + USAGE
  • src/docs/onboarding.ts — the gated agent question (injectable deps)
  • tests/shared/docs-llm-agent.test.ts (new), tests/shared/docs-onboarding.test.ts (opt out of the new gate)

Session Context

Session transcript

Summary by CodeRabbit

  • New Features
    • Added support for selecting which installed agent generates documentation.
    • Added hivemind docs agent to view the currently configured agent (or available options) and to set it for future runs.
    • Onboarding now includes an agent-choice step when multiple agents are available and none is already selected.
    • Documentation agent selection is persisted for reuse, with support for environment-based overrides.
  • Bug Fixes
    • Preserved existing configuration settings (e.g., embedding options) when saving documentation agent preferences.

Persist which host CLI authors the docs in ~/.deeplake/config.json
(docs.llmAgent). Resolution precedence in resolveDocLlmSpec becomes:
  HIVEMIND_DOCS_LLM_BIN > HIVEMIND_DOCS_LLM_AGENT (env) > config > auto-detect
so the env override stays a one-off knob while the persisted choice survives
restarts. Adds detectAvailableAgents()/knownDocsAgents() helpers and a
`hivemind docs agent [name]` command (show current + installed, or set with
validation; no org creds needed). Sonnet remains the slow-tier default.
runDocsOnboarding now asks "Which agent should write the docs?" — but only
when >1 host CLI is installed AND none is pinned yet; one agent (or an
existing pin) stays silent. The choice persists globally via config.json.
detect/get/set are injectable for tests. Updates the existing onboarding
tests to opt out of the new gate (detectAgents: () => []) so they stay
deterministic regardless of what's installed on the test host.
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds persistent docs LLM agent configuration, automatic agent discovery, onboarding selection when multiple agents are installed, and the hivemind docs agent command for viewing or updating the selection.

Changes

Docs LLM agent selection

Layer / File(s) Summary
Agent persistence and resolution
src/user-config.ts, src/docs/refresh-llm.ts, tests/shared/docs-llm-agent.test.ts
Adds persisted docs.llmAgent configuration, available-agent discovery, selection precedence, and coverage for configuration and detection behavior.
Onboarding agent selection
src/docs/onboarding.ts, tests/shared/docs-llm-agent.test.ts, tests/shared/docs-onboarding.test.ts
Prompts for an agent only when generation is accepted, multiple agents are available, and no agent is pinned; selected values are persisted and tested.
Agent management CLI
src/commands/docs.ts, tests/shared/docs-llm-agent.test.ts
Adds hivemind docs agent to display or update the configured agent, validate names, warn about missing binaries, persist selections, and test command behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant runDocsOnboarding
  participant detectAvailableAgents
  participant OnboardingIo
  participant setDocsLlmAgent

  User->>runDocsOnboarding: accept docs generation
  runDocsOnboarding->>detectAvailableAgents: detect installed agents
  detectAvailableAgents-->>runDocsOnboarding: available agents
  runDocsOnboarding->>OnboardingIo: ask for agent choice
  OnboardingIo-->>runDocsOnboarding: selected or blank answer
  runDocsOnboarding->>setDocsLlmAgent: persist selected agent
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is detailed, but it does not follow the required template and is missing the Summary, Version Bump, and checkbox-style Test plan sections. Add the required template sections: Summary, Version Bump, and a Test plan with checklist items, including whether package.json was bumped or a release is not needed.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: persisting the docs LLM agent choice via config, onboarding, and a CLI command.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/docs-llm-agent-config

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions

github-actions Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Coverage Report

Scope: files changed in this PR. Enforced threshold: 90% per metric (per file via vitest.config.ts).

Status Category Percentage Covered / Total
🟢 Lines 91.81% (🎯 90%) 639 / 696
🔴 Statements 89.68% (🎯 90%) 704 / 785
🔴 Functions 76.47% (🎯 90%) 78 / 102
🔴 Branches 82.00% (🎯 90%) 451 / 550
File Coverage — 4 files changed
File Stmts Branches Functions Lines
src/commands/docs.ts 🟢 94.3% 🔴 85.4% 🟢 93.5% 🟢 96.7%
src/docs/onboarding.ts 🔴 84.1% 🔴 82.3% 🔴 16.7% 🔴 84.1%
src/docs/refresh-llm.ts 🔴 54.3% 🔴 61.0% 🔴 59.5% 🔴 55.9%
src/user-config.ts 🟢 95.4% 🔴 76.9% 🟢 92.3% 🟢 95.1%

Generated for commit 47a0854.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
tests/shared/docs-llm-agent.test.ts (1)

144-145: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Assert on exact messages rather than using generic regex substrings.

As per path instructions for tests/**: "Prefer asserting on specific values (paths, messages) over generic substrings." The assertions against the asked array currently use regex partial matches. These should be refactored to verify the exact prompt strings or the entire asked array.

♻️ Proposed refactor
-    expect(res.generate).toBe(true);
-    expect(asked.some((q) => /Which agent should write the docs/.test(q))).toBe(true);
+    expect(res.generate).toBe(true);
+    expect(asked).toContain("Which agent should write the docs? [claude/codex] (default: claude) ");

And for the negative assertions, you can verify the exact array contents for better strictness:

Line 172:

-    expect(asked.some((q) => /Which agent/.test(q))).toBe(false);
+    expect(asked).toEqual([
+      "Generate wiki docs for this repo now? (one-time LLM cost) [y/N] ",
+      "Keep them automatically in sync on every commit? Docs stay fresh but this consumes more LLM tokens over time. [y/N] "
+    ]);

Line 186:

-    expect(asked.some((q) => /Which agent/.test(q))).toBe(false);
+    expect(asked).toEqual([
+      "Generate wiki docs for this repo now? (one-time LLM cost) [y/N] ",
+      "Keep them automatically in sync on every commit? Docs stay fresh but this consumes more LLM tokens over time. [y/N] "
+    ]);

Line 200:

-    expect(asked.some((q) => /Which agent/.test(q))).toBe(false);
+    expect(asked).toEqual([
+      "Generate wiki docs for this repo now? (one-time LLM cost) [y/N] "
+    ]);

Also applies to: 172-172, 186-186, 200-200

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/shared/docs-llm-agent.test.ts` around lines 144 - 145, Replace the
generic regex-based assertions on the asked array in this test with exact
prompt-string or full-array equality checks. Update the related assertions at
the other referenced cases, including the negative cases, to verify the complete
expected asked contents rather than partial substrings while preserving the
existing generate-result checks.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/shared/docs-llm-agent.test.ts`:
- Around line 144-145: Replace the generic regex-based assertions on the asked
array in this test with exact prompt-string or full-array equality checks.
Update the related assertions at the other referenced cases, including the
negative cases, to verify the complete expected asked contents rather than
partial substrings while preserving the existing generate-result checks.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 5d2b097f-7039-4452-86fa-7ea731bb4933

📥 Commits

Reviewing files that changed from the base of the PR and between f466b58 and d24d0d6.

📒 Files selected for processing (6)
  • src/commands/docs.ts
  • src/docs/onboarding.ts
  • src/docs/refresh-llm.ts
  • src/user-config.ts
  • tests/shared/docs-llm-agent.test.ts
  • tests/shared/docs-onboarding.test.ts

The new `hivemind docs agent` handler added uncovered branches to
commands/docs.ts, dropping its branch coverage below the 85% gate. Exercise
show (unset + pinned), set (case-insensitive persist), and the unknown-agent
rejection (process.exit) via runDocsCommand, restoring the threshold.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/shared/docs-llm-agent.test.ts`:
- Around line 233-255: Update the tests around runDocsCommand to assert exact
expected log or error messages and specific log array elements, replacing
generic regex/substring checks on logs.join("\n"). Preserve the existing
behavioral coverage for default, pinned, case-insensitive set, and unknown-agent
flows, including the persistence and process-exit assertions.
🪄 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: CHILL

Plan: Pro Plus

Run ID: 326b66f5-faee-4c65-a66e-ab3aab8b47b4

📥 Commits

Reviewing files that changed from the base of the PR and between d24d0d6 and 00b797e.

📒 Files selected for processing (1)
  • tests/shared/docs-llm-agent.test.ts

Comment on lines +233 to +255
it("no arg → shows current (auto when unset) + installed + how to set", async () => {
await runDocsCommand(["agent"]);
expect(logs.join("\n")).toMatch(/Docs LLM agent:/);
expect(logs.join("\n")).toMatch(/Set with: hivemind docs agent/);
});

it("no arg after a pin → shows the pinned agent", async () => {
setDocsLlmAgent("codex");
await runDocsCommand(["agent"]);
expect(logs.join("\n")).toContain("Docs LLM agent: codex");
});

it("set a known agent → persists it", async () => {
await runDocsCommand(["agent", "Codex"]); // case-insensitive
expect(getDocsLlmAgent()).toBe("codex");
expect(logs.join("\n")).toContain("Docs LLM agent set to: codex");
});

it("unknown agent → error + non-zero exit, nothing persisted", async () => {
await expect(runDocsCommand(["agent", "bogus"])).rejects.toThrow("process.exit");
expect(errs.join("\n")).toMatch(/Unknown agent/);
expect(getDocsLlmAgent()).toBeUndefined();
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Assert on specific messages rather than generic substrings.

As per path instructions, prefer asserting on exact messages and array elements instead of using .join("\n") with generic substring checks.

♻️ Proposed fixes
   it("no arg → shows current (auto when unset) + installed + how to set", async () => {
     await runDocsCommand(["agent"]);
-    expect(logs.join("\n")).toMatch(/Docs LLM agent:/);
-    expect(logs.join("\n")).toMatch(/Set with: hivemind docs agent/);
+    expect(logs[0]).toMatch(/^Docs LLM agent: auto \(/);
+    expect(logs[2]).toBe("Set with: hivemind docs agent <claude|codex|pi|cursor>  (or HIVEMIND_DOCS_LLM_BIN for any other CLI)");
   });
 
   it("no arg after a pin → shows the pinned agent", async () => {
     setDocsLlmAgent("codex");
     await runDocsCommand(["agent"]);
-    expect(logs.join("\n")).toContain("Docs LLM agent: codex");
+    expect(logs[0]).toBe("Docs LLM agent: codex");
   });
 
   it("set a known agent → persists it", async () => {
     await runDocsCommand(["agent", "Codex"]); // case-insensitive
     expect(getDocsLlmAgent()).toBe("codex");
-    expect(logs.join("\n")).toContain("Docs LLM agent set to: codex");
+    expect(logs[0]).toBe("Docs LLM agent set to: codex. Change with: hivemind docs agent <name>.");
   });
 
   it("unknown agent → error + non-zero exit, nothing persisted", async () => {
     await expect(runDocsCommand(["agent", "bogus"])).rejects.toThrow("process.exit");
-    expect(errs.join("\n")).toMatch(/Unknown agent/);
+    expect(errs[0]).toBe('Unknown agent "bogus". Known: claude, codex, pi, cursor. For any other CLI use HIVEMIND_DOCS_LLM_BIN.');
     expect(getDocsLlmAgent()).toBeUndefined();
   });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it("no arg → shows current (auto when unset) + installed + how to set", async () => {
await runDocsCommand(["agent"]);
expect(logs.join("\n")).toMatch(/Docs LLM agent:/);
expect(logs.join("\n")).toMatch(/Set with: hivemind docs agent/);
});
it("no arg after a pin → shows the pinned agent", async () => {
setDocsLlmAgent("codex");
await runDocsCommand(["agent"]);
expect(logs.join("\n")).toContain("Docs LLM agent: codex");
});
it("set a known agent → persists it", async () => {
await runDocsCommand(["agent", "Codex"]); // case-insensitive
expect(getDocsLlmAgent()).toBe("codex");
expect(logs.join("\n")).toContain("Docs LLM agent set to: codex");
});
it("unknown agent → error + non-zero exit, nothing persisted", async () => {
await expect(runDocsCommand(["agent", "bogus"])).rejects.toThrow("process.exit");
expect(errs.join("\n")).toMatch(/Unknown agent/);
expect(getDocsLlmAgent()).toBeUndefined();
});
it("no arg → shows current (auto when unset) + installed + how to set", async () => {
await runDocsCommand(["agent"]);
expect(logs[0]).toMatch(/^Docs LLM agent: auto \(/);
expect(logs[2]).toBe("Set with: hivemind docs agent <claude|codex|pi|cursor> (or HIVEMIND_DOCS_LLM_BIN for any other CLI)");
});
it("no arg after a pin → shows the pinned agent", async () => {
setDocsLlmAgent("codex");
await runDocsCommand(["agent"]);
expect(logs[0]).toBe("Docs LLM agent: codex");
});
it("set a known agent → persists it", async () => {
await runDocsCommand(["agent", "Codex"]); // case-insensitive
expect(getDocsLlmAgent()).toBe("codex");
expect(logs[0]).toBe("Docs LLM agent set to: codex. Change with: hivemind docs agent <name>.");
});
it("unknown agent → error + non-zero exit, nothing persisted", async () => {
await expect(runDocsCommand(["agent", "bogus"])).rejects.toThrow("process.exit");
expect(errs[0]).toBe('Unknown agent "bogus". Known: claude, codex, pi, cursor. For any other CLI use HIVEMIND_DOCS_LLM_BIN.');
expect(getDocsLlmAgent()).toBeUndefined();
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/shared/docs-llm-agent.test.ts` around lines 233 - 255, Update the tests
around runDocsCommand to assert exact expected log or error messages and
specific log array elements, replacing generic regex/substring checks on
logs.join("\n"). Preserve the existing behavioral coverage for default, pinned,
case-insensitive set, and unknown-agent flows, including the persistence and
process-exit assertions.

Source: Path instructions

@efenocchi
efenocchi merged commit 395ec85 into main Jul 14, 2026
11 checks passed
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