@phnx-labs/agents-cli 1.20.72 → 1.20.74

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 (234) hide show
  1. package/CHANGELOG.md +395 -0
  2. package/README.md +40 -4
  3. package/dist/bin/agents +0 -0
  4. package/dist/commands/activity.d.ts +12 -0
  5. package/dist/commands/activity.js +68 -0
  6. package/dist/commands/apply.d.ts +13 -0
  7. package/dist/commands/apply.js +28 -3
  8. package/dist/commands/browser.js +1 -1
  9. package/dist/commands/cloud.js +21 -11
  10. package/dist/commands/doctor.js +21 -5
  11. package/dist/commands/events.d.ts +11 -10
  12. package/dist/commands/events.js +30 -21
  13. package/dist/commands/exec.d.ts +34 -0
  14. package/dist/commands/exec.js +337 -39
  15. package/dist/commands/export.d.ts +11 -0
  16. package/dist/commands/export.js +215 -0
  17. package/dist/commands/feed.d.ts +13 -0
  18. package/dist/commands/feed.js +130 -29
  19. package/dist/commands/hq.d.ts +2 -0
  20. package/dist/commands/hq.js +58 -0
  21. package/dist/commands/import.js +51 -3
  22. package/dist/commands/lease.d.ts +19 -0
  23. package/dist/commands/lease.js +138 -11
  24. package/dist/commands/login.d.ts +2 -0
  25. package/dist/commands/login.js +123 -0
  26. package/dist/commands/logs.js +1 -1
  27. package/dist/commands/mine.d.ts +27 -0
  28. package/dist/commands/mine.js +207 -0
  29. package/dist/commands/monitors.js +79 -49
  30. package/dist/commands/plugins.js +1 -0
  31. package/dist/commands/profiles.js +158 -0
  32. package/dist/commands/repo.js +2 -60
  33. package/dist/commands/resources.d.ts +5 -0
  34. package/dist/commands/resources.js +95 -0
  35. package/dist/commands/routines.d.ts +2 -0
  36. package/dist/commands/routines.js +139 -37
  37. package/dist/commands/secrets.js +234 -47
  38. package/dist/commands/send.d.ts +15 -0
  39. package/dist/commands/send.js +62 -0
  40. package/dist/commands/sessions-tail.d.ts +1 -0
  41. package/dist/commands/sessions-tail.js +20 -9
  42. package/dist/commands/sessions.js +18 -3
  43. package/dist/commands/setup-mine.d.ts +18 -0
  44. package/dist/commands/setup-mine.js +106 -0
  45. package/dist/commands/setup-share.js +2 -2
  46. package/dist/commands/setup.js +9 -2
  47. package/dist/commands/share.d.ts +8 -1
  48. package/dist/commands/share.js +122 -37
  49. package/dist/commands/ssh.d.ts +9 -0
  50. package/dist/commands/ssh.js +196 -26
  51. package/dist/commands/sync.js +1 -1
  52. package/dist/commands/teams.js +11 -2
  53. package/dist/commands/versions.js +36 -5
  54. package/dist/commands/view.js +75 -27
  55. package/dist/index.js +80 -10
  56. package/dist/lib/activity.d.ts +124 -0
  57. package/dist/lib/activity.js +542 -0
  58. package/dist/lib/agents.d.ts +35 -0
  59. package/dist/lib/agents.js +79 -6
  60. package/dist/lib/artifact-actions.d.ts +1 -1
  61. package/dist/lib/artifact-actions.js +1 -1
  62. package/dist/lib/ask-classifier.d.ts +2 -1
  63. package/dist/lib/ask-classifier.js +3 -3
  64. package/dist/lib/auth-health.d.ts +32 -4
  65. package/dist/lib/auth-health.js +40 -1
  66. package/dist/lib/auto-pull.js +8 -1
  67. package/dist/lib/brand.d.ts +40 -0
  68. package/dist/lib/brand.js +122 -0
  69. package/dist/lib/browser/drivers/ssh.js +4 -7
  70. package/dist/lib/browser/service.js +6 -8
  71. package/dist/lib/channels/providers/index.d.ts +2 -0
  72. package/dist/lib/channels/providers/index.js +20 -0
  73. package/dist/lib/channels/providers/mailbox.d.ts +2 -0
  74. package/dist/lib/channels/providers/mailbox.js +26 -0
  75. package/dist/lib/channels/providers/openclaw-telegram.d.ts +2 -0
  76. package/dist/lib/channels/providers/openclaw-telegram.js +34 -0
  77. package/dist/lib/channels/providers/rush.d.ts +6 -0
  78. package/dist/lib/channels/providers/rush.js +55 -0
  79. package/dist/lib/channels/registry.d.ts +42 -0
  80. package/dist/lib/channels/registry.js +20 -0
  81. package/dist/lib/channels/resolve.d.ts +11 -0
  82. package/dist/lib/channels/resolve.js +12 -0
  83. package/dist/lib/cli-resources.d.ts +18 -0
  84. package/dist/lib/cli-resources.js +53 -4
  85. package/dist/lib/cloud/codex.js +6 -3
  86. package/dist/lib/cloud/factory.d.ts +1 -0
  87. package/dist/lib/cloud/factory.js +10 -4
  88. package/dist/lib/cloud/rush.d.ts +3 -1
  89. package/dist/lib/cloud/rush.js +6 -1
  90. package/dist/lib/cloud/types.d.ts +2 -0
  91. package/dist/lib/codex-home.d.ts +35 -0
  92. package/dist/lib/codex-home.js +136 -0
  93. package/dist/lib/config-transfer.d.ts +16 -0
  94. package/dist/lib/config-transfer.js +56 -0
  95. package/dist/lib/crabbox/cli.d.ts +44 -0
  96. package/dist/lib/crabbox/cli.js +120 -20
  97. package/dist/lib/crabbox/lease.d.ts +52 -1
  98. package/dist/lib/crabbox/lease.js +117 -16
  99. package/dist/lib/crabbox/progress.d.ts +31 -0
  100. package/dist/lib/crabbox/progress.js +76 -0
  101. package/dist/lib/crabbox/runtimes.d.ts +4 -0
  102. package/dist/lib/crabbox/runtimes.js +33 -8
  103. package/dist/lib/crabbox/setup-copy.d.ts +87 -0
  104. package/dist/lib/crabbox/setup-copy.js +127 -0
  105. package/dist/lib/daemon.d.ts +5 -5
  106. package/dist/lib/daemon.js +9 -9
  107. package/dist/lib/drive-sync.js +6 -3
  108. package/dist/lib/event-stream.d.ts +36 -0
  109. package/dist/lib/event-stream.js +68 -0
  110. package/dist/lib/events.d.ts +1 -1
  111. package/dist/lib/exec.d.ts +11 -0
  112. package/dist/lib/exec.js +23 -2
  113. package/dist/lib/export.d.ts +72 -0
  114. package/dist/lib/export.js +269 -0
  115. package/dist/lib/feed-policy.d.ts +1 -1
  116. package/dist/lib/feed-policy.js +14 -9
  117. package/dist/lib/feed-ranking.d.ts +32 -0
  118. package/dist/lib/feed-ranking.js +224 -0
  119. package/dist/lib/feed.d.ts +35 -2
  120. package/dist/lib/feed.js +66 -3
  121. package/dist/lib/fleet/auth-sync.d.ts +53 -0
  122. package/dist/lib/fleet/auth-sync.js +92 -0
  123. package/dist/lib/fleet/remote-login.d.ts +187 -0
  124. package/dist/lib/fleet/remote-login.js +556 -0
  125. package/dist/lib/format.d.ts +30 -3
  126. package/dist/lib/format.js +34 -5
  127. package/dist/lib/fs-atomic.d.ts +7 -2
  128. package/dist/lib/fs-atomic.js +8 -12
  129. package/dist/lib/git.d.ts +16 -0
  130. package/dist/lib/git.js +79 -2
  131. package/dist/lib/heal.js +11 -3
  132. package/dist/lib/hosts/progress.d.ts +28 -10
  133. package/dist/lib/hosts/progress.js +79 -22
  134. package/dist/lib/hosts/remote-cmd.js +4 -0
  135. package/dist/lib/hq/floor.d.ts +87 -0
  136. package/dist/lib/hq/floor.js +226 -0
  137. package/dist/lib/import.d.ts +23 -0
  138. package/dist/lib/import.js +74 -2
  139. package/dist/lib/isolation-boundary-report.d.ts +10 -0
  140. package/dist/lib/isolation-boundary-report.js +35 -0
  141. package/dist/lib/menubar/install-menubar.js +14 -20
  142. package/dist/lib/notify.d.ts +1 -0
  143. package/dist/lib/notify.js +3 -3
  144. package/dist/lib/open-url.d.ts +2 -0
  145. package/dist/lib/open-url.js +19 -0
  146. package/dist/lib/openclaw-keychain.d.ts +56 -0
  147. package/dist/lib/openclaw-keychain.js +236 -0
  148. package/dist/lib/overdue.js +10 -0
  149. package/dist/lib/permissions.d.ts +44 -1
  150. package/dist/lib/permissions.js +284 -7
  151. package/dist/lib/project-launch.d.ts +6 -12
  152. package/dist/lib/project-launch.js +13 -228
  153. package/dist/lib/project-resources.d.ts +7 -0
  154. package/dist/lib/project-resources.js +306 -0
  155. package/dist/lib/pty-client.d.ts +27 -0
  156. package/dist/lib/pty-client.js +136 -10
  157. package/dist/lib/refresh.js +14 -10
  158. package/dist/lib/resource-profiles.d.ts +26 -0
  159. package/dist/lib/resource-profiles.js +157 -0
  160. package/dist/lib/resources/permissions.js +7 -1
  161. package/dist/lib/resources/types.d.ts +1 -1
  162. package/dist/lib/resources.d.ts +1 -1
  163. package/dist/lib/resources.js +32 -3
  164. package/dist/lib/routines.d.ts +11 -0
  165. package/dist/lib/routines.js +56 -15
  166. package/dist/lib/runner.d.ts +1 -0
  167. package/dist/lib/runner.js +66 -5
  168. package/dist/lib/secrets/bundles.d.ts +16 -3
  169. package/dist/lib/secrets/bundles.js +206 -37
  170. package/dist/lib/secrets/icloud-import.d.ts +2 -2
  171. package/dist/lib/secrets/icloud-import.js +9 -6
  172. package/dist/lib/secrets/mcp.js +1 -1
  173. package/dist/lib/secrets/vault-age-helper.d.ts +1 -0
  174. package/dist/lib/secrets/vault-age-helper.js +34 -0
  175. package/dist/lib/secrets/vault.d.ts +49 -0
  176. package/dist/lib/secrets/vault.js +397 -0
  177. package/dist/lib/self-heal/checks/path.js +3 -1
  178. package/dist/lib/self-heal/checks/shadowing.js +10 -2
  179. package/dist/lib/self-heal/checks/shims.js +19 -11
  180. package/dist/lib/session/cloud.d.ts +2 -2
  181. package/dist/lib/session/cloud.js +2 -2
  182. package/dist/lib/session/db.d.ts +4 -0
  183. package/dist/lib/session/db.js +44 -7
  184. package/dist/lib/session/discover.d.ts +2 -0
  185. package/dist/lib/session/discover.js +114 -3
  186. package/dist/lib/session/stream-render.d.ts +3 -0
  187. package/dist/lib/session/stream-render.js +130 -0
  188. package/dist/lib/session/types.d.ts +6 -0
  189. package/dist/lib/share/analytics.d.ts +13 -0
  190. package/dist/lib/share/analytics.js +45 -0
  191. package/dist/lib/share/config.d.ts +19 -5
  192. package/dist/lib/share/config.js +44 -8
  193. package/dist/lib/share/provision.d.ts +58 -6
  194. package/dist/lib/share/provision.js +97 -22
  195. package/dist/lib/share/publish.d.ts +36 -13
  196. package/dist/lib/share/publish.js +55 -8
  197. package/dist/lib/share/worker-template.js +83 -17
  198. package/dist/lib/shims.d.ts +35 -0
  199. package/dist/lib/shims.js +159 -13
  200. package/dist/lib/ssh-exec.d.ts +14 -0
  201. package/dist/lib/ssh-exec.js +57 -0
  202. package/dist/lib/staleness/detectors/hooks.js +25 -1
  203. package/dist/lib/staleness/detectors/permissions.js +66 -0
  204. package/dist/lib/staleness/detectors/workflows.js +20 -0
  205. package/dist/lib/staleness/writers/hooks.js +58 -4
  206. package/dist/lib/staleness/writers/permissions.js +2 -2
  207. package/dist/lib/staleness/writers/skills.js +1 -1
  208. package/dist/lib/staleness/writers/sources.d.ts +10 -1
  209. package/dist/lib/staleness/writers/sources.js +68 -1
  210. package/dist/lib/star-nudge.d.ts +45 -0
  211. package/dist/lib/star-nudge.js +91 -0
  212. package/dist/lib/startup/command-registry.d.ts +7 -1
  213. package/dist/lib/startup/command-registry.js +19 -3
  214. package/dist/lib/state.d.ts +2 -0
  215. package/dist/lib/state.js +17 -4
  216. package/dist/lib/subagents-registry.d.ts +2 -0
  217. package/dist/lib/subagents-registry.js +3 -0
  218. package/dist/lib/teams/parsers.js +214 -6
  219. package/dist/lib/teams/supervisor.d.ts +7 -0
  220. package/dist/lib/teams/supervisor.js +2 -1
  221. package/dist/lib/template.d.ts +1 -1
  222. package/dist/lib/template.js +1 -1
  223. package/dist/lib/triggers/webhook.js +36 -3
  224. package/dist/lib/types.d.ts +84 -0
  225. package/dist/lib/uninstall.js +1 -45
  226. package/dist/lib/version.d.ts +40 -0
  227. package/dist/lib/version.js +94 -16
  228. package/dist/lib/versions.d.ts +24 -0
  229. package/dist/lib/versions.js +269 -78
  230. package/dist/lib/workflows.d.ts +2 -0
  231. package/dist/lib/workflows.js +88 -0
  232. package/package.json +2 -1
  233. package/dist/commands/daemon.d.ts +0 -10
  234. package/dist/commands/daemon.js +0 -121
@@ -0,0 +1,306 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import { AGENTS, agentConfigDirName } from './agents.js';
4
+ import { supports } from './capabilities.js';
5
+ import { buildCommandSkillContent, commandSkillName, readSkillSourceCommandMarker, shouldInstallCommandAsSkill } from './command-skills.js';
6
+ import { commandAppliesTo, parseCommandMetadata } from './commands.js';
7
+ import { markdownToToml } from './convert.js';
8
+ import { safeJoin } from './paths.js';
9
+ import { subagentTarget } from './subagents-registry.js';
10
+ import { parseSubagentFrontmatter } from './subagents.js';
11
+ import { syncWorkflowToVersion } from './workflows.js';
12
+ const MANIFEST_FILE = '.agents-managed.json';
13
+ const MANIFEST_VERSION = 1;
14
+ const COPY_IGNORE = new Set(['.DS_Store', '.git', '.gitignore', '.venv', '__pycache__', 'node_modules']);
15
+ export function projectAgentRoot(projectRoot, agent) {
16
+ return path.join(projectRoot, agentConfigDirName(agent));
17
+ }
18
+ export function syncProjectResourcesToAgent(agent, version, projectAgentsDir) {
19
+ const projectRoot = path.dirname(projectAgentsDir);
20
+ const agentRoot = projectAgentRoot(projectRoot, agent);
21
+ const manifest = loadProjectManifest(agentRoot);
22
+ const result = { synced: [], skipped: [] };
23
+ const next = new Set();
24
+ if (manifest) {
25
+ for (const rel of manifest.paths)
26
+ removeManagedPath(agentRoot, rel);
27
+ }
28
+ syncProjectCommands(agent, version, projectAgentsDir, agentRoot, result, next);
29
+ syncProjectSkills(agent, version, projectAgentsDir, agentRoot, result, next);
30
+ syncProjectSubagents(agent, version, projectAgentsDir, projectRoot, agentRoot, manifest, result, next);
31
+ syncProjectWorkflows(agent, version, projectAgentsDir, projectRoot, agentRoot, result, next);
32
+ if (next.size > 0 || manifest) {
33
+ writeProjectManifest(agentRoot, Array.from(next).sort());
34
+ }
35
+ return result;
36
+ }
37
+ function loadProjectManifest(agentRoot) {
38
+ try {
39
+ const raw = JSON.parse(fs.readFileSync(path.join(agentRoot, MANIFEST_FILE), 'utf-8'));
40
+ if (raw.v !== MANIFEST_VERSION || !Array.isArray(raw.paths))
41
+ return null;
42
+ if (!raw.paths.every((p) => typeof p === 'string' && p.length > 0))
43
+ return null;
44
+ return { ...raw, paths: raw.paths.map(toPosixRel) };
45
+ }
46
+ catch {
47
+ return null;
48
+ }
49
+ }
50
+ function writeProjectManifest(agentRoot, paths) {
51
+ fs.mkdirSync(agentRoot, { recursive: true });
52
+ const p = path.join(agentRoot, MANIFEST_FILE);
53
+ const tmp = p + '.tmp';
54
+ fs.writeFileSync(tmp, JSON.stringify({ v: MANIFEST_VERSION, paths }, null, 2));
55
+ fs.renameSync(tmp, p);
56
+ }
57
+ function removeManagedPath(agentRoot, rel) {
58
+ if (path.isAbsolute(rel) || rel.includes('..'))
59
+ return;
60
+ const target = path.resolve(agentRoot, rel);
61
+ const root = path.resolve(agentRoot);
62
+ if (target !== root && !target.startsWith(root + path.sep))
63
+ return;
64
+ removePath(target);
65
+ }
66
+ function removePath(p) {
67
+ try {
68
+ const st = fs.lstatSync(p);
69
+ if (st.isSymbolicLink() || st.isFile())
70
+ fs.unlinkSync(p);
71
+ else if (st.isDirectory())
72
+ fs.rmSync(p, { recursive: true, force: true });
73
+ }
74
+ catch {
75
+ // already absent
76
+ }
77
+ }
78
+ function pathExists(p) {
79
+ try {
80
+ fs.lstatSync(p);
81
+ return true;
82
+ }
83
+ catch {
84
+ return false;
85
+ }
86
+ }
87
+ function copyDir(src, dest) {
88
+ fs.mkdirSync(dest, { recursive: true });
89
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
90
+ if (entry.isSymbolicLink() || COPY_IGNORE.has(entry.name))
91
+ continue;
92
+ const s = safeJoin(src, entry.name);
93
+ const d = safeJoin(dest, entry.name);
94
+ if (entry.isDirectory())
95
+ copyDir(s, d);
96
+ else if (entry.isFile())
97
+ fs.copyFileSync(s, d);
98
+ }
99
+ }
100
+ function projectEntries(projectAgentsDir, kind) {
101
+ const dir = path.join(projectAgentsDir, kind);
102
+ try {
103
+ return fs.readdirSync(dir, { withFileTypes: true }).filter((entry) => !entry.name.startsWith('.'));
104
+ }
105
+ catch {
106
+ return [];
107
+ }
108
+ }
109
+ /**
110
+ * Manifest paths are persisted to `.agents-managed.json`, which lives in the
111
+ * version-controlled project dir and therefore travels between machines. Store
112
+ * them POSIX-style: `path.join` yields `skills\myskill` on Windows, and a
113
+ * manifest carrying that would silently fail to match — and so fail to clean up
114
+ * its managed files — when the same project is synced on macOS or Linux.
115
+ * Normalizing on both write and read also repairs manifests written by earlier
116
+ * Windows builds.
117
+ */
118
+ function toPosixRel(rel) {
119
+ return rel.replace(/\\/g, '/');
120
+ }
121
+ function record(kind, name, relPaths, result, manifestPaths) {
122
+ result.synced.push(`${kind}/${name}`);
123
+ for (const rel of relPaths)
124
+ manifestPaths.add(toPosixRel(rel));
125
+ }
126
+ function skip(dest, projectRoot, result) {
127
+ const rel = path.relative(projectRoot, dest);
128
+ result.skipped.push(rel);
129
+ console.warn(`Skipping project resource target ${rel}: already exists and is user-owned`);
130
+ }
131
+ function syncProjectCommands(agent, version, projectAgentsDir, agentRoot, result, manifestPaths) {
132
+ const cfg = AGENTS[agent];
133
+ const commandsAsSkills = shouldInstallCommandAsSkill(agent, version);
134
+ const supportsCommands = supports(agent, 'commands', version).ok;
135
+ if (!commandsAsSkills && !supportsCommands)
136
+ return;
137
+ const projectRoot = path.dirname(projectAgentsDir);
138
+ for (const entry of projectEntries(projectAgentsDir, 'commands')) {
139
+ if (!entry.isFile() || !entry.name.endsWith('.md'))
140
+ continue;
141
+ const name = entry.name.slice(0, -'.md'.length);
142
+ const srcFile = path.join(projectAgentsDir, 'commands', entry.name);
143
+ const metadata = parseCommandMetadata(srcFile);
144
+ if (!commandAppliesTo(agent, version, metadata).ok)
145
+ continue;
146
+ if (commandsAsSkills) {
147
+ const sourceMarker = readSkillSourceCommandMarker(name, [path.join(projectAgentsDir, 'skills')]);
148
+ if (pathExists(path.join(projectAgentsDir, 'skills', name)) && sourceMarker !== name)
149
+ continue;
150
+ const skillName = commandSkillName(name);
151
+ const rel = path.join('skills', skillName);
152
+ const destDir = path.join(agentRoot, rel);
153
+ if (pathExists(destDir)) {
154
+ skip(destDir, projectRoot, result);
155
+ continue;
156
+ }
157
+ fs.mkdirSync(destDir, { recursive: true });
158
+ fs.writeFileSync(path.join(destDir, 'SKILL.md'), buildCommandSkillContent(name, srcFile), 'utf-8');
159
+ record('commands', name, [rel], result, manifestPaths);
160
+ continue;
161
+ }
162
+ const ext = cfg.format === 'toml' ? '.toml' : '.md';
163
+ const rel = path.join(cfg.commandsSubdir, `${name}${ext}`);
164
+ const destFile = path.join(agentRoot, rel);
165
+ if (pathExists(destFile)) {
166
+ skip(destFile, projectRoot, result);
167
+ continue;
168
+ }
169
+ fs.mkdirSync(path.dirname(destFile), { recursive: true });
170
+ if (cfg.format === 'toml') {
171
+ fs.writeFileSync(destFile, markdownToToml(name, fs.readFileSync(srcFile, 'utf-8')), 'utf-8');
172
+ }
173
+ else {
174
+ fs.copyFileSync(srcFile, destFile);
175
+ }
176
+ record('commands', name, [rel], result, manifestPaths);
177
+ }
178
+ }
179
+ function syncProjectSkills(agent, version, projectAgentsDir, agentRoot, result, manifestPaths) {
180
+ if (!supports(agent, 'skills', version).ok)
181
+ return;
182
+ const projectRoot = path.dirname(projectAgentsDir);
183
+ for (const entry of projectEntries(projectAgentsDir, 'skills')) {
184
+ if (!entry.isDirectory())
185
+ continue;
186
+ const srcDir = path.join(projectAgentsDir, 'skills', entry.name);
187
+ if (!fs.existsSync(path.join(srcDir, 'SKILL.md')))
188
+ continue;
189
+ const rel = path.join('skills', entry.name);
190
+ const destDir = path.join(agentRoot, rel);
191
+ if (pathExists(destDir)) {
192
+ skip(destDir, projectRoot, result);
193
+ continue;
194
+ }
195
+ copyDir(srcDir, destDir);
196
+ record('skills', entry.name, [rel], result, manifestPaths);
197
+ }
198
+ }
199
+ function readProjectSubagents(projectAgentsDir) {
200
+ const map = new Map();
201
+ for (const entry of projectEntries(projectAgentsDir, 'subagents')) {
202
+ if (!entry.isDirectory())
203
+ continue;
204
+ const dir = path.join(projectAgentsDir, 'subagents', entry.name);
205
+ const agentMd = path.join(dir, 'AGENT.md');
206
+ if (!fs.existsSync(agentMd))
207
+ continue;
208
+ const frontmatter = parseSubagentFrontmatter(agentMd);
209
+ if (!frontmatter)
210
+ continue;
211
+ const files = fs.readdirSync(dir).filter((f) => f.endsWith('.md')).sort();
212
+ map.set(entry.name, { name: entry.name, path: dir, files, frontmatter });
213
+ }
214
+ return map;
215
+ }
216
+ function syncProjectSubagents(agent, version, projectAgentsDir, projectRoot, agentRoot, manifest, result, manifestPaths) {
217
+ if (!supports(agent, 'subagents', version).ok)
218
+ return;
219
+ const target = subagentTarget(agent);
220
+ if (!target)
221
+ return;
222
+ const all = readProjectSubagents(projectAgentsDir);
223
+ const dir = target.dir(projectRoot);
224
+ const targetDirRel = toPosixRel(path.relative(agentRoot, dir));
225
+ const hadManagedTarget = manifest?.paths.some((rel) => rel === targetDirRel || rel.startsWith(targetDirRel + '/')) ?? false;
226
+ for (const sub of all.values()) {
227
+ const occupied = target.occupied(dir, sub.name);
228
+ const existing = occupied.find((entry) => pathExists(entry.path));
229
+ if (existing) {
230
+ skip(existing.path, projectRoot, result);
231
+ continue;
232
+ }
233
+ try {
234
+ target.write(dir, sub);
235
+ record('subagents', sub.name, occupied.map((entry) => path.relative(agentRoot, entry.path)), result, manifestPaths);
236
+ }
237
+ catch {
238
+ // Malformed source or unsupported transform; skip this item.
239
+ }
240
+ }
241
+ const syncedNames = result.synced
242
+ .filter((name) => name.startsWith('subagents/'))
243
+ .map((name) => name.slice('subagents/'.length))
244
+ .filter((name) => all.has(name))
245
+ .map((name) => all.get(name));
246
+ if (target.finalize && (syncedNames.length > 0 || hadManagedTarget)) {
247
+ target.finalize(dir, syncedNames);
248
+ for (const entry of target.finalizeOccupied?.(dir) ?? []) {
249
+ // Same normalization as record(): this is the one manifest write that
250
+ // doesn't funnel through it (the parent subagent index, e.g. Kimi's
251
+ // agents/_agents-cli.yaml).
252
+ manifestPaths.add(toPosixRel(path.relative(agentRoot, entry.path)));
253
+ }
254
+ }
255
+ }
256
+ function workflowManagedRelPaths(agent, projectRoot, name, workflowDir) {
257
+ if (agent === 'kimi')
258
+ return [path.join('.kimi-code', 'skills', name)];
259
+ if (agent === 'goose') {
260
+ const rels = [path.join('.config', 'goose', 'recipes', `${name}.yaml`)];
261
+ const subagentsDir = path.join(workflowDir, 'subagents');
262
+ let hasSubagents = false;
263
+ try {
264
+ hasSubagents = fs.readdirSync(subagentsDir).some((f) => f.endsWith('.md'));
265
+ }
266
+ catch {
267
+ hasSubagents = false;
268
+ }
269
+ if (hasSubagents)
270
+ rels.push(path.join('.config', 'goose', 'recipes', `${name}.subrecipes`));
271
+ return rels;
272
+ }
273
+ if (agent === 'openclaw')
274
+ return [path.join('.openclaw', 'workflows', `${name}.lobster`)];
275
+ return [path.join(agentConfigDirName(agent), 'workflows', name)];
276
+ }
277
+ function syncProjectWorkflows(agent, version, projectAgentsDir, projectRoot, agentRoot, result, manifestPaths) {
278
+ if (!supports(agent, 'workflows', version).ok)
279
+ return;
280
+ if (agent === 'antigravity')
281
+ return;
282
+ for (const entry of projectEntries(projectAgentsDir, 'workflows')) {
283
+ if (!entry.isDirectory())
284
+ continue;
285
+ const workflowDir = path.join(projectAgentsDir, 'workflows', entry.name);
286
+ if (!fs.existsSync(path.join(workflowDir, 'WORKFLOW.md')))
287
+ continue;
288
+ const rels = workflowManagedRelPaths(agent, projectRoot, entry.name, workflowDir);
289
+ const existing = rels.map((rel) => path.join(projectRoot, rel)).find((dest) => pathExists(dest));
290
+ if (existing) {
291
+ skip(existing, projectRoot, result);
292
+ continue;
293
+ }
294
+ let success = false;
295
+ if (agent === 'kimi' || agent === 'goose' || agent === 'openclaw') {
296
+ success = syncWorkflowToVersion(workflowDir, entry.name, agent, projectRoot).success;
297
+ }
298
+ else {
299
+ copyDir(workflowDir, path.join(agentRoot, 'workflows', entry.name));
300
+ success = true;
301
+ }
302
+ if (success) {
303
+ record('workflows', entry.name, rels.map((rel) => path.relative(agentRoot, path.join(projectRoot, rel))), result, manifestPaths);
304
+ }
305
+ }
306
+ }
@@ -4,6 +4,14 @@
4
4
  * Thin client that connects to the PTY sidecar server over unix socket.
5
5
  * Each call opens a connection, sends a JSON request, reads the JSON response, and closes.
6
6
  */
7
+ export interface ServerSpawnArgs {
8
+ bin: string;
9
+ args: string[];
10
+ }
11
+ export interface ServerExit {
12
+ code: number | null;
13
+ signal: NodeJS.Signals | null;
14
+ }
7
15
  /** JSON response envelope from the PTY server. */
8
16
  export interface PtyResponse {
9
17
  ok: boolean;
@@ -15,6 +23,25 @@ export interface PtyResponse {
15
23
  * Auto-starts the server if not running.
16
24
  */
17
25
  export declare function ptyRequest(action: string, id?: string, params?: Record<string, any>): Promise<PtyResponse>;
26
+ export declare function getServerSpawnArgs(options?: {
27
+ isStandaloneExecutable?: boolean;
28
+ /** Physical binary path (test seam). Defaults to `process.execPath`. */
29
+ execPath?: string;
30
+ /** Locate a real `node` (test seam). Defaults to `which/where node`. */
31
+ resolveNode?: () => string | undefined;
32
+ /** File-existence check (test seam). Defaults to `fs.existsSync`. */
33
+ fileExists?: (p: string) => boolean;
34
+ }): ServerSpawnArgs;
35
+ export declare function isBunStandaloneExecutable(moduleUrl?: string): boolean;
36
+ export declare function readRecentLogLines(logPath: string, maxLines?: number): string[];
37
+ export declare function buildPtyStartFailureMessage(details: {
38
+ timeoutMs: number;
39
+ spawn: ServerSpawnArgs;
40
+ exit?: ServerExit;
41
+ spawnError?: Error;
42
+ lastReadinessError?: Error;
43
+ logPath: string;
44
+ }): string;
18
45
  /**
19
46
  * Parse escape sequences in user input strings.
20
47
  * Handles: \n \r \t \e \xHH \\
@@ -11,8 +11,10 @@ import { fileURLToPath } from 'url';
11
11
  import * as path from 'path';
12
12
  import { getSocketPath, getPtyLogPath, isPtyServerRunning } from './pty-server.js';
13
13
  import { backgroundSpawnOptions } from './platform/process.js';
14
+ import { BUN_VIRTUAL_ROOT } from './cli-entry.js';
14
15
  const CONNECT_TIMEOUT_MS = 5000;
15
16
  const RESPONSE_TIMEOUT_MS = 30000;
17
+ const START_TIMEOUT_MS = 5000;
16
18
  const IS_WINDOWS = process.platform === 'win32';
17
19
  /**
18
20
  * Send a request to the PTY server and return the response.
@@ -36,18 +38,44 @@ async function ensureServer() {
36
38
  // Find the entry point to spawn the server
37
39
  const { bin, args } = getServerSpawnArgs();
38
40
  const logPath = getPtyLogPath();
39
- const logFd = fs.openSync(logPath, 'a');
40
- const child = spawn(bin, args, {
41
- stdio: ['ignore', logFd, logFd],
42
- ...backgroundSpawnOptions({ fdStdio: true }),
41
+ let logFd;
42
+ try {
43
+ logFd = fs.openSync(logPath, 'a');
44
+ }
45
+ catch (err) {
46
+ const message = err instanceof Error ? err.message : String(err);
47
+ throw new Error([
48
+ 'PTY server failed to start before spawning.',
49
+ `Spawned: ${formatCommand({ bin, args })}`,
50
+ `Log: ${logPath}`,
51
+ `Log open error: ${message}`,
52
+ ].join('\n'));
53
+ }
54
+ let child;
55
+ try {
56
+ child = spawn(bin, args, {
57
+ stdio: ['ignore', logFd, logFd],
58
+ ...backgroundSpawnOptions({ fdStdio: true }),
59
+ });
60
+ }
61
+ finally {
62
+ fs.closeSync(logFd);
63
+ }
64
+ let childExit;
65
+ let childError;
66
+ let lastReadinessError;
67
+ child.once('exit', (code, signal) => {
68
+ childExit = { code, signal };
69
+ });
70
+ child.once('error', (err) => {
71
+ childError = err;
43
72
  });
44
73
  child.unref();
45
- fs.closeSync(logFd);
46
74
  // Wait for the server to become reachable. On Unix the socket file appearing is
47
75
  // a cheap readiness signal; on Windows the named pipe is not a filesystem object
48
76
  // (fs.existsSync always returns false), so we just attempt the ping directly.
49
77
  const socketPath = getSocketPath();
50
- const deadline = Date.now() + 5000;
78
+ const deadline = Date.now() + START_TIMEOUT_MS;
51
79
  while (Date.now() < deadline) {
52
80
  if (IS_WINDOWS || fs.existsSync(socketPath)) {
53
81
  // Verify we can connect
@@ -55,15 +83,50 @@ async function ensureServer() {
55
83
  await sendRequest({ action: 'ping' });
56
84
  return;
57
85
  }
58
- catch {
59
- // Not ready yet
86
+ catch (err) {
87
+ lastReadinessError = err instanceof Error ? err : new Error(String(err));
60
88
  }
61
89
  }
90
+ if (childError || childExit)
91
+ break;
62
92
  await new Promise(r => setTimeout(r, 100));
63
93
  }
64
- throw new Error('PTY server failed to start within 5 seconds. Check ~/.agents/.system/helpers/pty/logs.jsonl');
94
+ throw new Error(buildPtyStartFailureMessage({
95
+ timeoutMs: START_TIMEOUT_MS,
96
+ spawn: { bin, args },
97
+ exit: childExit,
98
+ spawnError: childError,
99
+ lastReadinessError,
100
+ logPath,
101
+ }));
65
102
  }
66
- function getServerSpawnArgs() {
103
+ export function getServerSpawnArgs(options = {}) {
104
+ const isStandaloneExecutable = options.isStandaloneExecutable
105
+ ?? isBunStandaloneExecutable();
106
+ if (isStandaloneExecutable) {
107
+ // A Bun `--compile` standalone binary cannot `require()` a native addon, so
108
+ // running the PTY sidecar AS this binary fails to load node-pty's pty.node
109
+ // (fatal on macOS; see #315). Prefer a real `node` running the dist/index.js
110
+ // that ships beside the binary — there node-pty loads from the on-disk
111
+ // prebuild. We can't do `<binary> dist/index.js` (the standalone misparses a
112
+ // script arg as a subcommand), so a genuine `node` is required. Fall back to
113
+ // the binary itself only when no node / no dist is found (a bare standalone
114
+ // install with no Node — PTY may then be unavailable, but nothing else can
115
+ // load the addon anyway).
116
+ const node = (options.resolveNode ?? resolveNodeExecutable)();
117
+ const exists = options.fileExists ?? fs.existsSync;
118
+ const execPath = options.execPath ?? process.execPath;
119
+ if (node) {
120
+ try {
121
+ const distIndex = path.join(path.dirname(execPath), '..', 'index.js');
122
+ if (exists(distIndex)) {
123
+ return { bin: node, args: [distIndex, 'pty', '_server'] };
124
+ }
125
+ }
126
+ catch { }
127
+ }
128
+ return { bin: process.execPath, args: ['pty', '_server'] };
129
+ }
67
130
  // Prefer the dist/index.js from the same installation as this code.
68
131
  // This avoids version mismatch when a globally installed `agents` is older.
69
132
  try {
@@ -86,6 +149,69 @@ function getServerSpawnArgs() {
86
149
  catch { }
87
150
  return { bin: 'agents', args: ['pty', '_server'] };
88
151
  }
152
+ export function isBunStandaloneExecutable(moduleUrl = import.meta.url) {
153
+ return BUN_VIRTUAL_ROOT.test(moduleUrl);
154
+ }
155
+ /**
156
+ * Locate a real `node` on PATH for running the sidecar's dist/index.js from a
157
+ * Bun standalone binary (which cannot itself load native addons). Returns
158
+ * undefined when no node is found — the caller then falls back to the binary.
159
+ */
160
+ function resolveNodeExecutable() {
161
+ try {
162
+ const lookup = IS_WINDOWS ? 'where node' : 'which node';
163
+ const node = execSync(lookup, { encoding: 'utf-8' }).split(/\r?\n/)[0].trim();
164
+ if (node && fs.existsSync(node))
165
+ return node;
166
+ }
167
+ catch { }
168
+ return undefined;
169
+ }
170
+ export function readRecentLogLines(logPath, maxLines = 20) {
171
+ try {
172
+ if (!fs.existsSync(logPath))
173
+ return [];
174
+ return fs.readFileSync(logPath, 'utf-8')
175
+ .split(/\r?\n/)
176
+ .filter(line => line.trim().length > 0)
177
+ .slice(-maxLines);
178
+ }
179
+ catch (err) {
180
+ const message = err instanceof Error ? err.message : String(err);
181
+ return [`Unable to read PTY server log: ${message}`];
182
+ }
183
+ }
184
+ export function buildPtyStartFailureMessage(details) {
185
+ const lines = [
186
+ `PTY server failed to start within ${Math.round(details.timeoutMs / 1000)} seconds.`,
187
+ `Spawned: ${formatCommand(details.spawn)}`,
188
+ ];
189
+ if (details.spawnError) {
190
+ lines.push(`Spawn error: ${details.spawnError.message}`);
191
+ }
192
+ if (details.exit) {
193
+ const reason = details.exit.signal
194
+ ? `signal ${details.exit.signal}`
195
+ : `code ${details.exit.code ?? 'unknown'}`;
196
+ lines.push(`PTY server process exited with ${reason} before listening.`);
197
+ }
198
+ if (details.lastReadinessError) {
199
+ lines.push(`Last readiness error: ${details.lastReadinessError.message}`);
200
+ }
201
+ lines.push(`Log: ${details.logPath}`);
202
+ const logLines = readRecentLogLines(details.logPath);
203
+ if (logLines.length > 0) {
204
+ lines.push('Recent PTY server log:');
205
+ lines.push(...logLines.map(line => ` ${line}`));
206
+ }
207
+ else {
208
+ lines.push('No PTY server log output was written.');
209
+ }
210
+ return lines.join('\n');
211
+ }
212
+ function formatCommand(spawnArgs) {
213
+ return [spawnArgs.bin, ...spawnArgs.args].map((part) => JSON.stringify(part)).join(' ');
214
+ }
89
215
  /**
90
216
  * Send a JSON request over the unix socket and return the parsed response.
91
217
  */
@@ -16,7 +16,7 @@ import { capableAgents } from './capabilities.js';
16
16
  import { AGENTS, ALL_AGENT_IDS, getAllCliStates, registerMcpToTargets, agentLabel, } from './agents.js';
17
17
  import { readManifest, MANIFEST_FILENAME } from './manifest.js';
18
18
  import { getUserAgentsDir } from './state.js';
19
- import { installVersion, listInstalledVersions, getGlobalDefault, setGlobalDefault, getVersionHomePath, syncResourcesToVersion, getAvailableResources, getActuallySyncedResources, getNewResources, getProjectOnlyResources, hasNewResources, promptNewResourceSelection, promptResourceSelection, resolveConfiguredAgentTargets, } from './versions.js';
19
+ import { installVersion, listInstalledVersions, isVersionIsolated, getGlobalDefault, setGlobalDefault, getVersionHomePath, syncResourcesToVersion, getAvailableResources, getActuallySyncedResources, getNewResources, getProjectOnlyResources, hasNewResources, promptNewResourceSelection, promptResourceSelection, resolveConfiguredAgentTargets, } from './versions.js';
20
20
  import { listCliStatus, installCli, describeMethod, describeCheck, selectInstallMethod, } from './cli-resources.js';
21
21
  import { ensureShimCurrent, isShimsInPath, addShimsToPath, getPathSetupInstructions, switchConfigSymlink, switchHomeFileSymlinks, } from './shims.js';
22
22
  import { parseHookManifest, registerHooksToSettings } from './hooks.js';
@@ -133,13 +133,9 @@ export async function refresh(options = {}) {
133
133
  actuallySynced.plugins.length > 0;
134
134
  try {
135
135
  let selection;
136
+ let forceFullSync = false;
136
137
  if (skipPrompts) {
137
- if (!hasAnySynced || hasNewResources(newResources, agentId)) {
138
- selection = {
139
- commands: 'all', skills: 'all', hooks: 'all', memory: 'all',
140
- mcp: 'all', permissions: 'all', subagents: 'all', plugins: 'all',
141
- };
142
- }
138
+ forceFullSync = true;
143
139
  }
144
140
  else if (!hasAnySynced) {
145
141
  console.log(chalk.yellow(`\n${agentLabel(agentId)}@${defaultVer} has no synced resources.`));
@@ -153,8 +149,11 @@ export async function refresh(options = {}) {
153
149
  if (userSelection)
154
150
  selection = userSelection;
155
151
  }
156
- if (selection && Object.keys(selection).length > 0) {
157
- const syncResult = syncResourcesToVersion(agentId, defaultVer, selection);
152
+ else {
153
+ forceFullSync = true;
154
+ }
155
+ if (forceFullSync || (selection && Object.keys(selection).length > 0)) {
156
+ const syncResult = syncResourcesToVersion(agentId, defaultVer, selection, forceFullSync ? { force: true } : undefined);
158
157
  const synced = [];
159
158
  if (syncResult.commands)
160
159
  synced.push('commands');
@@ -231,7 +230,12 @@ export async function refresh(options = {}) {
231
230
  }
232
231
  const selectedVersions = [];
233
232
  for (const agentId of agentsNeedingDefault) {
234
- const versions = listInstalledVersions(agentId);
233
+ // Isolated copies are not default-eligible — `agents use` refuses them and
234
+ // setting one here would also switch the config symlink, pointing the user's
235
+ // real ~/.<agent> at an isolated home. Keep them out of the picker entirely.
236
+ const versions = listInstalledVersions(agentId).filter((v) => !isVersionIsolated(agentId, v));
237
+ if (versions.length === 0)
238
+ continue;
235
239
  const agent = AGENTS[agentId];
236
240
  const shouldSwitch = await select({
237
241
  message: `${agentLabel(agent.id)} has no default version. Set one now?`,
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Top-level resource profiles.
3
+ *
4
+ * These are distinct from model-provider run profiles in lib/profiles.ts. A
5
+ * resource profile is a global mode switch stored in agents.yaml that filters
6
+ * the resolved DotAgents resource view and secrets bundles.
7
+ */
8
+ import type { ResourceProfilePreset } from './types.js';
9
+ export type ProfiledResourceKind = 'commands' | 'skills' | 'hooks' | 'subagents' | 'plugins' | 'workflows' | 'permissions' | 'mcp' | 'memory' | 'secrets';
10
+ export type PatternedProfileKind = Exclude<ProfiledResourceKind, 'memory' | 'secrets'>;
11
+ export interface ActiveResourceProfile {
12
+ name: string;
13
+ preset: ResourceProfilePreset;
14
+ }
15
+ export declare function validateResourceProfileName(name: string): void;
16
+ export declare function listResourceProfileNames(): string[];
17
+ export declare function getResourceProfilePreset(name: string): ResourceProfilePreset | null;
18
+ export declare function getActiveResourceProfileName(): string | null;
19
+ export declare function getActiveResourceProfile(): ActiveResourceProfile | null;
20
+ export declare function upsertResourceProfilePreset(name: string, preset: ResourceProfilePreset): void;
21
+ export declare function setActiveResourceProfile(name: string | null): void;
22
+ export declare function patternedProfileKinds(): PatternedProfileKind[];
23
+ export declare function activeRulesPreset(): string | null;
24
+ export declare function filterNamesForActiveResourceProfile(kind: ProfiledResourceKind, names: string[], sourceMap?: Map<string, string>): string[];
25
+ export declare function isNameActiveInResourceProfile(kind: ProfiledResourceKind, name: string, source?: string): boolean;
26
+ export declare function assertNameActiveInResourceProfile(kind: ProfiledResourceKind, name: string): void;