@phnx-labs/agents-cli 1.20.91 → 1.20.92

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 (68) hide show
  1. package/CHANGELOG.md +155 -0
  2. package/README.md +1 -1
  3. package/dist/bin/agents +0 -0
  4. package/dist/commands/activity.d.ts +72 -6
  5. package/dist/commands/activity.js +198 -49
  6. package/dist/commands/beta.js +1 -0
  7. package/dist/commands/doctor.js +4 -2
  8. package/dist/commands/exec.d.ts +14 -0
  9. package/dist/commands/exec.js +144 -14
  10. package/dist/commands/projects.d.ts +12 -0
  11. package/dist/commands/projects.js +358 -0
  12. package/dist/commands/sessions-picker.d.ts +15 -0
  13. package/dist/commands/sessions-picker.js +37 -12
  14. package/dist/commands/sessions-resume.d.ts +2 -0
  15. package/dist/commands/sessions-resume.js +9 -1
  16. package/dist/commands/sessions.d.ts +10 -5
  17. package/dist/commands/sessions.js +65 -27
  18. package/dist/index.js +2 -1
  19. package/dist/lib/activity.d.ts +69 -12
  20. package/dist/lib/activity.js +417 -74
  21. package/dist/lib/beta.d.ts +1 -1
  22. package/dist/lib/beta.js +1 -1
  23. package/dist/lib/devices/registry.d.ts +14 -0
  24. package/dist/lib/devices/registry.js +37 -0
  25. package/dist/lib/feed-post.js +8 -2
  26. package/dist/lib/hosts/remote-cmd.js +4 -0
  27. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  28. package/dist/lib/menubar/install-menubar.d.ts +14 -4
  29. package/dist/lib/menubar/install-menubar.js +20 -6
  30. package/dist/lib/project-key.d.ts +44 -0
  31. package/dist/lib/project-key.js +79 -0
  32. package/dist/lib/project-root.js +16 -0
  33. package/dist/lib/project-status.d.ts +69 -0
  34. package/dist/lib/project-status.js +101 -0
  35. package/dist/lib/projects.d.ts +138 -0
  36. package/dist/lib/projects.js +301 -0
  37. package/dist/lib/remote-agents-json.d.ts +9 -0
  38. package/dist/lib/remote-agents-json.js +11 -5
  39. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  40. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  41. package/dist/lib/session/bash-command.d.ts +53 -0
  42. package/dist/lib/session/bash-command.js +364 -0
  43. package/dist/lib/session/digest.d.ts +6 -0
  44. package/dist/lib/session/digest.js +19 -0
  45. package/dist/lib/session/relative-time.d.ts +23 -0
  46. package/dist/lib/session/relative-time.js +60 -8
  47. package/dist/lib/session/remote-list.js +5 -2
  48. package/dist/lib/session/render.d.ts +2 -9
  49. package/dist/lib/session/render.js +25 -56
  50. package/dist/lib/ssh-exec.d.ts +6 -0
  51. package/dist/lib/ssh-exec.js +10 -1
  52. package/dist/lib/startup/command-registry.d.ts +1 -0
  53. package/dist/lib/startup/command-registry.js +2 -0
  54. package/dist/lib/state.d.ts +2 -0
  55. package/dist/lib/state.js +5 -0
  56. package/dist/lib/terminal/backends/index.d.ts +10 -2
  57. package/dist/lib/terminal/backends/index.js +14 -2
  58. package/dist/lib/terminal/backends/terminal-app.d.ts +13 -0
  59. package/dist/lib/terminal/backends/terminal-app.js +73 -0
  60. package/dist/lib/terminal/index.d.ts +2 -1
  61. package/dist/lib/terminal/index.js +2 -1
  62. package/dist/lib/terminal/preferred.d.ts +89 -0
  63. package/dist/lib/terminal/preferred.js +87 -0
  64. package/dist/lib/terminal/run-surface.d.ts +82 -0
  65. package/dist/lib/terminal/run-surface.js +146 -0
  66. package/dist/lib/terminal/types.d.ts +1 -1
  67. package/dist/lib/types.d.ts +1 -1
  68. package/package.json +2 -1
@@ -0,0 +1,146 @@
1
+ import { BACKENDS } from './backends/index.js';
2
+ import { openSurface } from './engine.js';
3
+ import { getCliLaunch } from '../cli-entry.js';
4
+ import { shellQuote } from './quote.js';
5
+ import { resolveLaunchBackend, describeBackendChoice, } from './preferred.js';
6
+ /** Backends a user may name in `--terminal <backend>`. */
7
+ export const TERMINAL_FLAG_BACKENDS = Object.keys(BACKENDS);
8
+ /**
9
+ * Validate a `--terminal <value>`. Returns the backend, or an error message
10
+ * naming the valid ids — never a silent fallback to auto-detection, which would
11
+ * open a terminal the user did not ask for.
12
+ */
13
+ export function parseTerminalFlag(value) {
14
+ if (value === undefined || value === true || value === '')
15
+ return {};
16
+ const raw = String(value);
17
+ if (TERMINAL_FLAG_BACKENDS.includes(raw))
18
+ return { backend: raw };
19
+ // `--terminal [backend]` takes an OPTIONAL value, and commander assigns the
20
+ // next non-option token to it — so `agents run claude --terminal "fix the bug"`
21
+ // lands the prompt here. Say that, or the user just sees their prompt called a
22
+ // bad backend name and has no idea why.
23
+ const looksLikeAPrompt = /\s/.test(raw) || raw.length > 24;
24
+ const hint = looksLikeAPrompt
25
+ ? ` That looks like a prompt: put it BEFORE the flag — agents run <agent> "${raw.length > 40 ? `${raw.slice(0, 40)}…` : raw}" --terminal.`
26
+ : '';
27
+ return {
28
+ error: `Unknown --terminal backend '${raw}'. Use one of: ${TERMINAL_FLAG_BACKENDS.join(', ')} (or pass --terminal alone to auto-detect).${hint}`,
29
+ };
30
+ }
31
+ /**
32
+ * The argv to re-invoke, with `--terminal` (and the value commander consumed for
33
+ * it) removed. `consumedValue` is the parsed option value when it is a string —
34
+ * that is the only token after the flag that belongs to it, so a prompt or a
35
+ * following flag is never eaten.
36
+ */
37
+ export function stripTerminalFlag(argv, consumedValue) {
38
+ const out = [];
39
+ for (let i = 0; i < argv.length; i++) {
40
+ const tok = argv[i];
41
+ // Everything past a bare `--` is forwarded verbatim to the agent's own CLI
42
+ // (`agents run kimi -- --terminal`), so it is not ours to rewrite.
43
+ if (tok === '--') {
44
+ out.push(...argv.slice(i));
45
+ break;
46
+ }
47
+ if (tok === '--terminal') {
48
+ if (consumedValue !== undefined && argv[i + 1] === consumedValue)
49
+ i++;
50
+ continue;
51
+ }
52
+ if (tok.startsWith('--terminal='))
53
+ continue;
54
+ out.push(tok);
55
+ }
56
+ return out;
57
+ }
58
+ /** `agents run …` as a shell-safe command line for the surface to exec. */
59
+ export function buildRunCommand(argv) {
60
+ const { command, args } = getCliLaunch(argv);
61
+ return [command, ...args].map(shellQuote);
62
+ }
63
+ /**
64
+ * Turn live sessions into the samples the resolver reads, filling in the app
65
+ * each tmux-hosted session is currently VIEWED in.
66
+ *
67
+ * This step is what makes detection work for the common case: `agents run`
68
+ * wraps interactive runs in tmux, so a session the user started in Ghostty is
69
+ * attributed `host: 'tmux'` on the discovery path and would otherwise name no
70
+ * terminal at all. `resolveViewingIn` walks the attached tmux client's pid up to
71
+ * its host app — the same resolver `agents sessions` uses to print
72
+ * "viewing in Ghostty tab 2". Sessions that are detached (no client attached)
73
+ * legitimately have no viewer and keep their `tmux` host.
74
+ *
75
+ * One inherited nuance: `resolveViewingIn` labels a client whose app it cannot
76
+ * identify `'terminal'` (viewing-in.ts:87), so a tmux session viewed from an
77
+ * unrecognized emulator resolves to Terminal.app rather than falling through.
78
+ * That lands on the same every-Mac floor the fallback chain ends at anyway, so
79
+ * it costs nothing here — but it is a default, not a detection.
80
+ *
81
+ * Best-effort: any probe failure degrades to the plain host, never throws.
82
+ */
83
+ export async function toHostSamples(sessions) {
84
+ const samples = sessions.map((s) => ({
85
+ host: s.host,
86
+ lastActivityMs: s.lastActivityMs,
87
+ startedAtMs: s.startedAtMs,
88
+ }));
89
+ const tmuxIdx = sessions
90
+ .map((s, i) => ({ s, i }))
91
+ .filter(({ s }) => s.provenance?.mux?.kind === 'tmux' && s.provenance.mux.pane);
92
+ if (tmuxIdx.length === 0)
93
+ return samples;
94
+ try {
95
+ const { enumerateGhosttyTabs } = await import('../session/ghostty-tabs.js');
96
+ const { mapPanesToTargets, listClients } = await import('../tmux/session.js');
97
+ const { resolveViewingIn } = await import('../session/viewing-in.js');
98
+ // One Ghostty enumeration shared across sockets, as the sessions renderer does.
99
+ const ghosttySurfaces = await enumerateGhosttyTabs();
100
+ const sockets = new Set(tmuxIdx.map(({ s }) => s.provenance.mux.socket));
101
+ for (const socket of sockets) {
102
+ const paneToTarget = await mapPanesToTargets(socket);
103
+ if (paneToTarget.size === 0)
104
+ continue;
105
+ const clients = await listClients(socket);
106
+ for (const { s, i } of tmuxIdx) {
107
+ if (s.provenance.mux.socket !== socket)
108
+ continue;
109
+ const viewing = await resolveViewingIn(s, clients, { paneToTarget, ghosttySurfaces });
110
+ if (viewing)
111
+ samples[i].viewingApp = viewing.app;
112
+ }
113
+ }
114
+ }
115
+ catch {
116
+ // tmux/Ghostty probes are best-effort; fall back to the plain host values.
117
+ }
118
+ return samples;
119
+ }
120
+ /**
121
+ * Open the run as a tab in the resolved terminal. Never throws — a failure comes
122
+ * back as `ok: false` with the reason, so the caller can tell the user rather
123
+ * than exiting silently.
124
+ */
125
+ export async function openRunInTerminal(params) {
126
+ const choice = params.forced
127
+ ? { backend: params.forced, source: 'forced' }
128
+ : resolveLaunchBackend(params.ctx, params.sessions);
129
+ if (!choice) {
130
+ return {
131
+ ok: false,
132
+ error: 'No terminal this machine can drive (need iTerm, Ghostty, Terminal.app, VSCodium, or a tmux session). Run without --terminal.',
133
+ };
134
+ }
135
+ if (params.forced && !BACKENDS[choice.backend].isAvailable(params.ctx)) {
136
+ return { ok: false, error: `--terminal ${choice.backend} is not available here.` };
137
+ }
138
+ const command = buildRunCommand(stripTerminalFlag(params.argv, params.consumedValue));
139
+ const result = await openSurface({
140
+ backend: choice.backend,
141
+ layout: 'tab',
142
+ cwd: params.cwd,
143
+ command,
144
+ });
145
+ return { ok: result.ok, choice, description: describeBackendChoice(choice), error: result.error };
146
+ }
@@ -8,7 +8,7 @@
8
8
  * is attended and live. See docs/terminal-engine.md.
9
9
  */
10
10
  /** An interactive terminal backend the engine can drive. */
11
- export type Backend = 'iterm' | 'ghostty' | 'tmux' | 'vscodium-agent';
11
+ export type Backend = 'iterm' | 'ghostty' | 'tmux' | 'vscodium-agent' | 'terminal';
12
12
  /** Which way a split pane grows. `right` = side-by-side; `down` = stacked. */
13
13
  export type SplitDirection = 'right' | 'down';
14
14
  /** Where a surface lands: a new tab, or a split of the current pane. */
@@ -62,7 +62,7 @@ export interface BudgetConfig {
62
62
  require_confirm_over?: number;
63
63
  }
64
64
  /** Preview features that users can opt into via `agents beta`. */
65
- export type BetaFeatureName = 'drive' | 'factory' | 'session-sync';
65
+ export type BetaFeatureName = 'drive' | 'factory' | 'session-sync' | 'projects';
66
66
  /** Subset of chalk color names used for agent-specific terminal output. */
67
67
  export type ChalkColor = 'magenta' | 'green' | 'blue' | 'cyan' | 'yellowBright' | 'redBright' | 'whiteBright' | 'blueBright' | 'greenBright' | 'magentaBright' | 'cyanBright';
68
68
  /** Static configuration for a single agent -- paths, capabilities, and format conventions. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phnx-labs/agents-cli",
3
- "version": "1.20.91",
3
+ "version": "1.20.92",
4
4
  "description": "One CLI for all your AI coding agents - versions, config, cloud dispatch, sessions, and teams (now with first-class Grok Build CLI support)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -99,6 +99,7 @@
99
99
  "marked-terminal": "7.3.0",
100
100
  "ora": "9.4.1",
101
101
  "proper-lockfile": "4.1.2",
102
+ "shlex": "3.0.0",
102
103
  "simple-git": "3.36.0",
103
104
  "smol-toml": "1.7.0",
104
105
  "ws": "^8.21.0",