cmx_core/production.rs
1//! Production `AppContext` factory for embedding tools.
2//!
3//! Embedding tools (parite, gilt, hopper, …) construct a [`ProductionContext`]
4//! once at startup and use [`ProductionContext::ctx`] to obtain an `AppContext`
5//! for every library call, avoiding the need to know about gateway internals.
6
7use anyhow::Result;
8
9use crate::context::AppContext;
10use crate::gateway::real::{RealFilesystem, RealGitClient, SystemClock};
11use crate::paths::ConfigPaths;
12use crate::platform::Platform;
13
14/// Owns the production gateway implementations and the resolved paths.
15///
16/// Constructed once per process via [`from_env`](Self::from_env) and kept alive
17/// for the duration of the call — the `AppContext` it vends borrows from it.
18pub struct ProductionContext {
19 fs: RealFilesystem,
20 git: RealGitClient,
21 clock: SystemClock,
22 paths: ConfigPaths,
23}
24
25impl ProductionContext {
26 /// Build a production context bound to the Claude platform.
27 ///
28 /// This is the one-call default for tools that target Claude Code. It is
29 /// equivalent to `from_env(Platform::Claude)`.
30 ///
31 /// The platform binding determines which lock file and install directory are
32 /// used as the *default* for path resolution. It does **not** determine
33 /// which platforms a skill installs to — installation targets are resolved
34 /// from the cmx config and existing lock files at plan time.
35 pub fn claude() -> Result<Self> {
36 Self::from_env(Platform::Claude)
37 }
38
39 /// Build a production context from the real environment for the given
40 /// platform binding.
41 ///
42 /// `default_platform` controls the default lock file name and install
43 /// directory used for path resolution (e.g. which `cmx-lock*.json` file is
44 /// the primary one). It does **not** set the config root directory — that is
45 /// always `$HOME/.config/context-mixer` — and it does **not** determine
46 /// which platforms a skill installs to. Installation targets come from
47 /// the plan (resolved from the cmx config and existing lock files).
48 ///
49 /// For Claude Code tools, prefer [`claude()`](Self::claude) over calling
50 /// this directly.
51 pub fn from_env(default_platform: Platform) -> Result<Self> {
52 let paths = ConfigPaths::from_env(default_platform)?;
53 Ok(Self {
54 fs: RealFilesystem,
55 git: RealGitClient,
56 clock: SystemClock,
57 paths,
58 })
59 }
60
61 /// Borrow an `AppContext` for a single library call.
62 ///
63 /// The returned context does not include an LLM client — embedding tools
64 /// do not need LLM-powered analysis.
65 pub fn ctx(&self) -> AppContext<'_> {
66 AppContext {
67 fs: &self.fs,
68 git: &self.git,
69 clock: &self.clock,
70 paths: &self.paths,
71 llm: None,
72 }
73 }
74}