lwc 0.17.10

Agent-driven proactive memory CLI for AI agents — autonomously recall, maintain, and evolve persistent, source-grounded knowledge across sessions.
use super::*;
use std::fs;

pub(super) static CURSOR: CursorTarget = CursorTarget;
pub(super) struct CursorTarget;

impl CursorTarget {
    fn paths(&self, environment: &TargetEnvironment<'_>) -> TargetPaths {
        let global = environment.location == AgentLocation::Global;
        TargetPaths {
            mcp: Some(if global {
                environment.home.join(".cursor/mcp.json")
            } else {
                environment.cwd.join(".cursor/mcp.json")
            }),
            instruction: (!global).then(|| environment.cwd.join(".cursor/rules/lwc.mdc")),
            hook: Some(if global {
                environment.home.join(".cursor/hooks.json")
            } else {
                environment.cwd.join(".cursor/hooks.json")
            }),
            skill_dir: Some(if global {
                environment.home.join(".cursor/skills/using-lwc")
            } else {
                environment.cwd.join(".cursor/skills/using-lwc")
            }),
            aux: Vec::new(),
        }
    }
}

impl AgentTarget for CursorTarget {
    fn id(&self) -> &'static str {
        "cursor"
    }
    fn display_name(&self) -> &'static str {
        "Cursor"
    }
    fn mcp_mode(&self, _location: AgentLocation) -> &'static str {
        "installed"
    }
    fn lifecycle_mode(&self, _location: AgentLocation) -> &'static str {
        "installed"
    }
    fn permissions_mode(&self, _location: AgentLocation) -> &'static str {
        "not_applicable"
    }
    fn instructions_mode(&self, location: AgentLocation) -> &'static str {
        if location == AgentLocation::Global {
            "user_managed"
        } else {
            "installed"
        }
    }
    fn skills_mode(&self, _location: AgentLocation) -> &'static str {
        "installed"
    }
    fn supports_location(&self, _location: AgentLocation) -> bool {
        true
    }
    fn detect(&self, environment: &TargetEnvironment<'_>) -> DetectionResult {
        let paths = self.paths(environment);
        DetectionResult {
            installed: install::command_exists("cursor")
                || environment.home.join(".cursor").exists()
                || environment.cwd.join(".cursor").exists(),
            already_configured: paths.mcp.as_ref().is_some_and(|path| {
                fs::read_to_string(path).is_ok_and(|text| text.contains("\"lwc\""))
            }),
            config_path: paths.mcp,
        }
    }
    fn install(
        &self,
        environment: &TargetEnvironment<'_>,
        options: InstallOptions,
    ) -> Result<WriteResult> {
        let mut result = native_install(self, environment, options, self.paths(environment))?;
        if environment.location == AgentLocation::Global {
            result.notes.push("Cursor User Rules remain managed in Settings; LWC MCP, Skill and Hooks are installed as official files.".into());
        }
        Ok(result)
    }
    fn uninstall(&self, environment: &TargetEnvironment<'_>) -> Result<WriteResult> {
        native_uninstall(self, environment, self.paths(environment))
    }
    fn configure(&self, paths: &TargetPaths, options: InstallOptions) -> Result<()> {
        install::ensure_cursor_frontmatter(paths)?;
        install::configure_standard(self.id(), paths, options)
    }
    fn unconfigure(&self, paths: &TargetPaths, executable: &str) -> Result<()> {
        install::unconfigure_standard(self.id(), paths, executable)
    }
    fn print_config(&self, location: AgentLocation) -> String {
        let workspace = if location == AgentLocation::Global {
            "${workspaceFolder}".into()
        } else {
            std::env::current_dir()
                .and_then(|path| path.canonicalize())
                .unwrap_or_else(|_| PathBuf::from("."))
                .to_string_lossy()
                .into_owned()
        };
        let mcp = serde_json::json!({
            "mcpServers": {
                "lwc": {
                    "type": "stdio",
                    "command": "lwc",
                    "args": ["serve", "--mcp", "--path", workspace]
                }
            }
        });
        if location == AgentLocation::Local {
            return format!(
                "# cursor local AgentTarget integration (print-only)\n\
MCP: {mcp}\n\
Hook: lwc --scope all agent hook --agent cursor --event sessionStart\n\
Hook events: {}\n\
Skill: using-lwc\n\
Instructions:\n{}\n",
                install::hook_events_summary(self.id(), location),
                install::guidance()
            );
        }
        format!(
            "# Cursor user integration. Cursor has no official global rules file; guidance is injected by MCP initialize instructions.\n\
MCP: {mcp}\n\
Skill: ~/.cursor/skills/using-lwc\n\
Hook: ~/.cursor/hooks.json\n\
Hook events: {}\n\
Instructions: user_managed (Cursor Settings > Rules)\n{}\n",
            install::hook_events_summary(self.id(), location),
            install::guidance()
        )
    }
    fn describe_paths(&self, environment: &TargetEnvironment<'_>) -> Vec<PathBuf> {
        install::all_paths(&self.paths(environment))
    }
}