speiidev 1.4.2 → 1.4.4

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/bin/speiidev.js CHANGED
@@ -31,6 +31,7 @@ const srcDir = path.resolve(__dirname, '..', 'src');
31
31
  const args = process.argv.slice(2);
32
32
  const env = {
33
33
  ...process.env,
34
+ SPEIIDEV_INSTALLER: 'npm',
34
35
  PYTHONPATH: srcDir + (process.env.PYTHONPATH ? path.delimiter + process.env.PYTHONPATH : ''),
35
36
  };
36
37
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "speiidev",
3
- "version": "1.4.2",
3
+ "version": "1.4.4",
4
4
  "description": "Multi-agent terminal orchestrator wrapper",
5
5
  "author": "Toek Sopanha",
6
6
  "bin": {
@@ -11,4 +11,4 @@ To release a new version, change it in ONE place:
11
11
 
12
12
  """
13
13
 
14
- __version__ = "1.4.2"
14
+ __version__ = "1.4.4"
@@ -118,6 +118,7 @@ SLASH_COMMANDS = [
118
118
  "/model",
119
119
  "/effort",
120
120
  "/perms",
121
+ "/update",
121
122
  "/log",
122
123
  "/conv",
123
124
  "/rember",
@@ -2361,6 +2362,7 @@ def open_action_menu(state: AgentState) -> None:
2361
2362
  ("model", "🧠 Switch model"),
2362
2363
  ("effort", "⚡ Change effort"),
2363
2364
  ("perms", "🔐 Permission gate"),
2365
+ ("update", "🔄 Update SpeiiDev"),
2364
2366
  ("help", "❓ Help"),
2365
2367
  ("exit", "🚪 Exit"),
2366
2368
  ],
@@ -2380,6 +2382,8 @@ def open_action_menu(state: AgentState) -> None:
2380
2382
  handle_effort_switcher(state)
2381
2383
  elif action == "perms":
2382
2384
  handle_permissions(state)
2385
+ elif action == "update":
2386
+ handle_self_update()
2383
2387
  elif action == "help":
2384
2388
  show_help()
2385
2389
 
@@ -2418,10 +2422,86 @@ def show_help() -> None:
2418
2422
  table.add_row("/font <text>", "Render text in 3D wireframe shadow or mini block font")
2419
2423
  table.add_row("/clear", "Clear the screen and redraw the banner")
2420
2424
  table.add_row("Ctrl+L", "Clear the screen and redraw responsive banner")
2425
+ table.add_row("/update", "Update SpeiiDev to the latest version from npm or PyPI")
2421
2426
  table.add_row("/exit", "Quit SpeiiDev")
2422
2427
  console.print(table)
2423
2428
 
2424
2429
 
2430
+ def check_latest_version(timeout: float = 2.0) -> str | None:
2431
+ """Query PyPI or npm registry to check the latest released version."""
2432
+ import urllib.request
2433
+ try:
2434
+ url = "https://pypi.org/pypi/speiidev/json"
2435
+ req = urllib.request.Request(url, headers={"User-Agent": "speiidev-updater"})
2436
+ with urllib.request.urlopen(req, timeout=timeout) as resp:
2437
+ data = json.loads(resp.read().decode("utf-8"))
2438
+ v = data.get("info", {}).get("version")
2439
+ if v:
2440
+ return str(v)
2441
+ except Exception:
2442
+ pass
2443
+ try:
2444
+ url = "https://registry.npmjs.org/speiidev/latest"
2445
+ req = urllib.request.Request(url, headers={"User-Agent": "speiidev-updater"})
2446
+ with urllib.request.urlopen(req, timeout=timeout) as resp:
2447
+ data = json.loads(resp.read().decode("utf-8"))
2448
+ v = data.get("version")
2449
+ if v:
2450
+ return str(v)
2451
+ except Exception:
2452
+ pass
2453
+ return None
2454
+
2455
+
2456
+ def handle_self_update() -> int:
2457
+ """Automatically detect installation method and update SpeiiDev."""
2458
+ console.print(f"\n[bold {THEME['primary']}]🔄 Checking for SpeiiDev updates…[/bold {THEME['primary']}]")
2459
+ latest = check_latest_version(timeout=2.5)
2460
+ if latest:
2461
+ console.print(
2462
+ f"[{THEME['dim']}]Current version:[/] [bold]{APP_VERSION}[/] "
2463
+ f"[{THEME['dim']}]Latest version:[/] [bold green]{latest}[/]"
2464
+ )
2465
+ if latest == APP_VERSION:
2466
+ console.print(f"[bold {THEME['ok']}]✅ SpeiiDev is already up to date ({APP_VERSION})![/]\n")
2467
+ return 0
2468
+ console.print(f"[{THEME['accent']}]🚀 Upgrading SpeiiDev {APP_VERSION} → {latest}…[/]")
2469
+ else:
2470
+ console.print(f"[{THEME['dim']}]Checking latest release packages…[/]")
2471
+
2472
+ is_npm = os.environ.get("SPEIIDEV_INSTALLER") == "npm" or "node_modules" in sys.argv[0]
2473
+ npm_bin = shutil.which("npm")
2474
+ pipx_bin = shutil.which("pipx")
2475
+
2476
+ updated = False
2477
+ if is_npm and npm_bin:
2478
+ console.print(f"[{THEME['dim']}]Updating via npm: npm install -g --force speiidev@latest[/]")
2479
+ res = subprocess.run([npm_bin, "install", "-g", "--force", "speiidev@latest"])
2480
+ updated = (res.returncode == 0)
2481
+ elif pipx_bin and "pipx" in sys.prefix:
2482
+ console.print(f"[{THEME['dim']}]Updating via pipx: pipx upgrade speiidev[/]")
2483
+ res = subprocess.run([pipx_bin, "upgrade", "speiidev"])
2484
+ updated = (res.returncode == 0)
2485
+ else:
2486
+ console.print(f"[{THEME['dim']}]Updating via pip: python -m pip install -U speiidev[/]")
2487
+ res = subprocess.run([sys.executable, "-m", "pip", "install", "-U", "speiidev"])
2488
+ if res.returncode != 0:
2489
+ console.print(f"[{THEME['dim']}]Retrying with --break-system-packages…[/]")
2490
+ res = subprocess.run([sys.executable, "-m", "pip", "install", "-U", "--break-system-packages", "speiidev"])
2491
+ updated = (res.returncode == 0)
2492
+
2493
+ if updated:
2494
+ console.print(f"\n[bold {THEME['ok']}]🎉 SpeiiDev successfully updated to {latest or 'latest'}![/bold {THEME['ok']}]")
2495
+ console.print(f"[{THEME['text']}]Please restart speiidev to run the new version.[/]\n")
2496
+ return 0
2497
+ else:
2498
+ console.print(f"\n[{THEME['err']}]❌ Automatic update encountered an issue. Run one of these manually:[/]")
2499
+ console.print(f" [{THEME['primary']}]npm install -g --force speiidev@latest[/]")
2500
+ console.print(f" [{THEME['primary']}]pipx install speiidev --force[/]")
2501
+ console.print(f" [{THEME['primary']}]pip install -U speiidev[/]\n")
2502
+ return 1
2503
+
2504
+
2425
2505
  def handle_rember(state: AgentState, arg: str = "") -> None:
2426
2506
  """List remembered chats or resume one by index/id."""
2427
2507
  entries = load_rember()
@@ -2625,6 +2705,8 @@ def interactive_repl(state: AgentState) -> int:
2625
2705
  return 0
2626
2706
  elif user_input == "/help":
2627
2707
  show_help()
2708
+ elif user_input == "/update":
2709
+ handle_self_update()
2628
2710
  elif user_input == "/clear":
2629
2711
  console.clear()
2630
2712
  print_banner(state)
@@ -2720,8 +2802,17 @@ def main() -> int:
2720
2802
  default="claude",
2721
2803
  help="Font style: claude (3D wireframe shadow) or mini (compact 2-line)",
2722
2804
  )
2805
+ parser.add_argument("-v", "--version", action="store_true", help="Print version and exit")
2806
+ parser.add_argument("--update", action="store_true", help="Update SpeiiDev to latest version and exit")
2723
2807
  args = parser.parse_args()
2724
2808
 
2809
+ if args.version:
2810
+ console.print(f"SpeiiDev v{APP_VERSION}")
2811
+ return 0
2812
+
2813
+ if args.update:
2814
+ return handle_self_update()
2815
+
2725
2816
  if args.font is not None:
2726
2817
  console.print(f"[bold {THEME['primary']}]{render_block_text(args.font, style=args.font_style, color='none')}[/]")
2727
2818
  return 0
package/tests/test_cli.py CHANGED
@@ -204,6 +204,22 @@ class TestCLIHelpers(unittest.TestCase):
204
204
  f"Stream view panel line width {len(line)} exceeded terminal width {w} at {w}x{h}",
205
205
  )
206
206
 
207
+ def test_version_flag(self):
208
+ from autodev.cli import main
209
+ import sys
210
+ old_argv = sys.argv
211
+ try:
212
+ sys.argv = ["speiidev", "--version"]
213
+ rc = main()
214
+ self.assertEqual(rc, 0)
215
+ finally:
216
+ sys.argv = old_argv
217
+
218
+ def test_slash_commands_include_update(self):
219
+ from autodev.cli import SLASH_COMMANDS
220
+ self.assertIn("/update", SLASH_COMMANDS)
221
+
207
222
 
208
223
  if __name__ == "__main__":
209
224
  unittest.main()
225
+