Skip to main content

agent_profile/adapter/
cline.rs

1// Copyright (c) 2026 Costas Kirgoussios
2//
3// This software is licensed under the PolyForm Noncommercial License 1.0.0.
4// You may obtain a copy of the License at:
5// https://polyformproject.org/licenses/noncommercial/1.0.0/
6
7//! Cline CLI: `CLINE_DATA_DIR` env var (SP4 design §8.3, §9 outcome 5 — the env var is honoured,
8//! but the measured delta at the watched locations was empty; the agent wrote nothing).
9
10use std::path::PathBuf;
11
12use super::{
13    Adapter, AdapterEvidence, AdapterMetadata, Capability, CapabilityClaim, CapabilityState,
14    EnvOverride, Mechanism, PathKind, PlanContext, PlannedLaunch, SupportLevel, profile_dir,
15};
16use crate::config::AppRoot;
17use crate::error::Result;
18use crate::name::ProfileName;
19
20const VAR: &str = "CLINE_DATA_DIR";
21
22static METADATA: AdapterMetadata = AdapterMetadata {
23    id: "cline",
24    executable: "cline",
25    mechanism: Mechanism::Env(VAR),
26    support: SupportLevel::Experimental,
27    evidence: AdapterEvidence {
28        mechanism_id: "cline-cli-v1",
29        verified_at: "2026-11-09",
30        upstream_version: "3.0.74",
31        source_url: "measured",
32        notes: "measured in a sandbox at 3.0.73: --help lists `CLINE_DATA_DIR` as a Configuration \
33                directory and `--config`/`--data-dir` as directory flags; a launch with the env var set \
34                exits 0, but the delta at both watched locations (target and default) was \
35                `(no change: nothing was created, moved or removed at the watched locations)` — the \
36                agent writes nothing before it needs the network (§9 outcome 5); the strings scan \
37                found ANTHROPIC_API_KEY, CLINE_API_KEY, OPENAI_API_KEY and OPENROUTER_API_KEY \
38                documented; Q1 unanswered (no isolation or sandbox-mode side effects were observed \
39                before the agent needed the network); three `Unknown`s ⇒ Experimental under Gate C/D6",
40    },
41    capabilities: &[
42        CapabilityClaim {
43            capability: Capability::ConfigIsolation,
44            state: CapabilityState::Unknown,
45            basis: "unmeasured: the probe's delta at both watched locations — the target and the default \
46                    `/home/agent/.cline` — was `(no change: nothing was created, moved or removed at the \
47                    watched locations)`, so whether the agent's own config writes would follow CLINE_DATA_DIR \
48                    was not observable while the agent writes nothing before it needs the network \
49                    (§9 outcome 5)",
50        },
51        CapabilityClaim {
52            capability: Capability::CredentialIsolation,
53            state: CapabilityState::Unknown,
54            basis: "unmeasured: no login/auth step ran and no credential-bearing file surfaced anywhere \
55                    in the delta, so where stored credentials live was not established; the documented \
56                    bypass variables are `CLINE_API_KEY`, `ANTHROPIC_API_KEY`, `OPENAI_API_KEY` \
57                    and `OPENROUTER_API_KEY` (strings: `documented CLINE_API_KEY: present`)",
58        },
59        CapabilityClaim {
60            capability: Capability::StateIsolation,
61            state: CapabilityState::Unknown,
62            basis: "unmeasured: the agent's only pre-network write was `cli-node-extra-ca-certs.pem`, \
63                    present at the default location at baseline, and the measured delta recorded \
64                    `(no change: nothing was created, moved or removed at the watched locations)`, so no \
65                    state write was observable to place (§9 outcome 5)",
66        },
67    ],
68    env: &[EnvOverride { name: VAR, sensitive: false }],
69    conflicts: &[],
70    credential_env: &["CLINE_API_KEY", "ANTHROPIC_API_KEY", "OPENAI_API_KEY", "OPENROUTER_API_KEY"],
71};
72
73// The Cline adapter.
74#[derive(Debug, Clone, Copy)]
75pub struct Cline;
76
77impl Adapter for Cline {
78    fn metadata(&self) -> &'static AdapterMetadata {
79        &METADATA
80    }
81
82    fn paths(&self, root: &AppRoot, profile: &ProfileName) -> Vec<(PathBuf, PathKind)> {
83        // `CLINE_DATA_DIR` names a directory CONTAINING the agent's state (probe and transcript): the
84        // agent does not create a nested subdirectory, so the adapter declares only the profile
85        // directory itself.
86        vec![(profile_dir(root, profile, METADATA.id), PathKind::Dir)]
87    }
88
89    fn plan(&self, ctx: &PlanContext<'_>) -> Result<PlannedLaunch> {
90        METADATA.mechanism.plan(self, ctx)
91    }
92}