Skip to main content

trusty_common/
lib.rs

1//! Shared utility surface for trusty-* projects.
2//!
3//! Why: Port auto-detect, data-directory resolution, tracing init, NO_COLOR
4//! handling, and the OpenRouter chat-completions client appeared in both
5//! trusty-memory and trusty-search with subtle divergence. Centralising keeps
6//! them aligned and gives future trusty-* binaries a one-import surface.
7//!
8//! What: pure utility functions — no global state. Each subsystem is a free
9//! function or a small helper struct.
10//!
11//! Test: `cargo test -p trusty-common` covers port walking, data-dir creation,
12//! and the OpenRouter request shape (without hitting the network).
13//!
14//! # Test isolation: `TRUSTY_DATA_DIR_OVERRIDE`
15//!
16//! macOS's [`dirs::data_dir()`] resolves the application-support directory via
17//! `NSFileManager`, a native Cocoa API that completely ignores the `HOME` and
18//! `XDG_DATA_HOME` environment variables. This makes it impossible to redirect
19//! data-directory access in tests using ordinary env-var tricks, because the
20//! kernel query bypasses the environment entirely.
21//!
22//! To work around this, [`resolve_data_dir`] checks the
23//! [`DATA_DIR_OVERRIDE_ENV`] (`TRUSTY_DATA_DIR_OVERRIDE`) environment variable
24//! before consulting `dirs::data_dir()`. When set, the variable's value is used
25//! as the base directory verbatim, and `dirs::data_dir()` is never called.
26//!
27//! **This escape hatch is intended for testing only.** Do not set it in
28//! production deployments; rely on the OS-standard data directory instead.
29
30/// Shared trusty splash art + per-glyph shading (issue #3326).
31///
32/// Why: `tm`'s launch banner and `trusty-agents`' REPL startup splash must
33/// present the same trusty branding; centralising the art text and its
34/// color-bucket rule here stops the two binaries from drifting apart again.
35/// What: [`banner::TRUSTY_SPLASH_ART`] (embedded ASCII/block-art text) and
36/// [`banner::shade_bucket`] (glyph → RGB triple). Zero extra dependencies —
37/// pure `&str` + `match`.
38/// Test: `cargo test -p trusty-common -- banner::tests`.
39pub mod banner;
40
41pub mod chat;
42pub mod claude_config;
43
44/// Canonical environment-variable name constants shared across the workspace.
45///
46/// Why: the same credential env-var names were spelled as bare literals at ~40
47/// `std::env::var(...)` call sites across nine crates; centralizing them makes
48/// a typo a compile error instead of a silent misread.
49/// What: exposes [`ENV_OPENROUTER_API_KEY`](env_vars::ENV_OPENROUTER_API_KEY)
50/// and [`ENV_GITHUB_TOKEN`](env_vars::ENV_GITHUB_TOKEN).
51/// Test: `cargo test -p trusty-common -- env_var_names_are_stable`.
52pub mod env_vars;
53
54pub mod project_discovery;
55
56/// Shared graceful-shutdown signal helper for trusty-* daemons (issue #534).
57///
58/// Why: trusty-search, trusty-memory, and trusty-analyze all need the same
59/// SIGTERM + SIGINT shutdown future to pass to axum's `with_graceful_shutdown`.
60/// Centralising it here eliminates three-way duplication and guarantees every
61/// daemon responds identically to `launchctl bootout`.
62/// What: exposes [`shutdown_signal`] — an async fn that resolves on SIGTERM
63/// (unix) or SIGINT/Ctrl-C (all platforms), whichever fires first.
64/// Test: `cargo test -p trusty-common -- shutdown`.
65pub mod shutdown;
66pub use shutdown::shutdown_signal;
67
68/// Bounded in-memory ring buffer of recent tracing log lines.
69///
70/// Why: trusty-* daemons expose a `/logs/tail` endpoint so operators can read
71/// recent logs over HTTP without file I/O or a daemon restart. The buffer and
72/// its `tracing_subscriber::Layer` live here so every daemon shares one impl.
73/// What: `LogBuffer` (thread-safe capped `VecDeque<String>`) plus
74/// `LogBufferLayer` (the tracing layer that feeds it).
75/// Test: `cargo test -p trusty-common log_buffer` covers capacity eviction,
76/// tail semantics, and layer capture.
77pub mod log_buffer;
78
79/// Process RSS / CPU sampling and data-directory sizing for daemon health.
80///
81/// Why: every trusty-* daemon's `/health` endpoint reports its own resident
82/// memory, CPU usage, and on-disk footprint; the sampling logic is identical
83/// across them so it lives here once.
84/// What: `SysMetrics` (per-process RSS + CPU sampler) and `dir_size_bytes`
85/// (recursive directory byte count).
86/// Test: `cargo test -p trusty-common sys_metrics`.
87pub mod sys_metrics;
88
89/// Robust executable discovery and daemon `PATH` composition.
90///
91/// Why: launchd relaunches daemons with a minimal `PATH`, breaking spawns of
92/// Homebrew/user-installed tools (`tmux`, `claude`) until the inherited `PATH`
93/// is patched (#1298). This module composes the full set of well-known bin
94/// dirs for a generated launchd plist and provides a `PATH`-then-well-known
95/// binary resolver so the daemon spawns survive a minimal inherited `PATH`.
96/// What: `daemon_path_dirs`, `daemon_path_env`, `resolve_binary`.
97/// Test: `cargo test -p trusty-common bin_resolve`.
98pub mod bin_resolve;
99
100/// macOS LaunchAgent generation and lifecycle management. macOS-only —
101/// the module compiles to nothing on every other platform.
102#[cfg(target_os = "macos")]
103pub mod launchd;
104
105#[cfg(feature = "axum-server")]
106pub mod server;
107
108/// Shared JSON-RPC 2.0 / MCP primitives (formerly the `trusty-mcp-core` crate).
109///
110/// Why: Centralises `Request`/`Response`/`JsonRpcError` envelopes, the
111/// `initialize` response builder, an async stdio dispatch loop, and the
112/// OpenRPC `rpc.discover` helpers so every MCP server in the workspace
113/// imports the same types.
114/// What: Gated behind the `mcp` feature; pulls in no extra dependencies
115/// beyond `serde` / `tokio`, both of which are already required.
116/// Test: `cargo test -p trusty-common --features mcp` runs the module's
117/// own unit tests (envelope round-trips, stdio loop dispatch, OpenRPC
118/// builder shape).
119#[cfg(feature = "mcp")]
120pub mod mcp;
121
122/// General-purpose JSON-RPC client + transports (formerly the library half
123/// of the `trusty-rpc` crate).
124///
125/// Why: Both `trpc` (the CLI) and any future library consumer want one
126/// place that owns the JSON-RPC envelope construction, stdio-subprocess
127/// transport, HTTP transport, and pretty-printers.
128/// What: Gated behind the `rpc` feature; requires `uuid` for request id
129/// generation. The HTTP transport reuses the workspace `reqwest`.
130/// Test: `cargo test -p trusty-common --features rpc` runs the module's
131/// own unit tests (envelope extraction, pretty-print smoke tests).
132#[cfg(feature = "rpc")]
133pub mod rpc;
134
135/// Shared text-embedding abstraction (formerly the `trusty-embedder` crate).
136///
137/// Why: trusty-memory and trusty-search both ship near-identical `Embedder`
138/// traits and `FastEmbedder` implementations; centralising the surface here
139/// keeps them aligned and lets future consumers pick up embedding for free
140/// without a separate published crate.
141/// What: Gated behind the `embedder` feature. Exposes the `Embedder` trait,
142/// `FastEmbedder` (fastembed-rs, all-MiniLM-L6-v2, 384-d) with LRU caching
143/// and ORT warmup, and (under `embedder-test-support`) the `MockEmbedder`
144/// test double.
145/// Test: `cargo test -p trusty-common --features embedder,embedder-test-support`
146/// covers the mock embedder and ONNX-backed `#[ignore]`d integration tests.
147#[cfg(feature = "embedder")]
148pub mod embedder;
149
150/// Unified RPC client surface for the `trusty-embedderd` standalone process.
151///
152/// Why: absorbs both the former `trusty-embedder-client` HTTP crate (PR #163)
153/// and the former `embed_client` UDS module (PR #157) into a single unified
154/// module. Reduces workspace crate count and provides one trait (`EmbedderClient`)
155/// with three concrete implementations (InProcess, HTTP remote, UDS remote) so
156/// call sites are identical regardless of transport. The `embed-client` feature
157/// and `embed_client` module are retired by issue #164; use `embedder-client`
158/// and `trusty_common::embedder_client::UdsEmbedderClient` instead.
159/// What: Gated behind the `embedder-client` feature. Exposes the
160/// `EmbedderClient` trait, `InProcessEmbedderClient`, `RemoteEmbedderClient`
161/// (HTTP), `UdsEmbedderClient` (UDS), `EmbedRequest` / `EmbedResponse` wire
162/// types, and `EmbedderError`. The UDS impl uses `tokio::net::UnixStream`
163/// with newline-framed JSON-RPC 2.0 — no additional dependencies.
164/// Test: `cargo test -p trusty-common --features embedder-client` covers
165/// error-display, JSON round-trip, URL assembly, UDS wire types, and empty-
166/// batch short-circuits. ONNX-backed tests are in
167/// `trusty-embedderd/tests/bit_identical.rs` (`#[ignore]`).
168#[cfg(feature = "embedder-client")]
169pub mod embedder_client;
170
171/// Zero-dependency BM25 lexical index + code-aware tokenizer (issue #156).
172///
173/// Why: trusty-memory, trusty-search, and the per-palace
174/// `trusty-bm25-daemon` subprocess all want one shared BM25 implementation
175/// so the tokenizer's camelCase / PascalCase / alpha↔digit splits stay
176/// consistent across the workspace. Originally ported from open-mpm; now
177/// the single source of truth lives here.
178/// What: Gated behind the `bm25` feature. Adds no new dependencies — pure
179/// `std` + `tracing` (already required).
180/// Test: `cargo test -p trusty-common --features bm25`.
181#[cfg(feature = "bm25")]
182pub mod bm25;
183
184/// Reusable schema-migration kernel (issue #179).
185///
186/// Why: trusty-search, trusty-memory, and other long-lived stores have grown
187/// ad-hoc schema-migration loops that drift apart. Centralising the
188/// `SchemaVersion` newtype, the `Migration<S>` trait, and a `MigrationRunner`
189/// that applies pending steps in order (writing a stamp after each) collapses
190/// those into one shared kernel. The `file_stamp` helper covers the common
191/// "JSON sidecar in the store's data dir" stamp format; redb-stamp users get
192/// a documented recipe instead of a heavyweight dep.
193/// What: gated behind the `migrations` feature flag. Adds no new
194/// dependencies — pure `serde` + `serde_json` + `anyhow` + `tracing` which
195/// the crate already requires.
196/// Test: `cargo test -p trusty-common --features migrations` covers the
197/// runner ordering, crash resumption, write-stamp failure propagation, and
198/// the file-stamp round-trip / atomic-write behaviour.
199#[cfg(feature = "migrations")]
200pub mod migrations;
201
202/// UDS JSON-RPC client for the per-palace `trusty-bm25-daemon` subprocess
203/// (issue #156).
204///
205/// Why: trusty-memory needs a lexical-search lane without holding an
206/// in-process BM25 index. `Bm25Client` delegates to the per-palace daemon
207/// over `$TMPDIR/trusty-bm25-<palace>.sock`, matching the design of
208/// `EmbedClient` and `trusty-embed-daemon` (PR #157).
209/// What: Gated behind the `bm25-client` feature. Pure user of existing
210/// `tokio` / `serde_json` / `anyhow` workspace deps — adds no new
211/// dependencies.
212/// Test: `cargo test -p trusty-common --features bm25-client` covers
213/// request shape and path defaults; end-to-end coverage lives in
214/// `trusty-bm25-daemon/tests/`.
215#[cfg(feature = "bm25-client")]
216pub mod bm25_client;
217
218/// Symbol-graph engine (formerly the `trusty-symgraph` crate).
219///
220/// Why: All trusty-* tools that touch source code (open-mpm, trusty-search,
221/// trusty-analyze) want the same `EntityType` / `RawEntity` / `EdgeKind`
222/// data shapes and (for orchestrators) the same tree-sitter pipeline. Living
223/// here lets the workspace ship one tree-sitter `links =` slot instead of
224/// juggling two crates that both claim it.
225/// What: Gated behind two features. `symgraph` exposes only the contracts
226/// surface (`EntityType`, `RawEntity`, `EdgeKind`, `fact_hash_str`, tables)
227/// — no tree-sitter, no `links` conflict. `symgraph-parser` additionally
228/// pulls in tree-sitter and the full parse → registry → emit stack.
229/// `symgraph-server` enables the HTTP server frontend.
230/// Test: `cargo test -p trusty-common --features symgraph` exercises the
231/// contracts surface; `cargo test -p trusty-symgraph` covers the parser
232/// path through the thin re-export shim.
233#[cfg(feature = "symgraph")]
234pub mod symgraph;
235
236/// Memory Palace storage engine (formerly the `trusty-memory-core` crate).
237///
238/// Why: Centralises the Memory Palace data model (`Palace` / `Wing` /
239/// `Room` / `Drawer`), storage backends (usearch vector index + SQLite
240/// knowledge graph + chat-session log + payload store), retrieval handle,
241/// and the dream / decay / analytics / git-history surfaces so every
242/// trusty-* binary that talks to a palace reuses the same types. Absorbed
243/// into `trusty-common` (issue #5 phase 2d) so we ship one fewer published
244/// crate.
245/// What: Gated behind the `memory-core` feature because it pulls in heavy
246/// storage deps (`usearch`, `rusqlite`, `r2d2`, `git2`, `kuzu`). Enables
247/// the embedder surface automatically (memory-core → embedder).
248/// Test: `cargo test -p trusty-common --features memory-core` exercises
249/// the full surface.
250#[cfg(feature = "memory-core")]
251pub mod memory_core;
252
253/// Unified ticketing MCP server (formerly the `trusty-tickets` crate).
254///
255/// Why: Claude Code and the rest of the trusty-* suite need a single MCP
256/// surface that can talk to GitHub Issues, JIRA, and Linear without the
257/// caller needing to know which backend is configured. Absorbing into
258/// `trusty-common` reduces the workspace crate count and co-locates the
259/// HTTP client surface with the other protocol helpers.
260/// What: Gated behind the `tickets` feature. Exposes `tickets::api::*`
261/// (config, models, Backend trait, three concrete backends), `tickets::server`
262/// (MCP dispatch loop + `run_stdio`), and `tickets::tools` (the tool-list
263/// schema). Requires the `mcp` feature for the stdio loop.
264/// Test: `cargo test -p trusty-common --features tickets` runs the module's
265/// own unit tests (dispatch, tool-list counts, config parsing, serde
266/// round-trips). Live backend tests require env-var credentials.
267#[cfg(feature = "tickets")]
268pub mod tickets;
269
270/// Intent-source resolver (ISR) for the intent/method conformance gates (#1358).
271///
272/// Why: the DOC-15 conformance capability needs one shared resolver that both
273/// the FRONT gate (trusty-mpm) and the BACK gate (trusty-review) call, so
274/// ticket+spec resolution and the precedence rule (ticket > spec) are
275/// implemented once, centrally, and the two gates can never disagree
276/// (`SPEC-CONFORMANCE-03~draft`, spec §6).
277/// What: gated behind the `intent-source` feature (depends on `tickets` and
278/// `chat`). Exposes `intent_source::{resolve, ResolvedIntent, IntentQuery, …}`
279/// plus the pluggable `TicketFetcher` / `IntentTokenResolver` / `SpecLookup` /
280/// `MethodExtractor` seams. Fail-open throughout (`thiserror`, no `unwrap`).
281/// Test: `cargo test -p trusty-common --features intent-source` runs the
282/// module's AC-1..AC-7 unit tests with no network access.
283#[cfg(feature = "intent-source")]
284pub mod intent_source;
285
286/// Language-agnostic Spec-Linked Documentation (SLD) reference grammar (DOC-38).
287///
288/// Why: DOC-38 promotes SLD from an incidental, Rust-only rustdoc convention to
289/// a first-class, implementation-neutral standard usable in any language or
290/// repository. The `intent_source` resolver already reads the Rust form; the
291/// generalized grammar (frontmatter `spec_refs:`, per-language comment idioms,
292/// fenced-code exclusion, the open `~<rev>` token) needs a shared, reusable home
293/// so a documentation linter (`trusty-sld-lint`, DOC-38 §10 F1) and the resolver
294/// parse ONE grammar, not two.
295/// What: gated behind the lightweight `sld` feature (regex + serde_yaml +
296/// thiserror only — no `tickets`/git2/rusqlite). Exposes the canonical
297/// `SPEC-{SUBSYSTEM}-{NN}~{rev}` id grammar (`is_valid_spec_id`, `revision_of`,
298/// `base_id`, `reference_regex`), the per-extension `CommentSyntax` table,
299/// `parse_inline_refs` (fenced-code-aware `# Spec References` block parsing),
300/// `parse_frontmatter_refs` (`spec_refs:` YAML), and `spec_anchors` /
301/// `anchor_resolves` (heading-anchor scanning + revision-tolerant resolution).
302/// `intent_source::spec_resolve` reuses this module's `revision_of`/`base_id`.
303/// Test: `cargo test -p trusty-common --features sld` runs the module's unit
304/// tests (grammar, inline, frontmatter, anchor) with no I/O.
305#[cfg(feature = "sld")]
306pub mod sld;
307
308/// Declarative CLI help system with "did you mean?" suggestions (issue #216).
309///
310/// Why: every standalone trusty-* binary used to render its `--help` and
311/// unknown-subcommand error output independently, so the formats drifted
312/// apart over time. Centralising the help model into one YAML schema, one
313/// canonical renderer, and one Jaro-Winkler suggester keeps the six binaries
314/// (search, memory, analyze, mpm-cli, tga, open-mpm) speaking with a single
315/// user-facing voice.
316/// What: gated behind the `cli-help` feature. Pulls in `serde_yaml`, `strsim`,
317/// and `indexmap`. Exposes `HelpConfig` / `CommandDef` / `FlagDef` / `Example`
318/// + `load_help` / `render_help` / `suggest`.
319/// Test: `cargo test -p trusty-common --features cli-help`.
320#[cfg(feature = "cli-help")]
321pub mod help;
322
323/// Unified monitor TUI for the trusty-search and trusty-memory daemons
324/// (formerly the `trusty-monitor-tui` crate).
325///
326/// Why: operators run both daemons and want one terminal surface that shows
327/// the health of both at a glance. Living here behind the `monitor-tui`
328/// feature flag matches the workspace's "one fewer published crate" direction
329/// (issue #31 companion) and keeps the dashboard logic unit-testable.
330/// What: gated behind the `monitor-tui` feature, which pulls in `ratatui` and
331/// `crossterm`. Exposes `monitor::run` (the entry point the `trusty-monitor`
332/// binary calls) plus the pure `dashboard` / `search_client` / `memory_client`
333/// submodules.
334/// Test: `cargo test -p trusty-common --features monitor-tui` covers the
335/// rendering, layout, and HTTP-client pieces.
336#[cfg(feature = "monitor-tui")]
337pub mod monitor;
338
339// epic #1104: stdio MCP client + console metrics contract (feature-gated).
340#[cfg(feature = "console-metrics")]
341pub mod console_metrics;
342#[cfg(feature = "stdio-mcp-client")]
343pub mod stdio_mcp_client;
344
345/// Throttled crates.io update-notification helper.
346///
347/// Why: User-facing CLIs should nudge operators when a newer release is
348/// available without adding perceptible latency. A shared implementation
349/// keeps the throttle, cache, opt-out, and User-Agent logic consistent across
350/// every consumer in the workspace.
351/// What: Gated behind the `update-check` feature. Exposes
352/// [`update::check_throttled`] (the main entry — reads a per-crate JSON cache
353/// under the OS cache dir, queries crates.io at most once per 24 h),
354/// [`update::check_crates_io`] (the raw network call), [`update::notice`]
355/// (formatted upgrade message), and [`update::UpdateInfo`] (the result type).
356/// All failures degrade to `None` — the check is best-effort and will not
357/// panic or stall a CLI.
358/// Opt-out: set `TRUSTY_NO_UPDATE_CHECK` or `CI` to any non-empty value.
359/// Test: `cargo test -p trusty-common --features update-check`.
360#[cfg(feature = "update-check")]
361pub mod update;
362
363/// Error-capture layer for the trusty-* consent-gated bug-reporting system
364/// (bug-reporting Phase 1, issue #479).
365///
366/// Why: Every trusty-* daemon encounters runtime errors that developers need
367///      to see but that must be captured locally and only filed to GitHub after
368///      explicit user consent. A shared capture layer in `trusty-common` means
369///      all daemons gain error capture without per-binary changes.
370/// What: Gated behind the `bug-capture` feature. Exposes:
371///      - [`error_capture::CapturedError`] — structured error record.
372///      - [`error_capture::ErrorStore`] — ring buffer + JSONL store.
373///      - [`error_capture::BugCaptureLayer`] — the tracing Layer.
374///      - [`error_capture::bug_capture_layer`] — convenience constructor.
375///      - [`error_capture::TRUSTY_NO_BUG_CAPTURE_ENV`] — opt-out env name.
376///      Additive: does not alter stderr logging. Opt-out via
377///      `TRUSTY_NO_BUG_CAPTURE=1`. New dep: `sha2` (already workspace-optional).
378/// Test: `cargo test -p trusty-common --features bug-capture`.
379#[cfg(feature = "bug-capture")]
380pub mod error_capture;
381
382/// The `~/.trusty-tools/<crate>/config.yaml` cross-crate config convention (#1220).
383///
384/// Why: every trusty-* crate had its own config location/format; #1220
385/// standardises one convention so an operator always knows where a crate's
386/// configuration lives. Centralising the path resolution and typed YAML
387/// load/save here means each crate adopts it by calling two functions.
388/// What: Gated behind the `crate-config` feature. Exposes
389/// [`crate_config::crate_config_path`], [`crate_config::load`],
390/// [`crate_config::load_or_default`], and [`crate_config::save`].
391/// Test: `cargo test -p trusty-common --features crate-config -- crate_config::tests`.
392#[cfg(feature = "crate-config")]
393pub mod crate_config;
394
395/// Unified inference provider adapter layer (epic #2400).
396///
397/// Why: six trusty-* crates each hand-rolled their own LLM client, key
398/// lookup, and `.env.local` loading. Epic #2400 centralises the adapter,
399/// credential resolution, and capability registry here so every consumer
400/// shares one implementation.
401/// What: Gated behind the `credentials` feature. Wave 1 ticket #2401 ships
402/// the [`inference::credentials`] submodule (`KeyStore` trait + 3 backends +
403/// `resolve_key` precedence + `redact_secret`); later Wave 1/2 tickets add
404/// sibling modules (adapter trait, capability registry, provider clients).
405/// Test: `cargo test -p trusty-common --features credentials -- inference::`
406/// and `cargo test -p trusty-common --features keyring-store -- inference::`.
407#[cfg(feature = "credentials")]
408pub mod inference;
409
410// ─── Focused submodules (split from lib.rs in issue #1108) ────────────────
411
412/// TCP port auto-walking helper.
413///
414/// Why: Running multiple daemon instances shouldn't produce noisy failures
415/// when a port is already occupied.
416/// What: Exposes [`bind_with_auto_port`] which walks forward to the next free
417/// port within `max_attempts`.
418/// Test: `cargo test -p trusty-common -- port::tests`.
419pub mod port;
420
421/// Canonical project-slug derivation (issue #1348).
422///
423/// Why: trusty-memory and trusty-installer both need the identical
424/// directory-basename/repo-name → slug rule (the trusty-memory daemon's
425/// `validate_palace_name` rejects a palace whose slug disagrees with the one it
426/// re-derives). Centralising the rule here makes it the single source of truth
427/// so the two crates cannot silently diverge.
428/// What: Exposes [`slug::slugify_string`], re-exported at the crate root as
429/// [`slugify_string`].
430/// Test: `cargo test -p trusty-common -- slug::tests`.
431pub mod slug;
432pub use slug::slugify_string;
433
434/// Canonical trusty-search index-id derivation from a project path (issue #1373).
435///
436/// Why: trusty-mpm (register-and-pin at session launch) and trusty-search
437/// (`detect_project`, MCP serve pin) must derive the byte-for-byte identical
438/// index id from the same project root, or a session pins one id while querying
439/// another. Centralising the rule here — the crate both already depend on —
440/// keeps them in lockstep without a trusty-mpm → trusty-search dependency edge.
441/// What: Exposes [`index_id::derive_index_id`], [`index_id::resolve_project_root`],
442/// and [`index_id::find_git_root`].
443/// Test: `cargo test -p trusty-common -- index_id::tests`.
444pub mod index_id;
445pub use index_id::{derive_index_id, find_git_root, resolve_project_root};
446
447/// Shared best-effort trusty-search "ensure this project is indexed" helper
448/// (issues #1373 / #1908), gated behind the `search-index` feature.
449///
450/// Why: the register-and-populate logic originally lived only in trusty-mpm's
451/// session-launch path; trusty-code now wants the same behaviour at task start.
452/// Promoting it here makes it the ONE implementation both crates call, per the
453/// workspace common-entry-point rule, so they can never diverge.
454/// What: exposes [`search_index::ensure_project_indexed`] (derive id →
455/// best-effort find-or-create + freshness-gated reindex, fail-open) and the
456/// [`search_index::index_is_fresh`] predicate. Feature-gated because it enables
457/// `reqwest`'s `blocking` client; default builds pay nothing.
458/// Test: `cargo test -p trusty-common --features search-index -- search_index::tests`.
459#[cfg(feature = "search-index")]
460pub mod search_index;
461
462/// Shared trusty-search index READINESS probe (issue #2784), gated behind the
463/// `search-index` feature alongside the warming helper it complements.
464///
465/// Why: [`search_index::ensure_project_indexed`] *warms* a project's index at
466/// task start but never told the session whether it was actually ready — so a
467/// daily-driver session could silently query during the semantic warm-up
468/// window and get lexical-only results with no signal. This module adds the
469/// missing *surfacing* half so both crates that warm (trusty-code, trusty-mpm)
470/// can also report readiness from the ONE shared implementation.
471/// What: exposes [`search_readiness::probe_index_readiness`] (fail-open probe),
472/// the pure [`search_readiness::parse_readiness`] mapper, and
473/// [`search_readiness::log_index_readiness`] (one stderr line surfacing lane
474/// readiness to the session).
475/// Test: `cargo test -p trusty-common --features search-index -- search_readiness::tests`.
476#[cfg(feature = "search-index")]
477pub mod search_readiness;
478
479/// Canonical tmux-session naming shared by both session managers (SPEC-ONESM-01).
480///
481/// Why: trusty-mpm's `SessionManager` and trusty-agents' `TmManager` both create
482/// tmux sessions, but only names carrying a managed prefix are recognised by
483/// trusty-mpm's reconcile/prune/adopt/orphan-GC. Keeping the ONE naming rule here
484/// — the crate both already depend on — lets trusty-agents emit managed names
485/// without a `trusty-mpm` dependency edge, so its sessions stop being orphaned.
486/// trusty-mpm's `core::names` re-exports this module verbatim for compatibility.
487/// What: exposes the managed [`session_naming::PREFIX`] and legacy prefixes,
488/// [`session_naming::is_managed_session_name`], [`session_naming::name_from_uuid`],
489/// [`session_naming::name_from_dir`], [`session_naming::build_managed_session_name`]
490/// / [`session_naming::build_session_name`] and the serial helpers.
491/// Test: `cargo test -p trusty-common --features session-naming -- session_naming`.
492#[cfg(feature = "session-naming")]
493pub mod session_naming;
494
495/// Canonical trusty-memory palace-ID derivation from project identity (#1217/#1605).
496///
497/// Why: trusty-memory (default-palace derivation at the CLI/hook/MCP edges) and
498/// trusty-mpm (managed-session MCP injection — it pins `TRUSTY_MEMORY_PALACE` in
499/// a cloned session's `.mcp.json`) must derive the byte-for-byte identical
500/// palace slug from the same project identity, or a repo-cloned session resolves
501/// the wrong palace. Centralising the pure rule here — the crate both already
502/// depend on — keeps them in lockstep without a trusty-mpm → trusty-memory
503/// dependency edge, exactly as `index_id` does for trusty-search index pinning.
504/// What: Exposes [`palace_id::derive_palace_id`],
505/// [`palace_id::owner_repo_from_git_remote`], [`palace_id::parent_dir_slug`],
506/// and the [`palace_id::PALACE_OVERRIDE_ENV`] / [`palace_id::palace_override_from_env`]
507/// env helpers.
508/// Test: `cargo test -p trusty-common -- palace_id::tests`.
509pub mod palace_id;
510pub use palace_id::{
511    PALACE_OVERRIDE_ENV, derive_palace_id, owner_repo_from_git_remote, palace_override_from_env,
512    parent_dir_slug, repo_slug_from_git_remote,
513};
514
515/// Palace-level alias map: redirect one palace name to another (issue #1939).
516///
517/// Why: trusty-mpm pins a managed session to the `owner-repo` palace slug, but
518/// the pre-existing claude-mpm-era palace is the BARE repo name — so the pinned
519/// palace does not exist and memory splits in two. A persisted alias map lets the
520/// non-existent `owner-repo` name resolve to the existing bare palace. This is a
521/// PALACE-level redirect, distinct from the in-palace term/KG entity aliases.
522/// What: exposes [`palace_alias::PalaceAliasStore`] (load/register/resolve) plus
523/// [`palace_alias::default_palace_registry_dir`] and
524/// [`palace_alias::palace_registry_dir_from`] for locating the registry dir. This
525/// module is always compiled (no `memory-core` gate) so trusty-mpm's always-on
526/// session-launch path can register aliases without pulling the storage engine.
527/// Test: `cargo test -p trusty-common -- palace_alias::tests`.
528pub mod palace_alias;
529
530/// Shared GitHub `owner/repo` path derivation (issue #1220).
531///
532/// Why: trusty-mpm's managed-session workspace root
533/// (`~/trusty-mpm-projects/<owner>/<repo>/…`) and trusty-memory's palace-ID
534/// derivation both need the canonical `owner/repo` identity of a project's git
535/// origin remote. Centralising the parsing here keeps the two crates in lockstep.
536/// What: Exposes [`github_path::GithubPath`], [`github_path::parse_github_path`]
537/// (pure URL parse), and [`github_path::derive_github_path`] (reads
538/// `remote.origin.url`).
539/// Test: `cargo test -p trusty-common -- github_path::tests`.
540pub mod github_path;
541
542/// Canonical repository identity (DOC-37) — the path-independent join key that
543/// relates the live checkout, `.base` clone, and session worktrees of one repo.
544///
545/// Why: index ids are bare path basenames, so every facet of a repo registers
546/// as an unrelated index. [`repo_identity::RepoIdentity`] supplies the missing
547/// `owner/repo` (or content-hash) join key so trusty-search can group and filter
548/// indexes by repo; it lives here so trusty-search and trusty-mpm derive it
549/// identically.
550/// What: exposes [`repo_identity::RepoIdentity`] (`derive`/`canonical`/`parse`).
551/// Test: `cargo test -p trusty-common -- repo_identity::tests`.
552pub mod repo_identity;
553
554/// Shared Slack `mrkdwn` formatting/escaping primitives (epic #2636).
555///
556/// Why: the `mrkdwn` escape rule and code-fence helpers were born in
557/// trusty-mpm's inbound Slack gateway; the native Slack MCP server in
558/// trusty-channels now needs the byte-identical escaping to neutralise markup
559/// injection from untrusted channel/user text. Centralising the pure primitives
560/// here — the crate both already depend on — keeps them from diverging without a
561/// trusty-channels → trusty-mpm dependency edge.
562/// What: exposes [`slack_format::mrkdwn_escape`], [`slack_format::code_block`],
563/// and [`slack_format::code_inline`]. Pure `std` string ops — no dependencies,
564/// no feature gate.
565/// Test: `cargo test -p trusty-common -- slack_format::tests`.
566pub mod slack_format;
567
568/// Data-directory resolution and filesystem utilities.
569///
570/// Why: All trusty-* tools share the same per-app data-directory resolution
571/// logic including the macOS `NSFileManager` bypass needed for test isolation.
572/// What: Exposes [`data_dir::resolve_data_dir`], [`data_dir::sanitize_data_root`],
573/// [`data_dir::DATA_DIR_OVERRIDE_ENV`], and [`data_dir::is_dir`].
574/// Test: `cargo test -p trusty-common -- data_dir::tests`.
575pub mod data_dir;
576
577/// Shared CLI daemon-guard helper (probe + spinner + spawn).
578///
579/// Why: trusty-search, trusty-memory, and trusty-analyze each had an identical
580/// probe-spawn-poll-spinner loop in their `commands/daemon_guard.rs` files.
581/// Centralising it here (issue #985) removes the divergence risk and gives
582/// the three crates a single tested implementation to delegate to.
583/// What: Exposes [`daemon_guard::DaemonGuardConfig`],
584/// [`daemon_guard::probe_once`], [`daemon_guard::spin_until_ready`], and
585/// [`daemon_guard::spawn_current_exe`].
586/// Test: `cargo test -p trusty-common -- daemon_guard::tests`.
587pub mod daemon_guard;
588
589/// Daemon HTTP-address file helpers.
590///
591/// Why: Both trusty-search and trusty-memory persist their bound `host:port`
592/// to disk for discovery by CLI and MCP clients. Centralising keeps them in sync.
593/// What: Exposes [`daemon_addr::write_daemon_addr`], [`daemon_addr::read_daemon_addr`],
594/// [`daemon_addr::check_already_running`], and
595/// [`daemon_addr::resolve_daemon_base_url`] (discovery-first `http://` base
596/// URL resolution, issue #2033).
597/// Test: `cargo test -p trusty-common -- daemon_addr::tests`.
598pub mod daemon_addr;
599
600/// HTTP health-probe helper.
601///
602/// Why: Every daemon uses the same tight-timeout `/health` probe to detect
603/// whether a prior instance is still running.
604/// What: Exposes [`health_probe::probe_health`].
605/// Test: covered via daemon_addr integration tests.
606pub mod health_probe;
607
608/// Global tracing subscriber initialisation helpers.
609///
610/// Why: Every trusty-* binary needs the same verbosity ladder, `RUST_LOG`
611/// override, and (for daemons) the log-buffer + bug-capture layer composition.
612/// What: Exposes [`tracing_init::init_tracing`],
613/// [`tracing_init::init_tracing_with_buffer`],
614/// [`tracing_init::init_tracing_with_buffer_and_capture`] (feature-gated),
615/// and [`tracing_init::maybe_disable_color`].
616/// Test: side-effecting global — covered by downstream integration tests.
617pub mod tracing_init;
618
619/// Deprecated single-shot OpenRouter helpers.
620///
621/// Why: Backward-compatible wrapper for the pre-streaming OpenRouter API.
622/// New code should use `chat::OpenRouterProvider::chat_stream` instead.
623/// What: Exposes [`openrouter_legacy::ChatMessage`],
624/// [`openrouter_legacy::openrouter_chat`] (deprecated), and
625/// [`openrouter_legacy::openrouter_chat_stream`] (deprecated).
626/// Test: `chat_message_round_trips`, `openrouter_chat_rejects_empty_key`.
627pub mod openrouter_legacy;
628
629/// Incremental catch-up engine for the DOC-28 cutover bridge (#1762).
630///
631/// Why: when a native `tm` session starts, the operator needs a summary of
632/// activity since the last session (paused sessions, git commits, memory palace
633/// drawers). Hosting the engine here lets both trusty-mpm and (eventually)
634/// trusty-code share it without code duplication.
635/// What: gated behind the `catchup` feature (pulls in `rusqlite` via the
636/// `mpm_registry` submodule). Exposes `catchup::{CatchupOptions, run_catchup,
637/// run_catchup_blocking, generate_catchup_context, …}` plus per-source
638/// submodules (`git`, `palace`, `state`, `session_finder`, `mpm_session`,
639/// `mpm_registry`).
640/// Test: `cargo test -p trusty-common --features catchup`.
641// CUTOVER BRIDGE — remove post-migration (#1762)
642#[cfg(feature = "catchup")]
643pub mod catchup;
644
645/// Why: two independent tmux implementations (trusty-mpm, trusty-agents)
646/// meant the #2398/#2399 scrollback fix only reached one of them (issue
647/// #3004). Always-on: it is a small, dependency-light (`serde` only) pure
648/// command-construction layer, no feature gate needed.
649/// What: `TmuxTarget`/`TmuxCommand`/`tmux_argv`, the scrollback-ergonomics
650/// defaults, and the shared `managed_session_commands` ordering guarantee.
651/// Test: `cargo test -p trusty-common -- tmux::`.
652pub mod tmux;
653
654// ─── Re-exports preserving the pre-split public API ───────────────────────
655
656pub use chat::{
657    BedrockProvider, ChatEvent, ChatProvider, DEFAULT_BEDROCK_MODEL, LocalModelConfig,
658    OllamaProvider, OpenRouterProvider, SamplingParams, ToolCall, ToolDef,
659    auto_detect_local_provider,
660};
661
662// Port
663pub use port::bind_with_auto_port;
664
665// Data directory
666pub use data_dir::{DATA_DIR_OVERRIDE_ENV, is_dir, resolve_data_dir, sanitize_data_root};
667
668// Daemon address
669pub use daemon_addr::{
670    check_already_running, read_daemon_addr, remove_daemon_addr, resolve_daemon_base_url,
671    write_daemon_addr,
672};
673
674// Health probe
675pub use health_probe::probe_health;
676
677// Tracing init
678#[cfg(feature = "bug-capture")]
679pub use tracing_init::init_tracing_with_buffer_and_capture;
680pub use tracing_init::{init_tracing, init_tracing_with_buffer, maybe_disable_color};
681
682// OpenRouter legacy (deprecated but must remain reachable)
683#[allow(deprecated)]
684pub use openrouter_legacy::{ChatMessage, openrouter_chat, openrouter_chat_stream};