@brimveyn/aimux 1.20.1 → 1.20.2

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 (43) hide show
  1. package/README.md +9 -1
  2. package/package.json +1 -1
  3. package/skills/aimux-orchestrator/SKILL.md +39 -5
  4. package/skills/aimux-orchestrator/references/prompts.md +11 -0
  5. package/src/cli/client/workspace-resolver.ts +61 -8
  6. package/src/cli/commands/tab/await.ts +1 -1
  7. package/src/cli/commands/tab/close.ts +1 -1
  8. package/src/cli/commands/tab/create.ts +27 -3
  9. package/src/cli/commands/tab/focus.ts +1 -1
  10. package/src/cli/commands/tab/prompt-io.ts +6 -1
  11. package/src/cli/commands/tab/run.ts +10 -2
  12. package/src/cli/commands/tab/send.ts +12 -7
  13. package/src/cli/commands/tab/snapshot.ts +2 -1
  14. package/src/cli/commands/tab/tail.ts +1 -1
  15. package/src/cli/commands/tab/wait.ts +2 -1
  16. package/src/cli/commands/worker/await.ts +5 -4
  17. package/src/cli/commands/worker/doctor.ts +25 -1
  18. package/src/cli/commands/worker/list.ts +50 -8
  19. package/src/cli/commands/worker/prompt.ts +31 -6
  20. package/src/cli/commands/worker/run.ts +56 -10
  21. package/src/cli/commands/worker/shared.ts +312 -52
  22. package/src/cli/commands/worker/stop.ts +39 -10
  23. package/src/cli/commands/worker/submit.ts +40 -0
  24. package/src/cli/commands/workspace/close.ts +1 -1
  25. package/src/cli/commands/workspace/create.ts +2 -1
  26. package/src/cli/commands/workspace/switch.ts +1 -1
  27. package/src/cli/commands/worktree/create-core.ts +27 -7
  28. package/src/cli/commands/worktree/create.ts +18 -3
  29. package/src/cli/commands/worktree/remove.ts +3 -1
  30. package/src/cli/completion/entry.ts +181 -0
  31. package/src/cli/completion/install.ts +222 -0
  32. package/src/cli/completion/plan.ts +216 -0
  33. package/src/cli/completion/scripts.ts +147 -0
  34. package/src/cli/completion/sources.ts +74 -0
  35. package/src/cli/context.ts +15 -0
  36. package/src/cli/flags.ts +45 -2
  37. package/src/cli/index.ts +20 -10
  38. package/src/cli/output.ts +3 -0
  39. package/src/cli/registry.ts +2 -0
  40. package/src/doctor.ts +4 -0
  41. package/src/git/worktree.ts +15 -1
  42. package/src/index.tsx +35 -11
  43. package/src/platform/worktree-paths.ts +21 -1
package/src/index.tsx CHANGED
@@ -1,16 +1,20 @@
1
1
  #!/usr/bin/env bun
2
- import { runDaemon } from './daemon/daemon'
3
- import { getRuntimeProfile } from './daemon/runtime-paths'
4
- import { logDebug } from './debug/input-log'
5
- import { runDoctor } from './doctor'
6
- import { runRestartDaemon } from './restart-daemon'
7
- import { runRestartTerminalManager } from './restart-terminal-manager'
8
- import { createSessionBackend } from './session-backend/bootstrap'
9
- import { runTerminalManager } from './terminal-manager/terminal-manager'
10
- import { runUpdate } from './update'
11
-
2
+ // Every branch below imports dynamically ON PURPOSE. Static imports here would
3
+ // be paid by every invocation — including `aimux __complete`, which runs on
4
+ // each TAB press and must stay well under a keystroke's worth of latency.
12
5
  const command = process.argv[2]
13
- const runtimeProfile = getRuntimeProfile()
6
+
7
+ // Shell completion. First branch and cheapest: it loads the CLI registry and
8
+ // nothing else — no daemon client, no session backend, no renderer.
9
+ if (command === '__complete') {
10
+ const { runComplete } = await import('./cli/completion/entry')
11
+ process.exit(await runComplete(process.argv.slice(3)))
12
+ }
13
+
14
+ if (command === 'completion') {
15
+ const { runCompletion } = await import('./cli/completion/entry')
16
+ process.exit(runCompletion(process.argv.slice(3)))
17
+ }
14
18
 
15
19
  // CLI control plane (docs/reference/cli.md). Branch BEFORE the UI bootstrap so
16
20
  // `aimux tab list` from a non-TTY shell never spins up the React renderer.
@@ -28,28 +32,38 @@ if (command === '--version' || command === '-v' || command === 'version') {
28
32
  }
29
33
 
30
34
  if (command === 'doctor' || command === '--doctor') {
35
+ const { runDoctor } = await import('./doctor')
31
36
  process.exit(runDoctor())
32
37
  }
33
38
 
34
39
  if (command === 'restart-daemon') {
40
+ const { runRestartDaemon } = await import('./restart-daemon')
35
41
  process.exit(await runRestartDaemon())
36
42
  }
37
43
 
38
44
  if (command === 'restart-terminal-manager') {
45
+ const { runRestartTerminalManager } = await import('./restart-terminal-manager')
39
46
  process.exit(await runRestartTerminalManager())
40
47
  }
41
48
 
42
49
  if (command === 'update') {
50
+ const { runUpdate } = await import('./update')
43
51
  process.exit(await runUpdate())
44
52
  }
45
53
 
54
+ const { logDebug } = await import('./debug/input-log')
55
+ const { getRuntimeProfile } = await import('./daemon/runtime-paths')
56
+ const runtimeProfile = getRuntimeProfile()
57
+
46
58
  if (command === 'daemon') {
47
59
  logDebug('index.daemonMode', { runtimeProfile })
60
+ const { runDaemon } = await import('./daemon/daemon')
48
61
  await runDaemon()
49
62
  }
50
63
 
51
64
  if (command === 'terminal-manager') {
52
65
  logDebug('index.terminalManagerMode', { runtimeProfile })
66
+ const { runTerminalManager } = await import('./terminal-manager/terminal-manager')
53
67
  await runTerminalManager()
54
68
  }
55
69
 
@@ -65,6 +79,8 @@ const [
65
79
  { loadUserConfig },
66
80
  { BreakingUpdateScreen },
67
81
  { setHostPalette },
82
+ { createSessionBackend },
83
+ { maybeAutoInstallCompletion },
68
84
  ] = await Promise.all([
69
85
  import('@opentui/core'),
70
86
  import('@opentui/react'),
@@ -72,9 +88,17 @@ const [
72
88
  import('./config/loader'),
73
89
  import('./ui/breaking-update-screen'),
74
90
  import('./ui/host-palette'),
91
+ import('./session-backend/bootstrap'),
92
+ import('./cli/completion/install'),
75
93
  ])
76
94
  const resolvedConfig = await loadUserConfig()
77
95
 
96
+ // First launch (and after every upgrade): drop the shell completion script in
97
+ // the conventional location for $SHELL. Silent and best-effort — it writes one
98
+ // file, never a dotfile, and never blocks the TUI. Opt out with
99
+ // AIMUX_NO_COMPLETION_INSTALL=1; `aimux doctor` reports what landed where.
100
+ maybeAutoInstallCompletion()
101
+
78
102
  const renderer = await createCliRenderer({
79
103
  autoFocus: true,
80
104
  // Transparent clear color so cells untouched by BoxRenderable paints (e.g.
@@ -1,5 +1,5 @@
1
1
  import { createHash } from 'node:crypto'
2
- import { lstat, mkdir, realpath } from 'node:fs/promises'
2
+ import { lstat, mkdir, realpath, rmdir } from 'node:fs/promises'
3
3
  import { join, resolve } from 'node:path'
4
4
 
5
5
  const DEFAULT_WORKTREE_ROOT = '/tmp/aimux-wt'
@@ -55,6 +55,26 @@ export async function ensureAimuxWorktreeRoot(): Promise<string> {
55
55
  return root
56
56
  }
57
57
 
58
+ /**
59
+ * Drop the repo-scoped `<root>/r-<hash>` parent of a removed worktree when it is
60
+ * empty. `assertSafeAimuxWorktreePath` creates that directory before git runs,
61
+ * so a failed or rolled-back creation used to leave one behind forever. Silent
62
+ * on ENOTEMPTY (another worktree for the same repo is still live) and on any
63
+ * other error — this is housekeeping, never the point of the operation.
64
+ */
65
+ export async function pruneEmptyWorktreeParent(worktreePath: string): Promise<void> {
66
+ if (!isInsideAimuxWorktreeRoot(worktreePath)) return
67
+ const parent = resolve(worktreePath, '..')
68
+ // Never touch the root itself — only the per-repo directory below it.
69
+ if (parent === resolve(getAimuxWorktreeRoot())) return
70
+ if (!isInsideAimuxWorktreeRoot(parent)) return
71
+ try {
72
+ await rmdir(parent)
73
+ } catch {
74
+ // Non-empty (a sibling worktree is live) or already gone. Either is fine.
75
+ }
76
+ }
77
+
58
78
  export async function assertSafeAimuxWorktreePath(path: string): Promise<void> {
59
79
  const root = await ensureAimuxWorktreeRoot()
60
80
  if (!isInsideAimuxWorktreeRoot(path)) {