@phnx-labs/agents-cli 1.22.69 → 1.22.70

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 (64) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +3 -8
  3. package/dist/commands/accounts.js +23 -7
  4. package/dist/commands/exec.js +2 -2
  5. package/dist/commands/import.js +2 -2
  6. package/dist/commands/models.js +2 -2
  7. package/dist/commands/permissions.js +2 -2
  8. package/dist/commands/rules.js +1 -1
  9. package/dist/commands/send.js +9 -3
  10. package/dist/commands/traces.js +1 -1
  11. package/dist/lib/account-capabilities.js +0 -2
  12. package/dist/lib/account-registry.d.ts +3 -3
  13. package/dist/lib/account-registry.js +25 -7
  14. package/dist/lib/acp/client.d.ts +1 -1
  15. package/dist/lib/acp/client.js +12 -1
  16. package/dist/lib/acp/harnesses.js +1 -1
  17. package/dist/lib/add-dir.js +0 -2
  18. package/dist/lib/agent-cli-commands.js +0 -2
  19. package/dist/lib/agent-spec/agents.d.ts +1 -1
  20. package/dist/lib/agent-spec/agents.js +2 -83
  21. package/dist/lib/browser/ipc.js +9 -1
  22. package/dist/lib/browser/remote-control.d.ts +6 -3
  23. package/dist/lib/browser/remote-control.js +6 -3
  24. package/dist/lib/browser/service.d.ts +4 -1
  25. package/dist/lib/browser/service.js +7 -1
  26. package/dist/lib/channels/registry.d.ts +6 -0
  27. package/dist/lib/channels/send.d.ts +11 -2
  28. package/dist/lib/channels/send.js +11 -2
  29. package/dist/lib/cloud/rush.d.ts +10 -2
  30. package/dist/lib/cloud/rush.js +15 -9
  31. package/dist/lib/daemon/daemon.js +28 -8
  32. package/dist/lib/daemon/runner.js +6 -2
  33. package/dist/lib/exec.d.ts +2 -2
  34. package/dist/lib/exec.js +3 -34
  35. package/dist/lib/feed-broadcast.d.ts +12 -29
  36. package/dist/lib/feed-broadcast.js +28 -27
  37. package/dist/lib/hooks/install.js +0 -87
  38. package/dist/lib/installations/strategies.js +1 -1
  39. package/dist/lib/mcp-registry.js +0 -13
  40. package/dist/lib/mcp.js +2 -2
  41. package/dist/lib/model-tiers.js +1 -1
  42. package/dist/lib/models.js +0 -63
  43. package/dist/lib/notify.d.ts +11 -0
  44. package/dist/lib/notify.js +17 -4
  45. package/dist/lib/owner-message.d.ts +46 -3
  46. package/dist/lib/owner-message.js +26 -6
  47. package/dist/lib/permissions-registry.d.ts +0 -2
  48. package/dist/lib/permissions-registry.js +3 -50
  49. package/dist/lib/permissions.d.ts +3 -17
  50. package/dist/lib/permissions.js +4 -73
  51. package/dist/lib/rush-session.d.ts +19 -0
  52. package/dist/lib/rush-session.js +24 -0
  53. package/dist/lib/secrets/drivers/rush.js +2 -1
  54. package/dist/lib/session/cloud.js +2 -1
  55. package/dist/lib/sink-format.d.ts +34 -0
  56. package/dist/lib/sink-format.js +17 -0
  57. package/dist/lib/staleness/detectors/permissions.js +0 -20
  58. package/dist/lib/staleness/writers/commands.js +1 -1
  59. package/dist/lib/staleness/writers/hooks.js +2 -2
  60. package/dist/lib/subagents-registry.js +2 -12
  61. package/dist/lib/subagents.d.ts +0 -10
  62. package/dist/lib/subagents.js +0 -37
  63. package/dist/lib/types.d.ts +1 -1
  64. package/package.json +1 -1
@@ -13,6 +13,7 @@ import * as fs from 'fs';
13
13
  import * as os from 'os';
14
14
  import * as path from 'path';
15
15
  import * as yaml from 'yaml';
16
+ import { isRushSessionExpired } from '../../rush-session.js';
16
17
  const PROXY_BASE = 'https://api.prix.dev';
17
18
  const USER_YAML = path.join(os.homedir(), '.rush', 'user.yaml');
18
19
  const BUNDLE_ENDPOINT = '/api/v1/secrets/bundles';
@@ -27,7 +28,7 @@ function readRushToken() {
27
28
  throw new Error('No session token in ~/.rush/user.yaml. Run `rush login` first.');
28
29
  }
29
30
  const expiresAt = data.session?.expires_at;
30
- if (typeof expiresAt === 'number' && expiresAt <= Date.now() / 1000) {
31
+ if (isRushSessionExpired(expiresAt)) {
31
32
  const expiredAt = new Date(expiresAt * 1000).toISOString();
32
33
  throw new Error(`Rush session expired at ${expiredAt}. Run \`rush login\` to refresh.`);
33
34
  }
@@ -15,6 +15,7 @@ import * as os from 'os';
15
15
  import * as yaml from 'yaml';
16
16
  import { deriveShortId } from '../text/short-id.js';
17
17
  import { getCacheDir } from '../state.js';
18
+ import { isRushSessionExpired } from '../rush-session.js';
18
19
  const PROXY_BASE = process.env.RUSH_PROXY_BASE ?? 'https://api.prix.dev';
19
20
  const USER_YAML = path.join(os.homedir(), '.rush', 'user.yaml');
20
21
  const CLOUD_CACHE_DIR = path.join(getCacheDir(), 'cloud-runs');
@@ -30,7 +31,7 @@ function readToken() {
30
31
  throw new Error('No session token in ~/.rush/user.yaml. Run `rush login` first.');
31
32
  }
32
33
  const expiresAt = data.session?.expires_at;
33
- if (typeof expiresAt === 'number' && expiresAt <= Date.now() / 1000) {
34
+ if (isRushSessionExpired(expiresAt)) {
34
35
  const expiredAt = new Date(expiresAt * 1000).toISOString();
35
36
  throw new Error(`Rush session expired at ${expiredAt}. Run \`rush login\` to refresh.`);
36
37
  }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * How loudly a destination can render a link — the one decision that shapes the
3
+ * shared owner/broadcast `{message}` per channel (PHNX-3698).
4
+ *
5
+ * - `mrkdwn` — Slack, which renders `<url|label>` as blue tappable text. The
6
+ * session crumb and every ticket key the prose NAMES become inline labeled
7
+ * links, so nothing rides a trailing naked-URL line.
8
+ * - `plain` — iMessage, the owner-scoped rush message, a spawned `command:`
9
+ * sink, desktop banners: none can render a labeled link and a dumped naked
10
+ * URL reads as noise, so the message stays the human sentence with no URLs.
11
+ *
12
+ * The default is `plain`; only Slack opts into `mrkdwn`.
13
+ *
14
+ * This lives in its own tiny module because both the feed broadcast fan-out
15
+ * (`feed-broadcast.ts`) and the owner delivery fan-out (`notify.ts`) key on it,
16
+ * and those two modules already import each other — a shared symbol here keeps
17
+ * the format decision a single source of truth without a circular import.
18
+ */
19
+ export type SinkMessageFormat = 'plain' | 'mrkdwn';
20
+ /**
21
+ * Only Slack renders `<url|label>`, so it is the one format that gets labeled
22
+ * links. iMessage / owner-scoped rush / command / desktop sinks stay `plain`
23
+ * (they can't turn `claude/6fc1db18` blue, and dumping the raw URL is worse than
24
+ * leaving the crumb unlinked — PHNX-3698).
25
+ *
26
+ * The argument is the **resolved provider name**, not a sink's declared channel:
27
+ * an operator can point an arbitrary channel name at the Slack provider through
28
+ * `notify.transports` (e.g. `eng-alerts -> slack`), and delivery keys off that
29
+ * resolved provider (`lookupTransport`), so the format decision must too —
30
+ * otherwise an aliased Slack sink would compose plain while delivering to Slack,
31
+ * or a name remapped AWAY from Slack would emit `<url|label>` markup a non-Slack
32
+ * transport shows literally.
33
+ */
34
+ export declare function sinkMessageFormat(provider: string | undefined): SinkMessageFormat;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Only Slack renders `<url|label>`, so it is the one format that gets labeled
3
+ * links. iMessage / owner-scoped rush / command / desktop sinks stay `plain`
4
+ * (they can't turn `claude/6fc1db18` blue, and dumping the raw URL is worse than
5
+ * leaving the crumb unlinked — PHNX-3698).
6
+ *
7
+ * The argument is the **resolved provider name**, not a sink's declared channel:
8
+ * an operator can point an arbitrary channel name at the Slack provider through
9
+ * `notify.transports` (e.g. `eng-alerts -> slack`), and delivery keys off that
10
+ * resolved provider (`lookupTransport`), so the format decision must too —
11
+ * otherwise an aliased Slack sink would compose plain while delivering to Slack,
12
+ * or a name remapped AWAY from Slack would emit `<url|label>` markup a non-Slack
13
+ * transport shows literally.
14
+ */
15
+ export function sinkMessageFormat(provider) {
16
+ return provider?.trim().toLowerCase() === 'slack' ? 'mrkdwn' : 'plain';
17
+ }
@@ -202,25 +202,6 @@ function buildDroidDetector() {
202
202
  },
203
203
  };
204
204
  }
205
- function buildKiroDetector() {
206
- return {
207
- kind: 'permissions',
208
- agent: 'kiro',
209
- list({ versionHome }) {
210
- const permissionsPath = path.join(versionHome, '.kiro', 'settings', 'permissions.yaml');
211
- if (!fs.existsSync(permissionsPath))
212
- return [];
213
- try {
214
- const config = yaml.parse(fs.readFileSync(permissionsPath, 'utf-8'));
215
- if (config && Array.isArray(config.rules) && config.rules.length > 0) {
216
- return discoverPermissionGroups().map(g => g.name);
217
- }
218
- }
219
- catch { /* parse fail */ }
220
- return [];
221
- },
222
- };
223
- }
224
205
  function buildOpenClawDetector() {
225
206
  return {
226
207
  kind: 'permissions',
@@ -296,7 +277,6 @@ const handlers = {
296
277
  kimi: buildKimiDetector,
297
278
  cursor: buildCursorDetector,
298
279
  droid: buildDroidDetector,
299
- kiro: buildKiroDetector,
300
280
  openclaw: buildOpenClawDetector,
301
281
  copilot: buildCopilotDetector,
302
282
  hermes: buildHermesDetector,
@@ -13,7 +13,7 @@
13
13
  *
14
14
  * - native command file — `{agentDir}/<commandsSubdir>/<name>.md` (or .toml
15
15
  * when the agent's format is toml). Standard path for Claude, Codex
16
- * < 0.117.0, Cursor, OpenCode, Copilot, Amp, Kiro, Roo, Antigravity.
16
+ * < 0.117.0, Cursor, OpenCode, Copilot, Amp, Roo, Antigravity.
17
17
  *
18
18
  * Source resolution is `resolveCommandSource` (user → system → extras —
19
19
  * project layer intentionally excluded).
@@ -90,9 +90,9 @@ function buildHooksWriter(agent) {
90
90
  // so subrule-bundled guards (absolute paths outside the central hooks/
91
91
  // copy set) get registered into ~/.grok/hooks/hooks.json via
92
92
  // registerHooksForGrok — file copy alone only sees top-level available.hooks
93
- // names (RUSH-1353). Copilot/Kiro/Goose load managed *.json under their
93
+ // names (RUSH-1353). Copilot/Goose load managed *.json under their
94
94
  // hooks dirs the same way.
95
- if (agent === 'claude' || agent === 'codex' || agent === 'antigravity' || agent === 'kimi' || agent === 'droid' || agent === 'copilot' || agent === 'kiro' || agent === 'goose' || agent === 'cursor' || agent === 'grok' || agent === 'hermes' || agent === 'muse') {
95
+ if (agent === 'claude' || agent === 'codex' || agent === 'antigravity' || agent === 'kimi' || agent === 'droid' || agent === 'copilot' || agent === 'goose' || agent === 'cursor' || agent === 'grok' || agent === 'hermes' || agent === 'muse') {
96
96
  registerHooksToSettings(agent, versionHome);
97
97
  }
98
98
  return { synced, paths };
@@ -15,7 +15,7 @@
15
15
  *
16
16
  * - `flatFile` one `<name><ext>` file, body from a `transform` fn.
17
17
  * (claude, grok, pi, droid, codex, opencode, copilot,
18
- * cursor, kiro, goose, kimi)
18
+ * cursor, goose, kimi)
19
19
  * - `dirFile` a `<name>/` directory holding one generated `<file>`.
20
20
  * (antigravity: `<name>/agent.md`)
21
21
  * - `dirCopy` copy the whole source directory to `<name>/`, applying
@@ -35,7 +35,7 @@ import * as TOML from 'smol-toml';
35
35
  import * as yaml from 'yaml';
36
36
  import { safeJoin } from './paths.js';
37
37
  import { filesContentMatch, normalizeResourceContent } from './resource-content-diff.js';
38
- import { parseSubagentFrontmatter, transformSubagentForClaude, transformSubagentForCodex, transformSubagentForCopilot, transformSubagentForCursor, transformSubagentForDroid, transformSubagentForGoose, transformSubagentForKiro, transformSubagentForOpenCode, transformSubagentForAntigravity, } from './subagents.js';
38
+ import { parseSubagentFrontmatter, transformSubagentForClaude, transformSubagentForCodex, transformSubagentForCopilot, transformSubagentForCursor, transformSubagentForDroid, transformSubagentForGoose, transformSubagentForOpenCode, transformSubagentForAntigravity, } from './subagents.js';
39
39
  /** Read a file's UTF-8 content, or null when it is missing/unreadable. */
40
40
  function readFileSafe(filePath) {
41
41
  try {
@@ -54,7 +54,6 @@ function metaFrontmatterSkip(filePath) {
54
54
  function metaFrontmatterFallback(filePath, name) {
55
55
  return parseSubagentFrontmatter(filePath) ?? { name, description: '' };
56
56
  }
57
- /** Kiro custom-agent JSON: read name/description/model; skip on parse error. */
58
57
  function metaJson(filePath, name) {
59
58
  try {
60
59
  const cfg = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
@@ -283,9 +282,6 @@ export const SUBAGENT_TARGETS = {
283
282
  // Tier 1 -- flat markdown, Claude-compatible flatten.
284
283
  claude: flatFile({ subdir: ['.claude', 'agents'], ext: '.md', transform: transformSubagentForClaude }),
285
284
  grok: flatFile({ subdir: ['.grok', 'agents'], ext: '.md', transform: transformSubagentForClaude }),
286
- // Oh My Pi discovers Claude-shaped agent markdown from ~/.omp/agent/agents/
287
- // (`omp agents unpack` writes there); frontmatter name/description + body.
288
- pi: flatFile({ subdir: ['.omp', 'agent', 'agents'], ext: '.md', transform: transformSubagentForClaude, readMeta: metaFrontmatterFallback }),
289
285
  droid: flatFile({ subdir: ['.factory', 'droids'], ext: '.md', transform: transformSubagentForDroid }),
290
286
  // Bespoke frontmatter/format, still one flat file.
291
287
  codex: flatFile({
@@ -312,12 +308,6 @@ export const SUBAGENT_TARGETS = {
312
308
  transform: transformSubagentForCursor,
313
309
  readMeta: metaFrontmatterFallback,
314
310
  }),
315
- kiro: flatFile({
316
- subdir: ['.kiro', 'agents'],
317
- ext: '.json',
318
- transform: transformSubagentForKiro,
319
- readMeta: metaJson,
320
- }),
321
311
  goose: flatFile({
322
312
  subdir: ['.config', 'goose', 'agents'],
323
313
  ext: '.yaml',
@@ -106,16 +106,6 @@ export declare function transformSubagentForOpenCode(subagentDir: string): strin
106
106
  * https://developers.openai.com/codex/subagents (custom agents section)
107
107
  */
108
108
  export declare function transformSubagentForCodex(subagentDir: string): string;
109
- /**
110
- * Transform a subagent into a Kiro CLI custom-agent JSON file.
111
- *
112
- * Kiro custom agents live in `~/.kiro/agents/<name>.json` (or `.kiro/agents/`
113
- * workspace-local) and declare name, description, prompt, tools, and optional
114
- * model. We flatten the AGENT.md frontmatter + body plus any sibling .md files
115
- * as sections into a single `prompt`, and expose the standard built-in tool
116
- * set so the subagent can actually run.
117
- */
118
- export declare function transformSubagentForKiro(subagentDir: string): string;
119
109
  /**
120
110
  * Transform a subagent into a Goose recipe YAML file.
121
111
  *
@@ -403,43 +403,6 @@ function flattenSubagentInstructions(subagentDir) {
403
403
  }
404
404
  return instructions;
405
405
  }
406
- /**
407
- * Transform a subagent into a Kiro CLI custom-agent JSON file.
408
- *
409
- * Kiro custom agents live in `~/.kiro/agents/<name>.json` (or `.kiro/agents/`
410
- * workspace-local) and declare name, description, prompt, tools, and optional
411
- * model. We flatten the AGENT.md frontmatter + body plus any sibling .md files
412
- * as sections into a single `prompt`, and expose the standard built-in tool
413
- * set so the subagent can actually run.
414
- */
415
- export function transformSubagentForKiro(subagentDir) {
416
- const agentMd = path.join(subagentDir, 'AGENT.md');
417
- const frontmatter = parseSubagentFrontmatter(agentMd);
418
- const body = getSubagentBody(agentMd);
419
- if (!frontmatter) {
420
- throw new Error(`Invalid AGENT.md in ${subagentDir}`);
421
- }
422
- const files = fs.readdirSync(subagentDir)
423
- .filter(f => f.endsWith('.md') && f !== 'AGENT.md')
424
- .sort();
425
- let prompt = body;
426
- for (const file of files) {
427
- const content = fs.readFileSync(path.join(subagentDir, file), 'utf-8').trim();
428
- const sectionName = file.replace('.md', '');
429
- const title = sectionName.charAt(0).toUpperCase() + sectionName.slice(1).toLowerCase();
430
- prompt += `\n\n## ${title}\n\n${content}`;
431
- }
432
- const config = {
433
- name: frontmatter.name,
434
- description: frontmatter.description,
435
- prompt,
436
- tools: ['read', 'write', 'shell', 'web_search', 'web_fetch'],
437
- };
438
- if (frontmatter.model) {
439
- config.model = frontmatter.model;
440
- }
441
- return JSON.stringify(config, null, 2);
442
- }
443
406
  /**
444
407
  * Transform a subagent into a Goose recipe YAML file.
445
408
  *
@@ -8,7 +8,7 @@
8
8
  import type { CloudProviderId } from './cloud/types.js';
9
9
  import type { FeedBroadcastConfig } from './feed-broadcast.js';
10
10
  /** Unique identifier for a current or legacy AI coding agent. */
11
- export type AgentId = 'claude' | 'codex' | 'gemini' | 'cursor' | 'opencode' | 'openclaw' | 'copilot' | 'amp' | 'kiro' | 'goose' | 'antigravity' | 'grok' | 'kimi' | 'droid' | 'hermes' | 'pi' | 'muse' | 'warp';
11
+ export type AgentId = 'claude' | 'codex' | 'gemini' | 'cursor' | 'opencode' | 'openclaw' | 'copilot' | 'amp' | 'goose' | 'antigravity' | 'grok' | 'kimi' | 'droid' | 'hermes' | 'muse' | 'warp';
12
12
  /** How `agents run <agent>` chooses an installed version when none is pinned. */
13
13
  export type RunStrategy = 'pinned' | 'available' | 'balanced';
14
14
  export type RunEffort = 'low' | 'medium' | 'high' | 'xhigh' | 'max' | 'auto';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phnx-labs/agents-cli",
3
- "version": "1.22.69",
3
+ "version": "1.22.70",
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",