@phnx-labs/agents-cli 1.14.7 → 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 (105) hide show
  1. package/CHANGELOG.md +78 -39
  2. package/README.md +74 -7
  3. package/dist/commands/alias.js +2 -2
  4. package/dist/commands/beta.js +6 -1
  5. package/dist/commands/browser-picker.d.ts +21 -0
  6. package/dist/commands/browser-picker.js +114 -0
  7. package/dist/commands/browser.js +546 -75
  8. package/dist/commands/commands.js +72 -22
  9. package/dist/commands/daemon.js +2 -2
  10. package/dist/commands/exec.js +9 -2
  11. package/dist/commands/fork.js +2 -2
  12. package/dist/commands/hooks.js +71 -26
  13. package/dist/commands/mcp.js +85 -43
  14. package/dist/commands/plugins.js +48 -15
  15. package/dist/commands/prune.d.ts +0 -20
  16. package/dist/commands/prune.js +291 -16
  17. package/dist/commands/pull.js +3 -3
  18. package/dist/commands/repo.js +1 -1
  19. package/dist/commands/routines.js +2 -2
  20. package/dist/commands/secrets.js +37 -1
  21. package/dist/commands/sessions.js +62 -19
  22. package/dist/commands/{init.d.ts → setup.d.ts} +7 -6
  23. package/dist/commands/{init.js → setup.js} +32 -21
  24. package/dist/commands/skills.js +60 -19
  25. package/dist/commands/subagents.js +41 -13
  26. package/dist/commands/teams.js +2 -3
  27. package/dist/commands/usage.js +6 -0
  28. package/dist/commands/utils.d.ts +16 -0
  29. package/dist/commands/utils.js +32 -0
  30. package/dist/commands/versions.js +8 -6
  31. package/dist/commands/view.js +61 -16
  32. package/dist/index.d.ts +1 -1
  33. package/dist/index.js +17 -20
  34. package/dist/lib/agents.js +2 -2
  35. package/dist/lib/auto-pull-worker.js +2 -3
  36. package/dist/lib/auto-pull.js +2 -2
  37. package/dist/lib/browser/cdp.d.ts +7 -1
  38. package/dist/lib/browser/cdp.js +29 -1
  39. package/dist/lib/browser/chrome.js +6 -3
  40. package/dist/lib/browser/devices.d.ts +4 -0
  41. package/dist/lib/browser/devices.js +27 -0
  42. package/dist/lib/browser/drivers/local.js +9 -4
  43. package/dist/lib/browser/drivers/ssh.d.ts +1 -0
  44. package/dist/lib/browser/drivers/ssh.js +32 -4
  45. package/dist/lib/browser/ipc.js +145 -23
  46. package/dist/lib/browser/profiles.d.ts +5 -2
  47. package/dist/lib/browser/profiles.js +77 -37
  48. package/dist/lib/browser/service.d.ts +84 -13
  49. package/dist/lib/browser/service.js +806 -122
  50. package/dist/lib/browser/types.d.ts +81 -3
  51. package/dist/lib/browser/types.js +16 -0
  52. package/dist/lib/cloud/rush.js +2 -2
  53. package/dist/lib/cloud/store.js +2 -2
  54. package/dist/lib/commands.d.ts +1 -0
  55. package/dist/lib/commands.js +6 -2
  56. package/dist/lib/daemon.js +6 -7
  57. package/dist/lib/doctor-diff.js +4 -4
  58. package/dist/lib/events.d.ts +94 -1
  59. package/dist/lib/events.js +264 -6
  60. package/dist/lib/exec.js +16 -10
  61. package/dist/lib/hooks.d.ts +11 -7
  62. package/dist/lib/hooks.js +125 -49
  63. package/dist/lib/migrate.d.ts +1 -1
  64. package/dist/lib/migrate.js +1178 -21
  65. package/dist/lib/models.js +2 -2
  66. package/dist/lib/permissions.d.ts +14 -11
  67. package/dist/lib/permissions.js +46 -42
  68. package/dist/lib/plugins.d.ts +30 -1
  69. package/dist/lib/plugins.js +75 -3
  70. package/dist/lib/pty-server.js +9 -10
  71. package/dist/lib/resources/hooks.d.ts +5 -1
  72. package/dist/lib/resources/hooks.js +21 -4
  73. package/dist/lib/rotate.js +3 -4
  74. package/dist/lib/routines.d.ts +15 -0
  75. package/dist/lib/routines.js +68 -0
  76. package/dist/lib/runner.js +9 -5
  77. package/dist/lib/secrets/index.d.ts +14 -11
  78. package/dist/lib/secrets/index.js +49 -21
  79. package/dist/lib/secrets/linux.d.ts +27 -0
  80. package/dist/lib/secrets/linux.js +161 -0
  81. package/dist/lib/session/active.d.ts +3 -0
  82. package/dist/lib/session/active.js +92 -6
  83. package/dist/lib/session/cloud.js +2 -2
  84. package/dist/lib/session/db.d.ts +4 -0
  85. package/dist/lib/session/db.js +34 -3
  86. package/dist/lib/session/discover.js +30 -15
  87. package/dist/lib/session/team-filter.js +2 -2
  88. package/dist/lib/shims.d.ts +2 -2
  89. package/dist/lib/shims.js +6 -6
  90. package/dist/lib/skills.js +6 -2
  91. package/dist/lib/state.d.ts +86 -14
  92. package/dist/lib/state.js +150 -23
  93. package/dist/lib/subagents.d.ts +28 -0
  94. package/dist/lib/subagents.js +98 -1
  95. package/dist/lib/sync-manifest.d.ts +1 -1
  96. package/dist/lib/sync-manifest.js +3 -3
  97. package/dist/lib/teams/persistence.js +15 -5
  98. package/dist/lib/teams/registry.js +2 -2
  99. package/dist/lib/types.d.ts +32 -3
  100. package/dist/lib/types.js +3 -3
  101. package/dist/lib/usage.d.ts +1 -1
  102. package/dist/lib/usage.js +15 -48
  103. package/dist/lib/versions.js +31 -21
  104. package/package.json +1 -1
  105. package/scripts/postinstall.js +37 -9
@@ -13,7 +13,7 @@ import * as crypto from 'crypto';
13
13
  import * as readline from 'readline';
14
14
  import { execFile } from 'child_process';
15
15
  import { promisify } from 'util';
16
- import { getAgentsDir } from '../state.js';
16
+ import { getAgentsDir, getUserAgentsDir } from '../state.js';
17
17
  const execFileAsync = promisify(execFile);
18
18
  import { AGENTS, getCliVersion } from '../agents.js';
19
19
  import { walkForFiles } from '../fs-walk.js';
@@ -22,6 +22,13 @@ import { SESSION_AGENTS } from './types.js';
22
22
  import { extractSessionTopic } from './prompt.js';
23
23
  import { getDB, getScanStampByPath, getScanStampsForPaths, recordScans, syncLabels, upsertSessionsBatch, querySessions, countSessions, ftsSearch, } from './db.js';
24
24
  const HOME = os.homedir();
25
+ // Versions can live under either repo: the user repo (current canonical
26
+ // location, ~/.agents/versions/) or the system repo (legacy / npm-shipped,
27
+ // ~/.agents-system/versions/). Both must be scanned — sessions written by
28
+ // any installed version end up in that version's projects/ dir, and the user
29
+ // can be running one repo's version while another repo holds older versions
30
+ // whose JSONLs the user still wants to search.
31
+ const VERSIONS_ROOTS = [getUserAgentsDir(), getAgentsDir()];
25
32
  const AGENTS_DIR = getAgentsDir();
26
33
  const RUSH_SESSIONS_DIR = path.join(HOME, '.rush', 'sessions');
27
34
  const HERMES_SESSIONS_DIR = path.join(HOME, '.hermes', 'sessions');
@@ -184,8 +191,10 @@ export function getAgentSessionDirs(agent, subdir) {
184
191
  dirs.push(dir);
185
192
  }
186
193
  addDir(path.join(HOME, `.${agent}`, subdir));
187
- const versionsBase = path.join(AGENTS_DIR, 'versions', agent);
188
- if (fs.existsSync(versionsBase)) {
194
+ for (const root of VERSIONS_ROOTS) {
195
+ const versionsBase = path.join(root, 'versions', agent);
196
+ if (!fs.existsSync(versionsBase))
197
+ continue;
189
198
  try {
190
199
  for (const version of fs.readdirSync(versionsBase)) {
191
200
  addDir(path.join(versionsBase, version, 'home', `.${agent}`, subdir));
@@ -219,8 +228,10 @@ function getClaudeAccount() {
219
228
  path.join(HOME, '.claude', '.claude.json'),
220
229
  path.join(HOME, '.claude.json'),
221
230
  ];
222
- const versionsBase = path.join(AGENTS_DIR, 'versions', 'claude');
223
- if (fs.existsSync(versionsBase)) {
231
+ for (const root of VERSIONS_ROOTS) {
232
+ const versionsBase = path.join(root, 'versions', 'claude');
233
+ if (!fs.existsSync(versionsBase))
234
+ continue;
224
235
  try {
225
236
  for (const version of fs.readdirSync(versionsBase)) {
226
237
  candidates.push(path.join(versionsBase, version, 'home', '.claude', '.claude.json'));
@@ -405,8 +416,10 @@ function getCodexAccount() {
405
416
  if (cachedCodexAccount !== undefined)
406
417
  return cachedCodexAccount || undefined;
407
418
  const candidates = [path.join(HOME, '.codex', 'auth.json')];
408
- const versionsBase = path.join(AGENTS_DIR, 'versions', 'codex');
409
- if (fs.existsSync(versionsBase)) {
419
+ for (const root of VERSIONS_ROOTS) {
420
+ const versionsBase = path.join(root, 'versions', 'codex');
421
+ if (!fs.existsSync(versionsBase))
422
+ continue;
410
423
  try {
411
424
  for (const version of fs.readdirSync(versionsBase)) {
412
425
  candidates.push(path.join(versionsBase, version, 'home', '.codex', 'auth.json'));
@@ -1451,22 +1464,24 @@ function normalizeVersion(version) {
1451
1464
  const trimmed = version?.trim();
1452
1465
  return trimmed ? trimmed : undefined;
1453
1466
  }
1454
- /** Extract the version number from a managed ~/.agents/versions/<agent>/<version>/... path. */
1467
+ /** Extract the version number from a managed versions/<agent>/<version>/... path under either repo. */
1455
1468
  function extractVersionFromManagedPath(agent, sourcePath) {
1456
1469
  if (!sourcePath)
1457
1470
  return undefined;
1458
1471
  const candidates = [sourcePath, safeRealpathSync(sourcePath) || ''];
1459
- const marker = `/.agents/versions/${agent}/`;
1472
+ const markers = [`/.agents/versions/${agent}/`, `/.agents-system/versions/${agent}/`];
1460
1473
  for (const candidate of candidates) {
1461
1474
  if (!candidate)
1462
1475
  continue;
1463
1476
  const normalized = candidate.split(path.sep).join('/');
1464
- const start = normalized.indexOf(marker);
1465
- if (start === -1)
1466
- continue;
1467
- const version = normalized.slice(start + marker.length).split('/')[0];
1468
- if (version)
1469
- return version;
1477
+ for (const marker of markers) {
1478
+ const start = normalized.indexOf(marker);
1479
+ if (start === -1)
1480
+ continue;
1481
+ const version = normalized.slice(start + marker.length).split('/')[0];
1482
+ if (version)
1483
+ return version;
1484
+ }
1470
1485
  }
1471
1486
  return undefined;
1472
1487
  }
@@ -9,12 +9,12 @@
9
9
  import * as fs from 'fs';
10
10
  import * as os from 'os';
11
11
  import * as path from 'path';
12
- import { getAgentsDir } from '../state.js';
12
+ import { getTeamsAgentsDir } from '../state.js';
13
13
  const HOME = os.homedir();
14
14
  // Default path; tests can override via AGENTS_TEAMS_DIR env var.
15
15
  /** Resolve the directory containing per-session team metadata files. */
16
16
  function teamsAgentsDir() {
17
- return process.env.AGENTS_TEAMS_DIR ?? path.join(getAgentsDir(), 'teams', 'agents');
17
+ return process.env.AGENTS_TEAMS_DIR ?? getTeamsAgentsDir();
18
18
  }
19
19
  /**
20
20
  * Determine whether `session` was spawned by `agents teams`.
@@ -58,7 +58,7 @@ export declare function promptConflictStrategy(conflictInfos: ConflictInfo[]): P
58
58
  * v8 — versions moved from ~/.agents-system/versions to ~/.agents/versions
59
59
  * (two-repo split: system = shipped defaults, user = operational state).
60
60
  */
61
- export declare const SHIM_SCHEMA_VERSION = 8;
61
+ export declare const SHIM_SCHEMA_VERSION = 9;
62
62
  /**
63
63
  * Generate the full bash shim script for the given agent. The returned string
64
64
  * is written to ~/.agents/shims/{cliCommand} and made executable.
@@ -90,7 +90,7 @@ export declare function removeShim(agent: AgentId): boolean;
90
90
  * v6 — versions moved from ~/.agents-system/versions to ~/.agents/versions
91
91
  * (two-repo split: system = shipped defaults, user = operational state).
92
92
  */
93
- export declare const VERSIONED_ALIAS_SCHEMA_VERSION = 6;
93
+ export declare const VERSIONED_ALIAS_SCHEMA_VERSION = 7;
94
94
  /**
95
95
  * Generate a versioned alias script that directly execs a specific version.
96
96
  * e.g., claude@2.0.65 -> directly runs that version's binary
package/dist/lib/shims.js CHANGED
@@ -172,7 +172,7 @@ export async function promptConflictStrategy(conflictInfos) {
172
172
  * v8 — versions moved from ~/.agents-system/versions to ~/.agents/versions
173
173
  * (two-repo split: system = shipped defaults, user = operational state).
174
174
  */
175
- export const SHIM_SCHEMA_VERSION = 8;
175
+ export const SHIM_SCHEMA_VERSION = 9;
176
176
  /** Internal marker string used to embed the schema version in shim scripts. */
177
177
  const SHIM_VERSION_MARKER = 'agents-shim-version:';
178
178
  /**
@@ -287,7 +287,7 @@ if [ -z "$VERSION" ]; then
287
287
  exit 1
288
288
  fi
289
289
 
290
- VERSION_DIR="$AGENTS_USER_DIR/versions/$AGENT/$VERSION"
290
+ VERSION_DIR="$AGENTS_USER_DIR/.history/versions/$AGENT/$VERSION"
291
291
  BINARY="$VERSION_DIR/node_modules/.bin/$CLI_COMMAND"
292
292
 
293
293
  # Auto-install if not present
@@ -380,7 +380,7 @@ export function removeShim(agent) {
380
380
  * v6 — versions moved from ~/.agents-system/versions to ~/.agents/versions
381
381
  * (two-repo split: system = shipped defaults, user = operational state).
382
382
  */
383
- export const VERSIONED_ALIAS_SCHEMA_VERSION = 6;
383
+ export const VERSIONED_ALIAS_SCHEMA_VERSION = 7;
384
384
  /** Internal marker string used to embed the schema version in versioned alias scripts. */
385
385
  const VERSIONED_ALIAS_VERSION_MARKER = 'agents-versioned-alias-version:';
386
386
  // The version string is interpolated into a generated bash script and into
@@ -405,14 +405,14 @@ export function generateVersionedAliasScript(agent, version) {
405
405
  ? `
406
406
  # Claude stores OAuth credentials in the macOS keychain. Scope them to this
407
407
  # version's config directory so direct aliases also switch the live account.
408
- export CLAUDE_CONFIG_DIR="$HOME/.agents/versions/${agent}/${version}/home/${configDirName}"
408
+ export CLAUDE_CONFIG_DIR="$HOME/.agents/.history/versions/${agent}/${version}/home/${configDirName}"
409
409
  `
410
410
  : agent === 'codex'
411
411
  ? `
412
412
  # Codex reads its config (approval_policy, sandbox_mode, MCP servers, rules)
413
413
  # from CODEX_HOME. Point direct aliases at the versioned home so permissions
414
414
  # and rules written by agents-cli actually take effect.
415
- export CODEX_HOME="$HOME/.agents/versions/${agent}/${version}/home/${configDirName}"
415
+ export CODEX_HOME="$HOME/.agents/.history/versions/${agent}/${version}/home/${configDirName}"
416
416
  `
417
417
  : '';
418
418
  const launchArgs = agent === 'codex' ? ' -c check_for_update_on_startup=false' : '';
@@ -421,7 +421,7 @@ export CODEX_HOME="$HOME/.agents/versions/${agent}/${version}/home/${configDirNa
421
421
  # ${VERSIONED_ALIAS_VERSION_MARKER} ${VERSIONED_ALIAS_SCHEMA_VERSION}
422
422
  # Direct alias for ${agentConfig.name}@${version}
423
423
 
424
- BINARY="$HOME/.agents/versions/${agent}/${version}/node_modules/.bin/${agentConfig.cliCommand}"
424
+ BINARY="$HOME/.agents/.history/versions/${agent}/${version}/node_modules/.bin/${agentConfig.cliCommand}"
425
425
 
426
426
  if [ ! -x "$BINARY" ]; then
427
427
  echo "agents: ${agent}@${version} not installed" >&2
@@ -11,7 +11,7 @@ import * as path from 'path';
11
11
  import * as os from 'os';
12
12
  import * as yaml from 'yaml';
13
13
  import { SKILLS_CAPABLE_AGENTS, ensureSkillsDir } from './agents.js';
14
- import { getUserSkillsDir, getSkillsDir as getSystemSkillsDir, getProjectAgentsDir, getEnabledExtraRepos } from './state.js';
14
+ import { getUserSkillsDir, getSkillsDir as getSystemSkillsDir, getProjectAgentsDir, getEnabledExtraRepos, getTrashSkillsDir } from './state.js';
15
15
  import { getEffectiveHome, getVersionHomePath, listInstalledVersions } from './versions.js';
16
16
  import { emit } from './events.js';
17
17
  const HOME = os.homedir();
@@ -569,7 +569,11 @@ export function removeSkillFromVersion(agent, version, skillName) {
569
569
  return { success: true };
570
570
  }
571
571
  try {
572
- fs.rmSync(target, { recursive: true, force: true });
572
+ const stamp = new Date().toISOString().replace(/[:.]/g, '-');
573
+ const trashDir = path.join(getTrashSkillsDir(), agent, version, skillName);
574
+ const trashDest = path.join(trashDir, stamp);
575
+ fs.mkdirSync(trashDir, { recursive: true, mode: 0o700 });
576
+ fs.renameSync(target, trashDest);
573
577
  }
574
578
  catch (err) {
575
579
  return { success: false, error: err.message };
@@ -2,11 +2,18 @@
2
2
  * Filesystem layout and persistent state for agents-cli.
3
3
  *
4
4
  * Two roots:
5
- * - ~/.agents-system/ — system repo (npm-shipped resources and canonical read-side defaults)
6
- * - ~/.agents/ — user repo (user-authored commands, skills, hooks, rules, mcp,
7
- * permissions, subagents, profiles, secrets, agents.yaml,
8
- * packages, routines, runs, versions, shims, backups, plugins,
9
- * drive, trash)
5
+ * - ~/.agents-system/ — system repo (npm-shipped resources, read-only defaults)
6
+ * - ~/.agents/ — user repo (resources + agents.yaml + operational state)
7
+ *
8
+ * Inside ~/.agents/, top-level paths hold ONLY resources + agents.yaml.
9
+ * Operational state lives in two sibling buckets:
10
+ *
11
+ * - ~/.agents/.history/ — durable runtime data (sessions, versions, runs,
12
+ * teams/agents, trash, backups). Backed up by
13
+ * `agents repo push`.
14
+ * - ~/.agents/.cache/ — regenerable runtime data (shims, packages, helpers
15
+ * for daemon/pty, terminals, cloud, drive, browser
16
+ * chrome-data, logs, swarmify). Gitignored.
10
17
  *
11
18
  * Resolution precedence for resources: project > user > system.
12
19
  * Every module that needs a path or reads/writes agents.yaml goes through here.
@@ -48,8 +55,21 @@ export declare function getMcpDir(): string;
48
55
  export declare function getPermissionsDir(): string;
49
56
  /** Path to subagent definition directories — system repo. */
50
57
  export declare function getSubagentsDir(): string;
51
- /** Path to ~/.agents-system/promptcuts.yaml. */
58
+ /** Path to ~/.agents-system/hooks/promptcuts.yaml (system defaults). */
52
59
  export declare function getPromptcutsPath(): string;
60
+ /**
61
+ * Resolve the effective promptcuts file: user file if it exists, otherwise
62
+ * the system file. Use this for callers that need a single path (doctor
63
+ * diff, displaying which file is in play). Callers that need the merged
64
+ * shortcut set should use readMergedPromptcuts() instead.
65
+ */
66
+ export declare function getEffectivePromptcutsPath(): string;
67
+ /**
68
+ * Read promptcuts from system + user with user precedence. Returns the
69
+ * merged `shortcuts` map. Same layering model as parseHookManifest().
70
+ * Returns an empty object when neither file exists or both fail to parse.
71
+ */
72
+ export declare function readMergedPromptcuts(): Record<string, unknown>;
53
73
  /** Path to the legacy MCP config JSON. */
54
74
  export declare function getMcpConfigPath(): string;
55
75
  /** Path to the global instructions file. */
@@ -71,26 +91,78 @@ export declare function getUserPermissionsDir(): string;
71
91
  export declare function getUserSubagentsDir(): string;
72
92
  export declare function getUserSecretsDir(): string;
73
93
  export declare function getUserPromptcutsPath(): string;
74
- /** Path to cloned packages (~/.agents/packages/). */
94
+ /** Bucket root for durable runtime data (~/.agents/.history/). */
95
+ export declare function getHistoryDir(): string;
96
+ /** Bucket root for regenerable runtime data (~/.agents/.cache/). */
97
+ export declare function getCacheDir(): string;
98
+ /** Path to cloned packages (~/.agents/.cache/packages/). */
75
99
  export declare function getPackagesDir(): string;
76
100
  /** Path to routine YAML definitions (~/.agents/routines/). */
77
101
  export declare function getRoutinesDir(): string;
78
- /** Path to routine execution logs (~/.agents/runs/). */
102
+ /** Path to routine execution logs (~/.agents/.history/runs/). */
79
103
  export declare function getRunsDir(): string;
80
- /** Path to installed agent CLI binaries (~/.agents/versions/). */
104
+ /** Path to installed agent CLI binaries (~/.agents/.history/versions/). */
81
105
  export declare function getVersionsDir(): string;
82
- /** Path to version-switching shim scripts (~/.agents/shims/). */
106
+ /** Path to version-switching shim scripts (~/.agents/.cache/shims/). */
83
107
  export declare function getShimsDir(): string;
84
- /** Path to config backups (~/.agents/backups/). */
108
+ /** Path to per-agent installed CLI binaries (~/.agents/.cache/bin/). */
109
+ export declare function getBinDir(): string;
110
+ /** Path to config backups (~/.agents/.history/backups/). */
85
111
  export declare function getBackupsDir(): string;
86
- /** Path to plugin bundles (~/.agents/plugins/). */
112
+ /** Path to plugin bundles (~/.agents/.cache/plugins/). */
87
113
  export declare function getPluginsDir(): string;
88
- /** Path to synced remote session data (~/.agents/drive/). */
114
+ /** Path to synced remote session data (~/.agents/.cache/drive/). */
89
115
  export declare function getDriveDir(): string;
90
- /** Path to soft-deleted resources (~/.agents/trash/). */
116
+ /** Path to soft-deleted resources (~/.agents/.history/trash/). */
91
117
  export declare function getTrashDir(): string;
118
+ /** Path to local session indexer storage (~/.agents/.history/sessions/). */
119
+ export declare function getSessionsDir(): string;
120
+ /** Path to the session index database (~/.agents/.history/sessions.db). */
121
+ export declare function getSessionsDbPath(): string;
122
+ /** Path to teams config + registry (~/.agents/teams/). */
123
+ export declare function getTeamsDir(): string;
124
+ /** Path to teams execution history (~/.agents/.history/teams/agents/). */
125
+ export declare function getTeamsAgentsDir(): string;
126
+ /** Path to cloud dispatch cache (~/.agents/.cache/cloud/). */
127
+ export declare function getCloudDir(): string;
128
+ /** Path to terminal session metadata (~/.agents/.cache/terminals/). */
129
+ export declare function getTerminalsDir(): string;
130
+ /** Path to runtime logs (~/.agents/.cache/logs/). */
131
+ export declare function getLogsDir(): string;
132
+ /** Path to per-process runtime state (~/.agents/.cache/state/). */
133
+ export declare function getRuntimeStateDir(): string;
134
+ /** Path to swarmify-extension scratch (~/.agents/.cache/swarmify/). */
135
+ export declare function getSwarmifyDir(): string;
136
+ /** Path to browser runtime data — chrome-data, pids (~/.agents/.cache/browser/). */
137
+ export declare function getBrowserRuntimeDir(): string;
138
+ /** Path to helper subprocess scratch (~/.agents/.cache/helpers/). */
139
+ export declare function getHelpersDir(): string;
140
+ /** Path to scheduler daemon scratch (~/.agents/.cache/helpers/daemon/). */
141
+ export declare function getDaemonDir(): string;
142
+ /** Path to PTY server scratch (~/.agents/.cache/helpers/pty/). */
143
+ export declare function getPtyDir(): string;
144
+ /** Path to remote-resource auto-pull cache (~/.agents/.cache/.fetch/). */
145
+ export declare function getFetchCacheDir(): string;
146
+ /** Path to the CLI version cache file (~/.agents/.cache/.cli-version-cache.json). */
147
+ export declare function getCliVersionCachePath(): string;
148
+ /** Path to the models cache file (~/.agents/.cache/.models-cache.json). */
149
+ export declare function getModelsCachePath(): string;
150
+ /** Path to the daily update-check sentinel (~/.agents/.cache/.update-check). */
151
+ export declare function getUpdateCheckPath(): string;
152
+ /** Path to the migration sentinel (~/.agents/.cache/.migrated). */
153
+ export declare function getMigratedSentinelPath(): string;
92
154
  /** Path to soft-deleted version dirs (~/.agents/trash/versions/). */
93
155
  export declare function getTrashVersionsDir(): string;
156
+ /** Path to soft-deleted skills (~/.agents/trash/skills/). */
157
+ export declare function getTrashSkillsDir(): string;
158
+ /** Path to soft-deleted commands (~/.agents/trash/commands/). */
159
+ export declare function getTrashCommandsDir(): string;
160
+ /** Path to soft-deleted hooks (~/.agents/trash/hooks/). */
161
+ export declare function getTrashHooksDir(): string;
162
+ /** Path to soft-deleted plugins (~/.agents/trash/plugins/). */
163
+ export declare function getTrashPluginsDir(): string;
164
+ /** Path to soft-deleted subagents (~/.agents/trash/subagents/). */
165
+ export declare function getTrashSubagentsDir(): string;
94
166
  /**
95
167
  * Path to a single user-level extra DotAgent repo clone (~/.agents-<alias>/).
96
168
  *
package/dist/lib/state.js CHANGED
@@ -2,11 +2,18 @@
2
2
  * Filesystem layout and persistent state for agents-cli.
3
3
  *
4
4
  * Two roots:
5
- * - ~/.agents-system/ — system repo (npm-shipped resources and canonical read-side defaults)
6
- * - ~/.agents/ — user repo (user-authored commands, skills, hooks, rules, mcp,
7
- * permissions, subagents, profiles, secrets, agents.yaml,
8
- * packages, routines, runs, versions, shims, backups, plugins,
9
- * drive, trash)
5
+ * - ~/.agents-system/ — system repo (npm-shipped resources, read-only defaults)
6
+ * - ~/.agents/ — user repo (resources + agents.yaml + operational state)
7
+ *
8
+ * Inside ~/.agents/, top-level paths hold ONLY resources + agents.yaml.
9
+ * Operational state lives in two sibling buckets:
10
+ *
11
+ * - ~/.agents/.history/ — durable runtime data (sessions, versions, runs,
12
+ * teams/agents, trash, backups). Backed up by
13
+ * `agents repo push`.
14
+ * - ~/.agents/.cache/ — regenerable runtime data (shims, packages, helpers
15
+ * for daemon/pty, terminals, cloud, drive, browser
16
+ * chrome-data, logs, swarmify). Gitignored.
10
17
  *
11
18
  * Resolution precedence for resources: project > user > system.
12
19
  * Every module that needs a path or reads/writes agents.yaml goes through here.
@@ -37,16 +44,42 @@ const SYSTEM_SUBAGENTS_DIR = path.join(SYSTEM_AGENTS_DIR, 'subagents');
37
44
  const SYSTEM_PROMPTCUTS_FILE = path.join(SYSTEM_AGENTS_DIR, 'hooks', 'promptcuts.yaml');
38
45
  const SYSTEM_MCP_CONFIG_FILE = path.join(SYSTEM_AGENTS_DIR, 'mcp.json');
39
46
  const SYSTEM_INSTRUCTIONS_FILE = path.join(SYSTEM_AGENTS_DIR, 'instructions.md');
40
- // User-level operational state
41
- const PACKAGES_DIR = path.join(USER_AGENTS_DIR, 'packages');
47
+ // ─── User repo operational buckets ────────────────────────────────────────────
48
+ /** Durable runtime data (sessions, versions, runs, teams history, trash, backups). */
49
+ const HISTORY_DIR = path.join(USER_AGENTS_DIR, '.history');
50
+ /** Regenerable runtime data (shims, packages, helpers, terminals, cloud, drive, logs, browser). */
51
+ const CACHE_DIR = path.join(USER_AGENTS_DIR, '.cache');
52
+ // Top-level user dirs (config/definitions only — runtime moves into .history/.cache).
42
53
  const ROUTINES_DIR = path.join(USER_AGENTS_DIR, 'routines');
43
- const RUNS_DIR = path.join(ROUTINES_DIR, 'runs');
44
- const VERSIONS_DIR = path.join(USER_AGENTS_DIR, 'versions');
45
- const SHIMS_DIR = path.join(USER_AGENTS_DIR, 'shims');
46
- const BACKUPS_DIR = path.join(USER_AGENTS_DIR, '.backups');
47
- const PLUGINS_DIR = path.join(USER_AGENTS_DIR, 'plugins');
48
- const DRIVE_DIR = path.join(USER_AGENTS_DIR, 'drive');
49
- const TRASH_DIR = path.join(USER_AGENTS_DIR, '.trash');
54
+ const TEAMS_DIR = path.join(USER_AGENTS_DIR, 'teams');
55
+ // History bucket (durable).
56
+ const SESSIONS_DIR = path.join(HISTORY_DIR, 'sessions');
57
+ const SESSIONS_DB_PATH = path.join(SESSIONS_DIR, 'sessions.db');
58
+ const VERSIONS_DIR = path.join(HISTORY_DIR, 'versions');
59
+ const RUNS_DIR = path.join(HISTORY_DIR, 'runs');
60
+ const TEAMS_AGENTS_DIR = path.join(HISTORY_DIR, 'teams', 'agents');
61
+ const BACKUPS_DIR = path.join(HISTORY_DIR, 'backups');
62
+ const TRASH_DIR = path.join(HISTORY_DIR, 'trash');
63
+ // Cache bucket (regenerable).
64
+ const SHIMS_DIR = path.join(CACHE_DIR, 'shims');
65
+ const BIN_DIR = path.join(CACHE_DIR, 'bin');
66
+ const PACKAGES_DIR = path.join(CACHE_DIR, 'packages');
67
+ const PLUGINS_DIR = path.join(CACHE_DIR, 'plugins');
68
+ const CLOUD_DIR = path.join(CACHE_DIR, 'cloud');
69
+ const DRIVE_DIR = path.join(CACHE_DIR, 'drive');
70
+ const TERMINALS_DIR = path.join(CACHE_DIR, 'terminals');
71
+ const LOGS_DIR = path.join(CACHE_DIR, 'logs');
72
+ const RUNTIME_STATE_DIR = path.join(CACHE_DIR, 'state');
73
+ const SWARMIFY_DIR = path.join(CACHE_DIR, 'swarmify');
74
+ const BROWSER_RUNTIME_DIR = path.join(CACHE_DIR, 'browser');
75
+ const HELPERS_DIR = path.join(CACHE_DIR, 'helpers');
76
+ const DAEMON_DIR = path.join(HELPERS_DIR, 'daemon');
77
+ const PTY_DIR = path.join(HELPERS_DIR, 'pty');
78
+ const FETCH_CACHE_DIR = path.join(CACHE_DIR, '.fetch');
79
+ const CLI_VERSION_CACHE_FILE = path.join(CACHE_DIR, '.cli-version-cache.json');
80
+ const MODELS_CACHE_FILE = path.join(CACHE_DIR, '.models-cache.json');
81
+ const UPDATE_CHECK_FILE = path.join(CACHE_DIR, '.update-check');
82
+ const MIGRATED_SENTINEL_FILE = path.join(CACHE_DIR, '.migrated');
50
83
  // ─── User resource dirs ───────────────────────────────────────────────────────
51
84
  const USER_COMMANDS_DIR = path.join(USER_AGENTS_DIR, 'commands');
52
85
  const USER_HOOKS_DIR = path.join(USER_AGENTS_DIR, 'hooks');
@@ -147,8 +180,43 @@ export function getMcpDir() { return SYSTEM_MCP_DIR; }
147
180
  export function getPermissionsDir() { return SYSTEM_PERMISSIONS_DIR; }
148
181
  /** Path to subagent definition directories — system repo. */
149
182
  export function getSubagentsDir() { return SYSTEM_SUBAGENTS_DIR; }
150
- /** Path to ~/.agents-system/promptcuts.yaml. */
183
+ /** Path to ~/.agents-system/hooks/promptcuts.yaml (system defaults). */
151
184
  export function getPromptcutsPath() { return SYSTEM_PROMPTCUTS_FILE; }
185
+ /**
186
+ * Resolve the effective promptcuts file: user file if it exists, otherwise
187
+ * the system file. Use this for callers that need a single path (doctor
188
+ * diff, displaying which file is in play). Callers that need the merged
189
+ * shortcut set should use readMergedPromptcuts() instead.
190
+ */
191
+ export function getEffectivePromptcutsPath() {
192
+ if (fs.existsSync(USER_PROMPTCUTS_FILE))
193
+ return USER_PROMPTCUTS_FILE;
194
+ return SYSTEM_PROMPTCUTS_FILE;
195
+ }
196
+ /**
197
+ * Read promptcuts from system + user with user precedence. Returns the
198
+ * merged `shortcuts` map. Same layering model as parseHookManifest().
199
+ * Returns an empty object when neither file exists or both fail to parse.
200
+ */
201
+ export function readMergedPromptcuts() {
202
+ const merged = {};
203
+ for (const filePath of [SYSTEM_PROMPTCUTS_FILE, USER_PROMPTCUTS_FILE]) {
204
+ if (!fs.existsSync(filePath))
205
+ continue;
206
+ try {
207
+ const parsed = yaml.parse(fs.readFileSync(filePath, 'utf-8'));
208
+ if (!parsed?.shortcuts)
209
+ continue;
210
+ for (const [key, value] of Object.entries(parsed.shortcuts)) {
211
+ merged[key] = value;
212
+ }
213
+ }
214
+ catch {
215
+ // Skip unreadable file, keep going
216
+ }
217
+ }
218
+ return merged;
219
+ }
152
220
  /** Path to the legacy MCP config JSON. */
153
221
  export function getMcpConfigPath() { return SYSTEM_MCP_CONFIG_FILE; }
154
222
  /** Path to the global instructions file. */
@@ -173,26 +241,81 @@ export function getUserSubagentsDir() { return USER_SUBAGENTS_DIR; }
173
241
  export function getUserSecretsDir() { return USER_SECRETS_DIR; }
174
242
  export function getUserPromptcutsPath() { return USER_PROMPTCUTS_FILE; }
175
243
  // ─── User operational path getters ────────────────────────────────────────────
176
- /** Path to cloned packages (~/.agents/packages/). */
244
+ //
245
+ // Top-level dirs hold definitions/configs only; runtime data lives under
246
+ // .history/ (durable) or .cache/ (regenerable). See file header.
247
+ /** Bucket root for durable runtime data (~/.agents/.history/). */
248
+ export function getHistoryDir() { return HISTORY_DIR; }
249
+ /** Bucket root for regenerable runtime data (~/.agents/.cache/). */
250
+ export function getCacheDir() { return CACHE_DIR; }
251
+ /** Path to cloned packages (~/.agents/.cache/packages/). */
177
252
  export function getPackagesDir() { return PACKAGES_DIR; }
178
253
  /** Path to routine YAML definitions (~/.agents/routines/). */
179
254
  export function getRoutinesDir() { return ROUTINES_DIR; }
180
- /** Path to routine execution logs (~/.agents/runs/). */
255
+ /** Path to routine execution logs (~/.agents/.history/runs/). */
181
256
  export function getRunsDir() { return RUNS_DIR; }
182
- /** Path to installed agent CLI binaries (~/.agents/versions/). */
257
+ /** Path to installed agent CLI binaries (~/.agents/.history/versions/). */
183
258
  export function getVersionsDir() { return VERSIONS_DIR; }
184
- /** Path to version-switching shim scripts (~/.agents/shims/). */
259
+ /** Path to version-switching shim scripts (~/.agents/.cache/shims/). */
185
260
  export function getShimsDir() { return SHIMS_DIR; }
186
- /** Path to config backups (~/.agents/backups/). */
261
+ /** Path to per-agent installed CLI binaries (~/.agents/.cache/bin/). */
262
+ export function getBinDir() { return BIN_DIR; }
263
+ /** Path to config backups (~/.agents/.history/backups/). */
187
264
  export function getBackupsDir() { return BACKUPS_DIR; }
188
- /** Path to plugin bundles (~/.agents/plugins/). */
265
+ /** Path to plugin bundles (~/.agents/.cache/plugins/). */
189
266
  export function getPluginsDir() { return PLUGINS_DIR; }
190
- /** Path to synced remote session data (~/.agents/drive/). */
267
+ /** Path to synced remote session data (~/.agents/.cache/drive/). */
191
268
  export function getDriveDir() { return DRIVE_DIR; }
192
- /** Path to soft-deleted resources (~/.agents/trash/). */
269
+ /** Path to soft-deleted resources (~/.agents/.history/trash/). */
193
270
  export function getTrashDir() { return TRASH_DIR; }
271
+ /** Path to local session indexer storage (~/.agents/.history/sessions/). */
272
+ export function getSessionsDir() { return SESSIONS_DIR; }
273
+ /** Path to the session index database (~/.agents/.history/sessions.db). */
274
+ export function getSessionsDbPath() { return SESSIONS_DB_PATH; }
275
+ /** Path to teams config + registry (~/.agents/teams/). */
276
+ export function getTeamsDir() { return TEAMS_DIR; }
277
+ /** Path to teams execution history (~/.agents/.history/teams/agents/). */
278
+ export function getTeamsAgentsDir() { return TEAMS_AGENTS_DIR; }
279
+ /** Path to cloud dispatch cache (~/.agents/.cache/cloud/). */
280
+ export function getCloudDir() { return CLOUD_DIR; }
281
+ /** Path to terminal session metadata (~/.agents/.cache/terminals/). */
282
+ export function getTerminalsDir() { return TERMINALS_DIR; }
283
+ /** Path to runtime logs (~/.agents/.cache/logs/). */
284
+ export function getLogsDir() { return LOGS_DIR; }
285
+ /** Path to per-process runtime state (~/.agents/.cache/state/). */
286
+ export function getRuntimeStateDir() { return RUNTIME_STATE_DIR; }
287
+ /** Path to swarmify-extension scratch (~/.agents/.cache/swarmify/). */
288
+ export function getSwarmifyDir() { return SWARMIFY_DIR; }
289
+ /** Path to browser runtime data — chrome-data, pids (~/.agents/.cache/browser/). */
290
+ export function getBrowserRuntimeDir() { return BROWSER_RUNTIME_DIR; }
291
+ /** Path to helper subprocess scratch (~/.agents/.cache/helpers/). */
292
+ export function getHelpersDir() { return HELPERS_DIR; }
293
+ /** Path to scheduler daemon scratch (~/.agents/.cache/helpers/daemon/). */
294
+ export function getDaemonDir() { return DAEMON_DIR; }
295
+ /** Path to PTY server scratch (~/.agents/.cache/helpers/pty/). */
296
+ export function getPtyDir() { return PTY_DIR; }
297
+ /** Path to remote-resource auto-pull cache (~/.agents/.cache/.fetch/). */
298
+ export function getFetchCacheDir() { return FETCH_CACHE_DIR; }
299
+ /** Path to the CLI version cache file (~/.agents/.cache/.cli-version-cache.json). */
300
+ export function getCliVersionCachePath() { return CLI_VERSION_CACHE_FILE; }
301
+ /** Path to the models cache file (~/.agents/.cache/.models-cache.json). */
302
+ export function getModelsCachePath() { return MODELS_CACHE_FILE; }
303
+ /** Path to the daily update-check sentinel (~/.agents/.cache/.update-check). */
304
+ export function getUpdateCheckPath() { return UPDATE_CHECK_FILE; }
305
+ /** Path to the migration sentinel (~/.agents/.cache/.migrated). */
306
+ export function getMigratedSentinelPath() { return MIGRATED_SENTINEL_FILE; }
194
307
  /** Path to soft-deleted version dirs (~/.agents/trash/versions/). */
195
308
  export function getTrashVersionsDir() { return path.join(TRASH_DIR, 'versions'); }
309
+ /** Path to soft-deleted skills (~/.agents/trash/skills/). */
310
+ export function getTrashSkillsDir() { return path.join(TRASH_DIR, 'skills'); }
311
+ /** Path to soft-deleted commands (~/.agents/trash/commands/). */
312
+ export function getTrashCommandsDir() { return path.join(TRASH_DIR, 'commands'); }
313
+ /** Path to soft-deleted hooks (~/.agents/trash/hooks/). */
314
+ export function getTrashHooksDir() { return path.join(TRASH_DIR, 'hooks'); }
315
+ /** Path to soft-deleted plugins (~/.agents/trash/plugins/). */
316
+ export function getTrashPluginsDir() { return path.join(TRASH_DIR, 'plugins'); }
317
+ /** Path to soft-deleted subagents (~/.agents/trash/subagents/). */
318
+ export function getTrashSubagentsDir() { return path.join(TRASH_DIR, 'subagents'); }
196
319
  /**
197
320
  * Path to a single user-level extra DotAgent repo clone (~/.agents-<alias>/).
198
321
  *
@@ -242,6 +365,10 @@ export function ensureAgentsDir() {
242
365
  if (!fs.existsSync(SYSTEM_AGENTS_DIR)) {
243
366
  fs.mkdirSync(SYSTEM_AGENTS_DIR, opts);
244
367
  }
368
+ if (!fs.existsSync(HISTORY_DIR))
369
+ fs.mkdirSync(HISTORY_DIR, opts);
370
+ if (!fs.existsSync(CACHE_DIR))
371
+ fs.mkdirSync(CACHE_DIR, opts);
245
372
  if (!fs.existsSync(PACKAGES_DIR))
246
373
  fs.mkdirSync(PACKAGES_DIR, opts);
247
374
  if (!fs.existsSync(ROUTINES_DIR))
@@ -81,3 +81,31 @@ export declare const SUBAGENT_CAPABLE_AGENTS: AgentId[];
81
81
  * OpenClaw: scans ~/.openclaw/{name}/AGENTS.md
82
82
  */
83
83
  export declare function listSubagentsForAgent(agentId: AgentId, home: string): InstalledSubagent[];
84
+ export interface VersionSubagentDiff {
85
+ agent: AgentId;
86
+ version: string;
87
+ orphans: string[];
88
+ }
89
+ /**
90
+ * Compare a version home's subagents against discovered subagents.
91
+ * Returns orphan subagent names.
92
+ */
93
+ export declare function diffVersionSubagents(agent: AgentId, version: string): VersionSubagentDiff;
94
+ /**
95
+ * Iterate all (agent, version) pairs that support subagents and are installed.
96
+ */
97
+ export declare function iterSubagentsCapableVersions(filter?: {
98
+ agent?: AgentId;
99
+ version?: string;
100
+ }): Array<{
101
+ agent: AgentId;
102
+ version: string;
103
+ }>;
104
+ /**
105
+ * Remove a single subagent from a specific version home.
106
+ * Soft-deletes to ~/.agents/.trash/subagents/.
107
+ */
108
+ export declare function removeSubagentFromVersion(agent: AgentId, version: string, subagentName: string): {
109
+ success: boolean;
110
+ error?: string;
111
+ };