Skip to main content

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}