baldart 3.9.0 → 3.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,405 @@
1
+ <!-- contamination-scan: skip
2
+ This guide uses literal example paths (tsconfig.json, pyproject.toml,
3
+ `src/components/...`, etc.) as pedagogical examples of what
4
+ `baldart configure` autodetects. They are not project leak. -->
5
+ # LSP Symbol-Search Layer
6
+
7
+ A complete operator + agent reference for the LSP retrieval tier introduced in
8
+ **v3.10.0**. Read this when:
9
+
10
+ - you are upgrading a consumer repo from pre-v3.10 and want to know what
11
+ changed and how to roll the new feature out,
12
+ - you are an AI agent asked to extend, debug, or modify the layer,
13
+ - a contributor wants to add a new language adapter or change the fallback
14
+ rules.
15
+
16
+ For the *runtime* protocol (RAG → LSP → Grep → Git, query-type
17
+ discrimination, budget) read [`framework/agents/code-search-protocol.md`](../agents/code-search-protocol.md).
18
+ This document covers the *plumbing*: how the layer is installed, configured,
19
+ verified, and wired into agents/skills.
20
+
21
+ ---
22
+
23
+ ## 1. Why this exists
24
+
25
+ Pre-v3.10, every BALDART code-exploration flow ran:
26
+
27
+ > RAG hybrid → `git log` → **Grep** → read files.
28
+
29
+ Grep collapses semantically distinct symbols that share a name. A search for
30
+ `handleSubmit` in a medium repo dumps every form handler, every CLI utility,
31
+ every test helper into the model's context — Claude then burns turns opening
32
+ files to figure out which one is relevant. The signal is buried in the
33
+ textual noise.
34
+
35
+ LSP — Language Server Protocol — answers "give me every reference to *this*
36
+ symbol" by walking the type graph the compiler/interpreter already has. A
37
+ function named `handleSubmit` declared in `src/forms/Login.tsx` returns
38
+ exactly the callsites that resolve to *that* declaration; the CLI utility's
39
+ unrelated `handleSubmit` is filtered out before any file is read.
40
+
41
+ The layer is **opt-in** (some consumers don't want devDep churn, some target
42
+ languages we don't have an adapter for yet) and **degrades gracefully** (when
43
+ LSP is unavailable, agents silently fall back to the legacy RAG → Grep flow,
44
+ so existing behavior is preserved).
45
+
46
+ ---
47
+
48
+ ## 2. The four moving parts
49
+
50
+ ```
51
+ ┌─────────────────────────────────────────────────────────────────────┐
52
+ │ baldart.config.yml │
53
+ │ features.has_lsp_layer: true │
54
+ │ lsp.installed_servers: [typescript, python] │
55
+ │ lsp.auto_verify: true │
56
+ └──────────────┬──────────────────────────────────────────────────────┘
57
+ │ read by
58
+
59
+ ┌─────────────────────────────────────────────────────────────────────┐
60
+ │ Plumbing layer (CLI, this repo) │
61
+ │ src/utils/lsp-adapters/<lang>.js ← per-language recipes │
62
+ │ src/utils/lsp-installer.js ← high-level install+verify │
63
+ │ src/commands/configure.js (prompt + install on opt-in) │
64
+ │ src/commands/doctor.js (verify + lsp-fix action) │
65
+ └──────────────┬──────────────────────────────────────────────────────┘
66
+ │ writes installed_servers; surfaces broken servers
67
+
68
+ ┌─────────────────────────────────────────────────────────────────────┐
69
+ │ Protocol layer (framework/) │
70
+ │ framework/agents/code-search-protocol.md │
71
+ │ framework/agents/index.md (routing line) │
72
+ └──────────────┬──────────────────────────────────────────────────────┘
73
+ │ referenced by
74
+
75
+ ┌─────────────────────────────────────────────────────────────────────┐
76
+ │ Consumers (framework/.claude/) │
77
+ │ agents/codebase-architect.md (Investigation Protocol) │
78
+ │ agents/REGISTRY.md (capability declaration) │
79
+ │ skills/context-primer/SKILL.md (retrieval step 3) │
80
+ │ skills/bug/SKILL.md (Phase 0 step 3) │
81
+ │ skills/simplify/SKILL.md (deduplication step 3) │
82
+ │ skills/prd/SKILL.md, skills/new/SKILL.md (transitive note) │
83
+ │ skills/lsp-bootstrap/SKILL.md (/lsp-bootstrap) │
84
+ └─────────────────────────────────────────────────────────────────────┘
85
+ ```
86
+
87
+ Each layer is independently testable and individually replaceable. The
88
+ adapters never speak LSP themselves — they just describe *how to install*
89
+ the language server. Actual symbol resolution at runtime happens through
90
+ whatever Claude Code plugin / MCP server the consumer has loaded.
91
+
92
+ ---
93
+
94
+ ## 3. End-to-end lifecycle
95
+
96
+ ### 3.1 First install in a brand-new repo
97
+
98
+ ```bash
99
+ npx baldart add # subtree pull + symlink reconcile
100
+ # → triggers configure interactively
101
+ ```
102
+
103
+ `configure` autodetects supported languages by probing for marker files:
104
+
105
+ | Language | Marker(s) | Adapter |
106
+ |------------|----------------------------------------------------------|----------------------------------|
107
+ | TypeScript | `tsconfig.json`, `jsconfig.json`, `package.json` w/ typescript | `lsp-adapters/typescript.js` |
108
+ | Python | `pyproject.toml`, `setup.py`, `setup.cfg`, `requirements.txt`, `Pipfile`, top-level `*.py` | `lsp-adapters/python.js` |
109
+ | Go | `go.mod` | `lsp-adapters/go.js` |
110
+ | Rust | `Cargo.toml` | `lsp-adapters/rust.js` |
111
+ | Ruby | `Gemfile`, `.ruby-version`, `config.ru` | `lsp-adapters/ruby.js` |
112
+
113
+ If any adapter fires, `features.has_lsp_layer` defaults to `true` in the
114
+ configure prompt (the user can still say no). The prompt copy is
115
+ *"Enable LSP symbol-search layer? (recommended for large codebases)"*.
116
+
117
+ When the user confirms, for each detected language:
118
+
119
+ - **npm-dev mode** (TS, Python via pyright): `npm install --save-dev <pkg>`
120
+ runs in-process. Success → added to `lsp.installed_servers`.
121
+ - **system mode** (Go, Rust, Ruby): the install command is *printed*, never
122
+ executed. The server name is recorded as pending in `installed_servers`;
123
+ `baldart doctor` re-verifies on the next run and flags the mismatch if the
124
+ user hasn't run the printed command yet.
125
+
126
+ ### 3.2 Update from pre-v3.10
127
+
128
+ ```bash
129
+ npx baldart update # subtree pull
130
+ # → schema-drift detector runs
131
+ ```
132
+
133
+ `update.js:407–435` diffs `template.features` vs `cur.features`. It finds
134
+ `has_lsp_layer` missing and prints:
135
+
136
+ > ⚠ New config keys in this version: has_lsp_layer.
137
+ > Run `baldart configure` now to populate them? (y/N)
138
+
139
+ The user re-enters the configure flow above. **No silent default** — the
140
+ always-ask contract from `framework/agents/project-context.md` § 3 owns this.
141
+
142
+ ### 3.3 Routine doctor run
143
+
144
+ ```bash
145
+ npx baldart # smart doctor (no-arg, default since v3.2.0)
146
+ ```
147
+
148
+ `doctor.js` reads `config.lsp.installed_servers`. When
149
+ `lsp.auto_verify !== false`, it executes each adapter's `verifyCommand()`
150
+ (e.g. `npx --no-install typescript-language-server --version`). Output:
151
+
152
+ ```
153
+ · LSP layer 2 server(s) verified (typescript, python)
154
+ ```
155
+
156
+ If any server fails verify, the planner emits an `lsp-fix` action with
157
+ `autoOk: false` (so it asks before touching anything) and reinstalls only
158
+ the broken ones — with per-server confirmation, preserving any deliberate
159
+ uninstall the user may have done.
160
+
161
+ ### 3.4 Standalone bootstrap
162
+
163
+ ```
164
+ /lsp-bootstrap # inside Claude Code, when the layer is enabled
165
+ ```
166
+
167
+ The same install + verify flow exposed as a skill, useful when the user has
168
+ toggled `has_lsp_layer: true` manually in `baldart.config.yml` and now wants
169
+ to populate the layer without re-running the full configure prompt. The
170
+ skill refuses to run when the flag is `false` — opt-in is owned by configure.
171
+
172
+ ### 3.5 Runtime behavior in a Claude Code session
173
+
174
+ When `codebase-architect` or one of the code-exploration skills (`bug`,
175
+ `prd`, `new`, `simplify`, `context-primer`) is invoked:
176
+
177
+ 1. Read `baldart.config.yml`. If `features.has_lsp_layer !== true`, behave
178
+ exactly as v3.9.x (RAG → Grep → Git).
179
+ 2. Otherwise: classify the query.
180
+ - Identifier-shaped (function name, type name, class, exported symbol) →
181
+ **LSP first** (`ToolSearch("LSP")` → `find-references` / `definition`).
182
+ - Free text / phrase / comment / path / regex → **Grep first**, LSP is
183
+ useless here.
184
+ - Mixed (e.g. "refactor function X used in 5 places") → RAG hybrid for
185
+ conceptual context, then LSP for the structural part.
186
+ 3. Budget: max **3 LSP calls per task**. After 3 calls without convergence,
187
+ escalate to direct file reads — don't ping-pong references.
188
+ 4. If LSP tool isn't loaded / plugin missing / timeout > 8s / zero
189
+ references for a symbol you know exists → **silent fallback to Grep**.
190
+ The user does not see an error mid-task; `baldart doctor` is the
191
+ self-heal channel.
192
+
193
+ The protocol document
194
+ [`framework/agents/code-search-protocol.md`](../agents/code-search-protocol.md)
195
+ is the authoritative runtime spec — this doc points at it rather than
196
+ duplicating its content.
197
+
198
+ ---
199
+
200
+ ## 4. Field reference
201
+
202
+ ### 4.1 `baldart.config.yml`
203
+
204
+ ```yaml
205
+ features:
206
+ has_lsp_layer: true # gate
207
+
208
+ lsp:
209
+ installed_servers: # list of adapter names
210
+ - typescript
211
+ - python
212
+ auto_verify: true # doctor re-runs verifyCommand on each call
213
+ ```
214
+
215
+ Constraints:
216
+
217
+ - `has_lsp_layer` MUST be present and explicitly `true | false` once
218
+ configure has run (always-ask contract).
219
+ - `installed_servers` is **only meaningful when the flag is `true`**.
220
+ Configure resets it to `[]` if the flag flips to `false` (so the file
221
+ doesn't lie about pending installs).
222
+ - `auto_verify: false` is intended for noisy CI — agents still respect the
223
+ layer at runtime; only the doctor's verify probe is suppressed.
224
+ - Adapter names map 1:1 to filenames under `src/utils/lsp-adapters/`. An
225
+ unknown name in `installed_servers` produces a soft warning, never a hard
226
+ failure.
227
+
228
+ ### 4.2 Adapter contract (`src/utils/lsp-adapters/<lang>.js`)
229
+
230
+ Every adapter is a class exporting:
231
+
232
+ | Member | Type | Purpose |
233
+ |----------------------|---------------------|------------------------------------------------------------|
234
+ | `name` | string getter | Stable id used in `installed_servers` (e.g. `"typescript"`)|
235
+ | `label` | string getter | Human-readable for prompts/diagnostics |
236
+ | `binary` | string getter | Executable name expected on PATH or in node_modules |
237
+ | `installMode` | `"npm-dev"` / `"system"` | How the installer treats this language |
238
+ | `npmPackage` | string (npm-dev only) | What goes after `npm install --save-dev` |
239
+ | `installCommand()` | string | The exact shell command (printed or executed) |
240
+ | `verifyCommand()` | string | A no-side-effect probe (e.g. `--version`) |
241
+ | `claudePluginId()` | string \| null | Reserved: id of a Claude Code code-intelligence plugin |
242
+ | `static detect(cwd)` | boolean | Pure: does the language live in this repo? |
243
+
244
+ The contract is enforced by convention, not by an interface check. When you
245
+ add a new adapter, mirror the shape of `typescript.js` exactly, then add it
246
+ to the REGISTRY in `lsp-adapters/index.js`.
247
+
248
+ ### 4.3 Installer contract (`src/utils/lsp-installer.js`)
249
+
250
+ `LspInstaller` exposes:
251
+
252
+ - `detectLanguages()` → string[] — pure, no I/O beyond filesystem reads
253
+ - `recommend(langs?)` → entries with install metadata for UI consumption
254
+ - `installServers(names, { interactive=true, dryRun=false })` →
255
+ `{ installed, skipped, manual }` — returns rather than throws
256
+ - `verifyServers(names)` → `[{ name, ok, output|error }]` — never throws
257
+
258
+ All side-effecting methods are gated by `interactive` and a per-server
259
+ confirm; doctor passes `interactive: true` so the user retains veto power.
260
+
261
+ ---
262
+
263
+ ## 5. Adding a new language
264
+
265
+ Worked example: adding **Java** support.
266
+
267
+ 1. Pick the language server. For Java the de-facto choice is `jdtls`
268
+ (Eclipse JDT Language Server), installed via system means.
269
+ 2. Create `src/utils/lsp-adapters/java.js`:
270
+ ```js
271
+ const fs = require('fs');
272
+ const path = require('path');
273
+
274
+ class JavaAdapter {
275
+ constructor(cwd = process.cwd()) { this.cwd = cwd; }
276
+ get name() { return 'java'; }
277
+ get label() { return 'Java (jdtls)'; }
278
+ get binary() { return 'jdtls'; }
279
+ get installMode() { return 'system'; }
280
+ installCommand() { return 'brew install jdtls'; }
281
+ verifyCommand() { return 'jdtls --version'; }
282
+ claudePluginId() { return null; }
283
+ static detect(cwd = process.cwd()) {
284
+ return ['pom.xml', 'build.gradle', 'build.gradle.kts']
285
+ .some(m => fs.existsSync(path.join(cwd, m)));
286
+ }
287
+ }
288
+
289
+ module.exports = JavaAdapter;
290
+ ```
291
+ 3. Register in `src/utils/lsp-adapters/index.js`:
292
+ ```js
293
+ const JavaAdapter = require('./java');
294
+ // ...
295
+ const REGISTRY = {
296
+ // ...
297
+ java: JavaAdapter,
298
+ };
299
+ ```
300
+ 4. Smoke test:
301
+ ```bash
302
+ node -e 'const r = require("./src/utils/lsp-adapters"); console.log(r.listAdapters())'
303
+ ```
304
+ Should now include `java`.
305
+ 5. No change required in `configure.js`, `doctor.js`, the protocol module,
306
+ or any agent/skill — they all iterate over the REGISTRY.
307
+ 6. Update `CHANGELOG.md`, bump VERSION (PATCH if pure addition), document the
308
+ adapter in this file's § 3.1 marker table.
309
+ 7. Optional: add a per-language quirk paragraph to
310
+ `framework/templates/overlays/agents/codebase-architect.lsp-example.md`
311
+ if Java has gotchas worth flagging to consumers (e.g. classpath setup,
312
+ multi-module Maven projects).
313
+
314
+ ---
315
+
316
+ ## 6. Troubleshooting
317
+
318
+ ### 6.1 "Agents are still using Grep even though LSP is enabled"
319
+
320
+ Likely causes, in order of likelihood:
321
+
322
+ 1. The query is free text, not a symbol. Re-read § 3.5 — Grep is correct
323
+ here, not a regression.
324
+ 2. The Claude Code LSP plugin isn't loaded in this session. Check inside
325
+ Claude Code with `/plugin list`. Reload if necessary.
326
+ 3. The language server binary isn't on PATH. Run `baldart doctor` — the
327
+ LSP line will say `n/m broken (typescript, ...)` and propose `lsp-fix`.
328
+ 4. `features.has_lsp_layer` is `false` or missing in `baldart.config.yml`.
329
+ Run `baldart configure`.
330
+
331
+ ### 6.2 "configure prompted me but didn't install anything"
332
+
333
+ The autodetector found no supported language markers. Either:
334
+
335
+ - You're in a polyglot repo where the supported language lives in a
336
+ subdirectory configure didn't probe. Add the marker file at the repo root
337
+ (e.g. a stub `tsconfig.json`) or extend the adapter's `detect()`.
338
+ - You're using a language we don't ship an adapter for. See § 5.
339
+
340
+ ### 6.3 "doctor says my server is broken but it works in my editor"
341
+
342
+ `verifyCommand()` executes from the consumer cwd with a hard 8s timeout. If
343
+ the language server takes longer than that to print `--version` (rare, but
344
+ seen on first-run cold caches), the verify fails. Re-run `baldart doctor`
345
+ once warm. If still failing, run the exact `verifyCommand()` string in your
346
+ shell — the actual error will surface.
347
+
348
+ ### 6.4 "I want to enable the layer but not have devDeps in package.json"
349
+
350
+ For TypeScript / Python you can override the adapter's `installMode` by
351
+ writing an overlay at `.baldart/overlays/agents/codebase-architect.md`
352
+ documenting your alternative install path (e.g. system pyright via uv), and
353
+ then manually setting `lsp.installed_servers: [python]` in the config. The
354
+ runtime layer doesn't care *how* the binary got there, only that it's
355
+ reachable. `baldart doctor` will continue to verify it.
356
+
357
+ ---
358
+
359
+ ## 7. Mental model for agents extending this layer
360
+
361
+ When you (an AI agent) are asked to modify the LSP layer, internalize these
362
+ invariants — they are the rules the existing code obeys, and breaking them
363
+ silently corrupts consumer installs:
364
+
365
+ 1. **Opt-in is owned by configure.** No code path may flip
366
+ `features.has_lsp_layer` from `false` to `true` (or vice versa) without
367
+ going through `configure.js` or an explicit user-typed YAML edit. The
368
+ `lsp-bootstrap` skill and the doctor `lsp-fix` action both *refuse* to
369
+ change the gate; they only act when it's already on.
370
+ 2. **`installed_servers` is descriptive, not normative.** It records what
371
+ BALDART knows about; it does not enforce. Removing a name from the list
372
+ doesn't uninstall the binary, and adding a name doesn't install one. The
373
+ single source of truth for "is it usable" is the verify probe.
374
+ 3. **Fallback to Grep is silent.** Never abort an agent's task because LSP
375
+ failed. The user finds out about broken LSP through `baldart doctor`,
376
+ not by getting an error mid-bug-investigation.
377
+ 4. **The adapter REGISTRY is the only place to enumerate languages.**
378
+ `configure.js`, `doctor.js`, the bootstrap skill, and the docs all
379
+ iterate the registry. Hard-coding a language anywhere else is a bug.
380
+ 5. **Schema-change propagation rule** (from memory
381
+ `feedback_schema_change_propagation.md`): any new key under `lsp.*` or
382
+ `features.has_lsp_*` must land in the template, the configure prompt,
383
+ the update detector, the doctor diagnostic, the relevant skill(s), AND
384
+ the CHANGELOG. If you only add the template, you have introduced silent
385
+ drift between layers — the v3.9.0 pattern is the worked example to copy.
386
+ 6. **No MCP server lives in this repo.** BALDART does not ship its own LSP
387
+ bridge. The runtime tools come from Claude Code's plugin ecosystem (or
388
+ a third-party MCP the user installs); BALDART's job is to make sure the
389
+ binaries are present and to instruct agents how to use them.
390
+
391
+ If a feature request seems to break one of these invariants, push back and
392
+ propose an alternative that preserves them — they were chosen deliberately
393
+ in the design review (see the approved plan in
394
+ `/Users/antoniobaldassarre/.claude/plans/` from 2026-05-23).
395
+
396
+ ---
397
+
398
+ ## 8. See also
399
+
400
+ - [`framework/agents/code-search-protocol.md`](../agents/code-search-protocol.md) — runtime protocol
401
+ - [`framework/agents/project-context.md`](../agents/project-context.md) — always-ask contract
402
+ - [`framework/docs/PROJECT-CONFIGURATION.md`](PROJECT-CONFIGURATION.md) § 4.5 + § 4.6 — config schema
403
+ - [`framework/.claude/skills/lsp-bootstrap/SKILL.md`](../.claude/skills/lsp-bootstrap/SKILL.md) — invokable skill
404
+ - [`framework/templates/overlays/agents/codebase-architect.lsp-example.md`](../templates/overlays/agents/codebase-architect.lsp-example.md) — starter overlay
405
+ - [`CHANGELOG.md`](../../CHANGELOG.md) — v3.10.0 entry
@@ -148,6 +148,23 @@ Every flag MUST be present as `true` or `false` once `baldart configure` has run
148
148
  | `has_adrs` | ADR workflow. Architectural decisions get logged in `paths.adrs_dir`. |
149
149
  | `has_prd_workflow` | PRD workflow (`paths.prd_dir`). `/prd` and `/prd-add` skills refuse to run without it. |
150
150
  | `has_wiki_overlay` | LLM-wiki overlay (`paths.wiki_dir`). `/capture` skill refuses to run without it. |
151
+ | `has_lsp_layer` | LSP symbol-search layer (since v3.10.0). When `true`, `codebase-architect`, `context-primer`, `bug`, `prd`, `new`, and `simplify` prefer LSP `find-references` / `go-to-definition` over Grep for identifier queries. Wired by `framework/agents/code-search-protocol.md`. The flag also triggers `baldart configure` (and the `/lsp-bootstrap` skill) to install the matching language servers. |
152
+
153
+ ### 4.6 `lsp` — installed language servers (since v3.10.0)
154
+
155
+ Populated by `baldart configure` and the `/lsp-bootstrap` skill when `features.has_lsp_layer: true`. Leave empty otherwise.
156
+
157
+ | Key | Meaning |
158
+ |---|---|
159
+ | `lsp.installed_servers` | List of language IDs (`typescript`, `python`, `go`, `rust`, `ruby`, …) whose server BALDART has recorded for this project. Identifiers map 1:1 to adapter files under `src/utils/lsp-adapters/`. |
160
+ | `lsp.auto_verify` | When `true` (default), `baldart doctor` re-verifies the binaries are reachable on every run. Disable on CI if the verify step is too noisy. |
161
+
162
+ **Install modes per adapter:**
163
+
164
+ - `typescript`, `python` — installed as npm devDeps (`npm install --save-dev typescript-language-server typescript` / `pyright`). Project-local, version-pinned.
165
+ - `go`, `rust`, `ruby` — installed via system tools (`go install …`, `rustup component add …`, `gem install …`). BALDART prints the command; the user runs it.
166
+
167
+ **Fallback contract.** When the LSP layer is enabled but a server is missing or broken, agents silently fall back to Grep — code search never fails because of LSP issues. `baldart doctor` surfaces the mismatch and offers reinstall.
151
168
 
152
169
  ## 5. The overlay system
153
170
 
@@ -112,6 +112,28 @@ features:
112
112
  # LLM-wiki overlay (paths.wiki_dir + capture/wiki-curator loop).
113
113
  has_wiki_overlay: false
114
114
 
115
+ # LSP symbol-search layer. When true, agents and skills that explore code
116
+ # (codebase-architect, context-primer, bug, prd, new, simplify) prefer
117
+ # language-server "find references / go to definition" over Grep for
118
+ # identifier queries, falling back to Grep only when LSP is unavailable
119
+ # or the query isn't a symbol. See framework/agents/code-search-protocol.md.
120
+ # `baldart configure` installs the relevant language servers when enabled.
121
+ has_lsp_layer: false
122
+
123
+ # ─── LSP ─────────────────────────────────────────────────────────────────
124
+ # State of the LSP layer for this project. Populated by `baldart configure`
125
+ # (and by `npx baldart lsp install` / the `/lsp-bootstrap` skill). Leave
126
+ # empty if `features.has_lsp_layer: false`.
127
+ lsp:
128
+ # Languages whose language server BALDART has installed/verified locally.
129
+ # Identifiers map to adapters under src/utils/lsp-adapters/
130
+ # (typescript, python, go, rust, ruby — extend as needed).
131
+ installed_servers: []
132
+
133
+ # When true, `baldart doctor` re-verifies the binaries are reachable on
134
+ # every run. Disable on CI if the verify step is too noisy.
135
+ auto_verify: true
136
+
115
137
  # ─── GIT ─────────────────────────────────────────────────────────────────
116
138
  # Controls how worktree-manager (`/mw`) integrates a worktree's feature
117
139
  # branch back into `develop`.
@@ -0,0 +1,53 @@
1
+ ---
2
+ base_agent: codebase-architect
3
+ base_agent_version: 3.10.0
4
+ mode: extend
5
+ ---
6
+
7
+ > **How to use this example**
8
+ >
9
+ > Copy this file to `.baldart/overlays/agents/codebase-architect.md` (drop the
10
+ > `.lsp-example`) when you want to add project-specific LSP guidance on top of
11
+ > the shipped `agents/code-search-protocol.md`. The framework regenerates
12
+ > `.claude/agents/codebase-architect.md` from base + overlay on every
13
+ > `npx baldart update`.
14
+ >
15
+ > Section markers (see `framework/templates/overlays/README.md`):
16
+ > - `## [OVERRIDE] <heading>` — replace the entire matching H2 from the base.
17
+ > - `## [APPEND] <heading>` — add content after the base section.
18
+ > - `## [PREPEND] <heading>` — add content before the base section.
19
+ > - `## <heading>` with no marker — appended at the end as a new custom section.
20
+ >
21
+ > Skip this overlay entirely if the shipped protocol is enough — the base
22
+ > agent already wires LSP correctly when `features.has_lsp_layer: true`.
23
+
24
+ ## [APPEND] LSP Symbol Search
25
+
26
+ Project-specific tuning on top of `framework/agents/code-search-protocol.md`:
27
+
28
+ - **Preferred entry points.** For identifier-shaped lookups in this repo,
29
+ start with `find-references` on the symbol exported from
30
+ `src/<your-canonical-module>/index.ts` (or your equivalent). The export
31
+ surface is small enough that the references list converges in 1–2 calls.
32
+ - **Per-language quirks.**
33
+ - TypeScript: monorepo packages under `packages/<name>` need
34
+ `tsserver` workspaces enabled. If LSP returns zero references for a
35
+ symbol you know is used cross-package, fall back to Grep scoped to
36
+ `packages/*/src/`.
37
+ - Python: pyright honors `extraPaths` from `pyrightconfig.json` — if
38
+ you re-rooted sources, double-check the config before reporting "no
39
+ references found".
40
+ - **Don't chase generated code.** Skip LSP results pointing into `dist/`,
41
+ `.next/`, `coverage/`, or any path matching the project's `.gitignore` —
42
+ treat them as noise.
43
+
44
+ ## Project-only conventions
45
+
46
+ Custom rules with no equivalent in the base — appended as a new H2:
47
+
48
+ - All `find-references` outputs over 50 callsites trigger a refactor
49
+ conversation with the maintainer before any rename / signature change
50
+ proceeds.
51
+ - When LSP is unavailable, write the reason (binary missing / plugin not
52
+ loaded / language not yet adopted) into the analysis output so reviewers
53
+ understand why Grep was used instead.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "baldart",
3
- "version": "3.9.0",
3
+ "version": "3.10.0",
4
4
  "description": "Claude Agent Framework - Reusable framework for coordinating AI agents and humans in software projects",
5
5
  "bin": {
6
6
  "baldart": "./bin/baldart.js"
@@ -3,6 +3,8 @@ const path = require('path');
3
3
  const yaml = require('js-yaml');
4
4
  const UI = require('../utils/ui');
5
5
  const toolAdapters = require('../utils/tool-adapters');
6
+ const LspInstaller = require('../utils/lsp-installer');
7
+ const lspAdapters = require('../utils/lsp-adapters');
6
8
 
7
9
  const CONFIG_FILE = 'baldart.config.yml';
8
10
  // The subtree pull copies the entire BALDART repo (which itself has a
@@ -342,6 +344,9 @@ function detect(cwd = process.cwd()) {
342
344
  has_adrs: countMatches('docs/decisions', /^ADR-.*\.md$/) > 0,
343
345
  has_prd_workflow: exists('docs/prd'),
344
346
  has_wiki_overlay: exists('docs/wiki'),
347
+ // LSP recommended when any supported language server adapter would
348
+ // detect this project. Drives the default for the configure prompt.
349
+ has_lsp_layer: lspAdapters.detectAll(cwd).length > 0,
345
350
  },
346
351
  tools: {
347
352
  enabled: toolAdapters.defaultEnabled(cwd)
@@ -506,6 +511,7 @@ async function interactivePrompts(merged, detected) {
506
511
  ['has_adrs', 'Project uses ADR workflow?'],
507
512
  ['has_prd_workflow', 'Project uses PRD workflow (docs/prd/)?'],
508
513
  ['has_wiki_overlay', 'Project has LLM-wiki overlay (docs/wiki/)?'],
514
+ ['has_lsp_layer', 'Enable LSP symbol-search layer? (recommended for large codebases)'],
509
515
  ]) {
510
516
  const [key, question] = flag;
511
517
  const currentVal = merged.features[key];
@@ -544,6 +550,48 @@ async function interactivePrompts(merged, detected) {
544
550
  merged.paths[key] = await promptForKey(`${label}`, proposed);
545
551
  }
546
552
 
553
+ // ---- LSP layer (since v3.10.0) ----------------------------------------
554
+ // Only runs when the user opted in above. Idempotent: re-running configure
555
+ // on an already-installed project re-detects, asks which servers to keep,
556
+ // and updates lsp.installed_servers accordingly.
557
+ merged.lsp = merged.lsp || { installed_servers: [], auto_verify: true };
558
+ if (merged.features.has_lsp_layer === true) {
559
+ UI.section('LSP layer (install language servers for symbol search)');
560
+ const installer = new LspInstaller(process.cwd());
561
+ const detectedLangs = installer.detectLanguages();
562
+ if (detectedLangs.length === 0) {
563
+ UI.warning('No supported languages detected (tsconfig.json, pyproject.toml, go.mod, Cargo.toml, Gemfile).');
564
+ UI.info('Skipping LSP install. You can run `/lsp-bootstrap` or `npx baldart configure` later.');
565
+ } else {
566
+ const recs = installer.recommend(detectedLangs);
567
+ const already = new Set(merged.lsp.installed_servers || []);
568
+ const toInstall = [];
569
+ for (const r of recs) {
570
+ const note = already.has(r.name) ? ' (already recorded)' : '';
571
+ UI.info(`${r.label} → ${r.binary} (${r.installMode})${note}`);
572
+ const want = await UI.confirm(`Install ${r.label}?`, true);
573
+ if (want) toInstall.push(r.name);
574
+ }
575
+ if (toInstall.length) {
576
+ const { installed, skipped, manual } = await installer.installServers(toInstall, { interactive: false });
577
+ for (const m of manual) {
578
+ UI.warning(`System-level install required for ${m.label}:`);
579
+ UI.code(m.command, `Recorded as pending — run \`baldart doctor\` after installing to verify.`);
580
+ }
581
+ const verify = installer.verifyServers(installed.map(i => i.name));
582
+ const verified = verify.filter(v => v.ok).map(v => v.name);
583
+ const newSet = new Set([...(merged.lsp.installed_servers || []), ...verified, ...manual.map(m => m.name)]);
584
+ merged.lsp.installed_servers = Array.from(newSet);
585
+ if (skipped.length) {
586
+ UI.warning(`Skipped: ${skipped.map(s => `${s.name} (${s.reason})`).join(', ')}`);
587
+ }
588
+ }
589
+ }
590
+ } else {
591
+ // Feature disabled — wipe the state so `installed_servers` doesn't lie.
592
+ merged.lsp.installed_servers = [];
593
+ }
594
+
547
595
  UI.section('Stack (autodetected from package.json — confirm or override)');
548
596
  const charting = merged.stack.charting;
549
597
  const chartingCanonical = await promptForKey(