@phnx-labs/agents-cli 1.15.0 → 1.16.0

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 (87) hide show
  1. package/CHANGELOG.md +78 -39
  2. package/README.md +6 -6
  3. package/dist/commands/alias.js +2 -2
  4. package/dist/commands/browser-picker.d.ts +21 -0
  5. package/dist/commands/browser-picker.js +114 -0
  6. package/dist/commands/browser.js +546 -75
  7. package/dist/commands/commands.js +72 -22
  8. package/dist/commands/daemon.js +2 -2
  9. package/dist/commands/fork.js +2 -2
  10. package/dist/commands/hooks.js +71 -26
  11. package/dist/commands/mcp.js +81 -39
  12. package/dist/commands/plugins.js +48 -15
  13. package/dist/commands/prune.js +23 -1
  14. package/dist/commands/pull.js +3 -3
  15. package/dist/commands/repo.js +1 -1
  16. package/dist/commands/routines.js +2 -2
  17. package/dist/commands/secrets.js +37 -1
  18. package/dist/commands/sessions.js +62 -19
  19. package/dist/commands/{init.d.ts → setup.d.ts} +7 -6
  20. package/dist/commands/{init.js → setup.js} +22 -21
  21. package/dist/commands/skills.js +60 -19
  22. package/dist/commands/subagents.js +41 -13
  23. package/dist/commands/utils.d.ts +16 -0
  24. package/dist/commands/utils.js +32 -0
  25. package/dist/commands/view.js +61 -16
  26. package/dist/index.d.ts +1 -1
  27. package/dist/index.js +17 -20
  28. package/dist/lib/agents.js +2 -2
  29. package/dist/lib/auto-pull-worker.js +2 -3
  30. package/dist/lib/auto-pull.js +2 -2
  31. package/dist/lib/browser/cdp.d.ts +7 -1
  32. package/dist/lib/browser/cdp.js +29 -1
  33. package/dist/lib/browser/chrome.js +5 -2
  34. package/dist/lib/browser/devices.d.ts +4 -0
  35. package/dist/lib/browser/devices.js +27 -0
  36. package/dist/lib/browser/drivers/local.js +9 -4
  37. package/dist/lib/browser/drivers/ssh.js +9 -2
  38. package/dist/lib/browser/ipc.js +144 -23
  39. package/dist/lib/browser/profiles.d.ts +5 -2
  40. package/dist/lib/browser/profiles.js +77 -37
  41. package/dist/lib/browser/service.d.ts +81 -13
  42. package/dist/lib/browser/service.js +738 -131
  43. package/dist/lib/browser/types.d.ts +81 -3
  44. package/dist/lib/browser/types.js +16 -0
  45. package/dist/lib/cloud/rush.js +2 -2
  46. package/dist/lib/cloud/store.js +2 -2
  47. package/dist/lib/commands.d.ts +1 -0
  48. package/dist/lib/commands.js +6 -2
  49. package/dist/lib/daemon.js +2 -3
  50. package/dist/lib/doctor-diff.js +4 -4
  51. package/dist/lib/events.js +2 -2
  52. package/dist/lib/hooks.d.ts +11 -7
  53. package/dist/lib/hooks.js +125 -49
  54. package/dist/lib/migrate.d.ts +1 -1
  55. package/dist/lib/migrate.js +1178 -21
  56. package/dist/lib/models.js +2 -2
  57. package/dist/lib/permissions.d.ts +8 -8
  58. package/dist/lib/permissions.js +8 -8
  59. package/dist/lib/plugins.d.ts +30 -1
  60. package/dist/lib/plugins.js +75 -3
  61. package/dist/lib/pty-server.js +9 -10
  62. package/dist/lib/resources/hooks.d.ts +5 -1
  63. package/dist/lib/resources/hooks.js +21 -4
  64. package/dist/lib/rotate.js +3 -4
  65. package/dist/lib/session/active.d.ts +3 -0
  66. package/dist/lib/session/active.js +92 -6
  67. package/dist/lib/session/cloud.js +2 -2
  68. package/dist/lib/session/db.js +8 -3
  69. package/dist/lib/session/discover.js +30 -15
  70. package/dist/lib/session/team-filter.js +2 -2
  71. package/dist/lib/shims.d.ts +2 -2
  72. package/dist/lib/shims.js +6 -6
  73. package/dist/lib/skills.js +6 -2
  74. package/dist/lib/state.d.ts +86 -14
  75. package/dist/lib/state.js +150 -23
  76. package/dist/lib/subagents.d.ts +28 -0
  77. package/dist/lib/subagents.js +98 -1
  78. package/dist/lib/sync-manifest.d.ts +1 -1
  79. package/dist/lib/sync-manifest.js +3 -3
  80. package/dist/lib/teams/persistence.js +15 -5
  81. package/dist/lib/teams/registry.js +2 -2
  82. package/dist/lib/types.d.ts +32 -3
  83. package/dist/lib/types.js +3 -3
  84. package/dist/lib/usage.js +2 -2
  85. package/dist/lib/versions.js +20 -21
  86. package/package.json +1 -1
  87. package/scripts/postinstall.js +1 -1
@@ -9,7 +9,8 @@
9
9
  import * as fs from 'fs';
10
10
  import * as path from 'path';
11
11
  import * as yaml from 'yaml';
12
- import { getSubagentsDir, getUserSubagentsDir } from './state.js';
12
+ import { getSubagentsDir, getUserSubagentsDir, getTrashSubagentsDir } from './state.js';
13
+ import { listInstalledVersions, getVersionHomePath } from './versions.js';
13
14
  import { safeJoin } from './paths.js';
14
15
  /**
15
16
  * Parse AGENT.md frontmatter to extract subagent metadata
@@ -407,3 +408,99 @@ export function listSubagentsForAgent(agentId, home) {
407
408
  }
408
409
  return subagents;
409
410
  }
411
+ // Agents that support subagents
412
+ const SUBAGENTS_CAPABLE_AGENTS = ['claude', 'openclaw'];
413
+ /**
414
+ * Compare a version home's subagents against discovered subagents.
415
+ * Returns orphan subagent names.
416
+ */
417
+ export function diffVersionSubagents(agent, version) {
418
+ const versionHome = getVersionHomePath(agent, version);
419
+ const orphans = [];
420
+ // Get all discovered subagent names
421
+ const discovered = new Set();
422
+ for (const dir of [getSubagentsDir(), getUserSubagentsDir()]) {
423
+ if (fs.existsSync(dir)) {
424
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
425
+ if (entry.isDirectory()) {
426
+ discovered.add(entry.name);
427
+ }
428
+ }
429
+ }
430
+ }
431
+ // Check what's installed
432
+ if (agent === 'claude') {
433
+ const agentsDir = path.join(versionHome, '.claude', 'agents');
434
+ if (fs.existsSync(agentsDir)) {
435
+ for (const file of fs.readdirSync(agentsDir)) {
436
+ if (!file.endsWith('.md'))
437
+ continue;
438
+ const name = path.basename(file, '.md');
439
+ if (!discovered.has(name)) {
440
+ orphans.push(name);
441
+ }
442
+ }
443
+ }
444
+ }
445
+ else if (agent === 'openclaw') {
446
+ const openclawDir = path.join(versionHome, '.openclaw');
447
+ if (fs.existsSync(openclawDir)) {
448
+ for (const entry of fs.readdirSync(openclawDir, { withFileTypes: true })) {
449
+ if (!entry.isDirectory())
450
+ continue;
451
+ if (!discovered.has(entry.name)) {
452
+ orphans.push(entry.name);
453
+ }
454
+ }
455
+ }
456
+ }
457
+ return { agent, version, orphans: orphans.sort() };
458
+ }
459
+ /**
460
+ * Iterate all (agent, version) pairs that support subagents and are installed.
461
+ */
462
+ export function iterSubagentsCapableVersions(filter) {
463
+ const pairs = [];
464
+ const agents = filter?.agent ? [filter.agent] : SUBAGENTS_CAPABLE_AGENTS;
465
+ for (const agent of agents) {
466
+ if (!SUBAGENTS_CAPABLE_AGENTS.includes(agent))
467
+ continue;
468
+ const versions = listInstalledVersions(agent);
469
+ for (const version of versions) {
470
+ if (filter?.version && filter.version !== version)
471
+ continue;
472
+ pairs.push({ agent, version });
473
+ }
474
+ }
475
+ return pairs;
476
+ }
477
+ /**
478
+ * Remove a single subagent from a specific version home.
479
+ * Soft-deletes to ~/.agents/.trash/subagents/.
480
+ */
481
+ export function removeSubagentFromVersion(agent, version, subagentName) {
482
+ const versionHome = getVersionHomePath(agent, version);
483
+ const stamp = new Date().toISOString().replace(/[:.]/g, '-');
484
+ const trashDir = path.join(getTrashSubagentsDir(), agent, version, subagentName);
485
+ try {
486
+ if (agent === 'claude') {
487
+ const targetPath = path.join(versionHome, '.claude', 'agents', `${subagentName}.md`);
488
+ if (fs.existsSync(targetPath)) {
489
+ fs.mkdirSync(trashDir, { recursive: true, mode: 0o700 });
490
+ fs.renameSync(targetPath, path.join(trashDir, `${subagentName}.md.${stamp}`));
491
+ }
492
+ }
493
+ else if (agent === 'openclaw') {
494
+ const targetDir = path.join(versionHome, '.openclaw', subagentName);
495
+ if (fs.existsSync(targetDir)) {
496
+ const trashDest = path.join(trashDir, stamp);
497
+ fs.mkdirSync(trashDir, { recursive: true, mode: 0o700 });
498
+ fs.renameSync(targetDir, trashDest);
499
+ }
500
+ }
501
+ return { success: true };
502
+ }
503
+ catch (err) {
504
+ return { success: false, error: err.message };
505
+ }
506
+ }
@@ -47,7 +47,7 @@ interface RulesEntry {
47
47
  /** Permissions: all group files across all scopes (merged). */
48
48
  interface PermEntry {
49
49
  groups: Record<string, FileEntry>;
50
- permissionSet: string | null;
50
+ permissionPreset: string | null;
51
51
  }
52
52
  export interface SyncManifest {
53
53
  v: typeof MANIFEST_VERSION;
@@ -28,7 +28,7 @@ import { getVersionsDir, getProjectAgentsDir, getUserAgentsDir, getSkillsDir, ge
28
28
  import { resolveResource } from './resources.js';
29
29
  import { listMcpServerConfigs } from './mcp.js';
30
30
  import { isRulesStale } from './rules/compile.js';
31
- import { getActivePermissionSetName } from './permissions.js';
31
+ import { getActivePermissionPresetName } from './permissions.js';
32
32
  import { listInstalledSubagents } from './subagents.js';
33
33
  import { safeJoin } from './paths.js';
34
34
  // ─── Types ────────────────────────────────────────────────────────────────────
@@ -341,7 +341,7 @@ export function buildManifest(agent, version, available, cwd) {
341
341
  mcp,
342
342
  permissions: {
343
343
  groups: permGroups,
344
- permissionSet: getActivePermissionSetName(),
344
+ permissionPreset: getActivePermissionPresetName(),
345
345
  },
346
346
  subagents,
347
347
  };
@@ -420,7 +420,7 @@ export function isSyncStale(manifest, available, agent, version, cwd) {
420
420
  return true;
421
421
  }
422
422
  // ── Permissions ───────────────────────────────────────────────────────────
423
- if (manifest.permissions.permissionSet !== getActivePermissionSetName())
423
+ if (manifest.permissions.permissionPreset !== getActivePermissionPresetName())
424
424
  return true;
425
425
  const currentGroups = collectPermissionGroupFiles();
426
426
  if (nameSetDiffers(Object.keys(manifest.permissions.groups), Object.keys(currentGroups)))
@@ -13,7 +13,7 @@ import { homedir, tmpdir } from 'os';
13
13
  import { constants as fsConstants } from 'fs';
14
14
  import { randomBytes } from 'crypto';
15
15
  import lockfile from 'proper-lockfile';
16
- import { getUserAgentsDir } from '../state.js';
16
+ import { getTeamsDir, getTeamsAgentsDir } from '../state.js';
17
17
  /**
18
18
  * Atomic JSON write: writes to a sibling tmp file then renames over the
19
19
  * target. rename(2) is atomic on POSIX, so a crashed/interrupted write
@@ -62,8 +62,9 @@ async function withConfigLock(p, fn) {
62
62
  }
63
63
  // All supported teammate agent types
64
64
  const ALL_AGENTS = ['claude', 'codex', 'gemini', 'cursor', 'opencode'];
65
- // Teams data lives under ~/.agents/teams/
66
- const TEAMS_DIR = path.join(getUserAgentsDir(), 'teams');
65
+ // Teams config + registry live under ~/.agents/teams/ (definitions);
66
+ // per-run agent execution dirs live under ~/.agents/.history/teams/agents/.
67
+ const TEAMS_DIR = getTeamsDir();
67
68
  // Legacy paths (for migration)
68
69
  const LEGACY_CONFIG_DIR = path.join(homedir(), '.agents');
69
70
  // Legacy migration from pre-OSS brand; safe to remove after 2026-07
@@ -100,8 +101,17 @@ export async function resolveBaseDir() {
100
101
  throw new Error('Unable to determine a writable data directory for teams');
101
102
  }
102
103
  async function resolveAgentsPath() {
103
- const base = await resolveBaseDir();
104
- return path.join(base, 'agents');
104
+ const historyAgents = getTeamsAgentsDir();
105
+ if (await ensureWritableDir(historyAgents)) {
106
+ return historyAgents;
107
+ }
108
+ // Last-resort temp fallback so dispatch keeps working when ~/.agents is unwritable.
109
+ const tmpAgents = path.join(TMP_FALLBACK_DIR, 'agents');
110
+ if (await ensureWritableDir(tmpAgents)) {
111
+ console.warn(`[agents teams] Falling back to temp agents dir at ${tmpAgents}`);
112
+ return tmpAgents;
113
+ }
114
+ throw new Error('Unable to determine a writable agents directory');
105
115
  }
106
116
  async function resolveConfigPath() {
107
117
  await fs.mkdir(TEAMS_DIR, { recursive: true });
@@ -10,9 +10,9 @@ import * as fsSync from 'fs';
10
10
  import * as path from 'path';
11
11
  import { randomBytes } from 'crypto';
12
12
  import lockfile from 'proper-lockfile';
13
- import { getUserAgentsDir } from '../state.js';
13
+ import { getTeamsDir } from '../state.js';
14
14
  async function registryPath() {
15
- return path.join(getUserAgentsDir(), 'teams', 'registry.json');
15
+ return path.join(getTeamsDir(), 'registry.json');
16
16
  }
17
17
  /**
18
18
  * Atomic JSON write: writes to a unique sibling tmp file then renames over
@@ -186,9 +186,9 @@ export interface RepoInfo {
186
186
  lastSync: string;
187
187
  }
188
188
  /** Canonical system repo cloned into ~/.agents-system/. */
189
- export declare const DEFAULT_SYSTEM_REPO = "gh:muqsitnawaz/.agents-system";
190
- /** Mirror system repo (phnx-labs) will become the default once fully released. */
191
- export declare const MIRROR_SYSTEM_REPO = "gh:phnx-labs/.agents-system";
189
+ export declare const DEFAULT_SYSTEM_REPO = "gh:phnx-labs/.agents-system";
190
+ /** Legacy system repo — kept so existing installs still recognize their origin. */
191
+ export declare const MIRROR_SYSTEM_REPO = "gh:muqsitnawaz/.agents-system";
192
192
  /** Strip the `gh:` prefix and `.git` suffix to get a GitHub `owner/repo` slug. */
193
193
  export declare function systemRepoSlug(repo?: string): string;
194
194
  /** Kind of package that can be searched and installed from a registry. */
@@ -378,6 +378,35 @@ export interface Meta {
378
378
  * once. Tracked so a user `registry remove` won't silently re-seed.
379
379
  */
380
380
  seededPresets?: string[];
381
+ /**
382
+ * Hook manifest entries keyed by hook name. Folded into agents.yaml so the
383
+ * user has a single file to sync. Each entry shape matches ManifestHook
384
+ * (script, events, timeout, matches, enabled).
385
+ */
386
+ hooks?: Record<string, ManifestHook>;
387
+ /**
388
+ * Browser profile definitions keyed by profile name. Portable user config
389
+ * that syncs with `agents repo push/pull`. Runtime state (chrome-data, pids)
390
+ * lives separately in ~/.agents/.cache/browser/<profile>/.
391
+ */
392
+ browser?: Record<string, BrowserProfileConfig>;
393
+ }
394
+ /** Browser profile definition stored in agents.yaml. */
395
+ export interface BrowserProfileConfig {
396
+ description?: string;
397
+ browser: 'chrome' | 'comet' | 'chromium' | 'brave' | 'edge' | 'custom';
398
+ binary?: string;
399
+ electron?: boolean;
400
+ endpoints: string[];
401
+ chrome?: {
402
+ headless?: boolean;
403
+ args?: string[];
404
+ };
405
+ secrets?: string;
406
+ viewport?: {
407
+ width: number;
408
+ height: number;
409
+ };
381
410
  }
382
411
  /** Options controlling which agents and resources are synced during `agents pull` / `agents use`. */
383
412
  export interface SyncOptions {
package/dist/lib/types.js CHANGED
@@ -6,9 +6,9 @@
6
6
  * formats for each supported agent.
7
7
  */
8
8
  /** Canonical system repo cloned into ~/.agents-system/. */
9
- export const DEFAULT_SYSTEM_REPO = 'gh:muqsitnawaz/.agents-system';
10
- /** Mirror system repo (phnx-labs) will become the default once fully released. */
11
- export const MIRROR_SYSTEM_REPO = 'gh:phnx-labs/.agents-system';
9
+ export const DEFAULT_SYSTEM_REPO = 'gh:phnx-labs/.agents-system';
10
+ /** Legacy system repo — kept so existing installs still recognize their origin. */
11
+ export const MIRROR_SYSTEM_REPO = 'gh:muqsitnawaz/.agents-system';
12
12
  /** Strip the `gh:` prefix and `.git` suffix to get a GitHub `owner/repo` slug. */
13
13
  export function systemRepoSlug(repo = DEFAULT_SYSTEM_REPO) {
14
14
  return repo.replace(/^gh:/, '').replace(/\.git$/, '');
package/dist/lib/usage.js CHANGED
@@ -16,7 +16,7 @@ import { promisify } from 'util';
16
16
  import chalk from 'chalk';
17
17
  import { walkForFiles } from './fs-walk.js';
18
18
  import { getKeychainToken, setKeychainToken, deleteKeychainToken, } from './secrets/index.js';
19
- import { getAgentsDir } from './state.js';
19
+ import { getCacheDir } from './state.js';
20
20
  const execFileAsync = promisify(execFile);
21
21
  const CLAUDE_USAGE_URL = 'https://api.anthropic.com/api/oauth/usage';
22
22
  const CLAUDE_TOKEN_URL = 'https://platform.claude.com/v1/oauth/token';
@@ -31,7 +31,7 @@ const CLAUDE_SCOPES = [
31
31
  'user:file_upload',
32
32
  ];
33
33
  const CLAUDE_KEYCHAIN_SERVICE = 'Claude Code-credentials';
34
- const getClaudeUsageCachePath = () => path.join(getAgentsDir(), 'cache', 'claude-usage.json');
34
+ const getClaudeUsageCachePath = () => path.join(getCacheDir(), 'claude-usage.json');
35
35
  const CACHED_CLAUDE_USAGE_SOURCE_LABEL = 'last seen live account data';
36
36
  const COMPACT_BAR_LEN = 5;
37
37
  const USAGE_BAR_LEN = 10;
@@ -23,10 +23,10 @@ import { promisify } from 'util';
23
23
  import chalk from 'chalk';
24
24
  import * as TOML from 'smol-toml';
25
25
  import { checkbox, select } from '@inquirer/prompts';
26
- import { getVersionsDir, ensureAgentsDir, readMeta, writeMeta, getCommandsDir, getSkillsDir, getHooksDir, getResolvedRulesDir, getUserRulesDir, clearVersionResources, recordVersionResources, getProjectAgentsDir, getPromptcutsPath, getEnabledExtraRepos, getAgentsDir, getUserAgentsDir, getTrashVersionsDir, getActiveRulesPreset } from './state.js';
26
+ import { getVersionsDir, ensureAgentsDir, readMeta, writeMeta, getCommandsDir, getSkillsDir, getHooksDir, getResolvedRulesDir, getUserRulesDir, clearVersionResources, recordVersionResources, getProjectAgentsDir, getPromptcutsPath, getUserPromptcutsPath, getEnabledExtraRepos, getAgentsDir, getUserAgentsDir, getTrashVersionsDir, getActiveRulesPreset } from './state.js';
27
27
  import { resolveResource } from './resources.js';
28
28
  import { AGENTS, getAccountEmail, MCP_CAPABLE_AGENTS, COMMANDS_CAPABLE_AGENTS, getMcpConfigPathForHome, parseMcpConfig, resolveAgentName, formatAgentError } from './agents.js';
29
- import { applyPermissionsToVersion as applyPermsToVersion, PERMISSIONS_CAPABLE_AGENTS, discoverPermissionGroups, buildPermissionsFromGroups, CODEX_RULES_FILENAME, getActivePermissionSetName, readPermissionSetRecipe, PERMISSION_SET_ENV_VAR } from './permissions.js';
29
+ import { applyPermissionsToVersion as applyPermsToVersion, PERMISSIONS_CAPABLE_AGENTS, discoverPermissionGroups, buildPermissionsFromGroups, CODEX_RULES_FILENAME, getActivePermissionPresetName, readPermissionPresetRecipe, PERMISSION_PRESET_ENV_VAR } from './permissions.js';
30
30
  import { installMcpServers, parseMcpServerConfig } from './mcp.js';
31
31
  import { markdownToToml } from './convert.js';
32
32
  import { createVersionedAlias, removeVersionedAlias, getConfigSymlinkVersion, ensureClaudeInsideSymlink } from './shims.js';
@@ -192,10 +192,9 @@ export function getAvailableResources(cwd = process.cwd()) {
192
192
  // Plugins (directories with .claude-plugin/plugin.json)
193
193
  const allPlugins = discoverPlugins();
194
194
  result.plugins = allPlugins.map(p => p.name);
195
- // Promptcuts — single file at ~/.agents/promptcuts.yaml, not per-agent.
196
- // Project-scoped .agents/promptcuts.yaml is intentionally not supported
197
- // (user-global shortcuts only — they follow the user, not the repo).
198
- result.promptcuts = fs.existsSync(getPromptcutsPath());
195
+ // Promptcuts — present if either layer exists. Reads merge user + system
196
+ // with user precedence (see readMergedPromptcuts); writes always go to user.
197
+ result.promptcuts = fs.existsSync(getUserPromptcutsPath()) || fs.existsSync(getPromptcutsPath());
199
198
  return result;
200
199
  }
201
200
  // Files/dirs that are never synced into a version home (OS metadata, local tooling).
@@ -676,14 +675,14 @@ export async function promptResourceSelection(agent) {
676
675
  ];
677
676
  const availableCategories = categories.filter(c => c.available);
678
677
  if (availableCategories.length === 0) {
679
- console.log(chalk.gray('No resources available in ~/.agents/'));
678
+ console.log(chalk.gray('No resources available to sync.'));
680
679
  return {};
681
680
  }
682
681
  // Step 1: Select categories (with "Select All" shortcut at the top)
683
682
  console.log();
684
683
  const SELECT_ALL_KEY = '__select_all__';
685
684
  const selectedCategories = await checkbox({
686
- message: 'Which resources from ~/.agents/ would you like to sync?',
685
+ message: 'Which resources would you like to sync?',
687
686
  choices: [
688
687
  { name: chalk.bold('Select All (sync everything)'), value: SELECT_ALL_KEY, checked: false },
689
688
  ...availableCategories.map(c => ({
@@ -1189,7 +1188,7 @@ export function resolveVersionAliasLoose(agent, raw) {
1189
1188
  * Get version specified in a project-root agents.yaml (not the user ~/.agents-system/agents.yaml).
1190
1189
  */
1191
1190
  export function getProjectVersion(agent, startPath) {
1192
- const userAgentsYaml = path.join(getAgentsDir(), 'agents.yaml');
1191
+ const userAgentsYaml = path.join(getUserAgentsDir(), 'agents.yaml');
1193
1192
  let dir = path.resolve(startPath);
1194
1193
  while (dir !== path.dirname(dir)) {
1195
1194
  const manifestPath = path.join(dir, 'agents.yaml');
@@ -1641,39 +1640,39 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
1641
1640
  }
1642
1641
  // Apply permissions (if agent supports them).
1643
1642
  // Groups live in ~/.agents/permissions/groups/. Optional recipes in
1644
- // ~/.agents/permissions/sets/<name>.yaml pick a subset via `includes:`.
1645
- // If AGENTS_PERMISSION_SET is set, we resolve that recipe and use its
1643
+ // ~/.agents/permissions/presets/<name>.yaml pick a subset via `includes:`.
1644
+ // If AGENTS_PERMISSION_PRESET is set, we resolve that recipe and use its
1646
1645
  // includes list as the group filter (intersected with groups on disk).
1647
1646
  const permissionGroups = discoverPermissionGroups();
1648
1647
  const allGroupNames = permissionGroups.map(g => g.name);
1649
- const activeSetName = getActivePermissionSetName();
1650
- let setFilteredGroups = null;
1651
- if (activeSetName) {
1652
- const recipe = readPermissionSetRecipe(activeSetName);
1648
+ const activePresetName = getActivePermissionPresetName();
1649
+ let presetFilteredGroups = null;
1650
+ if (activePresetName) {
1651
+ const recipe = readPermissionPresetRecipe(activePresetName);
1653
1652
  if (recipe) {
1654
1653
  const available = new Set(allGroupNames);
1655
- setFilteredGroups = recipe.includes.filter(g => available.has(g));
1654
+ presetFilteredGroups = recipe.includes.filter(g => available.has(g));
1656
1655
  }
1657
1656
  else {
1658
- console.warn(`${PERMISSION_SET_ENV_VAR}=${activeSetName} but no recipe at ~/.agents/permissions/sets/${activeSetName}.yaml — falling back to all groups`);
1657
+ console.warn(`${PERMISSION_PRESET_ENV_VAR}=${activePresetName} but no recipe at ~/.agents/permissions/presets/${activePresetName}.yaml — falling back to all groups`);
1659
1658
  }
1660
1659
  }
1661
1660
  let permsToSync;
1662
1661
  if (selection) {
1663
1662
  permsToSync = resolveSelection(selection.permissions, allGroupNames);
1664
- // If a set recipe is active, the recipe's includes list always wins —
1663
+ // If a preset recipe is active, the recipe's includes list always wins —
1665
1664
  // even when the caller passed an explicit array via selection. Without
1666
1665
  // this intersection, `agents add`'s buildAutomaticSelection would pass
1667
1666
  // every group name discovered on disk (including 99-deny), bypassing
1668
1667
  // the sandbox filter.
1669
- if (setFilteredGroups) {
1670
- const filterSet = new Set(setFilteredGroups);
1668
+ if (presetFilteredGroups) {
1669
+ const filterSet = new Set(presetFilteredGroups);
1671
1670
  permsToSync = permsToSync.filter(g => filterSet.has(g));
1672
1671
  }
1673
1672
  }
1674
1673
  else {
1675
1674
  permsToSync = PERMISSIONS_CAPABLE_AGENTS.includes(agent)
1676
- ? (setFilteredGroups ?? allGroupNames)
1675
+ ? (presetFilteredGroups ?? allGroupNames)
1677
1676
  : [];
1678
1677
  }
1679
1678
  if (permsToSync.length > 0 && PERMISSIONS_CAPABLE_AGENTS.includes(agent)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phnx-labs/agents-cli",
3
- "version": "1.15.0",
3
+ "version": "1.16.0",
4
4
  "description": "One CLI for all your AI coding agents - versions, config, cloud dispatch, sessions, and teams",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -20,7 +20,7 @@ if (!isGlobalInstall) {
20
20
  fs.mkdirSync(USER_DIR, { recursive: true, mode: 0o700 });
21
21
  console.log(`
22
22
  agents-cli installed locally.
23
- To complete setup, run: npx agents init
23
+ To complete setup, run: npx agents setup
24
24
  `);
25
25
  process.exit(0);
26
26
  }