From 4c3764a418d6763bce8bc35dcc4ddb1672014fa4 Mon Sep 17 00:00:00 2001 From: Daniel Dunderfelt Date: Mon, 22 Jun 2026 11:55:07 +0300 Subject: [PATCH 1/2] Convert argent.md rules to Argent skill to prevent always-included context noise. Update skills that mention the rules to mention the Argent skill instead. --- packages/argent-mcp/src/mcp-server.ts | 2 +- packages/skills/rules/argent.md | 170 ------------------ .../skills/argent-device-interact/SKILL.md | 2 +- .../argent-react-native-app-workflow/SKILL.md | 2 +- .../skills/argent-test-ui-flow/SKILL.md | 2 +- packages/skills/skills/argent/SKILL.md | 93 ++++++++++ 6 files changed, 97 insertions(+), 174 deletions(-) delete mode 100644 packages/skills/rules/argent.md create mode 100644 packages/skills/skills/argent/SKILL.md diff --git a/packages/argent-mcp/src/mcp-server.ts b/packages/argent-mcp/src/mcp-server.ts index b5d586571..d210ac747 100644 --- a/packages/argent-mcp/src/mcp-server.ts +++ b/packages/argent-mcp/src/mcp-server.ts @@ -240,7 +240,7 @@ export async function startMcpServer(options: StartMcpServerOptions): Promise -If argent is installed and configured in this environment, its MCP tools are the preferred form of interaction with the application for iOS simulator, Android emulator, Chromium (CDP) app, and Vega (Amazon Fire TV) device control; otherwise see `` below before attempting any argent workflow. A "Chromium (CDP) app" is any Chromium runtime exposing a Chrome DevTools Protocol endpoint — an Electron app, or any Chromium-family browser (Chrome/Brave/Edge) launched with `--remote-debugging-port`; all are driven through the same tool surface and tagged `platform: "chromium"`. A "Vega device" is a virtual device (VVD) or physical unit — driven by tv-remote (D-pad) and tagged `platform: "vega"`. -Running MCP server and managing the Argent toolkit utilises `argent` command - if asked use `argent --help` for reference. -To check current version of MCP server run `argent --version` command. - -Use cases: - -- User mentions iOS simulator, Android emulator, device, or app interaction -- The app user is working with is a mobile application which can be run in a simulator/emulator -- Any tapping, swiping, typing, screenshotting, or inspecting a running app -- Any code change that affects visible mobile UI, layout, styling, copy, navigation, or screen composition -- Any request to execute manual QA, UI QA, or visual behavior validation for a mobile app -- Running, debugging, or testing a React Native app (iOS, Android or Vega) -- Profiling performance or diagnosing re-renders in a React Native app (iOS or Android) -- Running, debugging, or testing a Chromium (CDP) app — an Electron app (boot with `boot-device` + `electronAppPath`) or a Chromium browser exposing CDP (auto-discovered on port `9222` / `ARGENT_CHROMIUM_PORTS`); on Chromium scroll with `gesture-scroll` and drag with `gesture-drag` — `gesture-swipe` is touch-only - - - -Run this check once per session, before the first argent tool call or `argent` command. Do not re-probe before later calls. - -Confirm argent is available: - -1. Are `mcp__argent__*` tools in your tool list? If none are present, argent is not available. -2. If still unsure, run `command -v argent`. A non-zero exit means the CLI is not on PATH. - -If argent IS available, ignore the rest of this block and follow this rule normally. - -If argent is ABSENT, treat it as an expected state, not an error to retry. Do not call `mcp__argent__*` tools, do not run `argent` commands, and do not attempt any argent workflow. Tell the user once, and ask if you should continue without argent: - -> Argent isn't installed in this environment. To enable the mobile/Chromium tooling this repo is configured for, run `npx @swmansion/argent init -y` (or `npm i -g @swmansion/argent && argent init -y`). -> - - -**Never** derive tap coordinates from a screenshot -Before **every** tap, you MUST call a discovery tool and extract coordinates from the result. This is not optional. Preferred tools are, in order: - -- `describe` - native app-level components and safely targetable foreground apps (iOS and Android). -- `native-describe-screen` - accessibility screen description via injected native devtools (iOS only) -- `debugger-component-tree` - react-native specific components - -`native-user-interactable-view-at-point` / `native-view-at-point` are follow-up diagnostics once you already have a candidate point (iOS only). - -Whenever something changed YOU MUST first call `describe`, or another appropriate discovery tool so you do not hallucinate element positions. Do not guess coordinates if you can use discovery tool. Do not tap if you have not called a discovery tool in the current step. Screenshots alone are never sufficient for coordinates. - -If a **tap fails twice** at the same coordinates, **stop retrying**. Re-run the discovery tool. - -If `describe` fails, **read the exact error before reacting**, follow the recovery guidance in `argent-device-interact` to choose the correct next action. - -Before starting to interact with the app, read the `argent-device-interact` skill first. - - - -Before booting, running, or interacting with any app, call `list-devices` first - prefer running devices. - -Decision order: - -1. **Explicit user intent** - choose the user named platform or device. Look for words "simulator" and "emulator". -2. **Prefer a running device.** iOS simulators - state `Booted` and Android devices - `state: "device"` come first in `list-devices`; Chromium (CDP) apps appear as `platform: "chromium"`, `state: "Running"`. -3. **Single-platform project:** (per `argent-environment-inspector` flags `is_native_ios`/`is_native_android`, or RN with only one platform configured) → boot that platform. - - - -Always read relevant skills for guidance before executing argent-mcp tool - read skill_routing reference - - - - -- All simulator/emulator interactions go through argent MCP tools — never use `xcrun simctl`, - raw `curl` to simulator ports, or the simulator-server binary directly. -- Before calling any gesture tool for the first time, use ToolSearch to load its schema. -- Interaction tools (`gesture-tap`, `gesture-swipe`, `gesture-pinch`, `gesture-rotate`, `gesture-custom`, `launch-app`, etc.) return a screenshot automatically. - Call `screenshot` separately only for a baseline before any action or after a delay. -- Always open apps with `launch-app` or `open-url` — never tap home screen icons. -- Always use `run-sequence` when performing multiple sequential device actions where you don't need to observe the screen between steps. More in `argent-device-interact` skill. -- When the session ends or the user says they are done: call `stop-all-simulator-servers`. - If the user started Metro separately, ask whether to call `stop-metro` (specify the port if not 8081). -- If tools provided by mcp-server are not sufficient and action can be done using `xcrun`, `adb`, or other commands, use the command. Examples: changing device options, performing a device action such as lock, shake, etc. -- When waiting for an action, do not call `screenshot` repeatedly without a proper wait mechanism. Use the `await-ui-element` tool to block until the UI settles (e.g. wait for an element to become `visible`/`hidden`, or to contain expected `text`) instead of polling. - - - -Project type is determined by the `argent-environment-inspector` subagent (see `subagents` section). -When the subagent result is available, use its `is_react_native` field as the authoritative -source — do not re-inspect files manually. - -If the subagent has not run yet and project type is unknown, run it first before proceeding. Always use subagents if available to run `gather-workspace-data` data tool, if possible do not run yourself. - -When `is_react_native` is true: load `argent-react-native-app-workflow` skill. Use `debugger-component-tree` for element discovery - if the responses are large or unhelpful, try `describe`. - - - -Load the matching skill before starting work and executing tools from argent-mcp — skills contain the full step-by-step -procedure and edge-case handling for each workflow. - -PLATFORM DETECTION -If the user did not specify a platform, call `list-devices` first and pick the booted target — do not default to iOS. Vega (Amazon Fire TV) devices appear as `platform:"vega"`, when present load `argent-tv-interact` - -iOS SIMULATOR SETUP -Skill: `argent-ios-simulator-setup` -When: Beginning a task that involves the iOS simulator, no simulator booted yet, need UDID or simulator-server. - -ANDROID EMULATOR SETUP -Skill: `argent-android-emulator-setup` -When: Beginning a task that involves the Android emulator, no emulator running yet, need an adb serial, or about to install an APK. - -TAPPING, SWIPING, TYPING, GESTURES, SCREENSHOTS, SCROLLING -Skill: `argent-device-interact` -When: Performing touch interactions, typing, pressing hardware buttons, launching/restarting apps, opening URLs, rotating device, taking standalone screenshots, or verifying a visible UI code change. Phone/tablet iOS and Android only — for any TV target use the TV skill below. - -TV INTERACTION (APPLE TV / ANDROID TV / FIRE TV) -Skill: `argent-tv-interact` -When: Any TV target — a `list-devices` entry with `runtimeKind: "tv"` (Apple TV simulator or Android TV emulator) or `platform:"vega"` / `kind:"vvd"` (Amazon Fire TV / VVD), or the user mentions Apple TV / tvOS / Android TV / leanback / Vega / Fire TV. A TV UI is focus-driven, not touch-driven: drive it with `describe` (read focus) + `tv-remote` (D-pad presses) + `keyboard` (type); `gesture-*` tools do NOT apply. Covers booting the target, app lifecycle, focus navigation, typing, screenshots, and (Vega) VVD lifecycle + Fast Refresh. -Prompt keywords: apple tv, tvos, android tv, leanback, vega, fire tv, vvd, d-pad - -SCREENSHOT DIFF & VISUAL REGRESSION -Skill: `argent-screenshot-diff` -When: Explicit visual regression, screenshot diff, compare screenshots, before/after visual comparison requests, or visible UI changes where stable pixel comparison would add useful evidence. - -RUNNING / BUILDING / DEBUGGING REACT NATIVE APP -Skill: `argent-react-native-app-workflow` -When: Project is react-native, starting Metro or running the iOS or Android app, build failures, pod issues, lost Metro connection, reading logs, reloading JS bundle, reinstalling app. - -JS EVALUATION, METRO CONNECTION, REACT NATIVE -Skill: `argent-metro-debugger` -When: evaluating expressions, inspecting React component tree at source level, finding element placement via `debugger-component-tree`. - -REACT APP & COMPONENT PROFILING -Use skill: `argent-react-native-profiler` -When: To measure performance of specific components, to find app-wide bottlenecks. Investigating re-renders or CPU hotspots, producing ranked performance reports. - -NATIVE PROFILING -Use skill: `argent-native-profiler` -When: Profiling native performance (CPU hotspots, UI hangs, memory leaks). iOS only today; Android on the roadmap. Useful as a reference for platform-specific investigation when running dual profiling via `argent-react-native-profiler`. - -PERFORMANCE OPTIMIZATION -Use skill: `argent-react-native-optimization` -When: App feels slow, user asks to optimize, reducing bundle size, improving startup time, fixing re-renders, optimizing lists/images/navigation, or any performance-related task. This is the entry-point skill for all performance work — it delegates to `argent-react-native-profiler` for measurement. - -END-TO-END UI TESTING -Skill: `argent-test-ui-flow` -When: Verifying complete user flows, running interact → screenshot → verify loops, testing features by using the app, executing manual QA steps, or validating visible UI changes or visual behavior after implementation. - -RECORDING & REPLAYING FLOWS -Use skill: `argent-create-flow` -When: A multi-step interaction sequence needs to be repeated — re-profiling after a fix, A/B comparisons, regression checks, user says "again" / "run that flow", or you worked through a complex path worth saving. Also use proactively: if you are about to repeat steps you already performed, record first, then replay. -Prompt keywords: flow, repeat, test X times - -PROPOSING DESIGN VARIANTS FOR HUMAN SELECTION -Use skill: `argent-lens` -When: The user asks for design alternatives / options / A-B choices for a screen or component, or you have produced more than one candidate look for an element and want a human to pick before committing. Covers the build → navigate → screenshot → propose_variant loop and the single blocking await_user_selection call. (Gated behind the `argent-lens` flag, off by default — run `argent enable argent-lens` first.) -Prompt keywords: variant, design option, alternative, A/B, "let me pick", "show me options" - - - -ENVIRONMENT INSPECTION AT SESSION START -Use subagent: `argent-environment-inspector` -When: -- Environment context of the project is not yet known -- No "Project Environment" section exists in project memory / `MEMORY.md` or you lack information about basic setup workflows -- Need to determine build commands, startup scripts, metro port, platform support, or QA tooling - If the subagent already ran this session (result in memory), use that context directly — do NOT re-run. -Rules: - - Run the `argent-environment-inspector` subagent if possible. Never call `gather-workspace-data` yourself - do only if subagent is not available. - - The main agent is responsible for persisting the subagent's JSON result to project memory - diff --git a/packages/skills/skills/argent-device-interact/SKILL.md b/packages/skills/skills/argent-device-interact/SKILL.md index 1f73007c9..633ceefb8 100644 --- a/packages/skills/skills/argent-device-interact/SKILL.md +++ b/packages/skills/skills/argent-device-interact/SKILL.md @@ -27,7 +27,7 @@ Use `list-devices` to get a target id. Results are tagged with `platform` (`ios` ## 2. Best Practices -1. **Always refer to tapping_rule** from your argent.md rule before tapping. +1. **Before every tap, call a discovery tool** — never derive coordinates from screenshots (see the `argent` skill Mandatory Rules). 2. Before performing interactions, consider whether they can be **dispatched sequentially** - more on that in `run-sequence`. 3. **Use `gesture-swipe` for lists/scrolling**, not `gesture-custom`, unless you need non-linear movement. On Chromium use `gesture-scroll` instead — `gesture-swipe` is touch-only. Consider whether you need multiple swipes, if yes - use `run-sequence`. 4. **Tap a text field before typing**, then use `keyboard` to enter text. diff --git a/packages/skills/skills/argent-react-native-app-workflow/SKILL.md b/packages/skills/skills/argent-react-native-app-workflow/SKILL.md index eba0c7975..c196426d5 100644 --- a/packages/skills/skills/argent-react-native-app-workflow/SKILL.md +++ b/packages/skills/skills/argent-react-native-app-workflow/SKILL.md @@ -45,7 +45,7 @@ In a **separate** terminal (Metro keeps running in the first): **Use the project's custom build/run script if one exists** (e.g. `npm run ios`, `npm run android`, `yarn ios:debug`). Only fall back to the defaults below if no custom scripts are defined. -**Pass the target device explicitly** — derive it from `list-devices` (see ``): +**Pass the target device explicitly** — derive it from `list-devices` (see Device Selection in the `argent` skill): ```bash npx react-native run-ios --simulator="" # iOS (or --udid ) diff --git a/packages/skills/skills/argent-test-ui-flow/SKILL.md b/packages/skills/skills/argent-test-ui-flow/SKILL.md index 0c72b0db2..d9b79dbc8 100644 --- a/packages/skills/skills/argent-test-ui-flow/SKILL.md +++ b/packages/skills/skills/argent-test-ui-flow/SKILL.md @@ -7,7 +7,7 @@ description: Autonomously test an app UI (iOS or Android) by running interact-sc The interaction tool names are identical on iOS and Android — `gesture-tap`, `gesture-swipe`, `describe`, `screenshot`, `launch-app`, etc. — and the tool-server auto-dispatches based on the `udid` you pass (UUID-shape → iOS, adb serial → Android). -**Before testing, resolve which device to test on.** Call `list-devices` and follow ``: prefer a running device on any platform; +**Before testing, resolve which device to test on.** Call `list-devices` and follow Device Selection in the `argent` skill: prefer a running device on any platform; Once a platform is chosen, the per-platform setup skill takes over: diff --git a/packages/skills/skills/argent/SKILL.md b/packages/skills/skills/argent/SKILL.md new file mode 100644 index 000000000..98c8730da --- /dev/null +++ b/packages/skills/skills/argent/SKILL.md @@ -0,0 +1,93 @@ +--- +name: argent +description: Argent MCP routing and usage for iOS Simulator, Android Emulator, Chromium (CDP) apps, and TV targets (Apple TV, Android TV, Amazon Fire TV / Vega). Use when the user mentions simulator, emulator, device, TV, Fire TV, Vega, or app interaction; tapping, swiping, typing, D-pad navigation, screenshots, or inspecting a running app; visible mobile or TV UI changes; manual QA or visual behavior validation; running, debugging, or testing React Native (iOS, Android, or Vega) or a Chromium/Electron app; profiling performance or diagnosing re-renders; or any work that uses argent MCP tools or the argent CLI. +--- + +# Argent + +If Argent is installed, its MCP tools are the preferred way to control iOS simulators, Android emulators, Chromium (CDP) apps, and Vega (Amazon Fire TV) devices. Use the `argent` command only to manage or inspect the toolkit itself (`argent --help`, `argent --version`). + +A **Chromium (CDP) app** is any Chromium runtime exposing a Chrome DevTools Protocol endpoint — an Electron app (boot with `boot-device` + `electronAppPath`) or a Chromium-family browser (Chrome/Brave/Edge) launched with `--remote-debugging-port`. All are driven through the same tool surface and tagged `platform: "chromium"`. On Chromium, scroll with `gesture-scroll` and drag with `gesture-drag` — `gesture-swipe` is touch-only. + +A **Vega device** is a virtual device (VVD) or physical Amazon Fire TV unit — driven by `tv-remote` (D-pad) and tagged `platform: "vega"`. + +## Availability + +**IMPORTANT:** Run this check once per session, before the first Argent tool call or `argent` command. Do not re-probe before later calls. + +1. Are `mcp__argent__*` tools in your tool list? If none are present, Argent is not available. +2. If still unsure, run `command -v argent`. A non-zero exit means the CLI is not on PATH. + +If Argent **is** available, proceed normally. + +If Argent is **absent**, treat it as an expected state — not an error to retry. **IMPORTANT:** Do not call `mcp__argent__*` tools, do not run `argent` commands, and do not attempt any Argent workflow. Tell the user once, and ask if you should continue without Argent: + +> Argent isn't installed in this environment. To enable the mobile/Chromium tooling this repo is configured for, run `npx @swmansion/argent init -y` (or `npm i -g @swmansion/argent && argent init -y`). + +## Skill Loading + +**IMPORTANT:** Always read the relevant Argent sub-skill before executing Argent MCP tools — sub-skills contain the full step-by-step procedure and edge-case handling for each workflow. + +Before any touch interaction on phone or tablet, read `argent-device-interact`. Before any TV target, read `argent-tv-interact`. + +## MCP-First, With Fallback + +- All simulator/emulator/Chromium/TV interactions go through Argent MCP tools — never use `xcrun simctl`, raw `curl` to simulator ports, or the simulator-server binary directly. +- Interaction tools (`gesture-tap`, `gesture-swipe`, `gesture-pinch`, `gesture-rotate`, `gesture-custom`, `launch-app`, etc.) return a screenshot automatically. Call `screenshot` separately only for a baseline before any action or after a delay. +- If MCP tools are not sufficient and an action can be done with `xcrun`, `adb`, or other commands, use the command — for example changing device options or performing lock, shake, and similar device actions. + +## Mandatory Rules + +**IMPORTANT:** Never derive tap coordinates from screenshots. Before every tap, call a discovery tool and use coordinates from its result. + +- Before booting, running, or interacting with any app, call `list-devices`; prefer running devices unless the user named a platform or device. +- Prefer discovery tools in this order: `describe`, `native-describe-screen` on iOS, then `debugger-component-tree` for React Native. +- `native-user-interactable-view-at-point` / `native-view-at-point` are follow-up diagnostics once you already have a candidate point (iOS only). +- Re-run discovery whenever the screen changes. Screenshots alone are never sufficient for tap coordinates. +- Stop retrying if a tap fails twice at the same coordinates; re-run discovery. +- Read the exact error if `describe` fails, then follow recovery guidance in `argent-device-interact`. +- Before calling gesture tools for the first time, use ToolSearch to load the tool schema. +- Use `launch-app` or `open-url` to open apps; never tap home screen icons. +- Use `run-sequence` for multiple sequential device actions when you do not need to observe the screen between steps. +- When waiting for an action, do not call `screenshot` repeatedly without a proper wait mechanism. Use `await-ui-element` to block until the UI settles (e.g. wait for an element to become `visible`/`hidden`, or to contain expected `text`) instead of polling. +- End device sessions with `stop-all-simulator-servers`. If the user started Metro separately, ask before calling `stop-metro` (specify the port if not 8081). + +## Device Selection + +Choose devices in this order: + +1. Explicit user intent: choose the named platform or device. Look for words like "simulator" and "emulator". +2. Running devices: iOS simulators with state `Booted`, Android devices with `state: "device"`, Chromium (CDP) apps with `platform: "chromium"` and `state: "Running"`. +3. Single-platform project: use the supported platform from `argent-environment-inspector` (`is_native_ios` / `is_native_android`, or React Native with only one platform configured). + +If the platform is unspecified, call `list-devices` and pick the booted target. Do not default to iOS. Vega (Amazon Fire TV) devices appear as `platform: "vega"` — when present, load `argent-tv-interact`. + +## React Native Detection + +Use the `argent-environment-inspector` subagent as the authoritative source for project type and platform support — do not re-inspect files manually. If it has not run and project type is unknown, run it before proceeding. Use subagents to run `gather-workspace-data` when possible; do not call it yourself if the subagent is available. + +When `is_react_native` is true, load `argent-react-native-app-workflow`. Use `debugger-component-tree` for element discovery when useful; fall back to `describe` if responses are too large or unhelpful. + +## Skill Routing + +**IMPORTANT:** Load the matching Argent skill before executing Argent MCP tools. + +- `argent-ios-simulator-setup`: iOS simulator setup, no simulator booted, UDID needed, or simulator-server setup. +- `argent-android-emulator-setup`: Android emulator setup, no emulator running, adb serial needed, or APK install. +- `argent-device-interact`: tapping, swiping, typing, gestures, hardware buttons, app launch/restart, URLs, screenshots, scrolling, device rotation, or visible UI validation on phone/tablet iOS and Android. For any TV target, use the TV skill below. +- `argent-tv-interact`: any TV target — a `list-devices` entry with `runtimeKind: "tv"` (Apple TV simulator or Android TV emulator) or `platform: "vega"` / `kind: "vvd"` (Amazon Fire TV / VVD), or the user mentions Apple TV / tvOS / Android TV / leanback / Vega / Fire TV. TV UI is focus-driven, not touch-driven: use `describe` (read focus) + `tv-remote` (D-pad) + `keyboard` (type); `gesture-*` tools do not apply. Covers booting, app lifecycle, focus navigation, typing, screenshots, and (Vega) VVD lifecycle + Fast Refresh. Prompt keywords: apple tv, tvos, android tv, leanback, vega, fire tv, vvd, d-pad. +- `argent-screenshot-diff`: screenshot diff, visual regression, before/after comparison, or stable pixel comparison. +- `argent-react-native-app-workflow`: React Native app running, Metro, builds, pod issues, logs, bundle reload, or reinstall. +- `argent-metro-debugger`: JS evaluation, Metro connection work, React component tree inspection, or `debugger-component-tree`. +- `argent-react-native-profiler`: component profiling, re-render investigation, CPU hotspots, or ranked performance reports. +- `argent-native-profiler`: native profiling — CPU hotspots, UI hangs, memory leak investigation (iOS and Android). +- `argent-react-native-optimization`: startup, bundle size, slow UI, lists, images, navigation, re-renders, or performance optimization. Entry point for all performance work; delegates to `argent-react-native-profiler` for measurement. +- `argent-test-ui-flow`: end-to-end UI testing, manual QA loops, user flow verification, or validating visible UI behavior. +- `argent-create-flow`: record or replay repeated interaction flows, A/B comparisons, regression checks, or repeated profiling paths. Prompt keywords: flow, repeat, test X times. +- `argent-lens`: design alternatives, A/B choices, or variant selection for a screen or component (requires `argent enable argent-lens`). Prompt keywords: variant, design option, alternative, A/B, "let me pick", "show me options". + +## Environment Inspection + +Use subagent `argent-environment-inspector` when environment context is unknown, project memory lacks setup information, or you need build commands, startup scripts, Metro port, platform support, or QA tooling. + +If the subagent already ran this session, reuse its result. The main agent is responsible for persisting its JSON result to project memory when appropriate. From a6aa948755efca7b34ef75e29f9f3e20dde8150f Mon Sep 17 00:00:00 2001 From: Daniel Dunderfelt Date: Mon, 22 Jun 2026 12:00:41 +0300 Subject: [PATCH 2/2] Clean up stale argent.md rules on uninstall or update. --- packages/argent-installer/src/mcp-configs.ts | 60 ++++++++++++-- packages/argent-installer/src/uninstall.ts | 23 ++--- .../argent-installer/test/mcp-configs.test.ts | 83 +++++++++++++++++++ 3 files changed, 146 insertions(+), 20 deletions(-) diff --git a/packages/argent-installer/src/mcp-configs.ts b/packages/argent-installer/src/mcp-configs.ts index ee1532db9..3ff198c67 100644 --- a/packages/argent-installer/src/mcp-configs.ts +++ b/packages/argent-installer/src/mcp-configs.ts @@ -88,14 +88,15 @@ function hasEnv(entry: McpServerEntry): entry is McpServerEntry & { env: Record< return entry.env != null && Object.keys(entry.env).length > 0; } -function removeDirIfEmpty(dirPath: string): void { +function removeDirIfEmpty(dirPath: string): boolean { try { - if (!fs.existsSync(dirPath)) return; - if (!fs.statSync(dirPath).isDirectory()) return; - if (fs.readdirSync(dirPath).length > 0) return; + if (!fs.existsSync(dirPath)) return false; + if (!fs.statSync(dirPath).isDirectory()) return false; + if (fs.readdirSync(dirPath).length > 0) return false; fs.rmdirSync(dirPath); + return true; } catch { - // non-fatal + return false; } } @@ -1215,6 +1216,51 @@ export function removeCodexRules(configPath: string): boolean { return true; } +// ── Stale rule pruning ─────────────────────────────────────────────────────── + +// Argent guidance moved from always-on editor rules to the on-demand argent skill. +// Prune legacy argent.md files (and Codex developer_instructions injection) on +// init, update, and uninstall so stale always-on rules do not linger. + +/** @internal Exported for tests. */ +export const DEPRECATED_ARGENT_RULE_FILES = ["argent.md"] as const; + +export function pruneStaleArgentRules( + adapters: McpConfigAdapter[], + root: string, + scope: ManagedContentScope +): string[] { + const results: string[] = []; + const managedTargets = getManagedContentTargets(adapters, root, scope); + + for (const target of managedTargets.ruleTargets) { + for (const fileName of DEPRECATED_ARGENT_RULE_FILES) { + const filePath = path.join(target.targetPath, fileName); + try { + if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) continue; + fs.rmSync(filePath, { force: true }); + const removedRoot = removeDirIfEmpty(target.targetPath); + const rootLabel = removedRoot ? " (removed empty rules directory)" : ""; + results.push(`Removed stale ${fileName} from ${target.label}${rootLabel}`); + } catch (err) { + results.push(`Could not remove stale ${fileName} from ${target.label}: ${err}`); + } + } + } + + for (const target of managedTargets.codexConfigTargets) { + try { + if (removeCodexRules(target.targetPath)) { + results.push(`Removed stale argent rules from ${target.label}`); + } + } catch (err) { + results.push(`Could not remove stale argent rules from ${target.label}: ${err}`); + } + } + + return results; +} + // ── Copy orchestrator ──────────────────────────────────────────────────────── // MARK: Copy orchestrator @@ -1228,6 +1274,10 @@ export function copyRulesAndAgents( const results: string[] = []; const managedTargets = getManagedContentTargets(adapters, root, scope); + for (const message of pruneStaleArgentRules(adapters, root, scope)) { + results.push(` ${message}`); + } + for (const target of managedTargets.ruleTargets) { try { if (fs.existsSync(rulesDir)) { diff --git a/packages/argent-installer/src/uninstall.ts b/packages/argent-installer/src/uninstall.ts index 1498318ce..f9dafb532 100644 --- a/packages/argent-installer/src/uninstall.ts +++ b/packages/argent-installer/src/uninstall.ts @@ -9,7 +9,7 @@ import { FAILURE_CODES, type FailureSignal } from "@argent/registry"; import { ALL_ADAPTERS, getManagedContentTargets, - removeCodexRules, + pruneStaleArgentRules, type ManagedContentTarget, } from "./mcp-configs.js"; import { @@ -470,6 +470,13 @@ export async function uninstall(args: string[]): Promise { }, ]; + for (const message of [ + ...pruneStaleArgentRules(ALL_ADAPTERS, projectRoot, "local"), + ...pruneStaleArgentRules(ALL_ADAPTERS, projectRoot, "global"), + ]) { + pruneResults.push(`${pc.green("+")} ${message}`); + } + for (const { sourceDir, targets, contentLabel } of bundledTargets) { try { pruneResults.push(...cleanupBundledTargets(sourceDir, targets, contentLabel)); @@ -478,20 +485,6 @@ export async function uninstall(args: string[]): Promise { } } - // Codex: remove argent rules from developer_instructions in config.toml - for (const { targetPath, label } of [ - ...localTargets.codexConfigTargets, - ...globalTargets.codexConfigTargets, - ]) { - try { - if (removeCodexRules(targetPath)) { - pruneResults.push(`${pc.green("+")} Removed argent rules from ${label}`); - } - } catch (err) { - pruneResults.push(`${pc.red("x")} Could not clean ${label}: ${err}`); - } - } - if (pruneResults.length > 0) { p.note(pruneResults.join("\n"), "Pruned Argent Content"); } else { diff --git a/packages/argent-installer/test/mcp-configs.test.ts b/packages/argent-installer/test/mcp-configs.test.ts index 2450416b6..90fe3cd09 100644 --- a/packages/argent-installer/test/mcp-configs.test.ts +++ b/packages/argent-installer/test/mcp-configs.test.ts @@ -13,6 +13,7 @@ import { findConfiguredAdapterScopes, getManagedContentTargets, injectCodexRules, + pruneStaleArgentRules, removeCodexRules, } from "../src/mcp-configs.js"; @@ -1338,6 +1339,88 @@ describe("copyRulesAndAgents", () => { expect(content).toContain("argent rules"); expect(content).toContain("developer_instructions"); }); + + it("prunes stale argent.md when the bundled rules directory is empty", () => { + const claudeAdapter = ALL_ADAPTERS.find((a) => a.name === "Claude Code")!; + const emptyRulesDir = path.join(tmpDir, "empty-rules"); + fs.mkdirSync(emptyRulesDir, { recursive: true }); + const rulesTarget = path.join(tmpDir, ".claude", "rules"); + fs.mkdirSync(rulesTarget, { recursive: true }); + fs.writeFileSync(path.join(rulesTarget, "argent.md"), "# stale rule"); + fs.writeFileSync(path.join(rulesTarget, "custom.md"), "# keep me"); + + const results = copyRulesAndAgents( + [claudeAdapter], + tmpDir, + "local", + emptyRulesDir, + agentsDir + ); + + expect(results.some((r) => r.includes("Removed stale argent.md"))).toBe(true); + expect(fs.existsSync(path.join(rulesTarget, "argent.md"))).toBe(false); + expect(fs.existsSync(path.join(rulesTarget, "custom.md"))).toBe(true); + }); +}); + +// ── pruneStaleArgentRules ───────────────────────────────────────────────────── + +describe("pruneStaleArgentRules", () => { + let agentsDir: string; + + beforeEach(() => { + agentsDir = path.join(tmpDir, "src-agents"); + fs.mkdirSync(agentsDir, { recursive: true }); + }); + + it("removes stale argent.md from editor rule targets but keeps other rules", () => { + const cursorAdapter = ALL_ADAPTERS.find((a) => a.name === "Cursor")!; + const rulesTarget = path.join(tmpDir, ".cursor", "rules"); + fs.mkdirSync(rulesTarget, { recursive: true }); + fs.writeFileSync(path.join(rulesTarget, "argent.md"), "# stale"); + fs.writeFileSync(path.join(rulesTarget, "team-style.md"), "# keep"); + + const results = pruneStaleArgentRules([cursorAdapter], tmpDir, "local"); + + expect(results).toEqual(["Removed stale argent.md from .cursor/rules"]); + expect(fs.existsSync(path.join(rulesTarget, "argent.md"))).toBe(false); + expect(fs.existsSync(path.join(rulesTarget, "team-style.md"))).toBe(true); + }); + + it("removes an empty rules directory after deleting argent.md", () => { + const claudeAdapter = ALL_ADAPTERS.find((a) => a.name === "Claude Code")!; + const rulesTarget = path.join(tmpDir, ".claude", "rules"); + fs.mkdirSync(rulesTarget, { recursive: true }); + fs.writeFileSync(path.join(rulesTarget, "argent.md"), "# stale"); + + const results = pruneStaleArgentRules([claudeAdapter], tmpDir, "local"); + + expect(results[0]).toContain("removed empty rules directory"); + expect(fs.existsSync(rulesTarget)).toBe(false); + }); + + it("removes injected Codex developer_instructions", () => { + const codexAdapter = ALL_ADAPTERS.find((a) => a.name === "Codex")!; + const rulesDir = path.join(tmpDir, "src-rules"); + fs.mkdirSync(rulesDir, { recursive: true }); + fs.writeFileSync(path.join(rulesDir, "argent.md"), "Use argent tools."); + const configPath = path.join(tmpDir, ".codex", "config.toml"); + fs.mkdirSync(path.dirname(configPath), { recursive: true }); + fs.writeFileSync(configPath, 'developer_instructions = "Keep me."\n'); + injectCodexRules(configPath, rulesDir); + + const results = pruneStaleArgentRules([codexAdapter], tmpDir, "local"); + + expect(results).toEqual(["Removed stale argent rules from .codex/config.toml"]); + const content = fs.readFileSync(configPath, "utf8"); + expect(content).toContain("Keep me."); + expect(content).not.toContain("argent rules"); + }); + + it("returns no results when nothing stale is present", () => { + const claudeAdapter = ALL_ADAPTERS.find((a) => a.name === "Claude Code")!; + expect(pruneStaleArgentRules([claudeAdapter], tmpDir, "local")).toEqual([]); + }); }); // ── Codex developer_instructions injection ──────────────────────────────────