@phnx-labs/agents-cli 1.20.50 → 1.20.51

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 (82) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/commands/browser-picker.js +1 -18
  3. package/dist/commands/cloud.js +1 -25
  4. package/dist/commands/computer.d.ts +1 -0
  5. package/dist/commands/computer.js +129 -8
  6. package/dist/commands/exec.js +49 -6
  7. package/dist/commands/factory.js +1 -4
  8. package/dist/commands/inspect.js +1 -11
  9. package/dist/commands/mcp.js +2 -6
  10. package/dist/commands/message.js +1 -4
  11. package/dist/commands/profiles.js +1 -18
  12. package/dist/commands/repo.js +33 -14
  13. package/dist/commands/resource-view.d.ts +1 -0
  14. package/dist/commands/resource-view.js +5 -17
  15. package/dist/commands/secrets.d.ts +1 -0
  16. package/dist/commands/secrets.js +1 -28
  17. package/dist/commands/sessions-picker.js +1 -18
  18. package/dist/commands/sessions.js +6 -8
  19. package/dist/commands/teams-picker.js +1 -32
  20. package/dist/commands/teams.js +1 -27
  21. package/dist/commands/tmux.js +1 -3
  22. package/dist/commands/view.js +1 -9
  23. package/dist/commands/worktree.js +1 -4
  24. package/dist/lib/agents.d.ts +0 -4
  25. package/dist/lib/agents.js +20 -33
  26. package/dist/lib/auto-dispatch-linear.d.ts +18 -0
  27. package/dist/lib/auto-dispatch-linear.js +107 -0
  28. package/dist/lib/auto-dispatch-provider.d.ts +10 -0
  29. package/dist/lib/auto-dispatch-provider.js +25 -0
  30. package/dist/lib/auto-dispatch.d.ts +87 -0
  31. package/dist/lib/auto-dispatch.js +142 -0
  32. package/dist/lib/browser/cdp.js +11 -2
  33. package/dist/lib/browser/drivers/ssh.d.ts +28 -10
  34. package/dist/lib/browser/drivers/ssh.js +57 -18
  35. package/dist/lib/browser/refs.js +1 -5
  36. package/dist/lib/cli-resources.d.ts +0 -2
  37. package/dist/lib/cli-resources.js +30 -13
  38. package/dist/lib/cloud/rush.d.ts +0 -24
  39. package/dist/lib/cloud/rush.js +0 -31
  40. package/dist/lib/crabbox/cli.js +4 -1
  41. package/dist/lib/crabbox/lease.js +29 -1
  42. package/dist/lib/daemon.js +41 -0
  43. package/dist/lib/exec.js +31 -14
  44. package/dist/lib/format.d.ts +38 -0
  45. package/dist/lib/format.js +108 -0
  46. package/dist/lib/git.d.ts +21 -0
  47. package/dist/lib/git.js +92 -0
  48. package/dist/lib/hooks/cache.d.ts +9 -2
  49. package/dist/lib/hooks/cache.js +220 -8
  50. package/dist/lib/hooks.js +17 -8
  51. package/dist/lib/platform/exec.d.ts +4 -1
  52. package/dist/lib/platform/exec.js +8 -2
  53. package/dist/lib/resources.d.ts +0 -8
  54. package/dist/lib/resources.js +0 -10
  55. package/dist/lib/runner.js +10 -2
  56. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  57. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  58. package/dist/lib/session/active.d.ts +11 -1
  59. package/dist/lib/session/active.js +3 -0
  60. package/dist/lib/session/db.d.ts +1 -4
  61. package/dist/lib/session/db.js +20 -25
  62. package/dist/lib/session/discover.d.ts +2 -2
  63. package/dist/lib/session/discover.js +61 -48
  64. package/dist/lib/session/parse.js +35 -34
  65. package/dist/lib/session/render.d.ts +7 -3
  66. package/dist/lib/session/render.js +15 -9
  67. package/dist/lib/session/state.d.ts +55 -0
  68. package/dist/lib/session/state.js +87 -10
  69. package/dist/lib/session/types.d.ts +9 -0
  70. package/dist/lib/shims.d.ts +9 -3
  71. package/dist/lib/shims.js +44 -8
  72. package/dist/lib/ssh-tunnel.d.ts +33 -2
  73. package/dist/lib/ssh-tunnel.js +94 -7
  74. package/dist/lib/staleness/types.d.ts +0 -1
  75. package/dist/lib/types.d.ts +14 -1
  76. package/dist/lib/versions.d.ts +0 -26
  77. package/dist/lib/versions.js +2 -145
  78. package/dist/lib/warn-unpushed.d.ts +40 -0
  79. package/dist/lib/warn-unpushed.js +128 -0
  80. package/package.json +3 -1
  81. package/dist/lib/resources/index.d.ts +0 -53
  82. package/dist/lib/resources/index.js +0 -76
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Post-run guard against silently stranded work.
3
+ *
4
+ * A headless `agents run` in a writable mode can end with the agent having
5
+ * committed on a branch but never pushed it — the CLI's exit path does no git
6
+ * work, so those commits sit invisible in a worktree until someone audits the
7
+ * box. This module detects that state and prints a loud stderr warning with the
8
+ * exact push / PR commands. It is advisory only: it never pushes, never mutates
9
+ * the repo, and never throws (a non-repo cwd or any git error yields an inert
10
+ * result), so it can sit on the run's exit path without ever breaking it.
11
+ */
12
+ import { execFile } from 'child_process';
13
+ import { promisify } from 'util';
14
+ import chalk from 'chalk';
15
+ const execFileAsync = promisify(execFile);
16
+ // Unit-separator byte (0x1f) as the git-log field delimiter: it cannot occur in
17
+ // a commit subject, so splitting on it never truncates a subject with spaces.
18
+ const SEP = '\x1f';
19
+ /**
20
+ * Whether a just-finished run should be checked for stranded work: only
21
+ * writable modes can leave commits (plan is read-only), and only non-interactive
22
+ * runs need the warning (an interactive user sees their own shell). Centralizes
23
+ * the gate so every exit path in the run command applies it identically.
24
+ */
25
+ export function shouldWarnUnpushed(mode, interactive) {
26
+ return mode !== 'plan' && !interactive;
27
+ }
28
+ const INERT = { isRepo: false, branch: null, hasUpstream: false, unpushed: [] };
29
+ async function git(args, cwd) {
30
+ // These are all local, non-prompting reads, but the call sits on a run's exit
31
+ // path — a hard timeout guarantees a wedged git can never delay exit unbounded.
32
+ const { stdout } = await execFileAsync('git', args, { cwd, timeout: 5000 });
33
+ return stdout.trim();
34
+ }
35
+ /**
36
+ * Inspect `cwd` for commits on the current branch that have not reached any
37
+ * remote. Uses `git log --not --remotes` so it is correct even when the branch
38
+ * has no upstream set: commits already present on some `origin/*` ref are NOT
39
+ * reported (no false positive), and a never-pushed branch reports all its
40
+ * commits. Returns an inert result — never throws — for a non-repo cwd, a
41
+ * detached HEAD, or a repo with no remotes (nothing to push to).
42
+ */
43
+ export async function getUnpushedState(cwd) {
44
+ let branch;
45
+ try {
46
+ branch = await git(['rev-parse', '--abbrev-ref', 'HEAD'], cwd);
47
+ }
48
+ catch {
49
+ return INERT; // not a git repo
50
+ }
51
+ // Detached HEAD: agents commit on branches; nothing nameable to push.
52
+ if (!branch || branch === 'HEAD') {
53
+ return { isRepo: true, branch: null, hasUpstream: false, unpushed: [] };
54
+ }
55
+ // No remote configured -> `--remotes` matches nothing and would report the
56
+ // entire history as "unpushed". There's nowhere to push, so stay silent.
57
+ let hasRemote = false;
58
+ try {
59
+ hasRemote = (await git(['remote'], cwd)).length > 0;
60
+ }
61
+ catch {
62
+ hasRemote = false;
63
+ }
64
+ if (!hasRemote) {
65
+ return { isRepo: true, branch, hasUpstream: false, unpushed: [] };
66
+ }
67
+ let unpushed = [];
68
+ try {
69
+ // HEAD must precede `--not`: everything after `--not` is negated, so
70
+ // `--not --remotes HEAD` would negate HEAD too and always return empty.
71
+ const out = await git(['log', 'HEAD', '--not', '--remotes', `--pretty=format:%h${SEP}%s`], cwd);
72
+ unpushed = out
73
+ ? out.split('\n').map((line) => {
74
+ const idx = line.indexOf(SEP);
75
+ return idx === -1
76
+ ? { sha: line, subject: '' }
77
+ : { sha: line.slice(0, idx), subject: line.slice(idx + 1) };
78
+ })
79
+ : [];
80
+ }
81
+ catch {
82
+ return { isRepo: true, branch, hasUpstream: false, unpushed: [] };
83
+ }
84
+ let hasUpstream = false;
85
+ try {
86
+ await git(['rev-parse', '--abbrev-ref', '@{u}'], cwd);
87
+ hasUpstream = true;
88
+ }
89
+ catch {
90
+ hasUpstream = false;
91
+ }
92
+ return { isRepo: true, branch, hasUpstream, unpushed };
93
+ }
94
+ /**
95
+ * Render the warning for an unpushed state, or null when there is nothing to
96
+ * warn about. Split out from the printer so it is directly testable.
97
+ */
98
+ export function formatUnpushedWarning(state, cwd) {
99
+ if (!state.isRepo || !state.branch || state.unpushed.length === 0)
100
+ return null;
101
+ const n = state.unpushed.length;
102
+ const lines = [`\n⚠ agent left ${n} commit${n === 1 ? '' : 's'} on '${state.branch}' not pushed to any remote:`];
103
+ for (const c of state.unpushed.slice(0, 5))
104
+ lines.push(` ${c.sha} ${c.subject}`);
105
+ if (n > 5)
106
+ lines.push(` … and ${n - 5} more`);
107
+ const pushCmd = state.hasUpstream
108
+ ? `git -C "${cwd}" push`
109
+ : `git -C "${cwd}" push -u origin ${state.branch}`;
110
+ lines.push(` push them: ${pushCmd}`);
111
+ lines.push(` open a PR: gh pr create --head ${state.branch}`);
112
+ return lines.join('\n');
113
+ }
114
+ /**
115
+ * If the just-finished run left committed-but-unpushed work in `cwd`, print a
116
+ * loud stderr warning with the exact push / PR commands. Non-fatal by contract:
117
+ * any failure is swallowed so it can never break a run's exit path.
118
+ */
119
+ export async function warnUnpushedWork(cwd) {
120
+ try {
121
+ const warning = formatUnpushedWarning(await getUnpushedState(cwd), cwd);
122
+ if (warning)
123
+ process.stderr.write(chalk.yellow(warning) + '\n');
124
+ }
125
+ catch {
126
+ // Advisory only — never break the run over a warning.
127
+ }
128
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phnx-labs/agents-cli",
3
- "version": "1.20.50",
3
+ "version": "1.20.51",
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",
@@ -98,12 +98,14 @@
98
98
  "proper-lockfile": "4.1.2",
99
99
  "simple-git": "3.36.0",
100
100
  "smol-toml": "1.7.0",
101
+ "ws": "^8.21.0",
101
102
  "yaml": "2.9.0"
102
103
  },
103
104
  "devDependencies": {
104
105
  "@types/diff": "8.0.0",
105
106
  "@types/marked-terminal": "6.1.1",
106
107
  "@types/node": "26.1.0",
108
+ "@types/ws": "^8.18.1",
107
109
  "tsx": "4.23.0",
108
110
  "typescript": "6.0.3",
109
111
  "vitest": "4.1.9"
@@ -1,53 +0,0 @@
1
- /**
2
- * Unified resource system - exports all handlers and provides a registry.
3
- *
4
- * Usage:
5
- * import { handlers, getHandler } from './resources/index.js';
6
- * const cmds = handlers.commands.listAll('claude');
7
- */
8
- export * from './types.js';
9
- export { CommandsHandler, commandsHandler, type CommandItem } from './commands.js';
10
- export { HooksHandler, type HookItem } from './hooks.js';
11
- export { SkillsHandler, type SkillItem } from './skills.js';
12
- export { RulesHandler, type RuleItem } from './rules.js';
13
- export { McpHandler, getMcpConfigPath, type McpItem } from './mcp.js';
14
- export { PermissionsHandler, type PermissionItem } from './permissions.js';
15
- export { SubagentsHandler, subagentsHandler, type SubagentItem } from './subagents.js';
16
- export { WorkflowsHandler, type WorkflowItem } from './workflows.js';
17
- import type { ResourceKind, ResourceHandler } from './types.js';
18
- /** All resource handlers keyed by kind. */
19
- export declare const handlers: {
20
- readonly command: import("./commands.js").CommandsHandler;
21
- readonly commands: import("./commands.js").CommandsHandler;
22
- readonly hook: ResourceHandler<import("../types.js").ManifestHook>;
23
- readonly hooks: ResourceHandler<import("../types.js").ManifestHook>;
24
- readonly skill: ResourceHandler<import("./skills.js").SkillItem>;
25
- readonly skills: ResourceHandler<import("./skills.js").SkillItem>;
26
- readonly rule: ResourceHandler<import("./rules.js").RuleItem>;
27
- readonly rules: ResourceHandler<import("./rules.js").RuleItem>;
28
- readonly mcp: ResourceHandler<import("./mcp.js").McpItem>;
29
- readonly permission: ResourceHandler<import("../types.js").PermissionSet>;
30
- readonly permissions: ResourceHandler<import("../types.js").PermissionSet>;
31
- readonly subagent: import("./subagents.js").SubagentsHandler;
32
- readonly subagents: import("./subagents.js").SubagentsHandler;
33
- readonly workflow: {
34
- readonly kind: "workflow";
35
- listAll(_agent: import("./types.js").AgentId, cwd?: string): import("./types.js").ResolvedItem<import("./workflows.js").WorkflowItem>[];
36
- resolve(_agent: import("./types.js").AgentId, name: string, cwd?: string): import("./types.js").ResolvedItem<import("./workflows.js").WorkflowItem> | null;
37
- sync(_agent: import("./types.js").AgentId, _versionHome: string, _cwd?: string): void;
38
- format(_agent: import("./types.js").AgentId): "md";
39
- targetDir(_agent: import("./types.js").AgentId): string;
40
- };
41
- readonly workflows: {
42
- readonly kind: "workflow";
43
- listAll(_agent: import("./types.js").AgentId, cwd?: string): import("./types.js").ResolvedItem<import("./workflows.js").WorkflowItem>[];
44
- resolve(_agent: import("./types.js").AgentId, name: string, cwd?: string): import("./types.js").ResolvedItem<import("./workflows.js").WorkflowItem> | null;
45
- sync(_agent: import("./types.js").AgentId, _versionHome: string, _cwd?: string): void;
46
- format(_agent: import("./types.js").AgentId): "md";
47
- targetDir(_agent: import("./types.js").AgentId): string;
48
- };
49
- };
50
- /** Get a handler by resource kind. */
51
- export declare function getHandler(kind: ResourceKind): ResourceHandler<unknown> | null;
52
- /** All resource kinds. */
53
- export declare const RESOURCE_KINDS: ResourceKind[];
@@ -1,76 +0,0 @@
1
- /**
2
- * Unified resource system - exports all handlers and provides a registry.
3
- *
4
- * Usage:
5
- * import { handlers, getHandler } from './resources/index.js';
6
- * const cmds = handlers.commands.listAll('claude');
7
- */
8
- export * from './types.js';
9
- export { CommandsHandler, commandsHandler } from './commands.js';
10
- export { HooksHandler } from './hooks.js';
11
- export { SkillsHandler } from './skills.js';
12
- export { RulesHandler } from './rules.js';
13
- export { McpHandler, getMcpConfigPath } from './mcp.js';
14
- export { PermissionsHandler } from './permissions.js';
15
- export { SubagentsHandler, subagentsHandler } from './subagents.js';
16
- export { WorkflowsHandler } from './workflows.js';
17
- import { commandsHandler } from './commands.js';
18
- import { HooksHandler } from './hooks.js';
19
- import { SkillsHandler } from './skills.js';
20
- import { RulesHandler } from './rules.js';
21
- import { McpHandler } from './mcp.js';
22
- import { PermissionsHandler } from './permissions.js';
23
- import { subagentsHandler } from './subagents.js';
24
- import { WorkflowsHandler } from './workflows.js';
25
- /** All resource handlers keyed by kind. */
26
- export const handlers = {
27
- command: commandsHandler,
28
- commands: commandsHandler,
29
- hook: HooksHandler,
30
- hooks: HooksHandler,
31
- skill: SkillsHandler,
32
- skills: SkillsHandler,
33
- rule: RulesHandler,
34
- rules: RulesHandler,
35
- mcp: McpHandler,
36
- permission: PermissionsHandler,
37
- permissions: PermissionsHandler,
38
- subagent: subagentsHandler,
39
- subagents: subagentsHandler,
40
- workflow: WorkflowsHandler,
41
- workflows: WorkflowsHandler,
42
- };
43
- /** Get a handler by resource kind. */
44
- export function getHandler(kind) {
45
- switch (kind) {
46
- case 'command':
47
- return commandsHandler;
48
- case 'hook':
49
- return HooksHandler;
50
- case 'skill':
51
- return SkillsHandler;
52
- case 'rule':
53
- return RulesHandler;
54
- case 'mcp':
55
- return McpHandler;
56
- case 'permission':
57
- return PermissionsHandler;
58
- case 'subagent':
59
- return subagentsHandler;
60
- case 'workflow':
61
- return WorkflowsHandler;
62
- default:
63
- return null;
64
- }
65
- }
66
- /** All resource kinds. */
67
- export const RESOURCE_KINDS = [
68
- 'command',
69
- 'hook',
70
- 'skill',
71
- 'rule',
72
- 'mcp',
73
- 'permission',
74
- 'subagent',
75
- 'workflow',
76
- ];