@phnx-labs/agents-cli 1.20.4 → 1.20.6

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 (207) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +49 -18
  3. package/dist/commands/browser.js +31 -4
  4. package/dist/commands/cli.js +1 -1
  5. package/dist/commands/cloud.js +1 -1
  6. package/dist/commands/commands.js +2 -0
  7. package/dist/commands/computer.js +10 -2
  8. package/dist/commands/defaults.d.ts +7 -0
  9. package/dist/commands/defaults.js +89 -0
  10. package/dist/commands/doctor.js +1 -1
  11. package/dist/commands/exec.js +73 -19
  12. package/dist/commands/hooks.js +6 -6
  13. package/dist/commands/inspect.d.ts +26 -0
  14. package/dist/commands/inspect.js +590 -0
  15. package/dist/commands/mcp.js +17 -16
  16. package/dist/commands/models.js +1 -1
  17. package/dist/commands/packages.js +6 -4
  18. package/dist/commands/permissions.js +13 -12
  19. package/dist/commands/plugins.d.ts +13 -0
  20. package/dist/commands/plugins.js +100 -11
  21. package/dist/commands/prune.js +3 -2
  22. package/dist/commands/pull.d.ts +12 -5
  23. package/dist/commands/pull.js +26 -422
  24. package/dist/commands/push.d.ts +14 -0
  25. package/dist/commands/push.js +30 -0
  26. package/dist/commands/repo.d.ts +1 -1
  27. package/dist/commands/repo.js +155 -112
  28. package/dist/commands/resource-view.d.ts +2 -0
  29. package/dist/commands/resource-view.js +12 -3
  30. package/dist/commands/routines.js +32 -7
  31. package/dist/commands/rules.js +4 -4
  32. package/dist/commands/secrets.js +46 -9
  33. package/dist/commands/sessions.js +1 -0
  34. package/dist/commands/setup.d.ts +3 -3
  35. package/dist/commands/setup.js +17 -17
  36. package/dist/commands/skills.js +6 -5
  37. package/dist/commands/subagents.js +5 -4
  38. package/dist/commands/sync.d.ts +18 -5
  39. package/dist/commands/sync.js +251 -65
  40. package/dist/commands/teams.js +109 -11
  41. package/dist/commands/tmux.d.ts +25 -0
  42. package/dist/commands/tmux.js +415 -0
  43. package/dist/commands/trash.d.ts +2 -2
  44. package/dist/commands/trash.js +1 -1
  45. package/dist/commands/versions.js +2 -2
  46. package/dist/commands/view.d.ts +12 -1
  47. package/dist/commands/view.js +128 -40
  48. package/dist/commands/workflows.js +4 -3
  49. package/dist/commands/worktree.d.ts +4 -5
  50. package/dist/commands/worktree.js +4 -4
  51. package/dist/index.js +106 -41
  52. package/dist/lib/agents.d.ts +23 -10
  53. package/dist/lib/agents.js +88 -25
  54. package/dist/lib/auto-pull-worker.d.ts +1 -1
  55. package/dist/lib/auto-pull-worker.js +2 -2
  56. package/dist/lib/auto-pull.d.ts +1 -1
  57. package/dist/lib/auto-pull.js +1 -1
  58. package/dist/lib/beta.d.ts +1 -1
  59. package/dist/lib/beta.js +1 -1
  60. package/dist/lib/browser/chrome.d.ts +10 -0
  61. package/dist/lib/browser/chrome.js +84 -3
  62. package/dist/lib/capabilities.js +2 -0
  63. package/dist/lib/commands.d.ts +28 -1
  64. package/dist/lib/commands.js +125 -20
  65. package/dist/lib/doctor-diff.js +2 -2
  66. package/dist/lib/exec.d.ts +14 -0
  67. package/dist/lib/exec.js +59 -5
  68. package/dist/lib/fuzzy.d.ts +12 -2
  69. package/dist/lib/fuzzy.js +29 -4
  70. package/dist/lib/git.js +8 -1
  71. package/dist/lib/hooks.d.ts +2 -2
  72. package/dist/lib/hooks.js +97 -10
  73. package/dist/lib/mcp.js +32 -2
  74. package/dist/lib/migrate.d.ts +51 -0
  75. package/dist/lib/migrate.js +233 -5
  76. package/dist/lib/models.js +62 -15
  77. package/dist/lib/permissions.d.ts +59 -2
  78. package/dist/lib/permissions.js +299 -7
  79. package/dist/lib/plugin-marketplace.d.ts +98 -40
  80. package/dist/lib/plugin-marketplace.js +196 -93
  81. package/dist/lib/plugins.d.ts +21 -4
  82. package/dist/lib/plugins.js +130 -49
  83. package/dist/lib/profiles-presets.js +12 -12
  84. package/dist/lib/project-launch.d.ts +70 -0
  85. package/dist/lib/project-launch.js +404 -0
  86. package/dist/lib/pty-client.js +1 -1
  87. package/dist/lib/pty-server.d.ts +1 -1
  88. package/dist/lib/pty-server.js +8 -5
  89. package/dist/lib/refresh.d.ts +26 -0
  90. package/dist/lib/refresh.js +315 -0
  91. package/dist/lib/resource-patterns.d.ts +1 -1
  92. package/dist/lib/resource-patterns.js +1 -1
  93. package/dist/lib/resources/commands.js +2 -2
  94. package/dist/lib/resources/hooks.d.ts +1 -1
  95. package/dist/lib/resources/hooks.js +1 -1
  96. package/dist/lib/resources/mcp.d.ts +1 -1
  97. package/dist/lib/resources/mcp.js +5 -6
  98. package/dist/lib/resources/permissions.js +5 -2
  99. package/dist/lib/resources/rules.js +3 -2
  100. package/dist/lib/resources/skills.js +3 -2
  101. package/dist/lib/resources/types.d.ts +1 -1
  102. package/dist/lib/resources.d.ts +2 -0
  103. package/dist/lib/resources.js +4 -3
  104. package/dist/lib/rotate.d.ts +1 -1
  105. package/dist/lib/rotate.js +7 -19
  106. package/dist/lib/routines.d.ts +16 -4
  107. package/dist/lib/routines.js +67 -17
  108. package/dist/lib/rules/compile.js +22 -10
  109. package/dist/lib/rules/rules.js +3 -3
  110. package/dist/lib/run-config.d.ts +9 -0
  111. package/dist/lib/run-config.js +35 -0
  112. package/dist/lib/run-defaults.d.ts +42 -0
  113. package/dist/lib/run-defaults.js +180 -0
  114. package/dist/lib/runner.js +16 -3
  115. package/dist/lib/scheduler.js +15 -1
  116. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  117. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  118. package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +9 -1
  119. package/dist/lib/secrets/Agents CLI.app/Contents/embedded.provisionprofile +0 -0
  120. package/dist/lib/secrets/install-helper.d.ts +11 -3
  121. package/dist/lib/secrets/install-helper.js +48 -6
  122. package/dist/lib/secrets/linux.d.ts +56 -9
  123. package/dist/lib/secrets/linux.js +327 -59
  124. package/dist/lib/session/db.js +15 -2
  125. package/dist/lib/session/discover.js +118 -3
  126. package/dist/lib/session/parse.js +3 -0
  127. package/dist/lib/session/types.d.ts +1 -1
  128. package/dist/lib/session/types.js +1 -1
  129. package/dist/lib/shims.d.ts +18 -9
  130. package/dist/lib/shims.js +133 -50
  131. package/dist/lib/skills.d.ts +1 -1
  132. package/dist/lib/skills.js +10 -9
  133. package/dist/lib/staleness/detectors/commands.d.ts +3 -0
  134. package/dist/lib/staleness/detectors/commands.js +46 -0
  135. package/dist/lib/staleness/detectors/hooks.d.ts +3 -0
  136. package/dist/lib/staleness/detectors/hooks.js +44 -0
  137. package/dist/lib/staleness/detectors/mcp.d.ts +3 -0
  138. package/dist/lib/staleness/detectors/mcp.js +31 -0
  139. package/dist/lib/staleness/detectors/permissions.d.ts +3 -0
  140. package/dist/lib/staleness/detectors/permissions.js +201 -0
  141. package/dist/lib/staleness/detectors/plugins.d.ts +8 -0
  142. package/dist/lib/staleness/detectors/plugins.js +23 -0
  143. package/dist/lib/staleness/detectors/rules.d.ts +3 -0
  144. package/dist/lib/staleness/detectors/rules.js +34 -0
  145. package/dist/lib/staleness/detectors/skills.d.ts +3 -0
  146. package/dist/lib/staleness/detectors/skills.js +71 -0
  147. package/dist/lib/staleness/detectors/subagents.d.ts +3 -0
  148. package/dist/lib/staleness/detectors/subagents.js +50 -0
  149. package/dist/lib/staleness/detectors/types.d.ts +22 -0
  150. package/dist/lib/staleness/detectors/types.js +1 -0
  151. package/dist/lib/staleness/detectors/workflows.d.ts +3 -0
  152. package/dist/lib/staleness/detectors/workflows.js +28 -0
  153. package/dist/lib/staleness/registry.d.ts +26 -0
  154. package/dist/lib/staleness/registry.js +123 -0
  155. package/dist/lib/staleness/writers/commands.d.ts +3 -0
  156. package/dist/lib/staleness/writers/commands.js +111 -0
  157. package/dist/lib/staleness/writers/hooks.d.ts +3 -0
  158. package/dist/lib/staleness/writers/hooks.js +47 -0
  159. package/dist/lib/staleness/writers/kinds.d.ts +10 -0
  160. package/dist/lib/staleness/writers/kinds.js +15 -0
  161. package/dist/lib/staleness/writers/lazy-map.d.ts +13 -0
  162. package/dist/lib/staleness/writers/lazy-map.js +19 -0
  163. package/dist/lib/staleness/writers/mcp.d.ts +10 -0
  164. package/dist/lib/staleness/writers/mcp.js +19 -0
  165. package/dist/lib/staleness/writers/permissions.d.ts +13 -0
  166. package/dist/lib/staleness/writers/permissions.js +26 -0
  167. package/dist/lib/staleness/writers/plugins.d.ts +7 -0
  168. package/dist/lib/staleness/writers/plugins.js +31 -0
  169. package/dist/lib/staleness/writers/rules.d.ts +7 -0
  170. package/dist/lib/staleness/writers/rules.js +55 -0
  171. package/dist/lib/staleness/writers/skills.d.ts +3 -0
  172. package/dist/lib/staleness/writers/skills.js +81 -0
  173. package/dist/lib/staleness/writers/sources.d.ts +16 -0
  174. package/dist/lib/staleness/writers/sources.js +72 -0
  175. package/dist/lib/staleness/writers/subagents.d.ts +3 -0
  176. package/dist/lib/staleness/writers/subagents.js +53 -0
  177. package/dist/lib/staleness/writers/types.d.ts +36 -0
  178. package/dist/lib/staleness/writers/types.js +1 -0
  179. package/dist/lib/staleness/writers/workflows.d.ts +7 -0
  180. package/dist/lib/staleness/writers/workflows.js +31 -0
  181. package/dist/lib/state.d.ts +34 -11
  182. package/dist/lib/state.js +58 -13
  183. package/dist/lib/subagents.d.ts +0 -2
  184. package/dist/lib/subagents.js +6 -6
  185. package/dist/lib/teams/agents.js +1 -1
  186. package/dist/lib/teams/api.d.ts +67 -0
  187. package/dist/lib/teams/api.js +78 -0
  188. package/dist/lib/teams/parsers.d.ts +1 -1
  189. package/dist/lib/tmux/binary.d.ts +67 -0
  190. package/dist/lib/tmux/binary.js +141 -0
  191. package/dist/lib/tmux/index.d.ts +8 -0
  192. package/dist/lib/tmux/index.js +8 -0
  193. package/dist/lib/tmux/paths.d.ts +17 -0
  194. package/dist/lib/tmux/paths.js +30 -0
  195. package/dist/lib/tmux/session.d.ts +122 -0
  196. package/dist/lib/tmux/session.js +305 -0
  197. package/dist/lib/types.d.ts +73 -13
  198. package/dist/lib/types.js +1 -1
  199. package/dist/lib/usage.js +1 -1
  200. package/dist/lib/versions.d.ts +4 -4
  201. package/dist/lib/versions.js +138 -496
  202. package/dist/lib/workflows.d.ts +2 -4
  203. package/dist/lib/workflows.js +3 -4
  204. package/package.json +6 -3
  205. package/scripts/postinstall.js +16 -63
  206. package/dist/commands/status.d.ts +0 -9
  207. package/dist/commands/status.js +0 -25
@@ -0,0 +1,305 @@
1
+ /**
2
+ * tmux session lifecycle.
3
+ *
4
+ * Public surface used by both `agents tmux` commands and external consumers
5
+ * (swarmify, `agents teams` with a future multiplexer mode). Every state
6
+ * change goes through here so the CLI surface stays a thin parser.
7
+ *
8
+ * Liveness model: tmux itself owns liveness. The `<name>.json` meta files are
9
+ * pure provenance — `listSessions()` always reconciles them against
10
+ * `tmux list-sessions` and prunes stale entries on the fly.
11
+ */
12
+ import * as fs from 'fs';
13
+ import { runTmux, TmuxCommandError } from './binary.js';
14
+ import { ensureTmuxDir, getDefaultSocketPath, getSessionMetaPath } from './paths.js';
15
+ /** Tmux session names must not contain `.` or `:` — those are reserved for window/pane addressing. */
16
+ const VALID_NAME = /^[A-Za-z0-9_-]{1,64}$/;
17
+ export class TmuxSessionError extends Error {
18
+ cause;
19
+ constructor(message, cause) {
20
+ super(message);
21
+ this.cause = cause;
22
+ this.name = 'TmuxSessionError';
23
+ }
24
+ }
25
+ /** Reject invalid session names early — tmux's error for `.`/`:` is cryptic. */
26
+ export function assertValidSessionName(name) {
27
+ if (!VALID_NAME.test(name)) {
28
+ throw new TmuxSessionError(`Invalid session name: "${name}". Use 1-64 characters from [A-Za-z0-9_-]. tmux disallows '.' and ':'.`);
29
+ }
30
+ }
31
+ /** Slugify an arbitrary string into a valid session name. Useful for swarmify auto-generated names. */
32
+ export function slugifyName(input) {
33
+ const s = input.replace(/[^A-Za-z0-9_-]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 64);
34
+ return s || `s-${Date.now()}`;
35
+ }
36
+ /** True when a session by this name currently exists on the given socket. */
37
+ export async function hasSession(name, socket) {
38
+ assertValidSessionName(name);
39
+ const sock = socket ?? getDefaultSocketPath();
40
+ const res = await runTmux({
41
+ socket: sock,
42
+ args: ['has-session', '-t', `=${name}`],
43
+ throwOnError: false,
44
+ });
45
+ return res.code === 0;
46
+ }
47
+ /**
48
+ * Create a new detached session. Throws when the name is already taken unless
49
+ * `replace` or `attachExisting` is set.
50
+ */
51
+ export async function createSession(opts) {
52
+ assertValidSessionName(opts.name);
53
+ ensureTmuxDir();
54
+ const socket = opts.socket ?? getDefaultSocketPath();
55
+ if (opts.cwd && !fs.existsSync(opts.cwd)) {
56
+ throw new TmuxSessionError(`cwd does not exist: ${opts.cwd}`);
57
+ }
58
+ const existed = await hasSession(opts.name, socket);
59
+ if (existed) {
60
+ if (opts.attachExisting) {
61
+ const meta = readSessionMeta(opts.name);
62
+ return meta ?? {
63
+ name: opts.name,
64
+ socket,
65
+ createdAt: Date.now(),
66
+ source: opts.source ?? 'cli',
67
+ };
68
+ }
69
+ if (!opts.replace) {
70
+ throw new TmuxSessionError(`Session "${opts.name}" already exists. Use --replace to overwrite or --attach-existing to reuse it.`);
71
+ }
72
+ await killSession(opts.name, socket);
73
+ }
74
+ // Set remain-on-exit BEFORE the child command can finish — a fast-exiting
75
+ // cmd (e.g. `echo BRIEF && true`) would otherwise collapse the only session,
76
+ // exit the server, and the follow-up `set-option` would race with "no
77
+ // server running". Server-wide (`-g`) is applied in the same tmux
78
+ // invocation as new-session so they share one server lifetime.
79
+ const args = ['set-option', '-g', 'remain-on-exit', 'on', ';', 'new-session', '-d', '-s', opts.name];
80
+ if (opts.width)
81
+ args.push('-x', String(opts.width));
82
+ if (opts.height)
83
+ args.push('-y', String(opts.height));
84
+ if (opts.cwd)
85
+ args.push('-c', opts.cwd);
86
+ // Separator + child command. tmux passes the rest verbatim to exec, so no
87
+ // shell escaping is required — array args end-to-end.
88
+ if (opts.cmd) {
89
+ args.push('--', 'sh', '-c', opts.cmd);
90
+ }
91
+ await runTmux({ socket, args, env: opts.env });
92
+ const meta = {
93
+ name: opts.name,
94
+ socket,
95
+ createdAt: Date.now(),
96
+ cmd: opts.cmd,
97
+ cwd: opts.cwd,
98
+ source: opts.source ?? 'cli',
99
+ labels: opts.labels,
100
+ };
101
+ writeSessionMeta(meta);
102
+ return meta;
103
+ }
104
+ /** Kill one named session. Idempotent — killing a non-existent session is a no-op. */
105
+ export async function killSession(name, socket) {
106
+ assertValidSessionName(name);
107
+ const sock = socket ?? getDefaultSocketPath();
108
+ const existed = await hasSession(name, sock);
109
+ if (!existed) {
110
+ removeSessionMeta(name);
111
+ return false;
112
+ }
113
+ try {
114
+ await runTmux({ socket: sock, args: ['kill-session', '-t', `=${name}`] });
115
+ }
116
+ catch (err) {
117
+ if (err instanceof TmuxCommandError && err.code !== 0) {
118
+ // Already dead by the time the kill landed — treat as success.
119
+ }
120
+ else {
121
+ throw err;
122
+ }
123
+ }
124
+ removeSessionMeta(name);
125
+ return true;
126
+ }
127
+ /**
128
+ * Kill every session on the shared server AND the server itself, then prune
129
+ * meta files. Wipes the socket so the next `new` starts from a clean slate.
130
+ */
131
+ export async function killAll(socket) {
132
+ const sock = socket ?? getDefaultSocketPath();
133
+ let count = 0;
134
+ try {
135
+ const sessions = await listSessions({ socket: sock });
136
+ count = sessions.length;
137
+ await runTmux({ socket: sock, args: ['kill-server'], throwOnError: false });
138
+ }
139
+ catch {
140
+ // Server already gone — that's a successful kill-all.
141
+ }
142
+ // Clear meta files for every session we knew about.
143
+ const dir = ensureTmuxDir();
144
+ for (const f of fs.readdirSync(dir)) {
145
+ if (f.endsWith('.json')) {
146
+ try {
147
+ fs.unlinkSync(`${dir}/${f}`);
148
+ }
149
+ catch { /* race-tolerant */ }
150
+ }
151
+ }
152
+ // Stale socket can survive kill-server on some platforms — sweep it.
153
+ try {
154
+ fs.unlinkSync(sock);
155
+ }
156
+ catch { /* may not exist */ }
157
+ return count;
158
+ }
159
+ /**
160
+ * List live sessions on the socket. Reconciles meta JSONs against tmux's view:
161
+ * - tmux session with no meta → returned without `meta` (external session)
162
+ * - meta file with no tmux session → meta deleted (stale)
163
+ */
164
+ export async function listSessions(opts = {}) {
165
+ const socket = opts.socket ?? getDefaultSocketPath();
166
+ if (!fs.existsSync(socket)) {
167
+ // No server has ever run — clean orphan metas defensively.
168
+ pruneAllMetas();
169
+ return [];
170
+ }
171
+ // Pipe-separated format so we don't need to parse tmux's variable-width default output.
172
+ const fmt = '#{session_name}|#{session_created}|#{session_windows}|#{session_attached}';
173
+ const res = await runTmux({
174
+ socket,
175
+ args: ['list-sessions', '-F', fmt],
176
+ throwOnError: false,
177
+ });
178
+ // tmux returns nonzero with "no server running" or "no sessions" — both mean empty.
179
+ if (res.code !== 0) {
180
+ if (/no server running|no sessions|error connecting/i.test(res.stderr)) {
181
+ pruneAllMetas();
182
+ return [];
183
+ }
184
+ throw new TmuxCommandError(`tmux list-sessions failed: ${res.stderr}`, res.stderr, res.stdout, res.code);
185
+ }
186
+ const lines = res.stdout.split('\n').map(l => l.trim()).filter(Boolean);
187
+ const out = [];
188
+ const liveNames = new Set();
189
+ for (const line of lines) {
190
+ const [name, createdRaw, windowsRaw, attachedRaw] = line.split('|');
191
+ if (!name)
192
+ continue;
193
+ liveNames.add(name);
194
+ out.push({
195
+ name,
196
+ socket,
197
+ createdAtTmux: parseInt(createdRaw, 10) || 0,
198
+ windows: parseInt(windowsRaw, 10) || 1,
199
+ attached: attachedRaw === '1',
200
+ meta: readSessionMeta(name) ?? undefined,
201
+ });
202
+ }
203
+ // Drop metas with no matching live session.
204
+ pruneOrphanMetas(liveNames);
205
+ return out;
206
+ }
207
+ /**
208
+ * Split the active pane of a session. Returns the new pane's tmux pane id
209
+ * (e.g. `%3`) so callers can target it later via `send`/`capture`.
210
+ */
211
+ export async function splitPane(opts) {
212
+ assertValidSessionName(opts.name);
213
+ const socket = opts.socket ?? getDefaultSocketPath();
214
+ if (opts.cwd && !fs.existsSync(opts.cwd)) {
215
+ throw new TmuxSessionError(`cwd does not exist: ${opts.cwd}`);
216
+ }
217
+ // tmux split-window directions: -h splits the pane left/right (sibling on the
218
+ // right); -v splits top/bottom. swarmify treats H as "below" and V as "side"
219
+ // — keep tmux semantics (h=left/right, v=top/bottom) and document it.
220
+ // Pane-target ops use the bare name (no =) — the '=' exact-match modifier is
221
+ // a session-only feature; pane targets reject it with "can't find pane".
222
+ // Slug validation already guarantees the name is unambiguous.
223
+ const args = ['split-window', `-${opts.direction}`, '-t', opts.name, '-P', '-F', '#{pane_id}'];
224
+ if (opts.cwd)
225
+ args.push('-c', opts.cwd);
226
+ if (opts.cmd)
227
+ args.push('--', 'sh', '-c', opts.cmd);
228
+ const res = await runTmux({ socket, args });
229
+ return res.stdout.trim();
230
+ }
231
+ /** Send keystrokes to a session's active pane (or a specific pane via :pane). */
232
+ export async function sendKeys(opts) {
233
+ assertValidSessionName(opts.name);
234
+ const socket = opts.socket ?? getDefaultSocketPath();
235
+ // Bare name (no =) for pane targets — see note in splitPane.
236
+ const target = opts.pane ? `${opts.name}.${opts.pane}` : opts.name;
237
+ const args = ['send-keys', '-t', target];
238
+ if (opts.raw)
239
+ args.push('-l');
240
+ args.push(opts.keys);
241
+ if (!opts.noEnter)
242
+ args.push('Enter');
243
+ await runTmux({ socket, args });
244
+ }
245
+ /** Capture pane contents as a string. The cleaned form is what humans see. */
246
+ export async function capturePane(opts) {
247
+ assertValidSessionName(opts.name);
248
+ const socket = opts.socket ?? getDefaultSocketPath();
249
+ // Bare name (no =) for pane targets — see note in splitPane.
250
+ const target = opts.pane ? `${opts.name}.${opts.pane}` : opts.name;
251
+ const args = ['capture-pane', '-p', '-t', target];
252
+ if (opts.ansi)
253
+ args.push('-e');
254
+ if (opts.lines && opts.lines > 0) {
255
+ // -S -N means "start N lines back from current view"; we use -S -<lines> -E -.
256
+ args.push('-S', `-${opts.lines}`);
257
+ }
258
+ const res = await runTmux({ socket, args });
259
+ return res.stdout;
260
+ }
261
+ /** Read a session's provenance JSON, if present. */
262
+ export function readSessionMeta(name) {
263
+ try {
264
+ const raw = fs.readFileSync(getSessionMetaPath(name), 'utf8');
265
+ return JSON.parse(raw);
266
+ }
267
+ catch {
268
+ return null;
269
+ }
270
+ }
271
+ function writeSessionMeta(meta) {
272
+ ensureTmuxDir();
273
+ fs.writeFileSync(getSessionMetaPath(meta.name), JSON.stringify(meta, null, 2), { mode: 0o600 });
274
+ }
275
+ function removeSessionMeta(name) {
276
+ try {
277
+ fs.unlinkSync(getSessionMetaPath(name));
278
+ }
279
+ catch { /* may not exist */ }
280
+ }
281
+ function pruneOrphanMetas(liveNames) {
282
+ const dir = ensureTmuxDir();
283
+ for (const f of fs.readdirSync(dir)) {
284
+ if (!f.endsWith('.json'))
285
+ continue;
286
+ const name = f.slice(0, -5);
287
+ if (!liveNames.has(name)) {
288
+ try {
289
+ fs.unlinkSync(`${dir}/${f}`);
290
+ }
291
+ catch { /* race-tolerant */ }
292
+ }
293
+ }
294
+ }
295
+ function pruneAllMetas() {
296
+ const dir = ensureTmuxDir();
297
+ for (const f of fs.readdirSync(dir)) {
298
+ if (f.endsWith('.json')) {
299
+ try {
300
+ fs.unlinkSync(`${dir}/${f}`);
301
+ }
302
+ catch { /* race-tolerant */ }
303
+ }
304
+ }
305
+ }
@@ -6,9 +6,22 @@
6
6
  * formats for each supported agent.
7
7
  */
8
8
  /** Unique identifier for a supported AI coding agent. */
9
- export type AgentId = 'claude' | 'codex' | 'gemini' | 'cursor' | 'opencode' | 'openclaw' | 'copilot' | 'amp' | 'kiro' | 'goose' | 'roo' | 'antigravity' | 'grok';
9
+ export type AgentId = 'claude' | 'codex' | 'gemini' | 'cursor' | 'opencode' | 'openclaw' | 'copilot' | 'amp' | 'kiro' | 'goose' | 'roo' | 'antigravity' | 'grok' | 'kimi';
10
10
  /** How `agents run <agent>` chooses an installed version when none is pinned. */
11
11
  export type RunStrategy = 'pinned' | 'available' | 'balanced';
12
+ /** Per-agent run strategy config. */
13
+ export interface AgentRunConfig {
14
+ strategy?: RunStrategy;
15
+ }
16
+ /** Default launch options applied by `agents run` when flags are omitted. */
17
+ export interface RunDefaults {
18
+ mode?: Mode;
19
+ model?: string;
20
+ }
21
+ /** `run:` section in agents.yaml. Agent keys keep strategy; `defaults` stores selector rules. */
22
+ export type RunConfig = Partial<Record<AgentId, AgentRunConfig>> & {
23
+ defaults?: Record<string, RunDefaults>;
24
+ };
12
25
  /** Preview features that users can opt into via `agents beta`. */
13
26
  export type BetaFeatureName = 'drive' | 'factory';
14
27
  /** Subset of chalk color names used for agent-specific terminal output. */
@@ -40,6 +53,9 @@ export interface AgentConfig {
40
53
  skills: Capability;
41
54
  commands: Capability;
42
55
  plugins: Capability;
56
+ subagents: Capability;
57
+ rules: RulesCapability;
58
+ workflows: Capability;
43
59
  /**
44
60
  * Permission modes this agent natively supports. Modes outside this set
45
61
  * are gated by buildExecCommand: `auto` silently degrades to `edit`,
@@ -64,8 +80,12 @@ export type Capability = boolean | {
64
80
  since?: string;
65
81
  until?: string;
66
82
  };
83
+ /** Rules sync writes one composed instructions file per supported agent. */
84
+ export type RulesCapability = false | {
85
+ file: string;
86
+ };
67
87
  /** Names of every gateable capability on AgentConfig. */
68
- export type CapabilityName = 'hooks' | 'mcp' | 'allowlist' | 'skills' | 'commands' | 'plugins';
88
+ export type CapabilityName = 'hooks' | 'mcp' | 'allowlist' | 'skills' | 'commands' | 'plugins' | 'subagents' | 'rules' | 'workflows';
69
89
  /**
70
90
  * Permission modes controlling agent autonomy.
71
91
  * plan read-only investigation; no writes, no shell side-effects
@@ -189,9 +209,7 @@ export interface InstalledHook {
189
209
  /** Package manifest (agents.yaml) found inside a cloned config repo or package. */
190
210
  export interface Manifest {
191
211
  agents?: Partial<Record<AgentId, string>>;
192
- run?: Partial<Record<AgentId, {
193
- strategy?: RunStrategy;
194
- }>>;
212
+ run?: RunConfig;
195
213
  beta?: {
196
214
  enabled?: BetaFeatureName[];
197
215
  };
@@ -237,14 +255,14 @@ export interface InstalledSkill {
237
255
  scope: 'user' | 'project';
238
256
  agent: AgentId;
239
257
  }
240
- /** Git remote metadata for the ~/.agents-system/ config repository. */
258
+ /** Git remote metadata for the ~/.agents/.system/ config repository. */
241
259
  export interface RepoInfo {
242
260
  source: string;
243
261
  branch: string;
244
262
  commit: string;
245
263
  lastSync: string;
246
264
  }
247
- /** Canonical system repo cloned into ~/.agents-system/. */
265
+ /** Canonical system repo cloned into ~/.agents/.system/. */
248
266
  export declare const DEFAULT_SYSTEM_REPO = "gh:phnx-labs/.agents-system";
249
267
  /** Strip the `gh:` prefix and `.git` suffix to get a GitHub `owner/repo` slug. */
250
268
  export declare function systemRepoSlug(repo?: string): string;
@@ -351,7 +369,7 @@ export interface ResolvedPackage {
351
369
  export type ResourceType = 'commands' | 'skills' | 'hooks' | 'memory' | 'mcp' | 'permissions' | 'subagents' | 'plugins' | 'workflows';
352
370
  /**
353
371
  * A resource selection pattern stored in agents.yaml versions:
354
- * "system:*" — all resources from ~/.agents-system/
372
+ * "system:*" — all resources from ~/.agents/.system/
355
373
  * "user:*" — all resources from ~/.agents/
356
374
  * "rush:*" — all resources from ~/.agents-rush/ (extra repo alias)
357
375
  * "project:*" — all resources from .agents/ in the project root
@@ -416,6 +434,50 @@ export interface DiscoveredPlugin {
416
434
  hasMcp: boolean;
417
435
  /** Whether the plugin root contains a settings.json with non-permission keys to merge. */
418
436
  hasSettings: boolean;
437
+ /**
438
+ * Marketplace this plugin was discovered in (from marketplaceNameFor() of the
439
+ * owning MarketplaceSpec): "agents-cli" (user repo), "agents-<alias>" (extra
440
+ * repo), or "agents-project" (project repo). Absent on hand-built plugins
441
+ * (e.g. workflow-scoped) — those default to the user marketplace on sync.
442
+ */
443
+ marketplace?: string;
444
+ }
445
+ /**
446
+ * Identifies one DotAgents repo that contributes a plugin marketplace. Each
447
+ * repo synthesizes its own catalog and registers under its own name:
448
+ * user — ~/.agents/plugins/ → "agents-cli" (the canonical name)
449
+ * extra — ~/.agents-<alias>/plugins/ → "agents-<alias>" (e.g. "agents-extras")
450
+ * project — <cwd>/.agents/plugins/ → "agents-project"
451
+ *
452
+ * `root` on the extra/project variants is the absolute path to that repo's
453
+ * plugins/ directory (the source side). The user variant needs no path — it is
454
+ * always ~/.agents/plugins/ via getPluginsDir().
455
+ */
456
+ export type MarketplaceSpec = {
457
+ kind: 'user';
458
+ } | {
459
+ kind: 'extra';
460
+ alias: string;
461
+ root: string;
462
+ } | {
463
+ kind: 'project';
464
+ root: string;
465
+ } | {
466
+ kind: 'system';
467
+ root: string;
468
+ };
469
+ /**
470
+ * A marketplace found on the source side (before any per-version sync), with
471
+ * its resolved name, source plugins directory, and catalog description.
472
+ */
473
+ export interface DiscoveredMarketplace {
474
+ spec: MarketplaceSpec;
475
+ /** e.g. "agents-cli", "agents-extras", "agents-project". */
476
+ name: string;
477
+ /** Absolute path to the source plugins/ directory on disk. */
478
+ pluginsRoot: string;
479
+ /** Human description embedded in the synthesized catalog. */
480
+ description: string;
419
481
  }
420
482
  /** Frontmatter fields parsed from a subagent's agent.md file. */
421
483
  export interface SubagentFrontmatter {
@@ -450,12 +512,10 @@ export interface ExtraRepoConfig {
450
512
  path?: string;
451
513
  enabled: boolean;
452
514
  }
453
- /** Top-level structure of ~/.agents-system/agents.yaml -- the CLI's persistent state. */
515
+ /** Top-level structure of ~/.agents/.system/agents.yaml -- the CLI's persistent state. */
454
516
  export interface Meta {
455
517
  agents?: Partial<Record<AgentId, string>>;
456
- run?: Partial<Record<AgentId, {
457
- strategy?: RunStrategy;
458
- }>>;
518
+ run?: RunConfig;
459
519
  beta?: {
460
520
  enabled?: BetaFeatureName[];
461
521
  };
@@ -528,7 +588,7 @@ export interface BrowserProfileConfig {
528
588
  /** Optional SSH host where logDir lives, e.g. "user@remote-host". */
529
589
  logHost?: string;
530
590
  }
531
- /** Options controlling which agents and resources are synced during `agents pull` / `agents use`. */
591
+ /** Options controlling which agents and resources are synced during `agents repo refresh` / `agents use`. */
532
592
  export interface SyncOptions {
533
593
  agents?: AgentId[];
534
594
  yes?: boolean;
package/dist/lib/types.js CHANGED
@@ -7,7 +7,7 @@
7
7
  */
8
8
  /** Every canonical mode in declaration order. Useful for iteration / validation. */
9
9
  export const ALL_MODES = ['plan', 'edit', 'auto', 'skip'];
10
- /** Canonical system repo cloned into ~/.agents-system/. */
10
+ /** Canonical system repo cloned into ~/.agents/.system/. */
11
11
  export const DEFAULT_SYSTEM_REPO = 'gh:phnx-labs/.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) {
package/dist/lib/usage.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * Fetches live usage data from the Anthropic OAuth API (Claude) or parses
5
5
  * rate-limit events from Codex session logs. Results are normalized into a
6
6
  * common UsageSnapshot shape, cached to disk, and rendered as terminal
7
- * progress bars for the `agents view` and `agents status` commands.
7
+ * progress bars for the `agents view` command.
8
8
  */
9
9
  import { execFile } from 'child_process';
10
10
  import { createHash } from 'crypto';
@@ -166,10 +166,10 @@ export declare function installVersion(agent: AgentId, version: string, onProgre
166
166
  error?: string;
167
167
  }>;
168
168
  /**
169
- * Soft-delete a version directory by moving it to ~/.agents-system/trash/versions/.
169
+ * Soft-delete a version directory by moving it to ~/.agents/.system/trash/versions/.
170
170
  * Returns the trash path on success or null on failure / no source.
171
171
  *
172
- * Trash layout: ~/.agents-system/trash/versions/<agent>/<version>/<timestamp>/
172
+ * Trash layout: ~/.agents/.system/trash/versions/<agent>/<version>/<timestamp>/
173
173
  * The timestamp suffix lets a user soft-delete the same version twice (after
174
174
  * re-install) without collision and gives a chronological audit trail.
175
175
  *
@@ -182,7 +182,7 @@ export declare function softDeleteVersionDir(agent: AgentId, version: string): s
182
182
  * Remove a specific version of an agent.
183
183
  *
184
184
  * Soft-delete only: moves the entire version directory (including `home/`)
185
- * to ~/.agents-system/trash/versions/. Recoverable via `agents trash restore`.
185
+ * to ~/.agents/.system/trash/versions/. Recoverable via `agents trash restore`.
186
186
  * Nothing is hard-deleted.
187
187
  */
188
188
  export declare function removeVersion(agent: AgentId, version: string): boolean;
@@ -227,7 +227,7 @@ export declare function resolveVersionAlias(agent: AgentId, raw: string | undefi
227
227
  */
228
228
  export declare function resolveVersionAliasLoose(agent: AgentId, raw: string | undefined | null): string | undefined;
229
229
  /**
230
- * Get version specified in a project-root agents.yaml (not the user ~/.agents-system/agents.yaml).
230
+ * Get version specified in a project-root agents.yaml (not the user ~/.agents/.system/agents.yaml).
231
231
  */
232
232
  export declare function getProjectVersion(agent: AgentId, startPath: string): string | null;
233
233
  /**