1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
//! trusty-search MCP stub + project-index registration/reindex helpers.
//!
//! Why: split out of `settings.rs` (issue #610 SLOC cap) — the trusty-search
//! index lifecycle (build the pinned MCP stub, find-or-create the index, then
//! best-effort trigger a reindex so it is actually populated) is a cohesive
//! concern distinct from the output-style/hook/trust-preseed helpers that
//! remain in `settings.rs`.
//! What: [`trusty_search_mcp_value`] / [`inject_trusty_search_mcp`] build and
//! write the `.mcp.json` stub (issue #1373); [`register_project_index`] is now a
//! thin wrapper over the shared [`trusty_common::search_index::ensure_project_indexed`]
//! entry point, which find-or-creates the daemon-side index and ensures it is
//! populated (issue #1908). That register+reindex logic was PROMOTED into
//! trusty-common so trusty-code can reuse the ONE implementation (common
//! entry-point rule) — this module keeps only the MCP-stub concerns plus the
//! wrapper.
//! Test: each `pub(super)` function has a dedicated test in `tests.rs`.
use Path;
use PrepError;
use inject_mcp_server;
/// Build the `trusty-search` MCP server definition injected into a project's
/// `.mcp.json`, optionally pinned to a project index (issue #1373).
///
/// Why (issue #1270 / step 4): trusty-mpm-spawned sessions need the code-search
/// tools (`search`, `grep`, `get_call_chain`, …). Issue #1373: without pinning,
/// the contextless `trusty-search serve` stub left index selection to the LLM,
/// which routinely queried the WRONG index (usually the persistent `claude-mpm`
/// one) instead of the session's own project. Passing `--index <derived-id>`
/// pins the session to its project index so a bare `search` always resolves
/// correctly and fan-out never sweeps every index. The canonical stdio MCP
/// invocation is bare `serve` (stdio is the default transport; HTTP is off
/// unless `--with-http`).
/// What: returns the JSON `Value` for a stdio MCP server running
/// `trusty-search serve` — with `["serve", "--index", "<id>"]` when `index_id`
/// is `Some` (non-empty), else the unpinned `["serve"]`. Index *creation* is
/// handled separately by [`register_project_index`].
/// Test: `trusty_search_mcp_value_pins_index`, `trusty_search_mcp_value_unpinned`.
pub
/// Inject the `trusty-search` MCP server into the project's `.mcp.json`,
/// pinned to the project's index when one is known (issue #1373).
///
/// Why (issue #1270 / step 4): spawned sessions must reach the code-search
/// tools, but `trusty-search` was never registered alongside `trusty-memory`.
/// Issue #1373: the stub must additionally PIN the session to the project's own
/// index (`--index <id>`) so queries never resolve to the wrong index. When
/// `index_id` is `None` (derivation failed) the unpinned stub is written so the
/// session still gets the tools — backward-compatible with the pre-#1373 stub.
/// What: builds the (optionally pinned) server value via
/// [`trusty_search_mcp_value`] and registers it under the key `trusty-search`.
/// Test: `inject_trusty_search_mcp_adds_server`,
/// `inject_trusty_search_mcp_preserves_existing`,
/// `inject_trusty_search_mcp_is_idempotent`,
/// `inject_trusty_search_mcp_pins_index`,
/// `inject_both_mcp_servers_coexist`.
pub
/// Find-or-create the trusty-search index for `project_root`, best-effort
/// trigger a reindex so it is actually populated, and return its id (issues
/// #1373, #1908).
///
/// Why: pinning the serve stub to an index id is only useful if that index
/// actually exists in the daemon — otherwise a query against it returns nothing
/// and the LLM falls back to guessing (the very bug #1373 fixes). The
/// register-and-populate logic was PROMOTED into
/// [`trusty_common::search_index::ensure_project_indexed`] so trusty-code can
/// reuse the ONE implementation (common entry-point rule); this wrapper keeps
/// the session-launch call site and name stable. Behaviour is unchanged: derive
/// the canonical index id, best-effort `POST /indexes` + freshness-gated reindex
/// when the daemon is reachable, always return the derived id so the stub is
/// pinned even when the daemon is down, and never propagate an error (the
/// session must still launch).
/// What: delegates to [`trusty_common::search_index::ensure_project_indexed`]
/// with `allow_sensitive_path: false` (issue #2914). A trusty-mpm session
/// workspace is always either the user's checked-out repository or a
/// `.worktrees/<uuid>` leaf inside it — never a legitimate OS-temp path — so
/// this caller, unlike trusty-code's task-start caller, has no reason to
/// bypass the daemon's `SENSITIVE_PATH_PREFIXES` denylist. Before this fix the
/// bypass was unconditional, so a session-launch test standing in a `tempfile`
/// fixture for `project_root` (e.g. a workspace under `/var/folders/…`)
/// silently registered that throwaway directory against whatever trusty-search
/// daemon happened to be running on the developer/CI machine — the root cause
/// of the ephemeral-index leak this issue reports.
///
/// `skip_vector` (#5065 review): forwarded from [`worktree_skip_vector`] rather
/// than left at its `false` default, so launching a session in a worktree
/// cannot undo the BM25+KG-only decision worktree creation made. See that
/// function for why the ordering made this a real drift and not a theoretical
/// one.
/// Test: `register_project_index_returns_derived_id` (derivation + daemon-down
/// graceful path) and `register_project_index_never_bypasses_sensitive_path_denylist`
/// (issue #2914 regression) in `tests.rs`; the promoted logic is unit-tested in
/// `trusty_common::search_index::tests`.
pub
/// Should the index for `project_root` be registered BM25+KG-only (#5065 review)?
///
/// Why: BM25+KG-only was not an invariant, only the behaviour of whichever call
/// happened to land first. #5060 registers a worktree's index with
/// `skip_vector: true` at CREATION, but session launch reached the same
/// worktree path through [`register_project_index`] with `skip_vector` at its
/// `false` default. `POST /indexes` is find-or-create and short-circuits on an
/// existing id, so ordering decided the outcome: whenever creation-time
/// indexing failed, was skipped, or simply lost the race, launch minted a
/// vector-bearing index for the worktree and the daemon persisted that choice.
/// Deciding it from the path — the one thing both call sites agree on — removes
/// the ordering dependence entirely.
/// What: resolves the git-root the index will actually be keyed to (the same
/// `resolve_project_root` hop `ensure_project_indexed_with` performs
/// internally) and asks whether THAT directory is a git worktree. Testing
/// `project_root` itself would answer `false` for a session launched from a
/// SUBDIRECTORY of a worktree, whose index is still keyed to the worktree root
/// — reintroducing the same drift through a narrower door.
/// Test: `worktree_skip_vector_true_for_worktree_root`,
/// `worktree_skip_vector_true_from_worktree_subdirectory`,
/// `worktree_skip_vector_false_for_plain_clone`.
pub