@swmansion/argent 0.15.1-next.8 → 0.15.1-next.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,7 +12,9 @@
12
12
  **[Argent](https://argent.swmansion.com)** is an **agentic toolkit** that gives your AI assistant direct access to iOS Simulators, Android emulators and physical devices, TVs (Apple TV, Android TV, Fire TV) and Electron/Chromium desktop and web apps. Ask it to tap a button, run a profiler or reproduce an issue manually - all from within your CLI, without switching context.
13
13
 
14
14
  ```bash
15
- npx @swmansion/argent init
15
+ npx @swmansion/argent@latest init
16
+ # or, in a pnpm project (where npm's devEngines check may refuse to run npx):
17
+ pnpm dlx @swmansion/argent@latest init
16
18
  ```
17
19
 
18
20
  ## Supported platforms
@@ -98,7 +100,9 @@ Argent runs Android emulators on Linux but the default install can be slow if a
98
100
  From your project root:
99
101
 
100
102
  ```bash
101
- npx @swmansion/argent init
103
+ npx @swmansion/argent@latest init
104
+ # or, in a pnpm project (where npm's devEngines check may refuse to run npx):
105
+ pnpm dlx @swmansion/argent@latest init
102
106
  ```
103
107
 
104
108
  This command triggers an installation wizard which:
@@ -121,9 +125,14 @@ teammate gets the same setup on `npm install` — no per-developer global instal
121
125
  `argent init` — choose the local mode:
122
126
 
123
127
  ```bash
124
- npx @swmansion/argent init --local
128
+ npx @swmansion/argent@latest init --local
129
+ # or, in a pnpm project:
130
+ pnpm dlx @swmansion/argent@latest init --local
125
131
  ```
126
132
 
133
+ > Note: in a freshly `pnpm init`-ed project, `npx` itself may refuse to run
134
+ > (npm's `devEngines` check) — use the `pnpm dlx` form there.
135
+
127
136
  This adds `@swmansion/argent` to your project's `devDependencies` and writes MCP
128
137
  configs that launch the project-local copy (`node node_modules/@swmansion/argent/dist/cli.js mcp`).
129
138
  Commit `package.json` + your lockfile, the generated MCP config (`.mcp.json`,
package/dist/cli-cmds.mjs CHANGED
@@ -6557,6 +6557,8 @@ var ALLOWED = {
6557
6557
  action: PACKAGE_ACTION,
6558
6558
  is_success: bool,
6559
6559
  duration_ms: DURATION_MS,
6560
+ retry_count: COUNT,
6561
+ last_attempt_duration_ms: DURATION_MS,
6560
6562
  ...FAILURE_SIGNAL
6561
6563
  },
6562
6564
  "installation:cli_update_start": {},
@@ -16464,6 +16464,8 @@ var ALLOWED = {
16464
16464
  action: PACKAGE_ACTION,
16465
16465
  is_success: bool,
16466
16466
  duration_ms: DURATION_MS,
16467
+ retry_count: COUNT,
16468
+ last_attempt_duration_ms: DURATION_MS,
16467
16469
  ...FAILURE_SIGNAL
16468
16470
  },
16469
16471
  "installation:cli_update_start": {},
@@ -20043,12 +20045,30 @@ function detectPackageManager() {
20043
20045
  if (agent.startsWith("bun")) return "bun";
20044
20046
  return "npm";
20045
20047
  }
20048
+ function asKnownPm(name) {
20049
+ return name === "npm" || name === "yarn" || name === "pnpm" || name === "bun" ? name : null;
20050
+ }
20046
20051
  function pmFromPackageManagerField(dir) {
20047
20052
  try {
20048
20053
  const pkg = JSON.parse(fs8.readFileSync(path8.join(dir, "package.json"), "utf8"));
20049
- if (typeof pkg.packageManager !== "string") return null;
20050
- const name = pkg.packageManager.split("@")[0];
20051
- return name === "npm" || name === "yarn" || name === "pnpm" || name === "bun" ? name : null;
20054
+ if (typeof pkg.packageManager === "string") {
20055
+ return asKnownPm(pkg.packageManager.split("@")[0]);
20056
+ }
20057
+ return null;
20058
+ } catch {
20059
+ return null;
20060
+ }
20061
+ }
20062
+ function pmFromDevEngines(dir) {
20063
+ try {
20064
+ const pkg = JSON.parse(fs8.readFileSync(path8.join(dir, "package.json"), "utf8"));
20065
+ const devEnginesPm = pkg.devEngines?.packageManager;
20066
+ const names = /* @__PURE__ */ new Set();
20067
+ for (const entry of Array.isArray(devEnginesPm) ? devEnginesPm : [devEnginesPm]) {
20068
+ const name = asKnownPm(entry?.name);
20069
+ if (name) names.add(name);
20070
+ }
20071
+ return names.size === 1 ? [...names][0] : null;
20052
20072
  } catch {
20053
20073
  return null;
20054
20074
  }
@@ -20061,10 +20081,13 @@ function pmFromLockfile(dir) {
20061
20081
  if (has("package-lock.json") || has("npm-shrinkwrap.json")) return "npm";
20062
20082
  return null;
20063
20083
  }
20084
+ function pmFromWorkspaceMarker(dir) {
20085
+ return fs8.existsSync(path8.join(dir, "pnpm-workspace.yaml")) ? "pnpm" : null;
20086
+ }
20064
20087
  function detectProjectPackageManager(projectRoot) {
20065
20088
  let dir = path8.resolve(projectRoot);
20066
20089
  for (; ; ) {
20067
- const pm = pmFromPackageManagerField(dir) ?? pmFromLockfile(dir);
20090
+ const pm = pmFromPackageManagerField(dir) ?? pmFromLockfile(dir) ?? pmFromDevEngines(dir) ?? pmFromWorkspaceMarker(dir);
20068
20091
  if (pm) return pm;
20069
20092
  if (fs8.existsSync(path8.join(dir, ".git"))) break;
20070
20093
  const parent = path8.dirname(dir);
@@ -20726,10 +20749,291 @@ function writeTomlOrRemove(filePath, data) {
20726
20749
  }
20727
20750
  writeToml(filePath, data);
20728
20751
  }
20752
+ function dirHasEditorEvidence(dir, looksArgentOnly) {
20753
+ return dirExists(dir) && !looksArgentOnly(dir);
20754
+ }
20755
+ function fileHasEditorEvidence(filePath, looksArgentOnly) {
20756
+ return fs13.existsSync(filePath) && !looksArgentOnly(filePath);
20757
+ }
20758
+ function parseJsoncStrict(filePath) {
20759
+ let raw;
20760
+ try {
20761
+ raw = fs13.readFileSync(filePath, "utf8");
20762
+ } catch {
20763
+ return null;
20764
+ }
20765
+ if (raw.charCodeAt(0) === 65279) raw = raw.slice(1);
20766
+ const errors = [];
20767
+ const parsed = parse3(raw, errors, { allowTrailingComma: true });
20768
+ if (errors.length > 0 || parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
20769
+ return null;
20770
+ return parsed;
20771
+ }
20772
+ function parseTomlStrict(filePath) {
20773
+ try {
20774
+ return parse(fs13.readFileSync(filePath, "utf8"));
20775
+ } catch {
20776
+ return null;
20777
+ }
20778
+ }
20779
+ function parseYamlStrict(filePath) {
20780
+ try {
20781
+ const parsed = (0, import_yaml2.parse)(fs13.readFileSync(filePath, "utf8"));
20782
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return null;
20783
+ return parsed;
20784
+ } catch {
20785
+ return null;
20786
+ }
20787
+ }
20788
+ function jsonLooksArgentServerOnly(filePath, containerKey) {
20789
+ const config = parseJsoncStrict(filePath);
20790
+ if (config === null) return false;
20791
+ const keys = Object.keys(config);
20792
+ if (keys.length !== 1 || keys[0] !== containerKey) return false;
20793
+ const servers = config[containerKey] ?? {};
20794
+ const serverKeys = Object.keys(servers);
20795
+ return serverKeys.length === 1 && serverKeys[0] === MCP_SERVER_KEY;
20796
+ }
20797
+ var bundledManagedNamesCache = null;
20798
+ function bundledManagedNames(kind) {
20799
+ if (!bundledManagedNamesCache) bundledManagedNamesCache = /* @__PURE__ */ new Map();
20800
+ const cached2 = bundledManagedNamesCache.get(kind);
20801
+ if (cached2) return cached2;
20802
+ let names;
20803
+ try {
20804
+ names = new Set(fs13.readdirSync(kind === "rules" ? RULES_DIR : AGENTS_DIR));
20805
+ } catch {
20806
+ names = /* @__PURE__ */ new Set();
20807
+ }
20808
+ bundledManagedNamesCache.set(kind, names);
20809
+ return names;
20810
+ }
20811
+ function managedDirLooksArgentOnly(dir, kind) {
20812
+ let entries;
20813
+ try {
20814
+ entries = fs13.readdirSync(dir);
20815
+ } catch {
20816
+ return false;
20817
+ }
20818
+ if (entries.length === 0) return false;
20819
+ if (kind === "skills") return entries.every((name) => name.startsWith(ARGENT_SKILL_PREFIX));
20820
+ const bundled = bundledManagedNames(kind);
20821
+ return entries.every((name) => bundled.has(name));
20822
+ }
20823
+ function cursorDirLooksArgentOnly(dir) {
20824
+ let entries;
20825
+ try {
20826
+ entries = fs13.readdirSync(dir);
20827
+ } catch {
20828
+ return false;
20829
+ }
20830
+ if (entries.length === 0) return false;
20831
+ return entries.every((entry) => {
20832
+ const full = path13.join(dir, entry);
20833
+ if (entry === "mcp.json") {
20834
+ return jsonLooksArgentServerOnly(full, "mcpServers");
20835
+ }
20836
+ if (entry === "permissions.json") {
20837
+ const config = parseJsoncStrict(full);
20838
+ if (config === null) return false;
20839
+ const keys = Object.keys(config);
20840
+ if (keys.length !== 1 || keys[0] !== "mcpAllowlist") return false;
20841
+ const list = config.mcpAllowlist;
20842
+ return Array.isArray(list) && list.length > 0 && list.every((rule) => rule === CURSOR_ALLOWLIST_PATTERN);
20843
+ }
20844
+ if (entry === "rules" || entry === "agents" || entry === "skills") {
20845
+ return managedDirLooksArgentOnly(full, entry);
20846
+ }
20847
+ return false;
20848
+ });
20849
+ }
20850
+ function claudeSettingsLooksArgentOnly(filePath) {
20851
+ const config = parseJsoncStrict(filePath);
20852
+ if (config === null) return false;
20853
+ const keys = Object.keys(config);
20854
+ if (keys.length !== 1 || keys[0] !== "permissions") return false;
20855
+ const permissions = config.permissions;
20856
+ if (!isRecord(permissions)) return false;
20857
+ const permKeys = Object.keys(permissions);
20858
+ if (permKeys.length !== 1 || permKeys[0] !== "allow") return false;
20859
+ const allow = permissions.allow;
20860
+ return Array.isArray(allow) && allow.length > 0 && allow.every((rule) => rule === PERMISSION_RULE);
20861
+ }
20862
+ function claudeDirLooksArgentOnly(dir) {
20863
+ let entries;
20864
+ try {
20865
+ entries = fs13.readdirSync(dir);
20866
+ } catch {
20867
+ return false;
20868
+ }
20869
+ if (entries.length === 0) return false;
20870
+ return entries.every((entry) => {
20871
+ const full = path13.join(dir, entry);
20872
+ if (entry === "settings.json") return claudeSettingsLooksArgentOnly(full);
20873
+ if (entry === "rules" || entry === "agents" || entry === "skills") {
20874
+ return managedDirLooksArgentOnly(full, entry);
20875
+ }
20876
+ return false;
20877
+ });
20878
+ }
20879
+ function vscodeDirLooksArgentOnly(dir) {
20880
+ let entries;
20881
+ try {
20882
+ entries = fs13.readdirSync(dir);
20883
+ } catch {
20884
+ return false;
20885
+ }
20886
+ if (entries.length === 0) return false;
20887
+ return entries.every(
20888
+ (entry) => entry === "mcp.json" && jsonLooksArgentServerOnly(path13.join(dir, entry), "servers")
20889
+ );
20890
+ }
20891
+ function windsurfDirLooksArgentOnly(dir) {
20892
+ let entries;
20893
+ try {
20894
+ entries = fs13.readdirSync(dir);
20895
+ } catch {
20896
+ return false;
20897
+ }
20898
+ if (entries.length === 0) return false;
20899
+ return entries.every(
20900
+ (entry) => entry === "mcp_config.json" && jsonLooksArgentServerOnly(path13.join(dir, entry), "mcpServers")
20901
+ );
20902
+ }
20903
+ function zedSettingsLooksArgentOnly(filePath) {
20904
+ const config = parseJsoncStrict(filePath);
20905
+ if (config === null) return false;
20906
+ const keys = Object.keys(config);
20907
+ if (keys.length === 0) return false;
20908
+ return keys.every((key) => {
20909
+ if (key === "context_servers") {
20910
+ const servers = config.context_servers;
20911
+ if (!isRecord(servers)) return false;
20912
+ const serverKeys = Object.keys(servers);
20913
+ return serverKeys.length === 1 && serverKeys[0] === MCP_SERVER_KEY;
20914
+ }
20915
+ if (key === "agent") {
20916
+ const agent = config.agent;
20917
+ if (!isRecord(agent)) return false;
20918
+ const agentKeys = Object.keys(agent);
20919
+ if (agentKeys.length !== 1 || agentKeys[0] !== "tool_permissions") return false;
20920
+ const perms = agent.tool_permissions;
20921
+ if (!isRecord(perms)) return false;
20922
+ const permKeys = Object.keys(perms);
20923
+ return permKeys.length === 1 && permKeys[0] === "default" && (perms.default === "allow" || perms.default === "confirm");
20924
+ }
20925
+ return false;
20926
+ });
20927
+ }
20928
+ function zedDirLooksArgentOnly(dir) {
20929
+ let entries;
20930
+ try {
20931
+ entries = fs13.readdirSync(dir);
20932
+ } catch {
20933
+ return false;
20934
+ }
20935
+ if (entries.length === 0) return false;
20936
+ return entries.every(
20937
+ (entry) => entry === "settings.json" && zedSettingsLooksArgentOnly(path13.join(dir, entry))
20938
+ );
20939
+ }
20940
+ function geminiDirLooksArgentOnly(dir) {
20941
+ let entries;
20942
+ try {
20943
+ entries = fs13.readdirSync(dir);
20944
+ } catch {
20945
+ return false;
20946
+ }
20947
+ if (entries.length === 0) return false;
20948
+ return entries.every((entry) => {
20949
+ const full = path13.join(dir, entry);
20950
+ if (entry === "settings.json") return jsonLooksArgentServerOnly(full, "mcpServers");
20951
+ if (entry === "rules" || entry === "agents") return managedDirLooksArgentOnly(full, entry);
20952
+ return false;
20953
+ });
20954
+ }
20955
+ function hermesConfigLooksArgentOnly(filePath) {
20956
+ const config = parseYamlStrict(filePath);
20957
+ if (config === null) return false;
20958
+ const keys = Object.keys(config);
20959
+ if (keys.length !== 1 || keys[0] !== "mcp_servers") return false;
20960
+ const servers = config.mcp_servers;
20961
+ if (!isRecord(servers)) return false;
20962
+ const serverKeys = Object.keys(servers);
20963
+ return serverKeys.length === 1 && serverKeys[0] === MCP_SERVER_KEY;
20964
+ }
20965
+ function hermesDirLooksArgentOnly(dir) {
20966
+ let entries;
20967
+ try {
20968
+ entries = fs13.readdirSync(dir);
20969
+ } catch {
20970
+ return false;
20971
+ }
20972
+ if (entries.length === 0) return false;
20973
+ return entries.every(
20974
+ (entry) => entry === "config.yaml" && hermesConfigLooksArgentOnly(path13.join(dir, entry))
20975
+ );
20976
+ }
20977
+ function kiroDirLooksArgentOnly(dir) {
20978
+ let entries;
20979
+ try {
20980
+ entries = fs13.readdirSync(dir);
20981
+ } catch {
20982
+ return false;
20983
+ }
20984
+ if (entries.length === 0) return false;
20985
+ return entries.every((entry) => {
20986
+ if (entry !== "settings") return false;
20987
+ const settingsDir = path13.join(dir, entry);
20988
+ let settingsEntries;
20989
+ try {
20990
+ settingsEntries = fs13.readdirSync(settingsDir);
20991
+ } catch {
20992
+ return false;
20993
+ }
20994
+ if (settingsEntries.length === 0) return false;
20995
+ return settingsEntries.every(
20996
+ (name) => name === "mcp.json" && jsonLooksArgentServerOnly(path13.join(settingsDir, name), "mcpServers")
20997
+ );
20998
+ });
20999
+ }
21000
+ function codexDirLooksArgentOnly(dir) {
21001
+ let entries;
21002
+ try {
21003
+ entries = fs13.readdirSync(dir);
21004
+ } catch {
21005
+ return false;
21006
+ }
21007
+ if (entries.length === 0) return false;
21008
+ return entries.every((entry) => {
21009
+ const full = path13.join(dir, entry);
21010
+ if (entry === "config.toml") {
21011
+ const config = parseTomlStrict(full);
21012
+ if (config === null) return false;
21013
+ const configEntries = Object.entries(config);
21014
+ if (configEntries.length === 0) return false;
21015
+ return configEntries.every(([key, value]) => {
21016
+ if (key === "mcp_servers") {
21017
+ const servers = value ?? {};
21018
+ const serverKeys = Object.keys(servers);
21019
+ return serverKeys.length === 1 && serverKeys[0] === MCP_SERVER_KEY;
21020
+ }
21021
+ if (key === "developer_instructions") {
21022
+ return typeof value === "string" && removeArgentSection(value) === "";
21023
+ }
21024
+ return false;
21025
+ });
21026
+ }
21027
+ if (entry === "rules" || entry === "agents" || entry === "skills") {
21028
+ return managedDirLooksArgentOnly(full, entry);
21029
+ }
21030
+ return false;
21031
+ });
21032
+ }
20729
21033
  var cursorAdapter = {
20730
21034
  name: "Cursor",
20731
21035
  detect() {
20732
- return dirExists(path13.join(homedir4(), ".cursor")) || dirExists(path13.join(process.cwd(), ".cursor"));
21036
+ return dirHasEditorEvidence(path13.join(homedir4(), ".cursor"), cursorDirLooksArgentOnly) || dirHasEditorEvidence(path13.join(process.cwd(), ".cursor"), cursorDirLooksArgentOnly);
20733
21037
  },
20734
21038
  projectPath(root) {
20735
21039
  return path13.join(root, ".cursor", "mcp.json");
@@ -20833,7 +21137,8 @@ function claudeDisabledListFinding(settingsPath, label, projectConfined) {
20833
21137
  var claudeAdapter = {
20834
21138
  name: "Claude Code",
20835
21139
  detect() {
20836
- return fs13.existsSync(path13.join(process.cwd(), ".mcp.json")) || fs13.existsSync(path13.join(homedir4(), ".claude.json")) || dirExists(path13.join(process.cwd(), ".claude")) || dirExists(path13.join(homedir4(), ".claude"));
21140
+ const mcpJsonArgentOnly = (p) => jsonLooksArgentServerOnly(p, "mcpServers");
21141
+ return fileHasEditorEvidence(path13.join(process.cwd(), ".mcp.json"), mcpJsonArgentOnly) || fileHasEditorEvidence(path13.join(homedir4(), ".claude.json"), mcpJsonArgentOnly) || dirHasEditorEvidence(path13.join(process.cwd(), ".claude"), claudeDirLooksArgentOnly) || dirHasEditorEvidence(path13.join(homedir4(), ".claude"), claudeDirLooksArgentOnly);
20837
21142
  },
20838
21143
  projectPath(root) {
20839
21144
  return path13.join(root, ".mcp.json");
@@ -20941,7 +21246,7 @@ var claudeAdapter = {
20941
21246
  var vscodeAdapter = {
20942
21247
  name: "VS Code",
20943
21248
  detect() {
20944
- return dirExists(path13.join(process.cwd(), ".vscode")) || dirExists(path13.join(homedir4(), ".vscode"));
21249
+ return dirHasEditorEvidence(path13.join(process.cwd(), ".vscode"), vscodeDirLooksArgentOnly) || dirExists(path13.join(homedir4(), ".vscode"));
20945
21250
  },
20946
21251
  projectPath(root) {
20947
21252
  return path13.join(root, ".vscode", "mcp.json");
@@ -21024,7 +21329,10 @@ function vscodeUserDirs() {
21024
21329
  var windsurfAdapter = {
21025
21330
  name: "Windsurf",
21026
21331
  detect() {
21027
- return dirExists(path13.join(homedir4(), ".codeium", "windsurf"));
21332
+ return dirHasEditorEvidence(
21333
+ path13.join(homedir4(), ".codeium", "windsurf"),
21334
+ windsurfDirLooksArgentOnly
21335
+ );
21028
21336
  },
21029
21337
  projectPath() {
21030
21338
  return null;
@@ -21084,7 +21392,7 @@ var windsurfAdapter = {
21084
21392
  var zedAdapter = {
21085
21393
  name: "Zed",
21086
21394
  detect() {
21087
- return dirExists(path13.join(homedir4(), ".config", "zed"));
21395
+ return dirHasEditorEvidence(path13.join(homedir4(), ".config", "zed"), zedDirLooksArgentOnly);
21088
21396
  },
21089
21397
  projectPath(root) {
21090
21398
  return path13.join(root, ".zed", "settings.json");
@@ -21142,7 +21450,7 @@ var zedAdapter = {
21142
21450
  var geminiAdapter = {
21143
21451
  name: "Gemini",
21144
21452
  detect() {
21145
- return dirExists(path13.join(homedir4(), ".gemini")) || dirExists(path13.join(process.cwd(), ".gemini"));
21453
+ return dirHasEditorEvidence(path13.join(homedir4(), ".gemini"), geminiDirLooksArgentOnly) || dirHasEditorEvidence(path13.join(process.cwd(), ".gemini"), geminiDirLooksArgentOnly);
21146
21454
  },
21147
21455
  projectPath(root) {
21148
21456
  return path13.join(root, ".gemini", "settings.json");
@@ -21207,7 +21515,7 @@ var CODEX_FILENAME = ".codex";
21207
21515
  var codexAdapter = {
21208
21516
  name: "Codex",
21209
21517
  detect() {
21210
- return dirExists(path13.join(homedir4(), CODEX_FILENAME)) || dirExists(path13.join(process.cwd(), CODEX_FILENAME));
21518
+ return dirHasEditorEvidence(path13.join(homedir4(), CODEX_FILENAME), codexDirLooksArgentOnly) || dirHasEditorEvidence(path13.join(process.cwd(), CODEX_FILENAME), codexDirLooksArgentOnly);
21211
21519
  },
21212
21520
  projectPath(root) {
21213
21521
  return path13.join(root, CODEX_FILENAME, "config.toml");
@@ -21285,7 +21593,7 @@ var codexAdapter = {
21285
21593
  var hermesAdapter = {
21286
21594
  name: "Hermes",
21287
21595
  detect() {
21288
- return dirExists(path13.join(homedir4(), ".hermes"));
21596
+ return dirHasEditorEvidence(path13.join(homedir4(), ".hermes"), hermesDirLooksArgentOnly);
21289
21597
  },
21290
21598
  projectPath() {
21291
21599
  return null;
@@ -21409,7 +21717,7 @@ var KIRO_AUTO_APPROVE_ALL = ["*"];
21409
21717
  var kiroAdapter = {
21410
21718
  name: "Kiro",
21411
21719
  detect() {
21412
- return dirExists(path13.join(homedir4(), ".kiro")) || dirExists(path13.join(process.cwd(), ".kiro"));
21720
+ return dirHasEditorEvidence(path13.join(homedir4(), ".kiro"), kiroDirLooksArgentOnly) || dirHasEditorEvidence(path13.join(process.cwd(), ".kiro"), kiroDirLooksArgentOnly);
21413
21721
  },
21414
21722
  projectPath(root) {
21415
21723
  return path13.join(root, ".kiro", "settings", "mcp.json");
@@ -21773,16 +22081,19 @@ async function resolveTelemetryConsent(opts) {
21773
22081
  }
21774
22082
 
21775
22083
  // ../argent-installer/src/init-args.ts
21776
- function extractFlag(args, flag) {
21777
- const idx = args.indexOf(flag);
21778
- if (idx === -1 || idx + 1 >= args.length) return null;
21779
- return args[idx + 1];
21780
- }
21781
22084
  function parseInitArgs(args) {
22085
+ let fromTar = null;
22086
+ for (let i2 = 0; i2 < args.length; i2++) {
22087
+ const arg = args[i2];
22088
+ if (arg === "--from" || arg.startsWith("--from=")) {
22089
+ const value = arg === "--from" ? i2 + 1 < args.length ? args[++i2] : "" : arg.slice(7);
22090
+ if (value !== "" && fromTar === null) fromTar = value;
22091
+ }
22092
+ }
21782
22093
  return {
21783
22094
  nonInteractive: args.includes("--yes") || args.includes("-y"),
21784
22095
  noTelemetry: args.includes("--no-telemetry"),
21785
- fromTar: extractFlag(args, "--from"),
22096
+ fromTar,
21786
22097
  wantsLocal: args.includes("--local"),
21787
22098
  wantsGlobal: args.includes("--global")
21788
22099
  };
@@ -21875,12 +22186,13 @@ var InitTelemetry = class {
21875
22186
  });
21876
22187
  });
21877
22188
  }
21878
- async trackPackageAction(action, startedAt, isSuccess, failureSignal2) {
22189
+ async trackPackageAction(action, startedAt, isSuccess, failureSignal2, attemptInfo) {
21879
22190
  track("installation:package_action", {
21880
22191
  trigger: "init",
21881
22192
  action,
21882
22193
  is_success: isSuccess,
21883
22194
  duration_ms: performance.now() - startedAt,
22195
+ ...attemptInfo ?? {},
21884
22196
  ...failureSignal2 ?? {}
21885
22197
  });
21886
22198
  }
@@ -21930,6 +22242,16 @@ function execShellCommandSync(cmd, opts = {}) {
21930
22242
  ...opts.env ? { env: opts.env } : {}
21931
22243
  });
21932
22244
  }
22245
+ var ShellCommandError = class extends Error {
22246
+ constructor(message, exitCode, signal) {
22247
+ super(message);
22248
+ this.exitCode = exitCode;
22249
+ this.signal = signal;
22250
+ this.name = "ShellCommandError";
22251
+ }
22252
+ exitCode;
22253
+ signal;
22254
+ };
21933
22255
  function runShellCommand(cmd, opts = {}) {
21934
22256
  return new Promise((resolve10, reject) => {
21935
22257
  const child = spawn2(cmd.bin, cmd.args, {
@@ -21941,9 +22263,16 @@ function runShellCommand(cmd, opts = {}) {
21941
22263
  child.stderr?.on("data", (chunk) => {
21942
22264
  stderr += chunk.toString();
21943
22265
  });
21944
- child.on("close", (code) => {
22266
+ child.on("close", (code, signal) => {
21945
22267
  if (code === 0) resolve10();
21946
- else reject(new Error(stderr.trim() || `Command exited with code ${code}`));
22268
+ else
22269
+ reject(
22270
+ new ShellCommandError(
22271
+ stderr.trim() || (signal !== null ? `Command terminated by signal ${signal}` : `Command exited with code ${code}`),
22272
+ code,
22273
+ signal
22274
+ )
22275
+ );
21947
22276
  });
21948
22277
  child.on("error", reject);
21949
22278
  });
@@ -22104,17 +22433,52 @@ async function installLocally(opts) {
22104
22433
  materializeOnly ? `Installing project dependencies to materialize ${PACKAGE_NAME} (${pm})...` : `Adding ${PACKAGE_NAME} to devDependencies (${pm})...`
22105
22434
  );
22106
22435
  const startedAt = performance.now();
22107
- const { landed, exitError: installError } = await runTrustingDisk(
22436
+ const attempt = () => runTrustingDisk(
22108
22437
  () => runShellCommand(cmd, { cwd: projectRoot }),
22109
22438
  () => isLocallyInstalled(projectRoot) || isYarnPnp(projectRoot)
22110
22439
  );
22440
+ let lastAttemptStartedAt = performance.now();
22441
+ let retryCount = 0;
22442
+ let { landed, exitError: installError } = await attempt();
22443
+ const isMissingBinaryError = (err) => err !== null && (err.code === "ENOENT" || process.platform === "win32" && err instanceof ShellCommandError && err.exitCode === 9009);
22444
+ const missingBinary = !landed && isMissingBinaryError(installError);
22445
+ const wasInterrupted = (err) => err instanceof ShellCommandError && (err.signal !== null || err.exitCode === null);
22446
+ const interrupted = !landed && wasInterrupted(installError);
22447
+ if (!landed && installError && !missingBinary && !interrupted) {
22448
+ spinner2.message(`${pm} failed \u2014 retrying once...`);
22449
+ retryCount = 1;
22450
+ lastAttemptStartedAt = performance.now();
22451
+ ({ landed, exitError: installError } = await attempt());
22452
+ }
22453
+ const attemptTelemetry = () => ({
22454
+ retry_count: retryCount,
22455
+ last_attempt_duration_ms: performance.now() - lastAttemptStartedAt
22456
+ });
22111
22457
  if (!landed) {
22112
- spinner2.stop(import_picocolors3.default.red("Local install failed."));
22113
- log.error(
22114
- installError ? `${installError}` : `The install reported success but ${import_picocolors3.default.cyan(PACKAGE_NAME)} is not in node_modules.`
22458
+ spinner2.stop(import_picocolors3.default.red(interrupted ? "Local install interrupted." : "Local install failed."));
22459
+ if (missingBinary) {
22460
+ log.error(
22461
+ `This project uses ${import_picocolors3.default.cyan(pm)}, but the ${import_picocolors3.default.cyan(pm)} command was not found on PATH.`
22462
+ );
22463
+ log.info(
22464
+ `Install ${import_picocolors3.default.cyan(pm)} first` + (pm === "pnpm" || pm === "yarn" ? ` (e.g. ${import_picocolors3.default.cyan(`corepack enable ${pm}`)}, or see the ${pm} install docs)` : "") + `, then re-run ${import_picocolors3.default.cyan("argent init --local")}.`
22465
+ );
22466
+ } else if (interrupted) {
22467
+ log.error(`The ${import_picocolors3.default.cyan(pm)} install was interrupted before it finished.`);
22468
+ log.info(`Re-run ${import_picocolors3.default.cyan("argent init --local")} to try again.`);
22469
+ } else {
22470
+ log.error(
22471
+ installError ? `${installError}` : `The install reported success but ${import_picocolors3.default.cyan(PACKAGE_NAME)} is not in node_modules.`
22472
+ );
22473
+ log.info(`Install manually with: ${import_picocolors3.default.cyan(`cd ${projectRoot} && ${cmdStr}`)}`);
22474
+ }
22475
+ await tel.trackPackageAction(
22476
+ "fresh_install",
22477
+ startedAt,
22478
+ false,
22479
+ INSTALL_LOCAL_PACKAGE_FAILED,
22480
+ attemptTelemetry()
22115
22481
  );
22116
- log.info(`Install manually with: ${import_picocolors3.default.cyan(`cd ${projectRoot} && ${cmdStr}`)}`);
22117
- await tel.trackPackageAction("fresh_install", startedAt, false, INSTALL_LOCAL_PACKAGE_FAILED);
22118
22482
  await tel.finalize(INSTALL_LOCAL_PACKAGE_FAILED);
22119
22483
  process.exit(1);
22120
22484
  }
@@ -22133,7 +22497,7 @@ async function installLocally(opts) {
22133
22497
  );
22134
22498
  }
22135
22499
  }
22136
- await tel.trackPackageAction("fresh_install", startedAt, true);
22500
+ await tel.trackPackageAction("fresh_install", startedAt, true, void 0, attemptTelemetry());
22137
22501
  }
22138
22502
  async function runGlobal(opts) {
22139
22503
  const { fromTar, nonInteractive, tel } = opts;
@@ -22279,6 +22643,12 @@ async function runGlobal(opts) {
22279
22643
 
22280
22644
  // ../argent-installer/src/init-adapters.ts
22281
22645
  var import_picocolors4 = __toESM(require_picocolors(), 1);
22646
+ function previouslyConfiguredAdapters(eligible, installMode) {
22647
+ const wantedScope = installMode === "local" ? "project" : "global";
22648
+ const scopes = findConfiguredAdapterScopes(eligible, process.cwd());
22649
+ const configured = new Set(scopes.filter((s) => s.scope === wantedScope).map((s) => s.adapter));
22650
+ return eligible.filter((a3) => configured.has(a3));
22651
+ }
22282
22652
  async function chooseAdapters(opts) {
22283
22653
  let eligible = ALL_ADAPTERS;
22284
22654
  if (opts.installMode === "local") {
@@ -22294,12 +22664,16 @@ async function chooseAdapters(opts) {
22294
22664
  }
22295
22665
  const detected = detectAdapters().filter((a3) => eligible.includes(a3));
22296
22666
  const detectedNames = detected.map((a3) => a3.name);
22667
+ const previouslyConfigured = previouslyConfiguredAdapters(eligible, opts.installMode);
22668
+ const previouslyConfiguredNames = previouslyConfigured.map((a3) => a3.name);
22669
+ const preselected = [...detected, ...previouslyConfigured.filter((a3) => !detected.includes(a3))];
22297
22670
  if (opts.nonInteractive) {
22298
- return { selected: detected.length > 0 ? detected : eligible, detected };
22671
+ return { selected: preselected.length > 0 ? preselected : eligible, detected };
22299
22672
  }
22300
22673
  const choices = eligible.map((a3) => {
22301
22674
  const parts = [];
22302
22675
  if (detectedNames.includes(a3.name)) parts.push("detected");
22676
+ else if (previouslyConfiguredNames.includes(a3.name)) parts.push("already configured");
22303
22677
  const hasProject = a3.projectPath(process.cwd()) != null;
22304
22678
  const hasGlobal = a3.globalPath() != null;
22305
22679
  if (!hasProject && hasGlobal) {
@@ -22317,7 +22691,7 @@ async function chooseAdapters(opts) {
22317
22691
  const selected = await multiselect({
22318
22692
  message: "Which editors should Argent be configured for?",
22319
22693
  options: choices,
22320
- initialValues: detected,
22694
+ initialValues: preselected,
22321
22695
  required: true
22322
22696
  });
22323
22697
  if (isCancel(selected)) throw new InitCancelled("editors");
@@ -22876,7 +23250,11 @@ async function init2(args) {
22876
23250
  note(mcpLines.join("\n"), "MCP Configuration");
22877
23251
  const staleCleanup = await cleanupStaleMcpConfigs({
22878
23252
  writtenAdapters,
22879
- detectedAdapters: detected,
23253
+ // Sweep EVERY adapter, not just the detected set: the sweep is fully
23254
+ // gated on an existing argent entry (getArgentEntry), and the stale
23255
+ // entries most worth pruning live in argent-only dirs (~/.cursor,
23256
+ // ~/.codex) that detection now deliberately ignores.
23257
+ detectedAdapters: ALL_ADAPTERS,
22880
23258
  installMode: tel.installMode,
22881
23259
  scope: normalizedScope,
22882
23260
  effectiveRoot,
@@ -18004,6 +18004,8 @@ var ALLOWED = {
18004
18004
  action: PACKAGE_ACTION,
18005
18005
  is_success: bool,
18006
18006
  duration_ms: DURATION_MS,
18007
+ retry_count: COUNT,
18008
+ last_attempt_duration_ms: DURATION_MS,
18007
18009
  ...FAILURE_SIGNAL
18008
18010
  },
18009
18011
  "installation:cli_update_start": {},
@@ -103836,6 +103836,8 @@ var ALLOWED = {
103836
103836
  action: PACKAGE_ACTION,
103837
103837
  is_success: bool,
103838
103838
  duration_ms: DURATION_MS,
103839
+ retry_count: COUNT,
103840
+ last_attempt_duration_ms: DURATION_MS,
103839
103841
  ...FAILURE_SIGNAL2
103840
103842
  },
103841
103843
  "installation:cli_update_start": {},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swmansion/argent",
3
- "version": "0.15.1-next.8",
3
+ "version": "0.15.1-next.9",
4
4
  "description": "MCP server for iOS Simulator and Android Emulator control",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
package/rules/argent.md CHANGED
@@ -32,7 +32,7 @@ If argent IS available, ignore the rest of this block and follow this rule norma
32
32
 
33
33
  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:
34
34
 
35
- > 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`).
35
+ > Argent isn't installed in this environment. To enable the mobile/Chromium tooling this repo is configured for, run `npx @swmansion/argent@latest init -y` (or `npm i -g @swmansion/argent@latest && argent init -y`).
36
36
  > </availability_check>
37
37
 
38
38
  <tapping_rule>