@phnx-labs/agents-cli 1.20.3 → 1.20.5

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 (193) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +48 -17
  3. package/dist/commands/cli.js +1 -1
  4. package/dist/commands/cloud.js +1 -1
  5. package/dist/commands/commands.js +2 -0
  6. package/dist/commands/doctor.js +1 -1
  7. package/dist/commands/exec.js +52 -16
  8. package/dist/commands/hooks.js +6 -6
  9. package/dist/commands/import.js +90 -37
  10. package/dist/commands/inspect.d.ts +26 -0
  11. package/dist/commands/inspect.js +590 -0
  12. package/dist/commands/mcp.js +17 -16
  13. package/dist/commands/models.js +1 -1
  14. package/dist/commands/packages.js +6 -4
  15. package/dist/commands/permissions.js +13 -12
  16. package/dist/commands/plugins.d.ts +13 -0
  17. package/dist/commands/plugins.js +100 -11
  18. package/dist/commands/prune.js +3 -2
  19. package/dist/commands/pull.d.ts +12 -5
  20. package/dist/commands/pull.js +26 -422
  21. package/dist/commands/push.d.ts +14 -0
  22. package/dist/commands/push.js +30 -0
  23. package/dist/commands/repo.d.ts +1 -1
  24. package/dist/commands/repo.js +155 -112
  25. package/dist/commands/resource-view.d.ts +2 -0
  26. package/dist/commands/resource-view.js +12 -3
  27. package/dist/commands/routines.js +32 -7
  28. package/dist/commands/rules.js +1 -1
  29. package/dist/commands/sessions.js +1 -0
  30. package/dist/commands/setup.d.ts +3 -3
  31. package/dist/commands/setup.js +15 -15
  32. package/dist/commands/skills.js +6 -5
  33. package/dist/commands/subagents.js +5 -4
  34. package/dist/commands/sync.d.ts +18 -5
  35. package/dist/commands/sync.js +251 -65
  36. package/dist/commands/teams.js +1 -0
  37. package/dist/commands/tmux.d.ts +25 -0
  38. package/dist/commands/tmux.js +415 -0
  39. package/dist/commands/trash.d.ts +2 -2
  40. package/dist/commands/trash.js +1 -1
  41. package/dist/commands/versions.js +2 -2
  42. package/dist/commands/view.js +14 -4
  43. package/dist/commands/workflows.js +4 -3
  44. package/dist/commands/worktree.d.ts +4 -5
  45. package/dist/commands/worktree.js +4 -4
  46. package/dist/index.js +68 -20
  47. package/dist/lib/agents.d.ts +19 -10
  48. package/dist/lib/agents.js +102 -28
  49. package/dist/lib/auto-pull-worker.d.ts +1 -1
  50. package/dist/lib/auto-pull-worker.js +2 -2
  51. package/dist/lib/auto-pull.d.ts +1 -1
  52. package/dist/lib/auto-pull.js +1 -1
  53. package/dist/lib/beta.d.ts +1 -1
  54. package/dist/lib/beta.js +1 -1
  55. package/dist/lib/capabilities.js +2 -0
  56. package/dist/lib/commands.d.ts +28 -1
  57. package/dist/lib/commands.js +125 -20
  58. package/dist/lib/doctor-diff.js +2 -2
  59. package/dist/lib/exec.d.ts +14 -0
  60. package/dist/lib/exec.js +39 -5
  61. package/dist/lib/fuzzy.d.ts +12 -2
  62. package/dist/lib/fuzzy.js +29 -4
  63. package/dist/lib/git.js +8 -1
  64. package/dist/lib/hooks.d.ts +2 -2
  65. package/dist/lib/hooks.js +97 -10
  66. package/dist/lib/import.d.ts +21 -0
  67. package/dist/lib/import.js +55 -2
  68. package/dist/lib/mcp.js +32 -2
  69. package/dist/lib/migrate.d.ts +51 -0
  70. package/dist/lib/migrate.js +227 -1
  71. package/dist/lib/models.js +62 -15
  72. package/dist/lib/permissions.d.ts +36 -2
  73. package/dist/lib/permissions.js +217 -7
  74. package/dist/lib/plugin-marketplace.d.ts +108 -40
  75. package/dist/lib/plugin-marketplace.js +243 -94
  76. package/dist/lib/plugins.d.ts +21 -4
  77. package/dist/lib/plugins.js +130 -49
  78. package/dist/lib/profiles-presets.js +12 -12
  79. package/dist/lib/project-launch.d.ts +65 -0
  80. package/dist/lib/project-launch.js +367 -0
  81. package/dist/lib/pty-client.js +1 -1
  82. package/dist/lib/pty-server.d.ts +1 -1
  83. package/dist/lib/pty-server.js +28 -4
  84. package/dist/lib/refresh.d.ts +26 -0
  85. package/dist/lib/refresh.js +315 -0
  86. package/dist/lib/resource-patterns.d.ts +1 -1
  87. package/dist/lib/resource-patterns.js +1 -1
  88. package/dist/lib/resources/commands.js +2 -2
  89. package/dist/lib/resources/hooks.d.ts +1 -1
  90. package/dist/lib/resources/hooks.js +1 -1
  91. package/dist/lib/resources/mcp.d.ts +1 -1
  92. package/dist/lib/resources/mcp.js +5 -6
  93. package/dist/lib/resources/permissions.js +5 -2
  94. package/dist/lib/resources/rules.js +3 -2
  95. package/dist/lib/resources/skills.js +3 -2
  96. package/dist/lib/resources/types.d.ts +1 -1
  97. package/dist/lib/resources.js +2 -2
  98. package/dist/lib/rotate.d.ts +1 -1
  99. package/dist/lib/rotate.js +1 -1
  100. package/dist/lib/routines.d.ts +16 -4
  101. package/dist/lib/routines.js +67 -17
  102. package/dist/lib/rules/compile.js +22 -10
  103. package/dist/lib/rules/rules.js +3 -3
  104. package/dist/lib/runner.js +16 -3
  105. package/dist/lib/scheduler.js +15 -1
  106. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  107. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  108. package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +9 -1
  109. package/dist/lib/secrets/Agents CLI.app/Contents/embedded.provisionprofile +0 -0
  110. package/dist/lib/secrets/linux.d.ts +44 -9
  111. package/dist/lib/secrets/linux.js +302 -48
  112. package/dist/lib/session/db.js +15 -2
  113. package/dist/lib/session/discover.js +118 -3
  114. package/dist/lib/session/parse.js +3 -0
  115. package/dist/lib/session/types.d.ts +1 -1
  116. package/dist/lib/session/types.js +1 -1
  117. package/dist/lib/shims.d.ts +10 -9
  118. package/dist/lib/shims.js +101 -50
  119. package/dist/lib/skills.d.ts +1 -1
  120. package/dist/lib/skills.js +10 -9
  121. package/dist/lib/staleness/detectors/commands.d.ts +3 -0
  122. package/dist/lib/staleness/detectors/commands.js +46 -0
  123. package/dist/lib/staleness/detectors/hooks.d.ts +3 -0
  124. package/dist/lib/staleness/detectors/hooks.js +44 -0
  125. package/dist/lib/staleness/detectors/mcp.d.ts +3 -0
  126. package/dist/lib/staleness/detectors/mcp.js +31 -0
  127. package/dist/lib/staleness/detectors/permissions.d.ts +3 -0
  128. package/dist/lib/staleness/detectors/permissions.js +201 -0
  129. package/dist/lib/staleness/detectors/plugins.d.ts +8 -0
  130. package/dist/lib/staleness/detectors/plugins.js +23 -0
  131. package/dist/lib/staleness/detectors/rules.d.ts +3 -0
  132. package/dist/lib/staleness/detectors/rules.js +34 -0
  133. package/dist/lib/staleness/detectors/skills.d.ts +3 -0
  134. package/dist/lib/staleness/detectors/skills.js +71 -0
  135. package/dist/lib/staleness/detectors/subagents.d.ts +3 -0
  136. package/dist/lib/staleness/detectors/subagents.js +50 -0
  137. package/dist/lib/staleness/detectors/types.d.ts +22 -0
  138. package/dist/lib/staleness/detectors/types.js +1 -0
  139. package/dist/lib/staleness/detectors/workflows.d.ts +3 -0
  140. package/dist/lib/staleness/detectors/workflows.js +28 -0
  141. package/dist/lib/staleness/registry.d.ts +26 -0
  142. package/dist/lib/staleness/registry.js +123 -0
  143. package/dist/lib/staleness/writers/commands.d.ts +3 -0
  144. package/dist/lib/staleness/writers/commands.js +111 -0
  145. package/dist/lib/staleness/writers/hooks.d.ts +3 -0
  146. package/dist/lib/staleness/writers/hooks.js +47 -0
  147. package/dist/lib/staleness/writers/kinds.d.ts +10 -0
  148. package/dist/lib/staleness/writers/kinds.js +15 -0
  149. package/dist/lib/staleness/writers/lazy-map.d.ts +13 -0
  150. package/dist/lib/staleness/writers/lazy-map.js +19 -0
  151. package/dist/lib/staleness/writers/mcp.d.ts +10 -0
  152. package/dist/lib/staleness/writers/mcp.js +19 -0
  153. package/dist/lib/staleness/writers/permissions.d.ts +13 -0
  154. package/dist/lib/staleness/writers/permissions.js +26 -0
  155. package/dist/lib/staleness/writers/plugins.d.ts +7 -0
  156. package/dist/lib/staleness/writers/plugins.js +31 -0
  157. package/dist/lib/staleness/writers/rules.d.ts +7 -0
  158. package/dist/lib/staleness/writers/rules.js +55 -0
  159. package/dist/lib/staleness/writers/skills.d.ts +3 -0
  160. package/dist/lib/staleness/writers/skills.js +81 -0
  161. package/dist/lib/staleness/writers/sources.d.ts +16 -0
  162. package/dist/lib/staleness/writers/sources.js +72 -0
  163. package/dist/lib/staleness/writers/subagents.d.ts +3 -0
  164. package/dist/lib/staleness/writers/subagents.js +53 -0
  165. package/dist/lib/staleness/writers/types.d.ts +36 -0
  166. package/dist/lib/staleness/writers/types.js +1 -0
  167. package/dist/lib/staleness/writers/workflows.d.ts +7 -0
  168. package/dist/lib/staleness/writers/workflows.js +31 -0
  169. package/dist/lib/state.d.ts +34 -11
  170. package/dist/lib/state.js +58 -13
  171. package/dist/lib/subagents.d.ts +0 -2
  172. package/dist/lib/subagents.js +6 -6
  173. package/dist/lib/teams/agents.js +1 -1
  174. package/dist/lib/teams/parsers.d.ts +1 -1
  175. package/dist/lib/tmux/binary.d.ts +67 -0
  176. package/dist/lib/tmux/binary.js +141 -0
  177. package/dist/lib/tmux/index.d.ts +8 -0
  178. package/dist/lib/tmux/index.js +8 -0
  179. package/dist/lib/tmux/paths.d.ts +17 -0
  180. package/dist/lib/tmux/paths.js +30 -0
  181. package/dist/lib/tmux/session.d.ts +122 -0
  182. package/dist/lib/tmux/session.js +305 -0
  183. package/dist/lib/types.d.ts +58 -7
  184. package/dist/lib/types.js +1 -1
  185. package/dist/lib/usage.js +1 -1
  186. package/dist/lib/versions.d.ts +4 -4
  187. package/dist/lib/versions.js +154 -491
  188. package/dist/lib/workflows.d.ts +2 -4
  189. package/dist/lib/workflows.js +3 -4
  190. package/package.json +7 -7
  191. package/scripts/postinstall.js +16 -63
  192. package/dist/commands/status.d.ts +0 -9
  193. package/dist/commands/status.js +0 -25
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Per-(kind, agent) writer contract.
3
+ *
4
+ * The aggregator in `syncResourcesToVersion` (versions.ts) selects names per
5
+ * kind, then dispatches into a writer from `../registry.ts`. The writer owns
6
+ * everything kind-specific: the agent's storage format, the layered source
7
+ * search, the conversion/copy step. Writers MUST be reached only after a
8
+ * `supports(agent, kind, version).ok === true` precheck — they throw when
9
+ * called on a (kind, agent) pair that the capability matrix says is false.
10
+ *
11
+ * The shape is intentionally narrow. Writers don't accept ambient state; the
12
+ * caller passes the names already resolved against availability. Selection is
13
+ * a string[] for most kinds, a PermissionsSelection object for permissions,
14
+ * and a RulesSelection object for rules — see kind-specific writer modules.
15
+ */
16
+ import type { AgentId } from '../../types.js';
17
+ import type { ResourceKind } from './kinds.js';
18
+ export interface WriteArgs<Sel> {
19
+ /** Agent version (e.g. "1.2.3") — passed for version-gated capability checks and side files. */
20
+ version: string;
21
+ /** Absolute path to the version's home dir, i.e. `~/.agents/.history/versions/<agent>/<version>/home`. */
22
+ versionHome: string;
23
+ /** Kind-specific selection payload. */
24
+ selection: Sel;
25
+ /** Current working directory — used by writers that consult project-layer state. */
26
+ cwd: string;
27
+ }
28
+ export interface WriteResult {
29
+ /** Names actually written. Empty array = write produced nothing (not an error). */
30
+ synced: string[];
31
+ }
32
+ export interface ResourceWriter<Sel = string[]> {
33
+ readonly kind: ResourceKind;
34
+ readonly agent: AgentId;
35
+ write(args: WriteArgs<Sel>): WriteResult;
36
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Workflows writer — copies each workflow directory into
3
+ * `<versionHome>/workflows/<name>/` via `syncWorkflowToVersion`.
4
+ */
5
+ import type { AgentId } from '../../types.js';
6
+ import type { ResourceWriter } from './types.js';
7
+ export declare const workflowsWriters: Partial<Record<AgentId, ResourceWriter<string[]>>>;
@@ -0,0 +1,31 @@
1
+ import { capableAgents } from '../../capabilities.js';
2
+ import { listInstalledWorkflows, syncWorkflowToVersion } from '../../workflows.js';
3
+ import { lazyAgentMap } from './lazy-map.js';
4
+ function buildWorkflowsWriter(agent) {
5
+ return {
6
+ kind: 'workflows',
7
+ agent,
8
+ write({ versionHome, selection }) {
9
+ const all = listInstalledWorkflows();
10
+ const synced = [];
11
+ for (const name of selection) {
12
+ const wf = all.get(name);
13
+ if (!wf)
14
+ continue;
15
+ try {
16
+ const r = syncWorkflowToVersion(wf.path, name, agent, versionHome);
17
+ if (r.success)
18
+ synced.push(name);
19
+ }
20
+ catch { /* per-item failure: skip */ }
21
+ }
22
+ return { synced };
23
+ },
24
+ };
25
+ }
26
+ export const workflowsWriters = lazyAgentMap(() => {
27
+ const m = {};
28
+ for (const agent of capableAgents('workflows'))
29
+ m[agent] = buildWorkflowsWriter(agent);
30
+ return m;
31
+ });
@@ -1,28 +1,34 @@
1
1
  /**
2
2
  * Filesystem layout and persistent state for agents-cli.
3
3
  *
4
- * Two roots:
5
- * - ~/.agents-system/ — system repo (npm-shipped resources, read-only defaults)
6
- * - ~/.agents/ — user repo (resources + agents.yaml + operational state)
4
+ * Single root at ~/.agents/ with three internal buckets:
7
5
  *
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,
6
+ * ~/.agents/ — user repo: user-authored resources + agents.yaml
7
+ * (git-tracked via `agents repo push`).
8
+ * ~/.agents/.system/ — system repo: npm-shipped resources, regenerable.
9
+ * Don't hand-edit; maintained by npm install /
10
+ * `agents repo pull system`.
11
+ * ~/.agents/.history/ — durable runtime data (sessions, versions, runs,
12
12
  * teams/agents, trash, backups). Backed up by
13
13
  * `agents repo push`.
14
- * - ~/.agents/.cache/ — regenerable runtime data (shims, packages, helpers
14
+ * ~/.agents/.cache/ — regenerable runtime data (shims, packages, helpers
15
15
  * for daemon/pty, terminals, cloud, drive, browser
16
16
  * chrome-data, logs, companion). Gitignored.
17
17
  *
18
18
  * Resolution precedence for resources: project > user > system.
19
19
  * Every module that needs a path or reads/writes agents.yaml goes through here.
20
+ *
21
+ * Legacy layout (pre-fold): system repo lived at ~/.agents-system/ as a peer
22
+ * of ~/.agents/. runMigration() folds it into ~/.agents/.system/ on first run
23
+ * and leaves a back-compat symlink at the old path.
20
24
  */
21
25
  import type { Meta } from './types.js';
22
- /** Root of the system data directory (~/.agents-system/). */
26
+ /** Root of the system data directory (~/.agents/.system/). */
23
27
  export declare function getAgentsDir(): string;
24
- /** Root of the system data directory (~/.agents-system/). */
28
+ /** Root of the system data directory (~/.agents/.system/). */
25
29
  export declare function getSystemAgentsDir(): string;
30
+ /** Legacy system-repo location (~/.agents-system/). Exported for migration only. */
31
+ export declare function getLegacySystemAgentsDir(): string;
26
32
  /** Root of the user repo (~/.agents/). Always present after ensureAgentsDir(). */
27
33
  export declare function getUserAgentsDir(): string;
28
34
  /**
@@ -55,7 +61,7 @@ export declare function getMcpDir(): string;
55
61
  export declare function getPermissionsDir(): string;
56
62
  /** Path to subagent definition directories — system repo. */
57
63
  export declare function getSubagentsDir(): string;
58
- /** Path to ~/.agents-system/hooks/promptcuts.yaml (system defaults). */
64
+ /** Path to ~/.agents/.system/hooks/promptcuts.yaml (system defaults). */
59
65
  export declare function getPromptcutsPath(): string;
60
66
  /**
61
67
  * Resolve the effective promptcuts file: user file if it exists, otherwise
@@ -101,6 +107,15 @@ export declare function getCacheDir(): string;
101
107
  export declare function getPackagesDir(): string;
102
108
  /** Path to routine YAML definitions (~/.agents/routines/). */
103
109
  export declare function getRoutinesDir(): string;
110
+ /**
111
+ * Path to a project-scoped routines directory (`<project>/.agents/routines/`),
112
+ * or null when no project `.agents/` is found by walking up from cwd.
113
+ *
114
+ * Project routines participate in `list`/`view`/`run` for inspection but are
115
+ * NOT fired by the daemon (which runs from $HOME and only loads user routines).
116
+ * Opt-in firing for project routines is tracked as a follow-up.
117
+ */
118
+ export declare function getProjectRoutinesDir(cwd?: string): string | null;
104
119
  /** Path to routine execution logs (~/.agents/.history/runs/). */
105
120
  export declare function getRunsDir(): string;
106
121
  /** Path to installed agent CLI binaries (~/.agents/.history/versions/). */
@@ -117,6 +132,12 @@ export declare function getBinDir(): string;
117
132
  export declare function getBackupsDir(): string;
118
133
  /** Path to plugin bundles (~/.agents/plugins/) — user-authored resource. */
119
134
  export declare function getPluginsDir(): string;
135
+ /** Path to system plugin bundles (~/.agents/.system/plugins/) — npm-shipped, read-only defaults. */
136
+ export declare function getSystemPluginsDir(): string;
137
+ /** Path to an extra repo's plugin bundles (~/.agents-<alias>/plugins/). */
138
+ export declare function getExtraPluginsDir(alias: string): string;
139
+ /** Path to a project-scoped plugins directory (<project>/.agents/plugins/), or null when none. */
140
+ export declare function getProjectPluginsDir(cwd?: string): string | null;
120
141
  /** Path to synced remote session data (~/.agents/.cache/drive/). */
121
142
  export declare function getDriveDir(): string;
122
143
  /** Path to soft-deleted resources (~/.agents/.history/trash/). */
@@ -149,6 +170,8 @@ export declare function getHelpersDir(): string;
149
170
  export declare function getDaemonDir(): string;
150
171
  /** Path to PTY server scratch (~/.agents/.cache/helpers/pty/). */
151
172
  export declare function getPtyDir(): string;
173
+ /** Path to tmux scratch (~/.agents/.cache/helpers/tmux/) — shared server socket + per-session meta JSONs. */
174
+ export declare function getTmuxDir(): string;
152
175
  /** Path to remote-resource auto-pull cache (~/.agents/.cache/.fetch/). */
153
176
  export declare function getFetchCacheDir(): string;
154
177
  /** Path to the CLI version cache file (~/.agents/.cache/.cli-version-cache.json). */
package/dist/lib/state.js CHANGED
@@ -1,22 +1,26 @@
1
1
  /**
2
2
  * Filesystem layout and persistent state for agents-cli.
3
3
  *
4
- * Two roots:
5
- * - ~/.agents-system/ — system repo (npm-shipped resources, read-only defaults)
6
- * - ~/.agents/ — user repo (resources + agents.yaml + operational state)
4
+ * Single root at ~/.agents/ with three internal buckets:
7
5
  *
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,
6
+ * ~/.agents/ — user repo: user-authored resources + agents.yaml
7
+ * (git-tracked via `agents repo push`).
8
+ * ~/.agents/.system/ — system repo: npm-shipped resources, regenerable.
9
+ * Don't hand-edit; maintained by npm install /
10
+ * `agents repo pull system`.
11
+ * ~/.agents/.history/ — durable runtime data (sessions, versions, runs,
12
12
  * teams/agents, trash, backups). Backed up by
13
13
  * `agents repo push`.
14
- * - ~/.agents/.cache/ — regenerable runtime data (shims, packages, helpers
14
+ * ~/.agents/.cache/ — regenerable runtime data (shims, packages, helpers
15
15
  * for daemon/pty, terminals, cloud, drive, browser
16
16
  * chrome-data, logs, companion). Gitignored.
17
17
  *
18
18
  * Resolution precedence for resources: project > user > system.
19
19
  * Every module that needs a path or reads/writes agents.yaml goes through here.
20
+ *
21
+ * Legacy layout (pre-fold): system repo lived at ~/.agents-system/ as a peer
22
+ * of ~/.agents/. runMigration() folds it into ~/.agents/.system/ on first run
23
+ * and leaves a back-compat symlink at the old path.
20
24
  */
21
25
  import * as fs from 'fs';
22
26
  import * as path from 'path';
@@ -26,10 +30,16 @@ import { ensureLockTarget, atomicWriteFileSync, withFileLock } from './fs-atomic
26
30
  import { SEEDED_REGISTRIES } from './types.js';
27
31
  const HOME = process.env.HOME ?? os.homedir();
28
32
  // ─── Root directories ─────────────────────────────────────────────────────────
29
- /** System repo — npm-shipped, read-only from user commands. */
30
- const SYSTEM_AGENTS_DIR = path.join(HOME, '.agents-system');
31
33
  /** User repo — user-authored resources and agents.yaml. Always-on. */
32
34
  const USER_AGENTS_DIR = path.join(HOME, '.agents');
35
+ /** System repo — npm-shipped, read-only from user commands. Lives inside the user repo. */
36
+ const SYSTEM_AGENTS_DIR = path.join(USER_AGENTS_DIR, '.system');
37
+ /**
38
+ * Legacy system-repo location (pre-fold). Exported so the migrator can fold
39
+ * it into SYSTEM_AGENTS_DIR. No runtime code outside the migrator should
40
+ * reference this — use SYSTEM_AGENTS_DIR.
41
+ */
42
+ const LEGACY_SYSTEM_AGENTS_DIR = path.join(HOME, '.agents-system');
33
43
  // ─── Meta file (agents.yaml lives in the user repo) ──────────────────────────
34
44
  const META_FILE = path.join(USER_AGENTS_DIR, 'agents.yaml');
35
45
  /** Legacy location — used only for one-shot migration in readMeta(). */
@@ -43,6 +53,7 @@ const SYSTEM_MCP_DIR = path.join(SYSTEM_AGENTS_DIR, 'mcp');
43
53
  const SYSTEM_PERMISSIONS_DIR = path.join(SYSTEM_AGENTS_DIR, 'permissions');
44
54
  const SYSTEM_SUBAGENTS_DIR = path.join(SYSTEM_AGENTS_DIR, 'subagents');
45
55
  const SYSTEM_WORKFLOWS_DIR = path.join(SYSTEM_AGENTS_DIR, 'workflows');
56
+ const SYSTEM_PLUGINS_DIR = path.join(SYSTEM_AGENTS_DIR, 'plugins');
46
57
  const SYSTEM_PROMPTCUTS_FILE = path.join(SYSTEM_AGENTS_DIR, 'hooks', 'promptcuts.yaml');
47
58
  const SYSTEM_MCP_CONFIG_FILE = path.join(SYSTEM_AGENTS_DIR, 'mcp.json');
48
59
  const SYSTEM_INSTRUCTIONS_FILE = path.join(SYSTEM_AGENTS_DIR, 'instructions.md');
@@ -81,6 +92,7 @@ const BROWSER_RUNTIME_DIR = path.join(CACHE_DIR, 'browser');
81
92
  const HELPERS_DIR = path.join(CACHE_DIR, 'helpers');
82
93
  const DAEMON_DIR = path.join(HELPERS_DIR, 'daemon');
83
94
  const PTY_DIR = path.join(HELPERS_DIR, 'pty');
95
+ const TMUX_DIR = path.join(HELPERS_DIR, 'tmux');
84
96
  const FETCH_CACHE_DIR = path.join(CACHE_DIR, '.fetch');
85
97
  const CLI_VERSION_CACHE_FILE = path.join(CACHE_DIR, '.cli-version-cache.json');
86
98
  const MODELS_CACHE_FILE = path.join(CACHE_DIR, '.models-cache.json');
@@ -103,14 +115,18 @@ const META_HEADER = `# agents-cli metadata
103
115
 
104
116
  `;
105
117
  // ─── Root getters ─────────────────────────────────────────────────────────────
106
- /** Root of the system data directory (~/.agents-system/). */
118
+ /** Root of the system data directory (~/.agents/.system/). */
107
119
  export function getAgentsDir() {
108
120
  return SYSTEM_AGENTS_DIR;
109
121
  }
110
- /** Root of the system data directory (~/.agents-system/). */
122
+ /** Root of the system data directory (~/.agents/.system/). */
111
123
  export function getSystemAgentsDir() {
112
124
  return SYSTEM_AGENTS_DIR;
113
125
  }
126
+ /** Legacy system-repo location (~/.agents-system/). Exported for migration only. */
127
+ export function getLegacySystemAgentsDir() {
128
+ return LEGACY_SYSTEM_AGENTS_DIR;
129
+ }
114
130
  /** Root of the user repo (~/.agents/). Always present after ensureAgentsDir(). */
115
131
  export function getUserAgentsDir() {
116
132
  return USER_AGENTS_DIR;
@@ -187,7 +203,7 @@ export function getMcpDir() { return SYSTEM_MCP_DIR; }
187
203
  export function getPermissionsDir() { return SYSTEM_PERMISSIONS_DIR; }
188
204
  /** Path to subagent definition directories — system repo. */
189
205
  export function getSubagentsDir() { return SYSTEM_SUBAGENTS_DIR; }
190
- /** Path to ~/.agents-system/hooks/promptcuts.yaml (system defaults). */
206
+ /** Path to ~/.agents/.system/hooks/promptcuts.yaml (system defaults). */
191
207
  export function getPromptcutsPath() { return SYSTEM_PROMPTCUTS_FILE; }
192
208
  /**
193
209
  * Resolve the effective promptcuts file: user file if it exists, otherwise
@@ -261,6 +277,20 @@ export function getCacheDir() { return CACHE_DIR; }
261
277
  export function getPackagesDir() { return PACKAGES_DIR; }
262
278
  /** Path to routine YAML definitions (~/.agents/routines/). */
263
279
  export function getRoutinesDir() { return ROUTINES_DIR; }
280
+ /**
281
+ * Path to a project-scoped routines directory (`<project>/.agents/routines/`),
282
+ * or null when no project `.agents/` is found by walking up from cwd.
283
+ *
284
+ * Project routines participate in `list`/`view`/`run` for inspection but are
285
+ * NOT fired by the daemon (which runs from $HOME and only loads user routines).
286
+ * Opt-in firing for project routines is tracked as a follow-up.
287
+ */
288
+ export function getProjectRoutinesDir(cwd = process.cwd()) {
289
+ const projectAgentsDir = getProjectAgentsDir(cwd);
290
+ if (!projectAgentsDir)
291
+ return null;
292
+ return path.join(projectAgentsDir, 'routines');
293
+ }
264
294
  /** Path to routine execution logs (~/.agents/.history/runs/). */
265
295
  export function getRunsDir() { return RUNS_DIR; }
266
296
  /** Path to installed agent CLI binaries (~/.agents/.history/versions/). */
@@ -277,6 +307,19 @@ export function getBinDir() { return BIN_DIR; }
277
307
  export function getBackupsDir() { return BACKUPS_DIR; }
278
308
  /** Path to plugin bundles (~/.agents/plugins/) — user-authored resource. */
279
309
  export function getPluginsDir() { return PLUGINS_DIR; }
310
+ /** Path to system plugin bundles (~/.agents/.system/plugins/) — npm-shipped, read-only defaults. */
311
+ export function getSystemPluginsDir() { return SYSTEM_PLUGINS_DIR; }
312
+ /** Path to an extra repo's plugin bundles (~/.agents-<alias>/plugins/). */
313
+ export function getExtraPluginsDir(alias) {
314
+ return path.join(getExtraRepoDir(alias), 'plugins');
315
+ }
316
+ /** Path to a project-scoped plugins directory (<project>/.agents/plugins/), or null when none. */
317
+ export function getProjectPluginsDir(cwd = process.cwd()) {
318
+ const projectAgentsDir = getProjectAgentsDir(cwd);
319
+ if (!projectAgentsDir)
320
+ return null;
321
+ return path.join(projectAgentsDir, 'plugins');
322
+ }
280
323
  /** Path to synced remote session data (~/.agents/.cache/drive/). */
281
324
  export function getDriveDir() { return DRIVE_DIR; }
282
325
  /** Path to soft-deleted resources (~/.agents/.history/trash/). */
@@ -309,6 +352,8 @@ export function getHelpersDir() { return HELPERS_DIR; }
309
352
  export function getDaemonDir() { return DAEMON_DIR; }
310
353
  /** Path to PTY server scratch (~/.agents/.cache/helpers/pty/). */
311
354
  export function getPtyDir() { return PTY_DIR; }
355
+ /** Path to tmux scratch (~/.agents/.cache/helpers/tmux/) — shared server socket + per-session meta JSONs. */
356
+ export function getTmuxDir() { return TMUX_DIR; }
312
357
  /** Path to remote-resource auto-pull cache (~/.agents/.cache/.fetch/). */
313
358
  export function getFetchCacheDir() { return FETCH_CACHE_DIR; }
314
359
  /** Path to the CLI version cache file (~/.agents/.cache/.cli-version-cache.json). */
@@ -73,8 +73,6 @@ export declare function removeSubagentFromAgent(subagentName: string, agent: Age
73
73
  * Check if subagent content matches between source and installed
74
74
  */
75
75
  export declare function subagentContentMatches(installedDir: string, sourceDir: string): boolean;
76
- /** Agents that support the subagent system (Claude via flattened .md, OpenClaw via directory copy). */
77
- export declare const SUBAGENT_CAPABLE_AGENTS: AgentId[];
78
76
  /**
79
77
  * List subagents installed to a specific agent's home
80
78
  * Claude: scans ~/.claude/agents/{name}.md
@@ -9,6 +9,7 @@
9
9
  import * as fs from 'fs';
10
10
  import * as path from 'path';
11
11
  import * as yaml from 'yaml';
12
+ import { capableAgents } from './capabilities.js';
12
13
  import { getSubagentsDir, getUserSubagentsDir, getTrashSubagentsDir } from './state.js';
13
14
  import { listInstalledVersions, getVersionHomePath } from './versions.js';
14
15
  import { safeJoin } from './paths.js';
@@ -333,8 +334,9 @@ export function subagentContentMatches(installedDir, sourceDir) {
333
334
  }
334
335
  return true;
335
336
  }
336
- /** Agents that support the subagent system (Claude via flattened .md, OpenClaw via directory copy). */
337
- export const SUBAGENT_CAPABLE_AGENTS = ['claude', 'openclaw'];
337
+ // SUBAGENT_CAPABLE_AGENTS removed use `capableAgents('subagents')` from
338
+ // lib/capabilities.ts. The capability matrix on AgentConfig is the single
339
+ // source of truth.
338
340
  /**
339
341
  * List subagents installed to a specific agent's home
340
342
  * Claude: scans ~/.claude/agents/{name}.md
@@ -408,8 +410,6 @@ export function listSubagentsForAgent(agentId, home) {
408
410
  }
409
411
  return subagents;
410
412
  }
411
- // Agents that support subagents
412
- const SUBAGENTS_CAPABLE_AGENTS = ['claude', 'openclaw'];
413
413
  /**
414
414
  * Compare a version home's subagents against discovered subagents.
415
415
  * Returns orphan subagent names.
@@ -461,9 +461,9 @@ export function diffVersionSubagents(agent, version) {
461
461
  */
462
462
  export function iterSubagentsCapableVersions(filter) {
463
463
  const pairs = [];
464
- const agents = filter?.agent ? [filter.agent] : SUBAGENTS_CAPABLE_AGENTS;
464
+ const agents = filter?.agent ? [filter.agent] : capableAgents('subagents');
465
465
  for (const agent of agents) {
466
- if (!SUBAGENTS_CAPABLE_AGENTS.includes(agent))
466
+ if (!capableAgents('subagents').includes(agent))
467
467
  continue;
468
468
  const versions = listInstalledVersions(agent);
469
469
  for (const version of versions) {
@@ -172,7 +172,7 @@ export function captureProcessStartTime(pid) {
172
172
  }
173
173
  }
174
174
  /** Agent types the team runner supports. */
175
- const TEAM_AGENT_TYPES = ['codex', 'cursor', 'gemini', 'claude', 'opencode', 'grok', 'antigravity'];
175
+ const TEAM_AGENT_TYPES = ['codex', 'cursor', 'gemini', 'claude', 'opencode', 'grok', 'antigravity', 'kimi'];
176
176
  // Suffix appended to all prompts to ensure agents provide a summary
177
177
  const PROMPT_SUFFIX = `
178
178
 
@@ -1,5 +1,5 @@
1
1
  /** Supported agent CLI types for team spawning. */
2
- export type AgentType = 'codex' | 'gemini' | 'cursor' | 'claude' | 'opencode' | 'grok' | 'antigravity';
2
+ export type AgentType = 'codex' | 'gemini' | 'cursor' | 'claude' | 'opencode' | 'grok' | 'antigravity' | 'kimi';
3
3
  /** Normalize a raw JSON event from any agent type into an array of unified event objects. */
4
4
  export declare function normalizeEvents(agentType: AgentType, raw: any): any[];
5
5
  /** Normalize a raw JSON event, returning only the first unified event (convenience wrapper). */
@@ -0,0 +1,67 @@
1
+ /**
2
+ * tmux binary discovery + spawn helpers.
3
+ *
4
+ * Every shell-out goes through `runTmux()` so:
5
+ * - args are passed as an array, never interpolated into a shell string (no
6
+ * quoting bugs like swarmify's `command.replace(/'/g, "'\\''")` hack);
7
+ * - the socket arg is positioned correctly (`-S <sock>` MUST come before the
8
+ * subcommand);
9
+ * - stdout/stderr capture is consistent for the session module to parse.
10
+ */
11
+ /**
12
+ * Locate the tmux binary on PATH. Cached after first call — tmux either is or
13
+ * isn't installed for the duration of the process.
14
+ *
15
+ * Returns null when tmux is not installed.
16
+ */
17
+ export declare function findTmuxBinary(): string | null;
18
+ /** True when tmux is installed somewhere on PATH. */
19
+ export declare function isTmuxInstalled(): boolean;
20
+ /** Best-effort tmux version string (e.g. "tmux 3.6a"). Returns null when not installed or version probe fails. */
21
+ export declare function getTmuxVersion(): string | null;
22
+ /**
23
+ * Throw a user-friendly error when tmux isn't installed. Command handlers call
24
+ * this first thing so the error message is the same shape every time.
25
+ */
26
+ export declare function assertTmuxAvailable(): string;
27
+ export declare class TmuxUnavailableError extends Error {
28
+ constructor(message: string);
29
+ }
30
+ export declare class TmuxCommandError extends Error {
31
+ readonly stderr: string;
32
+ readonly stdout: string;
33
+ readonly code: number | null;
34
+ constructor(message: string, stderr: string, stdout: string, code: number | null);
35
+ }
36
+ export interface RunTmuxOptions {
37
+ /** Socket path (default: shared server socket). */
38
+ socket?: string;
39
+ /** Args after `tmux -S <socket>` — e.g. `['has-session', '-t', 'foo']`. */
40
+ args: string[];
41
+ /** Throw on nonzero exit. Defaults true. */
42
+ throwOnError?: boolean;
43
+ /** Child process env. */
44
+ env?: NodeJS.ProcessEnv;
45
+ }
46
+ /**
47
+ * Run a tmux command and capture stdout/stderr. The socket arg is hoisted in
48
+ * front of `args` so callers never have to remember the `-S` position.
49
+ *
50
+ * For interactive `attach`, use `attachTmux()` instead — this helper is for
51
+ * scripted commands where you want output back as strings.
52
+ */
53
+ export declare function runTmux(opts: RunTmuxOptions): Promise<{
54
+ stdout: string;
55
+ stderr: string;
56
+ code: number;
57
+ }>;
58
+ /**
59
+ * Foreground attach. Replaces this process's stdio with tmux's so the user is
60
+ * fully inside tmux. Returns the tmux exit code (which the caller should mirror
61
+ * via process.exit so detach/Ctrl-D propagates cleanly).
62
+ */
63
+ export declare function attachTmux(opts: {
64
+ socket: string;
65
+ args: string[];
66
+ env?: NodeJS.ProcessEnv;
67
+ }): Promise<number>;
@@ -0,0 +1,141 @@
1
+ /**
2
+ * tmux binary discovery + spawn helpers.
3
+ *
4
+ * Every shell-out goes through `runTmux()` so:
5
+ * - args are passed as an array, never interpolated into a shell string (no
6
+ * quoting bugs like swarmify's `command.replace(/'/g, "'\\''")` hack);
7
+ * - the socket arg is positioned correctly (`-S <sock>` MUST come before the
8
+ * subcommand);
9
+ * - stdout/stderr capture is consistent for the session module to parse.
10
+ */
11
+ import { spawn, spawnSync } from 'child_process';
12
+ import { existsSync } from 'fs';
13
+ let cachedBin;
14
+ /**
15
+ * Locate the tmux binary on PATH. Cached after first call — tmux either is or
16
+ * isn't installed for the duration of the process.
17
+ *
18
+ * Returns null when tmux is not installed.
19
+ */
20
+ export function findTmuxBinary() {
21
+ if (cachedBin !== undefined)
22
+ return cachedBin;
23
+ // Try `which` first (respects PATH); fall back to common Homebrew/Linux paths
24
+ // so a stripped CI shell with a sparse PATH still works.
25
+ const fromWhich = spawnSync('sh', ['-c', 'command -v tmux'], { encoding: 'utf8' });
26
+ if (fromWhich.status === 0) {
27
+ const out = fromWhich.stdout.trim();
28
+ if (out && existsSync(out)) {
29
+ cachedBin = out;
30
+ return cachedBin;
31
+ }
32
+ }
33
+ for (const p of ['/opt/homebrew/bin/tmux', '/usr/local/bin/tmux', '/usr/bin/tmux']) {
34
+ if (existsSync(p)) {
35
+ cachedBin = p;
36
+ return cachedBin;
37
+ }
38
+ }
39
+ cachedBin = null;
40
+ return null;
41
+ }
42
+ /** True when tmux is installed somewhere on PATH. */
43
+ export function isTmuxInstalled() {
44
+ return findTmuxBinary() !== null;
45
+ }
46
+ /** Best-effort tmux version string (e.g. "tmux 3.6a"). Returns null when not installed or version probe fails. */
47
+ export function getTmuxVersion() {
48
+ const bin = findTmuxBinary();
49
+ if (!bin)
50
+ return null;
51
+ const res = spawnSync(bin, ['-V'], { encoding: 'utf8' });
52
+ if (res.status !== 0)
53
+ return null;
54
+ return res.stdout.trim() || null;
55
+ }
56
+ /**
57
+ * Throw a user-friendly error when tmux isn't installed. Command handlers call
58
+ * this first thing so the error message is the same shape every time.
59
+ */
60
+ export function assertTmuxAvailable() {
61
+ const bin = findTmuxBinary();
62
+ if (!bin) {
63
+ const platform = process.platform;
64
+ const hint = platform === 'darwin'
65
+ ? 'Install with: brew install tmux'
66
+ : platform === 'linux'
67
+ ? 'Install with: apt install tmux (or dnf/yum/pacman equivalent)'
68
+ : 'Install tmux from https://github.com/tmux/tmux';
69
+ throw new TmuxUnavailableError(`tmux is not installed. ${hint}`);
70
+ }
71
+ return bin;
72
+ }
73
+ export class TmuxUnavailableError extends Error {
74
+ constructor(message) {
75
+ super(message);
76
+ this.name = 'TmuxUnavailableError';
77
+ }
78
+ }
79
+ export class TmuxCommandError extends Error {
80
+ stderr;
81
+ stdout;
82
+ code;
83
+ constructor(message, stderr, stdout, code) {
84
+ super(message);
85
+ this.name = 'TmuxCommandError';
86
+ this.stderr = stderr;
87
+ this.stdout = stdout;
88
+ this.code = code;
89
+ }
90
+ }
91
+ /**
92
+ * Run a tmux command and capture stdout/stderr. The socket arg is hoisted in
93
+ * front of `args` so callers never have to remember the `-S` position.
94
+ *
95
+ * For interactive `attach`, use `attachTmux()` instead — this helper is for
96
+ * scripted commands where you want output back as strings.
97
+ */
98
+ export async function runTmux(opts) {
99
+ const bin = assertTmuxAvailable();
100
+ const fullArgs = [];
101
+ if (opts.socket)
102
+ fullArgs.push('-S', opts.socket);
103
+ fullArgs.push(...opts.args);
104
+ return new Promise((resolve, reject) => {
105
+ const child = spawn(bin, fullArgs, {
106
+ stdio: ['ignore', 'pipe', 'pipe'],
107
+ env: opts.env ?? process.env,
108
+ });
109
+ let stdout = '';
110
+ let stderr = '';
111
+ child.stdout?.on('data', (b) => { stdout += b.toString('utf8'); });
112
+ child.stderr?.on('data', (b) => { stderr += b.toString('utf8'); });
113
+ child.on('error', reject);
114
+ child.on('close', (code) => {
115
+ const exitCode = code ?? -1;
116
+ const throwOnError = opts.throwOnError !== false;
117
+ if (throwOnError && exitCode !== 0) {
118
+ reject(new TmuxCommandError(`tmux ${fullArgs.join(' ')} failed (exit ${exitCode}): ${stderr.trim() || stdout.trim()}`, stderr, stdout, exitCode));
119
+ return;
120
+ }
121
+ resolve({ stdout, stderr, code: exitCode });
122
+ });
123
+ });
124
+ }
125
+ /**
126
+ * Foreground attach. Replaces this process's stdio with tmux's so the user is
127
+ * fully inside tmux. Returns the tmux exit code (which the caller should mirror
128
+ * via process.exit so detach/Ctrl-D propagates cleanly).
129
+ */
130
+ export function attachTmux(opts) {
131
+ const bin = assertTmuxAvailable();
132
+ const fullArgs = ['-S', opts.socket, ...opts.args];
133
+ return new Promise((resolve, reject) => {
134
+ const child = spawn(bin, fullArgs, {
135
+ stdio: 'inherit',
136
+ env: opts.env ?? process.env,
137
+ });
138
+ child.on('error', reject);
139
+ child.on('close', (code) => resolve(code ?? 0));
140
+ });
141
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Public entry-point for the tmux integration. Consumers outside the CLI
3
+ * (swarmify extension, `agents teams` multiplexer mode, future MCP wrapper)
4
+ * should import from here.
5
+ */
6
+ export { findTmuxBinary, isTmuxInstalled, getTmuxVersion, assertTmuxAvailable, TmuxUnavailableError, TmuxCommandError, runTmux, attachTmux, } from './binary.js';
7
+ export { getDefaultSocketPath, getSessionMetaPath, ensureTmuxDir, } from './paths.js';
8
+ export { assertValidSessionName, slugifyName, hasSession, createSession, killSession, killAll, listSessions, splitPane, sendKeys, capturePane, readSessionMeta, TmuxSessionError, type SessionMeta, type CreateSessionOptions, type ListedSession, type SplitOptions, type SendOptions, type CaptureOptions, } from './session.js';
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Public entry-point for the tmux integration. Consumers outside the CLI
3
+ * (swarmify extension, `agents teams` multiplexer mode, future MCP wrapper)
4
+ * should import from here.
5
+ */
6
+ export { findTmuxBinary, isTmuxInstalled, getTmuxVersion, assertTmuxAvailable, TmuxUnavailableError, TmuxCommandError, runTmux, attachTmux, } from './binary.js';
7
+ export { getDefaultSocketPath, getSessionMetaPath, ensureTmuxDir, } from './paths.js';
8
+ export { assertValidSessionName, slugifyName, hasSession, createSession, killSession, killAll, listSessions, splitPane, sendKeys, capturePane, readSessionMeta, TmuxSessionError, } from './session.js';
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Filesystem paths for the tmux integration.
3
+ *
4
+ * Layout under ~/.agents/.cache/helpers/tmux/:
5
+ * server.sock — shared tmux server socket hosting all named sessions
6
+ * <name>.json — per-session provenance (cmd, cwd, created_at, source)
7
+ *
8
+ * A single shared server is simpler than per-session sockets: one round-trip
9
+ * for `tmux ls`, one place to clean up, and no extra tmux servers eating
10
+ * memory. Per-session isolation can still be opted into via `--socket`.
11
+ */
12
+ /** Default shared server socket — every `agents tmux *` command targets this unless overridden. */
13
+ export declare function getDefaultSocketPath(): string;
14
+ /** Per-session provenance JSON. tmux itself is the source of truth for liveness; this file is metadata only. */
15
+ export declare function getSessionMetaPath(name: string): string;
16
+ /** Ensure the tmux scratch dir exists with restrictive permissions (sockets are user-private). */
17
+ export declare function ensureTmuxDir(): string;