@cats-inc/cats-platform 0.1.7 → 0.1.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.
Files changed (76) hide show
  1. package/README.md +8 -8
  2. package/build/desktop/bootstrapPage.d.ts.map +1 -1
  3. package/build/desktop/bootstrapPage.js +5 -1
  4. package/build/desktop/bootstrapPage.js.map +1 -1
  5. package/build/desktop/cliInventoryProbe.d.ts.map +1 -1
  6. package/build/desktop/cliInventoryProbe.js +12 -0
  7. package/build/desktop/cliInventoryProbe.js.map +1 -1
  8. package/build/desktop/contracts.d.ts +1 -1
  9. package/build/desktop/contracts.d.ts.map +1 -1
  10. package/build/desktop/contracts.js +4 -0
  11. package/build/desktop/contracts.js.map +1 -1
  12. package/build/desktop/main.js +41 -9
  13. package/build/desktop/main.js.map +1 -1
  14. package/build/desktop/packaging.d.ts.map +1 -1
  15. package/build/desktop/packaging.js +62 -0
  16. package/build/desktop/packaging.js.map +1 -1
  17. package/build/desktop/setupAssets.d.ts.map +1 -1
  18. package/build/desktop/setupAssets.js +134 -0
  19. package/build/desktop/setupAssets.js.map +1 -1
  20. package/build/desktop/updateInstallHandoff.d.ts +14 -0
  21. package/build/desktop/updateInstallHandoff.d.ts.map +1 -1
  22. package/build/desktop/updateInstallHandoff.js +23 -0
  23. package/build/desktop/updateInstallHandoff.js.map +1 -1
  24. package/build/desktop/updateManager.d.ts.map +1 -1
  25. package/build/desktop/updateManager.js +8 -0
  26. package/build/desktop/updateManager.js.map +1 -1
  27. package/build/desktop/updaterAdapter.d.ts +16 -1
  28. package/build/desktop/updaterAdapter.d.ts.map +1 -1
  29. package/build/desktop/updaterAdapter.js +49 -2
  30. package/build/desktop/updaterAdapter.js.map +1 -1
  31. package/build/renderer/assets/{ActiveSessionPermissionChip-BRMLwkvf.js → ActiveSessionPermissionChip-Dl2UOFgh.js} +1 -1
  32. package/build/renderer/assets/{App-BM2nzo6s.js → App-C57f4p8k.js} +1 -1
  33. package/build/renderer/assets/{App-CVQAwCMi.js → App-CPqGDXVi.js} +1 -1
  34. package/build/renderer/assets/{App-Dl5FLaR_.js → App-ChvP0G9X.js} +1 -1
  35. package/build/renderer/assets/{WorkspaceAppRoutes-C3DUzdbj.js → WorkspaceAppRoutes-DYg2vnjz.js} +1 -1
  36. package/build/renderer/assets/{app-hP5h2jAt.js → app-DVN9Ghrk.js} +3 -3
  37. package/build/renderer/assets/{conversationSidebarRecentEntries-B-mNvfiy.js → conversationSidebarRecentEntries-XZVCnMLw.js} +1 -1
  38. package/build/renderer/assets/{useDraftSessionChips-DuXIoptW.js → useDraftSessionChips-By3XXabI.js} +1 -1
  39. package/build/renderer/index.html +1 -1
  40. package/build/server/shared/providerCatalog.d.ts +2 -2
  41. package/build/server/shared/providerCatalog.d.ts.map +1 -1
  42. package/build/server/shared/providerCatalogData.d.ts +11 -1
  43. package/build/server/shared/providerCatalogData.d.ts.map +1 -1
  44. package/build/server/shared/providerCatalogData.js +8 -0
  45. package/build/server/shared/providerCatalogData.js.map +1 -1
  46. package/build/server/shared/providerCatalogInstances.d.ts.map +1 -1
  47. package/build/server/shared/providerCatalogInstances.js +6 -0
  48. package/build/server/shared/providerCatalogInstances.js.map +1 -1
  49. package/package.json +6 -3
  50. package/scripts/README.md +51 -2
  51. package/scripts/linux/install-aider.sh +8 -0
  52. package/scripts/linux/install-cline.sh +8 -0
  53. package/scripts/linux/install-devin.sh +8 -0
  54. package/scripts/linux/install-grok.sh +8 -0
  55. package/scripts/linux/install-pi.sh +1 -1
  56. package/scripts/linux/node-cli-common.sh +43 -3
  57. package/scripts/linux/provider-cli-common.sh +162 -24
  58. package/scripts/linux/test-linux-package-smoke.sh +16 -0
  59. package/scripts/linux/upgrade-cli-tools.sh +2 -2
  60. package/scripts/macos/install-aider.sh +8 -0
  61. package/scripts/macos/install-cline.sh +8 -0
  62. package/scripts/macos/install-devin.sh +8 -0
  63. package/scripts/macos/install-grok.sh +8 -0
  64. package/scripts/macos/install-pi.sh +1 -1
  65. package/scripts/macos/node-cli-common.sh +43 -3
  66. package/scripts/macos/provider-cli-common.sh +162 -24
  67. package/scripts/macos/test-macos-package-smoke.sh +15 -0
  68. package/scripts/macos/upgrade-cli-tools.sh +2 -2
  69. package/scripts/windows/Check-WindowsSetupReadiness.ps1 +32 -1
  70. package/scripts/windows/Install-Aider.ps1 +368 -0
  71. package/scripts/windows/Install-Cline.ps1 +39 -0
  72. package/scripts/windows/Install-Devin.ps1 +385 -0
  73. package/scripts/windows/Install-Grok.ps1 +355 -0
  74. package/scripts/windows/Install-Pi.ps1 +1 -1
  75. package/scripts/windows/Test-WindowsInstallerSmoke.ps1 +23 -0
  76. package/scripts/windows/_NpmCliInstaller.ps1 +41 -0
@@ -50,6 +50,9 @@ platform_label() {
50
50
  provider_display_name() {
51
51
  case "$1" in
52
52
  antigravity) printf '%s\n' 'Antigravity CLI' ;;
53
+ grok) printf '%s\n' 'Grok CLI' ;;
54
+ devin) printf '%s\n' 'Devin CLI' ;;
55
+ aider) printf '%s\n' 'Aider' ;;
53
56
  claude) printf '%s\n' 'Claude Code CLI' ;;
54
57
  cursor) printf '%s\n' 'Cursor Agent CLI' ;;
55
58
  goose) printf '%s\n' 'Goose CLI' ;;
@@ -62,6 +65,9 @@ provider_display_name() {
62
65
  provider_primary_command() {
63
66
  case "$1" in
64
67
  antigravity) printf '%s\n' 'agy' ;;
68
+ grok) printf '%s\n' 'grok' ;;
69
+ devin) printf '%s\n' 'devin' ;;
70
+ aider) printf '%s\n' 'aider' ;;
65
71
  claude) printf '%s\n' 'claude' ;;
66
72
  cursor) printf '%s\n' 'cursor-agent' ;;
67
73
  goose) printf '%s\n' 'goose' ;;
@@ -90,6 +96,9 @@ provider_alias_target() {
90
96
  provider_install_url() {
91
97
  case "$1" in
92
98
  antigravity) printf '%s\n' 'https://antigravity.google/cli/install.sh' ;;
99
+ grok) printf '%s\n' 'https://x.ai/cli/install.sh' ;;
100
+ devin) printf '%s\n' 'https://cli.devin.ai/install.sh' ;;
101
+ aider) printf '%s\n' 'https://aider.chat/install.sh' ;;
93
102
  claude) printf '%s\n' 'https://claude.ai/install.sh' ;;
94
103
  cursor) printf '%s\n' 'https://cursor.com/install' ;;
95
104
  goose) printf '%s\n' 'https://github.com/block/goose/releases/download/stable/download_cli.sh' ;;
@@ -99,28 +108,25 @@ provider_install_url() {
99
108
  esac
100
109
  }
101
110
 
111
+ provider_install_dir() {
112
+ case "$1" in
113
+ grok) printf '%s\n' "$HOME/.grok/bin" ;;
114
+ *) printf '%s\n' "$HOME/.local/bin" ;;
115
+ esac
116
+ }
117
+
102
118
  provider_binary_candidates() {
103
119
  local platform="$1"
104
120
  local provider="$2"
121
+ local install_dir
122
+ local primary_command
123
+
124
+ install_dir="$(provider_install_dir "$provider")"
125
+ primary_command="$(provider_primary_command "$provider")"
126
+ printf '%s\n' "$install_dir/$primary_command"
105
127
 
106
128
  case "$provider" in
107
- antigravity)
108
- printf '%s\n' "$HOME/.local/bin/agy"
109
- ;;
110
- claude)
111
- printf '%s\n' "$HOME/.local/bin/claude"
112
- ;;
113
- cursor)
114
- printf '%s\n' "$HOME/.local/bin/cursor-agent"
115
- ;;
116
- goose)
117
- printf '%s\n' "$HOME/.local/bin/goose"
118
- ;;
119
- junie)
120
- printf '%s\n' "$HOME/.local/bin/junie"
121
- ;;
122
129
  kiro)
123
- printf '%s\n' "$HOME/.local/bin/kiro-cli"
124
130
  printf '%s\n' '/usr/local/bin/kiro-cli'
125
131
  printf '%s\n' '/opt/homebrew/bin/kiro-cli'
126
132
  if [ "$platform" = 'macos' ]; then
@@ -169,11 +175,21 @@ prepend_path_if_missing() {
169
175
  fi
170
176
  }
171
177
 
172
- ensure_local_bin_path_export() {
178
+ ensure_provider_bin_path_export() {
173
179
  local shell_rc="$1"
180
+ local provider="$2"
181
+ local install_dir
182
+ local path_export
183
+
184
+ install_dir="$(provider_install_dir "$provider")"
185
+ if [ "$provider" = 'grok' ]; then
186
+ path_export='export PATH="$HOME/.grok/bin:$PATH"'
187
+ else
188
+ path_export='export PATH="$HOME/.local/bin:$PATH"'
189
+ fi
174
190
  append_line_if_missing "$shell_rc" '# Added by cats-platform self-hosted provider helpers' '# Added by cats-platform self-hosted provider helpers'
175
- append_line_if_missing "$shell_rc" 'export PATH="$HOME/.local/bin:$PATH"' 'export PATH="$HOME/.local/bin:$PATH"'
176
- prepend_path_if_missing "$HOME/.local/bin"
191
+ append_line_if_missing "$shell_rc" "$path_export" "$path_export"
192
+ prepend_path_if_missing "$install_dir"
177
193
  }
178
194
 
179
195
  rewrite_alias_line() {
@@ -263,6 +279,56 @@ provider_version_line() {
263
279
  "$command_path" --version 2>&1 | head -1 || true
264
280
  }
265
281
 
282
+ # Devin's official install.sh ends by launching the interactive `devin setup`
283
+ # wizard. Packaged setup runs non-interactively with no console a prompt can
284
+ # reach, so an unstripped installer would stall the step rather than fail.
285
+ #
286
+ # Verified against https://cli.devin.ai/install.sh on 2026-08-09: the file is
287
+ # 286 lines and its final line is
288
+ # "$VERSION_DIR/bin/$COMPILED_BIN_NAME" setup
289
+ # which is also the only line in the file ending in " setup".
290
+ #
291
+ # The helper neutralises exactly that line and refuses to run the script when
292
+ # the shape does not match, rather than executing an unrecognised installer on
293
+ # a guess. stdin from /dev/null stays as insurance for any prompt added earlier
294
+ # in the script.
295
+ run_devin_installer() {
296
+ local installer_path=''
297
+ local patched_path=''
298
+ local trailing_matches=0
299
+ local status=0
300
+
301
+ installer_path="$(mktemp)"
302
+ patched_path="$(mktemp)"
303
+
304
+ if ! curl -fsSL "$(provider_install_url devin)" -o "$installer_path"; then
305
+ rm -f "$installer_path" "$patched_path"
306
+ printf 'Failed to download the Devin installer.\n' >&2
307
+ return 1
308
+ fi
309
+
310
+ trailing_matches="$(grep -cE ' setup$' "$installer_path" || true)"
311
+ if [ "$trailing_matches" != '1' ]; then
312
+ rm -f "$installer_path" "$patched_path"
313
+ printf 'Refusing to run the Devin installer: expected exactly one interactive setup invocation, found %s. Install Devin with the upstream installer and report this.\n' \
314
+ "$trailing_matches" >&2
315
+ return 1
316
+ fi
317
+
318
+ # Restrict the edit to the final line so an unrelated match elsewhere can
319
+ # never be silently rewritten.
320
+ sed -e '$ s|^.* setup$|: skipped interactive devin setup|' "$installer_path" > "$patched_path"
321
+ if cmp -s "$installer_path" "$patched_path"; then
322
+ rm -f "$installer_path" "$patched_path"
323
+ printf 'Refusing to run the Devin installer: the interactive setup invocation is no longer the final line.\n' >&2
324
+ return 1
325
+ fi
326
+
327
+ bash "$patched_path" < /dev/null || status=$?
328
+ rm -f "$installer_path" "$patched_path"
329
+ return "$status"
330
+ }
331
+
266
332
  run_remote_pipe_installer() {
267
333
  local provider="$1"
268
334
  shift
@@ -270,12 +336,15 @@ run_remote_pipe_installer() {
270
336
  url="$(provider_install_url "$provider")"
271
337
 
272
338
  case "$provider" in
273
- antigravity)
339
+ antigravity|grok)
274
340
  curl -fsSL "$url" | bash
275
341
  ;;
276
342
  goose)
277
343
  curl -fsSL "$url" | env CONFIGURE=false bash
278
344
  ;;
345
+ aider)
346
+ curl -LsSf "$url" | sh
347
+ ;;
279
348
  cursor)
280
349
  curl "$url" -fsSL | bash
281
350
  ;;
@@ -349,6 +418,15 @@ run_provider_install_action() {
349
418
  run_remote_pipe_installer "$provider"
350
419
  fi
351
420
  ;;
421
+ grok)
422
+ run_remote_pipe_installer "$provider"
423
+ ;;
424
+ devin)
425
+ run_devin_installer
426
+ ;;
427
+ aider)
428
+ run_remote_pipe_installer "$provider"
429
+ ;;
352
430
  claude)
353
431
  if [ "$action" = 'upgrade' ] && current_command="$(detect_provider_command "$platform" "$provider")"; then
354
432
  "$current_command" update || true
@@ -399,6 +477,20 @@ __CATS_UNINSTALL_APPLIED=()
399
477
  __CATS_UNINSTALL_WARNINGS=()
400
478
  __CATS_UNINSTALL_REMAINING=''
401
479
 
480
+ # Steps the helper cannot perform for the user. Packaged Devin installs strip
481
+ # the installer's trailing interactive wizard, so authentication is always still
482
+ # owed after a successful install; a "ready" result must not imply otherwise.
483
+ provider_manual_steps() {
484
+ case "$1" in
485
+ devin)
486
+ printf '%s\n' 'Run devin auth login once to authenticate; packaged setup skips the interactive wizard. Check with devin auth status.'
487
+ ;;
488
+ *)
489
+ :
490
+ ;;
491
+ esac
492
+ }
493
+
402
494
  uninstall_provider_native_paths() {
403
495
  local platform="$1"
404
496
  local provider="$2"
@@ -413,7 +505,7 @@ uninstall_provider_native_paths() {
413
505
  local planned_paths=()
414
506
 
415
507
  primary_command="$(provider_primary_command "$provider")"
416
- if primary_path="$(command -v "$primary_command" 2>/dev/null)"; then
508
+ if [ "$provider" != 'grok' ] && primary_path="$(command -v "$primary_command" 2>/dev/null)"; then
417
509
  case "$primary_path" in
418
510
  "$HOME"/*) planned_paths+=("$primary_path") ;;
419
511
  esac
@@ -447,6 +539,50 @@ UPNP_EOF
447
539
  fi
448
540
  fi
449
541
 
542
+ if [ "$provider" = 'grok' ]; then
543
+ extra="$(provider_install_dir "$provider")/agent"
544
+ if [ -e "$extra" ] || [ -L "$extra" ]; then
545
+ planned_paths+=("$extra")
546
+ fi
547
+ fi
548
+
549
+ # Devin's shim in ~/.local/bin points at a versioned tree; removing only the
550
+ # shim would leave every installed version behind.
551
+ if [ "$provider" = 'devin' ]; then
552
+ extra="${XDG_DATA_HOME:-$HOME/.local/share}/devin/cli"
553
+ case "$extra" in
554
+ "$HOME"/*)
555
+ if [ -d "$extra" ]; then
556
+ planned_paths+=("$extra")
557
+ fi
558
+ ;;
559
+ esac
560
+ fi
561
+
562
+ # ~/.local/bin/aider is a uv tool shim, not the tool. Removing only the shim
563
+ # leaves aider-chat installed in the uv tool environment and the shim
564
+ # regenerable by any later `uv tool` command, so the tool is uninstalled
565
+ # through uv first and path removal only mops up what uv left behind.
566
+ #
567
+ # The uv binary itself is deliberately left in place: Aider's installer drops
568
+ # its own uv into ~/.local/bin, users commonly have another, and the helper
569
+ # cannot tell them apart. Removing a uv the user installed separately is a
570
+ # destructive false positive, so this warns instead (SPEC-112 PD4).
571
+ if [ "$provider" = 'aider' ]; then
572
+ if command -v uv >/dev/null 2>&1; then
573
+ if [ "$dry_run" = 'true' ]; then
574
+ __CATS_UNINSTALL_PLANNED+=("uv_tool_uninstall:aider-chat")
575
+ elif uv tool uninstall aider-chat >/dev/null 2>&1; then
576
+ __CATS_UNINSTALL_APPLIED+=("uv_tool_uninstalled:aider-chat")
577
+ else
578
+ __CATS_UNINSTALL_WARNINGS+=("uv_tool_uninstall_failed:aider-chat")
579
+ fi
580
+ else
581
+ __CATS_UNINSTALL_WARNINGS+=("uv_missing_aider_chat_may_remain")
582
+ fi
583
+ __CATS_UNINSTALL_WARNINGS+=("bundled_uv_left_in_place_provenance_unknown")
584
+ fi
585
+
450
586
  for path in "${planned_paths[@]}"; do
451
587
  __CATS_UNINSTALL_PLANNED+=("remove:$path")
452
588
  done
@@ -683,7 +819,7 @@ run_native_provider_installer() {
683
819
  fi
684
820
 
685
821
  if [ "$force" = 'false' ] && [ "$upgrade" = 'false' ]; then
686
- ensure_local_bin_path_export "$shell_rc"
822
+ ensure_provider_bin_path_export "$shell_rc" "$provider"
687
823
  ensure_provider_alias "$shell_rc" "$provider"
688
824
  if [ "$emit_json" = 'true' ]; then
689
825
  printf '{'
@@ -787,7 +923,7 @@ run_native_provider_installer() {
787
923
  fi
788
924
  applied_changes=("${planned_actions[@]}")
789
925
 
790
- ensure_local_bin_path_export "$shell_rc"
926
+ ensure_provider_bin_path_export "$shell_rc" "$provider"
791
927
  ensure_provider_alias "$shell_rc" "$provider"
792
928
 
793
929
  while [ $attempt -le 3 ]; do
@@ -811,7 +947,9 @@ run_native_provider_installer() {
811
947
  printf '"warnings":'
812
948
  json_string_array "${warnings[@]}"
813
949
  printf ','
814
- printf '"manualSteps":[],'
950
+ printf '"manualSteps":'
951
+ json_string_array "$(provider_manual_steps "$provider")"
952
+ printf ','
815
953
  printf '"interruptions":[]'
816
954
  printf '}\n'
817
955
  else
@@ -50,6 +50,10 @@ assert_file "${RESOURCES_ROOT}/desktop/setup-assets/linux/install-github-cli.sh"
50
50
  assert_file "${RESOURCES_ROOT}/desktop/setup-assets/linux/setup-node-global-prefix.sh" 'bundled Linux npm prefix helper'
51
51
  assert_file "${RESOURCES_ROOT}/desktop/setup-assets/linux/install-codex.sh" 'bundled Linux OpenAI Codex installer helper'
52
52
  assert_file "${RESOURCES_ROOT}/desktop/setup-assets/linux/install-antigravity.sh" 'bundled Linux Antigravity installer helper'
53
+ assert_file "${RESOURCES_ROOT}/desktop/setup-assets/linux/install-grok.sh" 'bundled Linux Grok installer helper'
54
+ assert_file "${RESOURCES_ROOT}/desktop/setup-assets/linux/install-cline.sh" 'bundled Linux Cline installer helper'
55
+ assert_file "${RESOURCES_ROOT}/desktop/setup-assets/linux/install-devin.sh" 'bundled Linux Devin installer helper'
56
+ assert_file "${RESOURCES_ROOT}/desktop/setup-assets/linux/install-aider.sh" 'bundled Linux Aider installer helper'
53
57
  assert_file "${RESOURCES_ROOT}/desktop/setup-assets/linux/install-copilot.sh" 'bundled Linux GitHub Copilot installer helper'
54
58
  assert_file "${RESOURCES_ROOT}/desktop/setup-assets/linux/install-opencode.sh" 'bundled Linux OpenCode installer helper'
55
59
  assert_file "${RESOURCES_ROOT}/desktop/setup-assets/linux/install-kilo.sh" 'bundled Linux Kilo Code installer helper'
@@ -66,6 +70,12 @@ assert_file "${RESOURCES_ROOT}/desktop/setup-assets/linux/node-cli-common.sh" 'b
66
70
  assert_file "${RESOURCES_ROOT}/desktop/setup-assets/manifest.json" 'bundled setup-assets manifest'
67
71
  assert_file "${PLAN_PATH}" 'bundled desktop packaging plan'
68
72
 
73
+ GROK_HELPER="${RESOURCES_ROOT}/desktop/setup-assets/linux/install-grok.sh"
74
+ bash "${GROK_HELPER}" --check --json >/dev/null
75
+ pass 'bundled Linux Grok helper check mode completed'
76
+ bash "${GROK_HELPER}" --force --dry-run --json >/dev/null
77
+ pass 'bundled Linux Grok helper dry-run mode completed'
78
+
69
79
  node - <<'NODE' "${PLAN_PATH}"
70
80
  const fs = require('node:fs');
71
81
 
@@ -89,10 +99,16 @@ assert(linuxTarget.installerFormats.includes('deb'), 'Linux target includes deb
89
99
  assert(linuxTarget.artifacts.some((artifact) => artifact.id === 'linux-opencode-native-installer-script'), 'Linux target includes the bundled OpenCode installer asset');
90
100
  assert(linuxTarget.artifacts.some((artifact) => artifact.id === 'linux-codex-native-installer-script'), 'Linux target includes the bundled OpenAI Codex installer asset');
91
101
  assert(linuxTarget.artifacts.some((artifact) => artifact.id === 'linux-antigravity-native-installer-script'), 'Linux target includes the bundled Antigravity installer asset');
102
+ assert(linuxTarget.artifacts.some((artifact) => artifact.id === 'linux-grok-native-installer-script'), 'Linux target includes the bundled Grok installer asset');
103
+ assert(linuxTarget.artifacts.some((artifact) => artifact.id === 'linux-cline-native-installer-script'), 'Linux target includes the bundled Cline installer asset');
104
+ assert(linuxTarget.artifacts.some((artifact) => artifact.id === 'linux-devin-native-installer-script'), 'Linux target includes the bundled Devin installer asset');
105
+ assert(linuxTarget.artifacts.some((artifact) => artifact.id === 'linux-aider-native-installer-script'), 'Linux target includes the bundled Aider installer asset');
92
106
  assert(linuxTarget.artifacts.some((artifact) => artifact.id === 'linux-setup-readiness-audit-script'), 'Linux target includes the bundled readiness audit asset');
93
107
  assert(linuxTarget.artifacts.some((artifact) => artifact.id === 'linux-provider-cli-common-support-script'), 'Linux target includes the Linux provider helper asset');
94
108
  assert(linuxTarget.artifacts.some((artifact) => artifact.id === 'linux-node-cli-common-support-script'), 'Linux target includes the Linux npm helper asset');
95
109
  assert(plan.installer.providerSetup.localProviders.some((provider) => provider.id === 'kiro' && provider.platform === 'linux'), 'installer contract keeps Kiro in the Linux bundled local-provider rollout');
110
+ assert(plan.installer.providerSetup.localProviders.some((provider) => provider.id === 'grok' && provider.platform === 'linux'), 'installer contract keeps Grok in the Linux bundled local-provider rollout');
111
+ assert(plan.installer.providerSetup.localProviders.some((provider) => provider.id === 'cline' && provider.platform === 'linux'), 'installer contract keeps Cline in the Linux bundled local-provider rollout');
96
112
  assert(plan.installer.providerSetup.helperCatalog.some((helper) => helper.id === 'linux-install-readiness-audit'), 'installer contract includes Linux readiness helper metadata');
97
113
  assert(!plan.installer.providerSetup.helperCatalog.some((helper) => helper.id === 'windows-install-readiness-audit'), 'installer contract omits Windows-only helper metadata from the Linux package');
98
114
  NODE
@@ -35,12 +35,12 @@ while [ $# -gt 0 ]; do
35
35
  shift
36
36
  done
37
37
 
38
- for script_name in install-claude-code.sh install-antigravity.sh install-cursor-agent.sh install-goose.sh install-junie.sh install-kiro-cli.sh; do
38
+ for script_name in install-claude-code.sh install-antigravity.sh install-cursor-agent.sh install-devin.sh install-aider.sh install-goose.sh install-junie.sh install-kiro-cli.sh install-grok.sh; do
39
39
  "$SCRIPT_DIR/$script_name" -Upgrade
40
40
  done
41
41
 
42
42
  if [ "$skip_node" = 'false' ]; then
43
- for script_name in install-codex.sh install-copilot.sh install-opencode.sh install-kilo.sh install-auggie.sh install-pi.sh; do
43
+ for script_name in install-codex.sh install-copilot.sh install-opencode.sh install-kilo.sh install-auggie.sh install-pi.sh install-cline.sh; do
44
44
  "$SCRIPT_DIR/$script_name" -upgrade
45
45
  done
46
46
  fi
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
5
+ # shellcheck source=provider-cli-common.sh
6
+ . "$SCRIPT_DIR/provider-cli-common.sh"
7
+
8
+ run_native_provider_installer 'macos' 'aider' "$@"
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
5
+ # shellcheck source=node-cli-common.sh
6
+ . "$SCRIPT_DIR/node-cli-common.sh"
7
+
8
+ run_npm_cli_provider 'macos' 'cline' 'cline' 'cline' 'Cline CLI' "$@"
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
5
+ # shellcheck source=provider-cli-common.sh
6
+ . "$SCRIPT_DIR/provider-cli-common.sh"
7
+
8
+ run_native_provider_installer 'macos' 'devin' "$@"
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ # shellcheck source=provider-cli-common.sh
6
+ source "$SCRIPT_DIR/provider-cli-common.sh"
7
+
8
+ run_native_provider_installer 'macos' 'grok' "$@"
@@ -5,4 +5,4 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
5
5
  # shellcheck source=node-cli-common.sh
6
6
  . "$SCRIPT_DIR/node-cli-common.sh"
7
7
 
8
- run_npm_cli_provider 'macos' 'pi' '@mariozechner/pi-coding-agent' 'pi' 'Pi Coding Agent' "$@"
8
+ run_npm_cli_provider 'macos' 'pi' '@earendil-works/pi-coding-agent' 'pi' 'Pi Coding Agent' "$@"
@@ -316,6 +316,37 @@ run_node_prefix_setup() {
316
316
  printf 'Reload your shell if PATH is stale: source %s\n' "$shell_rc"
317
317
  }
318
318
 
319
+ # Packages whose npm name changed upstream. npm resolves an abandoned name to its
320
+ # final published version and reports it as current forever, so `npm outdated -g`
321
+ # never flags it and every upgrade path silently skips it. Two packages shipping
322
+ # the same bin also fight over the shim, which resolves to whichever installed
323
+ # last. The superseded package is therefore removed before its replacement is
324
+ # installed, matching environment-bootstrap cfe7785.
325
+ node_cli_superseded_packages() {
326
+ case "$1" in
327
+ '@earendil-works/pi-coding-agent')
328
+ printf '%s\n' '@mariozechner/pi-coding-agent'
329
+ ;;
330
+ *)
331
+ :
332
+ ;;
333
+ esac
334
+ }
335
+
336
+ remove_superseded_npm_packages() {
337
+ local package_name="$1"
338
+ local legacy=''
339
+
340
+ while IFS= read -r legacy; do
341
+ [ -n "$legacy" ] || continue
342
+ npm list -g "$legacy" --depth=0 >/dev/null 2>&1 || continue
343
+ printf 'Removing superseded package %s (replaced by %s).\n' "$legacy" "$package_name"
344
+ npm uninstall -g "$legacy" >/dev/null 2>&1 || true
345
+ done <<EOF
346
+ $(node_cli_superseded_packages "$package_name")
347
+ EOF
348
+ }
349
+
319
350
  node_cli_package_rows() {
320
351
  cat <<'EOF'
321
352
  codex|codex|@openai/codex|OpenAI Codex
@@ -323,7 +354,8 @@ copilot|copilot|@github/copilot|GitHub Copilot CLI
323
354
  opencode|opencode|opencode-ai|OpenCode
324
355
  kilo|kilo|@kilocode/cli|Kilo Code CLI
325
356
  auggie|auggie|@augmentcode/auggie|Auggie CLI
326
- pi|pi|@mariozechner/pi-coding-agent|Pi CLI
357
+ pi|pi|@earendil-works/pi-coding-agent|Pi CLI
358
+ cline|cline|cline|Cline CLI
327
359
  EOF
328
360
  }
329
361
 
@@ -569,6 +601,10 @@ EOF
569
601
  is_outdated='true'
570
602
  fi
571
603
 
604
+ if [ "$check_only" != 'true' ]; then
605
+ remove_superseded_npm_packages "$package_name"
606
+ fi
607
+
572
608
  if [ "$check_only" = 'true' ]; then
573
609
  if [ "$installed" = 'true' ]; then
574
610
  printf '%s installed.\n' "$display_name"
@@ -918,7 +954,7 @@ EOF
918
954
  fi
919
955
 
920
956
  if [ "$collection_mode" = 'serial' ]; then
921
- for provider in claude antigravity cursor goose junie kiro; do
957
+ for provider in claude antigravity cursor goose junie kiro grok; do
922
958
  if command_path="$(detect_provider_command "$platform" "$provider")"; then
923
959
  handle_audited_check_result "$provider" "$(provider_display_name "$provider")" 'true' 'native' 'host' 'ready'
924
960
  unset command_path
@@ -951,7 +987,7 @@ EOF
951
987
  fi
952
988
  fi
953
989
  else
954
- for provider in claude antigravity cursor goose junie kiro; do
990
+ for provider in claude antigravity cursor goose junie kiro grok; do
955
991
  async_file="$(mktemp)"
956
992
  async_files+=("$async_file")
957
993
  (
@@ -1118,6 +1154,10 @@ run_npm_cli_provider() {
1118
1154
  shift
1119
1155
  done
1120
1156
 
1157
+ if [ "$check_only" != 'true' ] && [ "$uninstall" != 'true' ]; then
1158
+ remove_superseded_npm_packages "$package_name"
1159
+ fi
1160
+
1121
1161
  if [ "$force" = 'true' ]; then
1122
1162
  upgrade='false'
1123
1163
  fi