@phnx-labs/agents-cli 1.22.41 → 1.22.43

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 (253) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/README.md +10 -4
  3. package/dist/bin/agents +0 -0
  4. package/dist/bootstrap.js +25 -7
  5. package/dist/cli/command-registry.d.ts +2 -3
  6. package/dist/cli/command-registry.js +4 -8
  7. package/dist/commands/accounts.d.ts +29 -0
  8. package/dist/commands/accounts.js +220 -29
  9. package/dist/commands/alias.d.ts +7 -4
  10. package/dist/commands/alias.js +34 -9
  11. package/dist/commands/auth.d.ts +9 -0
  12. package/dist/commands/auth.js +108 -0
  13. package/dist/commands/browser-sessions-picker.js +42 -62
  14. package/dist/commands/browser.js +30 -38
  15. package/dist/commands/computer-actions.d.ts +1 -1
  16. package/dist/commands/computer-actions.js +1 -1
  17. package/dist/commands/computer-sessions-picker.js +32 -55
  18. package/dist/commands/computer.d.ts +1 -1
  19. package/dist/commands/computer.js +22 -8
  20. package/dist/commands/daemon.js +1 -1
  21. package/dist/commands/doctor.d.ts +5 -3
  22. package/dist/commands/doctor.js +23 -8
  23. package/dist/commands/exec.js +3 -2
  24. package/dist/commands/feed.js +2 -31
  25. package/dist/commands/harness.js +3 -3
  26. package/dist/commands/hooks.js +1 -1
  27. package/dist/commands/import.js +1 -1
  28. package/dist/commands/insights.js +163 -85
  29. package/dist/commands/inspect.js +2 -2
  30. package/dist/commands/mine.js +1 -1
  31. package/dist/commands/monitors.js +1 -1
  32. package/dist/commands/org.d.ts +6 -0
  33. package/dist/commands/org.js +175 -0
  34. package/dist/commands/packages.js +1 -1
  35. package/dist/commands/profiles.js +2 -2
  36. package/dist/commands/prune.js +1 -1
  37. package/dist/commands/routines.js +44 -26
  38. package/dist/commands/run-account-picker.d.ts +19 -0
  39. package/dist/commands/run-account-picker.js +81 -0
  40. package/dist/commands/sessions-picker-factory.d.ts +40 -0
  41. package/dist/commands/sessions-picker-factory.js +67 -0
  42. package/dist/commands/sessions-picker.js +124 -122
  43. package/dist/commands/setup-browser.js +4 -4
  44. package/dist/commands/setup-mine.js +1 -1
  45. package/dist/commands/setup-preferences.js +2 -2
  46. package/dist/commands/setup.js +10 -4
  47. package/dist/commands/snapshot.d.ts +1 -1
  48. package/dist/commands/snapshot.js +4 -4
  49. package/dist/commands/status.d.ts +4 -2
  50. package/dist/commands/status.js +10 -7
  51. package/dist/commands/sync.d.ts +1 -0
  52. package/dist/commands/sync.js +30 -3
  53. package/dist/commands/versions.js +1 -1
  54. package/dist/commands/view.js +7 -1
  55. package/dist/commands/webhook.js +1 -1
  56. package/dist/lib/account-capabilities.d.ts +10 -0
  57. package/dist/lib/account-capabilities.js +34 -0
  58. package/dist/lib/accounting/rotate.d.ts +5 -3
  59. package/dist/lib/accounting/rotate.js +36 -7
  60. package/dist/lib/accounting/usage.d.ts +18 -4
  61. package/dist/lib/accounting/usage.js +138 -27
  62. package/dist/lib/agent-spec/agents.d.ts +584 -0
  63. package/dist/lib/agent-spec/agents.js +3127 -0
  64. package/dist/lib/agent-spec/provider.js +1 -1
  65. package/dist/lib/agents.d.ts +2 -584
  66. package/dist/lib/agents.js +2 -3127
  67. package/dist/lib/browser/drivers/local.d.ts +8 -2
  68. package/dist/lib/browser/drivers/local.js +8 -2
  69. package/dist/lib/browser/drivers/ssh.d.ts +8 -2
  70. package/dist/lib/browser/drivers/ssh.js +10 -4
  71. package/dist/lib/browser/hygiene.d.ts +1 -1
  72. package/dist/lib/browser/hygiene.js +5 -1
  73. package/dist/lib/browser/profiles.d.ts +74 -1
  74. package/dist/lib/browser/profiles.js +114 -5
  75. package/dist/lib/browser/runtime-state.d.ts +11 -5
  76. package/dist/lib/browser/runtime-state.js +13 -9
  77. package/dist/lib/browser/service.d.ts +68 -30
  78. package/dist/lib/browser/service.js +305 -194
  79. package/dist/lib/browser/types.d.ts +95 -3
  80. package/dist/lib/browser/types.js +51 -0
  81. package/dist/lib/catchup.js +1 -1
  82. package/dist/lib/cloud/rush.d.ts +0 -15
  83. package/dist/lib/cloud/rush.js +0 -35
  84. package/dist/lib/computer/actions.d.ts +1 -1
  85. package/dist/lib/computer/actions.js +1 -1
  86. package/dist/lib/{computer-rpc.js → computer/computer-rpc.js} +5 -5
  87. package/dist/lib/computer/dispatch.d.ts +1 -1
  88. package/dist/lib/computer/download.d.ts +1 -1
  89. package/dist/lib/computer/download.js +3 -3
  90. package/dist/lib/{ssh-tunnel.d.ts → computer/ssh-tunnel.d.ts} +1 -1
  91. package/dist/lib/{ssh-tunnel.js → computer/ssh-tunnel.js} +12 -12
  92. package/dist/lib/crabbox/runtimes.d.ts +6 -2
  93. package/dist/lib/crabbox/runtimes.js +21 -5
  94. package/dist/lib/daemon/daemon.js +87 -59
  95. package/dist/lib/{runner.d.ts → daemon/runner.d.ts} +28 -11
  96. package/dist/lib/{runner.js → daemon/runner.js} +143 -123
  97. package/dist/lib/devices/doctor-findings.d.ts +1 -1
  98. package/dist/lib/devices/doctor-findings.js +4 -4
  99. package/dist/lib/devices/fleet-inventory.js +1 -1
  100. package/dist/lib/devices/harness-inventory.js +1 -1
  101. package/dist/lib/devices/health-report.js +1 -1
  102. package/dist/lib/devices/registry.js +4 -22
  103. package/dist/lib/doctor-diff.d.ts +1 -1
  104. package/dist/lib/doctor-diff.js +1 -1
  105. package/dist/lib/drift-sync.d.ts +1 -1
  106. package/dist/lib/drift-sync.js +2 -2
  107. package/dist/lib/drift.d.ts +1 -1
  108. package/dist/lib/drift.js +1 -1
  109. package/dist/lib/entitlement.d.ts +31 -0
  110. package/dist/lib/entitlement.js +137 -0
  111. package/dist/lib/exec.js +52 -205
  112. package/dist/lib/feed/feed.js +2 -8
  113. package/dist/lib/format.d.ts +9 -0
  114. package/dist/lib/format.js +16 -0
  115. package/dist/lib/fs-atomic.d.ts +7 -0
  116. package/dist/lib/fs-atomic.js +9 -0
  117. package/dist/lib/github/pr-mergeable.d.ts +49 -0
  118. package/dist/lib/github/pr-mergeable.js +157 -0
  119. package/dist/lib/github/pr-verdict.d.ts +67 -0
  120. package/dist/lib/github/pr-verdict.js +72 -0
  121. package/dist/lib/harness/adapter.d.ts +161 -0
  122. package/dist/lib/harness/adapter.js +47 -0
  123. package/dist/lib/harness/adapters/claude.d.ts +2 -0
  124. package/dist/lib/harness/adapters/claude.js +110 -0
  125. package/dist/lib/harness/adapters/codex.d.ts +2 -0
  126. package/dist/lib/harness/adapters/codex.js +83 -0
  127. package/dist/lib/harness/adapters/copilot.d.ts +2 -0
  128. package/dist/lib/harness/adapters/copilot.js +23 -0
  129. package/dist/lib/harness/adapters/cursor.d.ts +2 -0
  130. package/dist/lib/harness/adapters/cursor.js +38 -0
  131. package/dist/lib/harness/adapters/droid.d.ts +2 -0
  132. package/dist/lib/harness/adapters/droid.js +17 -0
  133. package/dist/lib/harness/adapters/grok.d.ts +2 -0
  134. package/dist/lib/harness/adapters/grok.js +14 -0
  135. package/dist/lib/harness/adapters/kimi.d.ts +2 -0
  136. package/dist/lib/harness/adapters/kimi.js +49 -0
  137. package/dist/lib/harness/adapters/muse.d.ts +2 -0
  138. package/dist/lib/harness/adapters/muse.js +44 -0
  139. package/dist/lib/harness/adapters/opencode.d.ts +2 -0
  140. package/dist/lib/harness/adapters/opencode.js +14 -0
  141. package/dist/lib/harness/exec-config-version.d.ts +8 -0
  142. package/dist/lib/harness/exec-config-version.js +22 -0
  143. package/dist/lib/harness/index.d.ts +1 -0
  144. package/dist/lib/harness/index.js +34 -0
  145. package/dist/lib/{hooks.d.ts → hooks/install.d.ts} +1 -1
  146. package/dist/lib/{hooks.js → hooks/install.js} +11 -11
  147. package/dist/lib/hosts/passthrough.d.ts +10 -0
  148. package/dist/lib/hosts/passthrough.js +28 -6
  149. package/dist/lib/hosts/session-index.js +1 -1
  150. package/dist/lib/import.js +1 -1
  151. package/dist/lib/installations/index.d.ts +4 -4
  152. package/dist/lib/installations/index.js +3 -4
  153. package/dist/lib/installations/migrate.js +6 -6
  154. package/dist/lib/{shims.d.ts → installations/shims.d.ts} +2 -2
  155. package/dist/lib/{shims.js → installations/shims.js} +13 -115
  156. package/dist/lib/installations/store.d.ts +141 -3
  157. package/dist/lib/installations/store.js +475 -5
  158. package/dist/lib/installations/versions.d.ts +40 -125
  159. package/dist/lib/installations/versions.js +57 -462
  160. package/dist/lib/isolation-boundary-report.d.ts +1 -1
  161. package/dist/lib/isolation-boundary-report.js +1 -1
  162. package/dist/lib/mailbox-gc.js +2 -3
  163. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  164. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  165. package/dist/lib/menubar/install-menubar.js +19 -8
  166. package/dist/lib/monitors/config.d.ts +2 -2
  167. package/dist/lib/monitors/config.js +3 -2
  168. package/dist/lib/monitors/dispatch.d.ts +1 -1
  169. package/dist/lib/monitors/dispatch.js +2 -2
  170. package/dist/lib/monitors/state.d.ts +1 -1
  171. package/dist/lib/origin-machine.d.ts +18 -0
  172. package/dist/lib/origin-machine.js +34 -0
  173. package/dist/lib/perf/db.js +1 -1
  174. package/dist/lib/perf/spool.js +1 -1
  175. package/dist/lib/plugins/plugins.js +1 -1
  176. package/dist/lib/plugins/skills.js +1 -1
  177. package/dist/lib/prix-account.d.ts +158 -0
  178. package/dist/lib/prix-account.js +214 -0
  179. package/dist/lib/profiles.d.ts +7 -0
  180. package/dist/lib/profiles.js +26 -1
  181. package/dist/lib/project-key.js +2 -2
  182. package/dist/lib/project-launch.js +1 -1
  183. package/dist/lib/refresh.js +2 -2
  184. package/dist/lib/resource-inventory.d.ts +1 -1
  185. package/dist/lib/resource-inventory.js +1 -1
  186. package/dist/lib/rules/rules.js +1 -1
  187. package/dist/lib/sandbox.d.ts +24 -0
  188. package/dist/lib/sandbox.js +101 -0
  189. package/dist/lib/scheduling/routines.d.ts +18 -9
  190. package/dist/lib/scheduling/routines.js +17 -9
  191. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  192. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  193. package/dist/lib/secrets/agent.js +9 -1
  194. package/dist/lib/secrets/index.js +1 -1
  195. package/dist/lib/secrets/list-filter.js +1 -1
  196. package/dist/lib/self-heal/checks/hook-runtime.js +1 -1
  197. package/dist/lib/self-heal/checks/path.js +1 -1
  198. package/dist/lib/self-heal/checks/shadowing.js +1 -1
  199. package/dist/lib/self-heal/checks/shims.js +1 -1
  200. package/dist/lib/service-manifest.d.ts +20 -0
  201. package/dist/lib/service-manifest.js +34 -0
  202. package/dist/lib/session/cloud.js +1 -1
  203. package/dist/lib/session/db.js +1 -1
  204. package/dist/lib/session/discover.d.ts +39 -0
  205. package/dist/lib/session/discover.js +3 -3
  206. package/dist/lib/session/fork.js +1 -1
  207. package/dist/lib/session/live-metadata.js +1 -1
  208. package/dist/lib/session/origin-machine.d.ts +1 -18
  209. package/dist/lib/session/origin-machine.js +4 -34
  210. package/dist/lib/session/parse.js +24 -42
  211. package/dist/lib/session/recovery.js +1 -1
  212. package/dist/lib/session/relative-time.d.ts +1 -44
  213. package/dist/lib/session/relative-time.js +4 -120
  214. package/dist/lib/session/remote-list.js +1 -1
  215. package/dist/lib/session/remote.js +2 -2
  216. package/dist/lib/session/short-id.d.ts +1 -17
  217. package/dist/lib/session/short-id.js +4 -20
  218. package/dist/lib/session/width.d.ts +1 -29
  219. package/dist/lib/session/width.js +4 -101
  220. package/dist/lib/snapshot.d.ts +2 -2
  221. package/dist/lib/snapshot.js +1 -1
  222. package/dist/lib/staleness/prune.d.ts +1 -1
  223. package/dist/lib/staleness/prune.js +1 -1
  224. package/dist/lib/staleness/writers/hooks.js +1 -1
  225. package/dist/lib/startup/command-registry.d.ts +4 -1
  226. package/dist/lib/startup/command-registry.js +12 -5
  227. package/dist/lib/teams/agents.js +2 -19
  228. package/dist/lib/teams/parsers.js +20 -26
  229. package/dist/lib/teams/placement-probe.js +1 -1
  230. package/dist/lib/teams/registry.js +2 -21
  231. package/dist/lib/teams/scheduler.js +1 -1
  232. package/dist/lib/text/relative-time.d.ts +44 -0
  233. package/dist/lib/text/relative-time.js +120 -0
  234. package/dist/lib/text/short-id.d.ts +17 -0
  235. package/dist/lib/text/short-id.js +20 -0
  236. package/dist/lib/text/width.d.ts +29 -0
  237. package/dist/lib/text/width.js +101 -0
  238. package/dist/lib/triggers/handlers.d.ts +7 -0
  239. package/dist/lib/triggers/handlers.js +9 -1
  240. package/dist/lib/triggers/webhook.d.ts +1 -0
  241. package/dist/lib/triggers/webhook.js +3 -2
  242. package/dist/lib/uninstall.js +1 -1
  243. package/dist/lib/version.d.ts +9 -2
  244. package/dist/lib/version.js +11 -9
  245. package/dist/lib/view-types.d.ts +4 -0
  246. package/package.json +1 -1
  247. package/dist/commands/tickets.d.ts +0 -2
  248. package/dist/commands/tickets.js +0 -43
  249. package/dist/lib/observe-aliases.d.ts +0 -30
  250. package/dist/lib/observe-aliases.js +0 -38
  251. package/dist/lib/tickets/list.d.ts +0 -53
  252. package/dist/lib/tickets/list.js +0 -153
  253. /package/dist/lib/{computer-rpc.d.ts → computer/computer-rpc.d.ts} +0 -0
@@ -17,29 +17,30 @@ import { spawn, execFileSync } from 'child_process';
17
17
  import * as fs from 'fs';
18
18
  import * as path from 'path';
19
19
  import * as os from 'os';
20
- import { getCliLaunch, getAgentsBinDir } from './cli-entry.js';
21
- import { resolveJobPrompt, parseTimeout, writeRunMeta, listRuns, getJobRunsDir, getRunDir, checkJobDeviceEligibility, finalizeRunMeta, resolveJobExecutionContext, slotRunId, claimRunSlot, readRunMeta, resolveHostStrategy, } from './scheduling/routines.js';
22
- import { getRunsDir, getUserAgentsDir, readMeta, getDaemonDir } from './state.js';
23
- import { shortCodexHome } from './codex-home.js';
24
- import { prepareJobHome, buildSpawnEnv, getJobHomePath } from './sandbox.js';
25
- import { resolveModel, buildReasoningFlags } from './models.js';
26
- import { createTimer, redactPrompt, emitRoutineEnd } from './feed/events.js';
27
- import { codexEditWritableRoots, codexPolicyArgs } from './codex-policy.js';
28
- import { applyAddDirs } from './add-dir.js';
29
- import { normalizeMode, resolveHeadlessMode, buildExecEnv, detectRateLimit, detectAuthFailure, isAuthFailureFromLog, authFailureReason, AGENT_COMMANDS, } from './exec.js';
30
- import { resolveActor } from './actor.js';
31
- import { loadTask as loadHostTask } from './hosts/tasks.js';
32
- import { reconcileTask as reconcileHostTask } from './hosts/reconcile.js';
33
- import { backgroundSpawnOptions, killTree } from './platform/process.js';
20
+ import { getCliLaunch, getAgentsBinDir } from '../cli-entry.js';
21
+ import { resolveJobPrompt, parseTimeout, writeRunMeta, listRuns, getJobRunsDir, getRunDir, checkJobDeviceEligibility, finalizeRunMeta, resolveJobExecutionContext, slotRunId, claimRunSlot, readRunMeta, resolveHostStrategy, } from '../scheduling/routines.js';
22
+ import { getRunsDir, getUserAgentsDir, readMeta, getDaemonDir } from '../state.js';
23
+ import { shortCodexHome } from '../codex-home.js';
24
+ import { prepareJobHome, buildSpawnEnv, getJobHomePath, assertSandboxForwardsHostGhAuth } from '../sandbox.js';
25
+ import { resolveModel, buildReasoningFlags } from '../models.js';
26
+ import { createTimer, redactPrompt, emitRoutineEnd } from '../feed/events.js';
27
+ import { resolveHarnessAdapter } from '../harness/index.js';
28
+ import { applyAddDirs } from '../add-dir.js';
29
+ import { normalizeMode, resolveHeadlessMode, buildExecEnv, detectRateLimit, detectAuthFailure, isAuthFailureFromLog, authFailureReason, AGENT_COMMANDS, } from '../exec.js';
30
+ import { resolveActor } from '../actor.js';
31
+ import { loadTask as loadHostTask } from '../hosts/tasks.js';
32
+ import { reconcileTask as reconcileHostTask } from '../hosts/reconcile.js';
33
+ import { backgroundSpawnOptions, killTree } from '../platform/process.js';
34
34
  import lockfile from 'proper-lockfile';
35
- import { ensureLockTarget } from './fs-atomic.js';
36
- import { walkForFiles } from './fs-walk.js';
37
- import { getBinaryPath, isVersionInstalled, resolveVersion, getVersionHomePath } from './installations/versions.js';
38
- import { resolveClaudeSetupToken } from './claude-account-token.js';
39
- import { getConfiguredRunStrategy, resolveRunVersion, resolveAccountVersion, rotationFailoverChain, readinessFromCandidate, formatNoHealthyAccountError, } from './accounting/rotate.js';
40
- import { readAuthHealth, isDeadVerdict } from './auth-health.js';
41
- import { machineId } from './machine-id.js';
42
- import { isSelfUpdatingAgent, ROUTINE_AGENT_COMMANDS, isAgentHardDeprecated, hardDeprecationError } from './agents.js';
35
+ import { ensureLockTarget } from '../fs-atomic.js';
36
+ import { walkForFiles } from '../fs-walk.js';
37
+ import { getBinaryPath, isVersionInstalled, resolveVersion, getVersionHomePath } from '../installations/versions.js';
38
+ import { resolveClaudeSetupToken } from '../claude-account-token.js';
39
+ import { getConfiguredRunStrategy, resolveRunVersion, resolveAccountVersion, rotationFailoverChain, readinessFromCandidate, formatNoHealthyAccountError, } from '../accounting/rotate.js';
40
+ import { readAuthHealth, isDeadVerdict } from '../auth-health.js';
41
+ import { machineId } from '../machine-id.js';
42
+ import { isSelfUpdatingAgent, ROUTINE_AGENT_IDS, isAgentHardDeprecated, hardDeprecationError } from '../agents.js';
43
+ import { isCustomHarnessName, readProfile } from '../profiles.js';
43
44
  export class RoutineAlreadyRunningError extends Error {
44
45
  constructor(jobName, runId) {
45
46
  super(`Routine '${jobName}' already has a running execution (${runId})`);
@@ -308,7 +309,7 @@ function allocateRoutineAttempt(config, trigger) {
308
309
  // harness leaves the command table (gemini did), and the legacy routine must
309
310
  // still land a visible 'blocked' record, never a generic buildJobCommand
310
311
  // failure (RUSH-2202).
311
- if (!config.workflow && config.agent && isAgentHardDeprecated(config.agent)) {
312
+ if (!config.workflow && config.agent && !isCustomHarnessName(config.agent) && isAgentHardDeprecated(config.agent)) {
312
313
  const reason = hardDeprecationError(config.agent);
313
314
  return {
314
315
  proceed: false,
@@ -488,6 +489,48 @@ export function routineSpawnCwd(config) {
488
489
  const ctx = resolveJobExecutionContext(config, { mode: 'local' });
489
490
  return ctx.absoluteCwd ?? os.homedir();
490
491
  }
492
+ /**
493
+ * Bake the daemon-job argv skeleton from AGENT_COMMANDS.
494
+ *
495
+ * The two tables used to be independent copies of the same launch decision.
496
+ * Argv now comes from AGENT_COMMANDS.base / promptFlag / jsonFlags / modeFlags.
497
+ * Two documented exceptions, not a second table:
498
+ * - kimi uses `--prompt` (long form). `-p` is the exec alias; combining
499
+ * either with --plan/--auto/--yolo aborts, so routineModeArgs emits no
500
+ * mode flag. The daemon has always spawned the long form.
501
+ * - claude places `--verbose` (from jsonFlags) before the prompt and bakes
502
+ * modeFlags.plan so claudeAdapter.routineModeArgs can splice
503
+ * plan → acceptEdits / auto / skip.
504
+ *
505
+ * Returns undefined when `agent` is outside ROUTINE_AGENT_IDS (gemini, grok,
506
+ * and every other harness the daemon does not fire locally).
507
+ */
508
+ export function bakeRoutineArgv(agent) {
509
+ if (!ROUTINE_AGENT_IDS.includes(agent))
510
+ return undefined;
511
+ const template = AGENT_COMMANDS[agent];
512
+ if (!template)
513
+ return undefined;
514
+ const json = template.jsonFlags ?? [];
515
+ const cmd = [...template.base];
516
+ if (agent === 'kimi') {
517
+ cmd.push('--prompt', '{prompt}', ...json);
518
+ return cmd;
519
+ }
520
+ if (agent === 'claude') {
521
+ const verbose = json.includes('--verbose') ? ['--verbose'] : [];
522
+ const jsonRest = json.filter((flag) => flag !== '--verbose');
523
+ cmd.push(template.promptFlag, ...verbose, '{prompt}', ...jsonRest, ...(template.modeFlags.plan ?? []));
524
+ return cmd;
525
+ }
526
+ if (template.promptFlag === 'positional') {
527
+ cmd.push('{prompt}', ...json);
528
+ }
529
+ else {
530
+ cmd.push(template.promptFlag, '{prompt}', ...json);
531
+ }
532
+ return cmd;
533
+ }
491
534
  /** Build the full CLI argv for executing a job, applying mode, model, and permission flags. */
492
535
  export function buildJobCommand(config, resolvedPrompt, forwardAccount = true) {
493
536
  // Workflow branch: delegate to `agents run <workflow>` which handles subagent
@@ -514,94 +557,36 @@ export function buildJobCommand(config, resolvedPrompt, forwardAccount = true) {
514
557
  cmd.push('--account', config.account);
515
558
  return cmd;
516
559
  }
517
- const template = ROUTINE_AGENT_COMMANDS[agent];
560
+ // Custom-harness branch: delegate to `agents run <name>`, which owns profile
561
+ // resolution (host binary, model env, provider auth) — the same delegation
562
+ // the workflow and resume branches use. The profile pins its own version and
563
+ // auth, so no command template, binary pinning, or account-env injection
564
+ // applies here.
565
+ if (isCustomHarnessName(agent)) {
566
+ const cmd = ['agents', 'run', agent, resolvedPrompt, '--mode', config.mode];
567
+ if (config.account && forwardAccount)
568
+ cmd.push('--account', config.account);
569
+ return cmd;
570
+ }
571
+ const template = bakeRoutineArgv(agent);
518
572
  if (!template) {
519
- throw new Error(`Unsupported agent for daemon jobs: ${agent}`);
573
+ // A name outside both tables was either never valid or was a custom
574
+ // harness whose profile has since been deleted — validateJob accepted it
575
+ // against a profile that existed then. Name the repair, not just the miss.
576
+ throw new Error(`Unsupported agent for daemon jobs: ${agent}. ` +
577
+ `If '${agent}' was a custom harness, its profile no longer exists on this device — ` +
578
+ `recreate it (agents harness add ${agent} ...) or point the routine at another agent.`);
520
579
  }
521
580
  let cmd = template.map((part) => part.replace('{prompt}', resolvedPrompt));
522
581
  // Canonicalize mode (accepts legacy `full` as alias for `skip`).
523
582
  const mode = normalizeMode(config.mode);
524
- if (config.agent === 'claude') {
525
- if (mode === 'edit') {
526
- const planIndex = cmd.indexOf('plan');
527
- if (planIndex !== -1)
528
- cmd[planIndex] = 'acceptEdits';
529
- }
530
- else if (mode === 'auto') {
531
- const planIndex = cmd.indexOf('plan');
532
- if (planIndex !== -1)
533
- cmd[planIndex] = 'auto';
534
- }
535
- else if (mode === 'skip') {
536
- // Replace --permission-mode plan with --dangerously-skip-permissions
537
- const pmIndex = cmd.indexOf('--permission-mode');
538
- if (pmIndex !== -1)
539
- cmd.splice(pmIndex, 2, '--dangerously-skip-permissions');
540
- }
541
- appendModelAndReasoning(cmd, config);
542
- }
543
- if (config.agent === 'codex') {
544
- const policyMode = mode === 'plan' || mode === 'skip' ? mode : 'edit';
545
- const routineRoots = (config.allow?.dirs ?? []).map((dir) => {
546
- if (dir.startsWith('-')) {
547
- throw new Error(`allow.dirs entries must not start with '-': ${JSON.stringify(dir)}`);
548
- }
549
- return dir.replace(/^~/, os.homedir());
550
- });
551
- cmd.push(...codexPolicyArgs(policyMode, [...codexEditWritableRoots(), ...routineRoots]));
552
- appendModelAndReasoning(cmd, config);
553
- }
554
- if (config.agent === 'cursor') {
555
- if (mode === 'plan') {
556
- cmd.push('--plan');
557
- }
558
- else if (mode === 'skip') {
559
- cmd.push('-f');
560
- }
561
- else {
562
- // The configured cwd is the user's workspace trust decision. --trust is
563
- // narrower than --yolo/-f because it does not bypass tool permissions.
564
- cmd.push('--trust');
565
- }
566
- appendModelAndReasoning(cmd, config);
567
- }
568
- if (config.agent === 'kimi') {
569
- // kimi daemon jobs always run headless via `--prompt`, which cannot be
570
- // combined with any startup-mode flag (--plan/--auto/--yolo all abort with
571
- // "Cannot combine --prompt with --X"). edit/auto/skip reduce to kimi's default
572
- // headless auto-run, so emit no flag. plan has no headless read-only
573
- // equivalent, so resolveHeadlessMode downgrades a plan request to auto with a
574
- // stderr warning (kimi's headlessPlan is false) — routines run headless, so
575
- // interactive is always false here. The returned mode carries no flag either.
576
- resolveHeadlessMode('kimi', mode, false);
577
- appendModelAndReasoning(cmd, config);
578
- }
579
- if (config.agent === 'droid') {
580
- // droid exec defaults to read-only (plan). Escalate autonomy per mode.
581
- if (mode === 'edit') {
582
- cmd.push('--auto', 'low');
583
- }
584
- else if (mode === 'auto') {
585
- cmd.push('--auto', 'high');
586
- }
587
- else if (mode === 'skip') {
588
- cmd.push('--skip-permissions-unsafe');
589
- }
590
- appendModelAndReasoning(cmd, config);
591
- }
592
- if (config.agent === 'muse') {
593
- // muse exec: plan ≈ no non-shell writes; auto skips approval prompts but
594
- // keeps the OS sandbox; skip is --yolo (no approval, no sandbox, trust).
595
- if (mode === 'plan') {
596
- cmd.push('--disable-write');
597
- }
598
- else if (mode === 'auto') {
599
- cmd.push('--disable-approval');
600
- }
601
- else if (mode === 'skip') {
602
- cmd.push('--yolo');
603
- }
604
- // edit: default on-request approval + sandbox
583
+ // Routine launch-arg quirks (the harness axis of Move 3): each per-agent arm
584
+ // moves to its harness adapter; runner appends model/reasoning flags after,
585
+ // exactly as every arm did. Agents with no routine quirk have no adapter
586
+ // override, so they skip both — the old behavior for an entry with no arm.
587
+ const routineAdapter = resolveHarnessAdapter(agent);
588
+ if (routineAdapter.routineModeArgs) {
589
+ routineAdapter.routineModeArgs(cmd, { mode, config, resolveHeadlessMode });
605
590
  appendModelAndReasoning(cmd, config);
606
591
  }
607
592
  // allow.dirs → harness-specific grants. Codex is handled in its branch above
@@ -628,7 +613,8 @@ export function buildJobCommand(config, resolvedPrompt, forwardAccount = true) {
628
613
  * Reasoning level (config.config.reasoning) maps to per-agent flags via models.ts.
629
614
  */
630
615
  function appendModelAndReasoning(cmd, config) {
631
- // Only called from buildJobCommand's agent path config.agent is set.
616
+ // Only called from buildJobCommand's agent path AFTER the custom-harness
617
+ // branch returned — config.agent is a native id here.
632
618
  const agent = config.agent;
633
619
  const model = config.config?.model;
634
620
  if (model) {
@@ -710,6 +696,8 @@ function transcriptBasePath(runDir) {
710
696
  export function snapshotRoutineTranscriptBase(meta, runDir, overlayHome) {
711
697
  if (!meta.agent)
712
698
  return;
699
+ // A host/cloud custom-harness run records the harness name, which has no
700
+ // transcript spec — the null-specs guard below skips it, as before.
713
701
  const specs = ROUTINE_TRANSCRIPT_SPECS[meta.agent];
714
702
  if (!specs)
715
703
  return;
@@ -748,6 +736,8 @@ function readTranscriptBase(runDir) {
748
736
  export function archiveRoutineTranscripts(meta, runDir, overlayHome) {
749
737
  if (!meta.agent)
750
738
  return;
739
+ // A host/cloud custom-harness run records the harness name, which has no
740
+ // transcript spec — the null-specs guard below skips it, as before.
751
741
  const specs = ROUTINE_TRANSCRIPT_SPECS[meta.agent];
752
742
  if (!specs)
753
743
  return;
@@ -882,10 +872,17 @@ export async function resolveRoutineLaunch(config, cwd = process.cwd(), deps = {
882
872
  if (config.workflow) {
883
873
  return { chain: [], rotation: null, pinned: false };
884
874
  }
875
+ if (config.agent && isCustomHarnessName(config.agent)) {
876
+ // A custom harness pins its own version/auth in the profile, so there is
877
+ // no version/account chain to resolve here — `agents run <name>` owns it
878
+ // (matching exec's "strategy ignored: custom harness pins its own
879
+ // version/auth").
880
+ return { chain: [], rotation: null, pinned: false };
881
+ }
885
882
  // resolveRoutineLaunch is only called for agent jobs (workflow returns above;
886
883
  // command jobs branch out of execute*Job before reaching this).
887
884
  const agent = config.agent;
888
- const { findAccount, findUnifiedAccount, resolveAccountSelection, resolveCredentialAccount } = await import('./account-registry.js');
885
+ const { findAccount, findUnifiedAccount, resolveAccountSelection, resolveCredentialAccount } = await import('../account-registry.js');
889
886
  const meta = (deps.readMeta ?? readMeta)();
890
887
  const explicitCredential = config.account
891
888
  ? (deps.findCredentialAccount?.(config.account) ?? (deps.resolveCredentialAccount !== undefined || findAccount(config.account) !== null))
@@ -1041,7 +1038,7 @@ export async function assertRoutineAccountLocalForPlacement(config, mode, deps =
1041
1038
  return;
1042
1039
  let account = deps.account;
1043
1040
  if (account === undefined) {
1044
- const { findUnifiedAccount } = await import('./account-registry.js');
1041
+ const { findUnifiedAccount } = await import('../account-registry.js');
1045
1042
  account = findUnifiedAccount(config.account, (deps.readMeta ?? readMeta)());
1046
1043
  }
1047
1044
  if (!account) {
@@ -1086,7 +1083,7 @@ export function buildHostDispatchOptions(config, ctx) {
1086
1083
  };
1087
1084
  }
1088
1085
  export function dispatchesViaAgentsRun(config) {
1089
- return Boolean(config.workflow || config.resume);
1086
+ return Boolean(config.workflow || config.resume || (config.agent && isCustomHarnessName(config.agent)));
1090
1087
  }
1091
1088
  /**
1092
1089
  * Merge sandbox/base env with the canonical per-version exec env
@@ -1261,7 +1258,7 @@ async function executeJobPlaced(config, deps, attempt) {
1261
1258
  // do not apply. Sync callers (manual `routines run`, catchup) follow the
1262
1259
  // remote run to completion when possible.
1263
1260
  {
1264
- const { resolvePlacementTarget } = await import('./routines-placement.js');
1261
+ const { resolvePlacementTarget } = await import('../routines-placement.js');
1265
1262
  const target = await resolvePlacementTarget(config);
1266
1263
  const placed = await dispatchPlacedJob(config, target, attempt);
1267
1264
  if (placed)
@@ -1288,7 +1285,11 @@ async function executeJobPlaced(config, deps, attempt) {
1288
1285
  // the agent's config dir, and the sandbox overlay home has only a freshly-generated
1289
1286
  // config with no session store. So a resume job is never sandboxed, regardless of
1290
1287
  // `config.sandbox` (see the resume branch in buildJobCommand).
1291
- const useSandbox = config.sandbox !== false && !config.resume;
1288
+ // Resume needs the REAL home (session store); a custom harness needs it too —
1289
+ // the delegated `agents run <name>` resolves ~/.agents (profiles, setup
1290
+ // sentinel, version homes) from HOME, which the overlay would hide. Exec
1291
+ // still isolates the run in the host version home it swaps HOME into.
1292
+ const useSandbox = config.sandbox !== false && !config.resume && !(config.agent && isCustomHarnessName(config.agent));
1292
1293
  const overlayHome = useSandbox ? prepareJobHome(config) : undefined;
1293
1294
  const runId = attempt.runId;
1294
1295
  const runDir = getRunDir(config.name, runId);
@@ -1299,9 +1300,13 @@ async function executeJobPlaced(config, deps, attempt) {
1299
1300
  // Workflows run via `agents run <workflow>` which delegates to claude under the hood.
1300
1301
  // Use 'claude' as the effective agent for report extraction and metadata when workflow is set.
1301
1302
  // (command jobs branched out earlier, so config.agent is set on the non-workflow path.)
1302
- const effectiveAgent = config.workflow ? 'claude' : config.agent;
1303
+ const effectiveAgent = config.workflow
1304
+ ? 'claude'
1305
+ : isCustomHarnessName(config.agent)
1306
+ ? readProfile(config.agent).host.agent
1307
+ : config.agent;
1303
1308
  if (!dispatchesViaAgentsRun(config)) {
1304
- const { findUnifiedAccount, resolveAccountSelection, resolveCredentialAccount } = await import('./account-registry.js');
1309
+ const { findUnifiedAccount, resolveAccountSelection, resolveCredentialAccount } = await import('../account-registry.js');
1305
1310
  const meta = readMeta();
1306
1311
  const selectedAccount = resolveAccountSelection(config.account, effectiveAgent, meta);
1307
1312
  if (selectedAccount) {
@@ -1312,6 +1317,9 @@ async function executeJobPlaced(config, deps, attempt) {
1312
1317
  Object.assign(baseEnv, resolveCredentialAccount(selectedAccount, effectiveAgent).env);
1313
1318
  }
1314
1319
  }
1320
+ // RUSH-2860: if this host holds gh auth, the sandbox child MUST see it.
1321
+ if (useSandbox)
1322
+ assertSandboxForwardsHostGhAuth(baseEnv);
1315
1323
  const meta = {
1316
1324
  jobName: config.name,
1317
1325
  runId,
@@ -1374,7 +1382,7 @@ async function executeJobPlaced(config, deps, attempt) {
1374
1382
  .map((d) => d.replace(/^~/, os.homedir())),
1375
1383
  } : {}),
1376
1384
  };
1377
- const { runLoop } = await import('./loop.js');
1385
+ const { runLoop } = await import('../loop.js');
1378
1386
  const loopResult = await runLoop(execOptions, config.loop, {
1379
1387
  runId,
1380
1388
  runDir,
@@ -1530,8 +1538,8 @@ async function executeJobOnCloud(config, opts, attempt) {
1530
1538
  if (!config.agent) {
1531
1539
  throw new Error(`Routine '${config.name}' hostStrategy: cloud requires an agent`);
1532
1540
  }
1533
- const { resolveProvider } = await import('./cloud/registry.js');
1534
- const { insertTask } = await import('./cloud/store.js');
1541
+ const { resolveProvider } = await import('../cloud/registry.js');
1542
+ const { insertTask } = await import('../cloud/store.js');
1535
1543
  const provider = resolveProvider(undefined, config.agent);
1536
1544
  const timer = createTimer('agent.run', {
1537
1545
  agent: config.agent,
@@ -1604,9 +1612,9 @@ async function executeJobOnHost(config, opts, attempt) {
1604
1612
  if (config.command) {
1605
1613
  throw new Error(`Routine '${config.name}' uses 'command:', which can't execute on a host yet — remove 'host:' or 'command:'.`);
1606
1614
  }
1607
- const { resolveHostRunTarget, dispatchPromptToHost } = await import('./hosts/run-target.js');
1615
+ const { resolveHostRunTarget, dispatchPromptToHost } = await import('../hosts/run-target.js');
1608
1616
  const host = await resolveHostRunTarget(config.host);
1609
- const { evaluateHostActivationReadiness } = await import('./routine-readiness.js');
1617
+ const { evaluateHostActivationReadiness } = await import('../routine-readiness.js');
1610
1618
  const readiness = await evaluateHostActivationReadiness(config);
1611
1619
  if (!readiness.ready)
1612
1620
  throw new Error(`${readiness.readiness?.code ?? 'not_ready'}: ${readiness.readiness?.message ?? 'target is not ready'}`);
@@ -1754,7 +1762,7 @@ async function executeJobDetachedClaimed(config, attempt, hooks) {
1754
1762
  // (the monitor tick observes an already-finalized record), so notify-desktop
1755
1763
  // sends the finish notification only for local detached runs below (RUSH-2030).
1756
1764
  {
1757
- const { resolvePlacementTarget } = await import('./routines-placement.js');
1765
+ const { resolvePlacementTarget } = await import('../routines-placement.js');
1758
1766
  const target = await resolvePlacementTarget(config);
1759
1767
  if (target.mode === 'host' || target.mode === 'cloud') {
1760
1768
  await assertRoutineAccountLocalForPlacement(config, target.mode);
@@ -1802,7 +1810,11 @@ async function executeJobDetachedClaimed(config, attempt, hooks) {
1802
1810
  // the agent's config dir, and the sandbox overlay home has only a freshly-generated
1803
1811
  // config with no session store. So a resume job is never sandboxed, regardless of
1804
1812
  // `config.sandbox` (see the resume branch in buildJobCommand).
1805
- const useSandbox = config.sandbox !== false && !config.resume;
1813
+ // Resume needs the REAL home (session store); a custom harness needs it too —
1814
+ // the delegated `agents run <name>` resolves ~/.agents (profiles, setup
1815
+ // sentinel, version homes) from HOME, which the overlay would hide. Exec
1816
+ // still isolates the run in the host version home it swaps HOME into.
1817
+ const useSandbox = config.sandbox !== false && !config.resume && !(config.agent && isCustomHarnessName(config.agent));
1806
1818
  const overlayHome = useSandbox ? prepareJobHome(config) : undefined;
1807
1819
  const runId = attempt.runId;
1808
1820
  const runDir = getRunDir(config.name, runId);
@@ -1813,7 +1825,7 @@ async function executeJobDetachedClaimed(config, attempt, hooks) {
1813
1825
  ? buildSpawnEnv(overlayHome, config.env)
1814
1826
  : { ...process.env }, config);
1815
1827
  if (!dispatchesViaAgentsRun(config)) {
1816
- const { findAccount, resolveAccountSelection, resolveCredentialAccount } = await import('./account-registry.js');
1828
+ const { findAccount, resolveAccountSelection, resolveCredentialAccount } = await import('../account-registry.js');
1817
1829
  const selectedAccount = config.account && !findAccount(config.account)
1818
1830
  ? undefined
1819
1831
  : resolveAccountSelection(config.account, config.agent, readMeta());
@@ -1829,7 +1841,15 @@ async function executeJobDetachedClaimed(config, attempt, hooks) {
1829
1841
  })()
1830
1842
  // Non-command path only: config.agent is always set here (command/workflow branch earlier).
1831
1843
  : buildRoutineSpawnEnv(baseEnv, config.agent, version, config.timezone, overlayHome);
1832
- const effectiveAgent = config.workflow ? 'claude' : config.agent;
1844
+ // RUSH-2860: if this host holds gh auth, the sandbox child MUST see it —
1845
+ // otherwise monitors runs records ok while every gh call inside fails.
1846
+ if (useSandbox)
1847
+ assertSandboxForwardsHostGhAuth(spawnEnv);
1848
+ const effectiveAgent = config.workflow
1849
+ ? 'claude'
1850
+ : isCustomHarnessName(config.agent)
1851
+ ? readProfile(config.agent).host.agent
1852
+ : config.agent;
1833
1853
  const meta = {
1834
1854
  jobName: config.name,
1835
1855
  runId,
@@ -1,5 +1,5 @@
1
1
  import type { AgentId } from '../types.js';
2
- import type { DuplicateVersionHook } from '../hooks.js';
2
+ import type { DuplicateVersionHook } from '../hooks/install.js';
3
3
  import type { AgentsBinaryShadow } from '../binary-shadow.js';
4
4
  import type { RcSecretFinding } from '../secrets/rc-hygiene.js';
5
5
  import type { OwnerSinkStatus } from '../channels/owner-sink.js';
@@ -41,13 +41,13 @@ import chalk from 'chalk';
41
41
  import { AGENTS, ALL_AGENT_IDS, supportsAccountInspection } from '../agents.js';
42
42
  import { blocksLocalScripts } from '../platform/winpath.js';
43
43
  import { loginHint } from '../signin-badge.js';
44
- import { CONFIG_ENV_ISOLATED_AGENTS } from '../shims.js';
45
- import { padToWidth, stringWidth } from '../session/width.js';
44
+ import { CONFIG_ENV_ISOLATED_AGENTS } from '../installations/shims.js';
45
+ import { padToWidth, stringWidth } from '../text/width.js';
46
46
  import { windowsSshEnrollmentProblem } from './windows-ssh-enrollment.js';
47
47
  const AGENT_NAMES = Object.fromEntries(ALL_AGENT_IDS.map((id) => [id, AGENTS[id].name]));
48
48
  /** Agents with NO per-version credential isolation: their login is shared across
49
49
  * every installed version, so a "log into THIS version" remediation would be a
50
- * lie. Derived from `CONFIG_ENV_ISOLATED_AGENTS` in `lib/shims.ts` — the shim
50
+ * lie. Derived from `CONFIG_ENV_ISOLATED_AGENTS` in `lib/installations/shims.ts` — the shim
51
51
  * generator is what actually exports the per-version isolation env var, so it is
52
52
  * the source of truth. Do not hand-maintain a second copy: an agent gaining
53
53
  * isolation there must not leave a stale "login is shared" hint here. */
@@ -193,7 +193,7 @@ export function remediationFor(finding) {
193
193
  }
194
194
  // Isolated per-version home: the login must RUN INSIDE that home. A bare
195
195
  // `<cli> login` afterwards would NOT — the native shim resolves to the
196
- // project/default version (`lib/shims.ts:471-474`), so it would log into
196
+ // project/default version (`lib/installations/shims.ts:471-474`), so it would log into
197
197
  // whichever version is default, not the one that is logged out.
198
198
  switch (loginShape(agent)) {
199
199
  case 'subcommand':
@@ -10,7 +10,7 @@
10
10
  */
11
11
  import { getAvailableResources, getVersionHomePath, isVersionIsolated, listInstalledVersions } from '../installations/versions.js';
12
12
  import { supports } from '../capabilities.js';
13
- import { checkVersionHookWiring } from '../hooks.js';
13
+ import { checkVersionHookWiring } from '../hooks/install.js';
14
14
  import { getUserAgentsDir, getSystemAgentsDir, readMeta } from '../state.js';
15
15
  import { listNativeAccounts } from '../account-registry.js';
16
16
  import { readRepoState } from '../git.js';
@@ -16,7 +16,7 @@
16
16
  import chalk from 'chalk';
17
17
  import { ALL_AGENT_IDS, accountDisplayLabel, getAccountInfo, } from '../agents.js';
18
18
  import { deriveUsageStatusFromSnapshot, getUsageInfoByIdentity, getUsageLookupKey, } from '../accounting/usage.js';
19
- import { getVersionHomePath, listInstalledVersions } from '../installations/versions.js';
19
+ import { getVersionHomePath, listInstalledVersions } from '../installations/store.js';
20
20
  import { readMeta } from '../state.js';
21
21
  import { listNativeAccounts } from '../account-registry.js';
22
22
  /**
@@ -1,5 +1,5 @@
1
1
  import chalk from 'chalk';
2
- import { padToWidth, stringWidth, terminalWidth, truncateToWidth } from '../session/width.js';
2
+ import { padToWidth, stringWidth, terminalWidth, truncateToWidth } from '../text/width.js';
3
3
  import { fmtBytes, headroom } from './health.js';
4
4
  import { formatCheckedAge } from '../auth-health.js';
5
5
  import { compareFleetInventories } from './fleet-divergence.js';
@@ -16,9 +16,9 @@
16
16
  import * as fs from 'fs/promises';
17
17
  import * as fsSync from 'fs';
18
18
  import * as path from 'path';
19
- import { randomBytes } from 'crypto';
20
19
  import lockfile from 'proper-lockfile';
21
20
  import { getDevicesRegistryPath, getDevicesIgnoredPath } from '../state.js';
21
+ import { atomicWriteJsonSync } from '../fs-atomic.js';
22
22
  /** A device's effective role, defaulting to `worker` when unset. */
23
23
  export function deviceRole(d) {
24
24
  return d.role ?? 'worker';
@@ -94,24 +94,6 @@ export function platformFromOs(os) {
94
94
  export function shellForPlatform(platform) {
95
95
  return platform === 'windows' ? 'powershell' : 'posix';
96
96
  }
97
- /**
98
- * Atomic JSON write: write to a unique sibling tmp file then rename over the
99
- * target. rename(2) is atomic on POSIX, so a crashed write leaves the old file
100
- * untouched instead of producing a half-written registry that loadDevices()
101
- * would reject.
102
- */
103
- async function atomicWriteJson(p, data) {
104
- await fs.mkdir(path.dirname(p), { recursive: true });
105
- const tmp = `${p}.tmp.${process.pid}.${randomBytes(4).toString('hex')}`;
106
- await fs.writeFile(tmp, JSON.stringify(data, null, 2));
107
- try {
108
- await fs.rename(tmp, p);
109
- }
110
- catch (err) {
111
- await fs.unlink(tmp).catch(() => { });
112
- throw err;
113
- }
114
- }
115
97
  /**
116
98
  * Run `fn` while holding an exclusive cross-process lock on the registry file.
117
99
  * proper-lockfile requires the target to exist, so we touch it first. Stale
@@ -187,7 +169,7 @@ export function loadDevicesSync() {
187
169
  }
188
170
  }
189
171
  async function saveDevices(reg) {
190
- await atomicWriteJson(registryPath(), reg);
172
+ atomicWriteJsonSync(registryPath(), reg);
191
173
  }
192
174
  /** Get a single device profile, or null if it is not registered. */
193
175
  export async function getDevice(name) {
@@ -309,7 +291,7 @@ export async function addIgnored(name) {
309
291
  return withRegistryLock(p, async () => {
310
292
  const set = await loadIgnored();
311
293
  set.add(name);
312
- await atomicWriteJson(p, { ignored: [...set].sort(), updatedAt: new Date().toISOString() });
294
+ atomicWriteJsonSync(p, { ignored: [...set].sort(), updatedAt: new Date().toISOString() });
313
295
  return set;
314
296
  });
315
297
  }
@@ -321,7 +303,7 @@ export async function removeIgnored(name) {
321
303
  const set = await loadIgnored();
322
304
  if (!set.delete(name))
323
305
  return false;
324
- await atomicWriteJson(p, { ignored: [...set].sort(), updatedAt: new Date().toISOString() });
306
+ atomicWriteJsonSync(p, { ignored: [...set].sort(), updatedAt: new Date().toISOString() });
325
307
  return true;
326
308
  });
327
309
  }
@@ -21,7 +21,7 @@
21
21
  */
22
22
  import type { AgentId } from './types.js';
23
23
  import type { DiscoveredPlugin } from './types.js';
24
- import { type HookWiringReport } from './hooks.js';
24
+ import { type HookWiringReport } from './hooks/install.js';
25
25
  import { type ResourceInventory } from './resource-inventory.js';
26
26
  export type DoctorKind = 'commands' | 'skills' | 'hooks' | 'rules' | 'mcp' | 'permissions' | 'subagents' | 'plugins' | 'promptcuts';
27
27
  export type DiffStatus = 'ok' | 'diff' | 'missing' | 'extra';
@@ -33,7 +33,7 @@ import { shouldInstallCommandAsSkill, commandSkillMatches, commandSkillName } fr
33
33
  import { gooseCommandMatches, gooseCommandsDir } from './goose-commands.js';
34
34
  import { supports } from './capabilities.js';
35
35
  import { listSkillsInVersionHome, getVersionSkillsDir } from './plugins/skills.js';
36
- import { listHookEntriesFromDir } from './hooks.js';
36
+ import { listHookEntriesFromDir } from './hooks/install.js';
37
37
  import { getResourceInventory } from './resource-inventory.js';
38
38
  const RULES_DOC_FILENAME = 'README.md';
39
39
  const ALL_KINDS = [
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Interactive drift-sync flow — the single "we detected drift, want to fix it?"
3
- * action, shared by `agents status`, `agents doctor`, and the menu-bar "NEEDS
3
+ * action, shared by `agents sync status`, `agents doctor`, and the menu-bar "NEEDS
4
4
  * SYNC" row.
5
5
  *
6
6
  * It composes existing pieces, re-implementing nothing:
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Interactive drift-sync flow — the single "we detected drift, want to fix it?"
3
- * action, shared by `agents status`, `agents doctor`, and the menu-bar "NEEDS
3
+ * action, shared by `agents sync status`, `agents doctor`, and the menu-bar "NEEDS
4
4
  * SYNC" row.
5
5
  *
6
6
  * It composes existing pieces, re-implementing nothing:
@@ -115,7 +115,7 @@ export async function promptDriftSync(opts = {}) {
115
115
  if (opts.yes || !isInteractiveTerminal()) {
116
116
  if (!opts.yes) {
117
117
  // Non-TTY without --yes: report, don't act, don't throw.
118
- console.log(chalk.gray('\nRun `agents status --yes` to sync, or `agents status` in a terminal to choose.'));
118
+ console.log(chalk.gray('\nRun `agents sync status --yes` to sync, or `agents sync status` in a terminal to choose.'));
119
119
  return base;
120
120
  }
121
121
  const systemPulled = await pullSystem(status);
@@ -62,7 +62,7 @@ export interface DriftSummary {
62
62
  * True when the install is out of sync: any installed version is stale,
63
63
  * never-synced, carries unwired hooks, or has a broken generated hook runtime,
64
64
  * OR a source layer is behind origin.
65
- * `agents doctor` surfaces it as "run `agents status`"; `agents doctor --check`
65
+ * `agents doctor` surfaces it as "run `agents sync status`"; `agents doctor --check`
66
66
  * maps it to a non-zero exit. Orphans are a `prune` concern, not sync drift, so they do
67
67
  * NOT set this flag (mirrors the sync-status engine: an orphan alone never
68
68
  * flags needsSync).
package/dist/lib/drift.js CHANGED
@@ -4,7 +4,7 @@ import { loadManifest, isStale } from './staleness/index.js';
4
4
  import { diffVersionResources } from './doctor-diff.js';
5
5
  import { diffVersionCommands, iterCommandsCapableVersions } from './commands.js';
6
6
  import { diffVersionSkills, iterSkillsCapableVersions } from './plugins/skills.js';
7
- import { iterHooksCapableVersions, listUnmanagedHooksInVersionHome, checkVersionHookWiring } from './hooks.js';
7
+ import { iterHooksCapableVersions, listUnmanagedHooksInVersionHome, checkVersionHookWiring } from './hooks/install.js';
8
8
  import { commitsBehindUpstream } from './git.js';
9
9
  import { getUserAgentsDir, getSystemAgentsDir, getEnabledExtraRepos } from './state.js';
10
10
  // Lines naming exactly what's out of sync for a version, plugins prioritized: