@phnx-labs/agents-cli 1.20.77 → 1.20.82
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.
- package/CHANGELOG.md +589 -0
- package/README.md +27 -25
- package/dist/bin/agents +0 -0
- package/dist/commands/activity.d.ts +9 -0
- package/dist/commands/activity.js +153 -24
- package/dist/commands/apply.js +11 -4
- package/dist/commands/cli.js +1 -1
- package/dist/commands/commands.d.ts +1 -1
- package/dist/commands/commands.js +7 -6
- package/dist/commands/defaults.js +3 -11
- package/dist/commands/doctor.d.ts +74 -0
- package/dist/commands/doctor.js +718 -40
- package/dist/commands/events.js +1 -1
- package/dist/commands/exec.js +53 -8
- package/dist/commands/feed.d.ts +4 -0
- package/dist/commands/feed.js +87 -11
- package/dist/commands/harness.js +2 -2
- package/dist/commands/hooks.js +1 -1
- package/dist/commands/import.js +8 -4
- package/dist/commands/logs.js +6 -0
- package/dist/commands/models.js +1 -1
- package/dist/commands/packages.js +6 -6
- package/dist/commands/permissions.js +1 -1
- package/dist/commands/profiles.js +1 -1
- package/dist/commands/pull.d.ts +1 -1
- package/dist/commands/pull.js +4 -4
- package/dist/commands/repo.d.ts +78 -0
- package/dist/commands/repo.js +195 -11
- package/dist/commands/routines.js +16 -4
- package/dist/commands/rules.js +13 -12
- package/dist/commands/secrets.d.ts +3 -1
- package/dist/commands/secrets.js +108 -34
- package/dist/commands/sessions-browser.d.ts +45 -3
- package/dist/commands/sessions-browser.js +118 -12
- package/dist/commands/sessions-export.d.ts +3 -0
- package/dist/commands/sessions-export.js +13 -10
- package/dist/commands/sessions-inject.d.ts +7 -4
- package/dist/commands/sessions-inject.js +18 -18
- package/dist/commands/sessions-picker.d.ts +15 -1
- package/dist/commands/sessions-picker.js +90 -7
- package/dist/commands/sessions-resume.d.ts +16 -0
- package/dist/commands/sessions-resume.js +14 -13
- package/dist/commands/sessions.d.ts +126 -1
- package/dist/commands/sessions.js +365 -42
- package/dist/commands/set.d.ts +15 -0
- package/dist/commands/set.js +79 -0
- package/dist/commands/setup-fleet.d.ts +21 -0
- package/dist/commands/setup-fleet.js +201 -0
- package/dist/commands/setup-secrets.d.ts +20 -0
- package/dist/commands/setup-secrets.js +223 -0
- package/dist/commands/setup.js +17 -3
- package/dist/commands/skills.js +1 -1
- package/dist/commands/ssh.js +9 -1
- package/dist/commands/sync.js +7 -2
- package/dist/commands/teams-picker.js +0 -1
- package/dist/commands/teams.d.ts +33 -1
- package/dist/commands/teams.js +208 -44
- package/dist/commands/versions.js +10 -5
- package/dist/commands/view.d.ts +1 -0
- package/dist/commands/view.js +11 -0
- package/dist/index.js +8 -13
- package/dist/lib/acp/harnesses.js +0 -7
- package/dist/lib/activity.d.ts +156 -0
- package/dist/lib/activity.js +282 -0
- package/dist/lib/agents.d.ts +9 -23
- package/dist/lib/agents.js +72 -100
- package/dist/lib/auto-pull.d.ts +16 -8
- package/dist/lib/auto-pull.js +23 -28
- package/dist/lib/browser/service.js +3 -0
- package/dist/lib/browser/types.d.ts +7 -0
- package/dist/lib/capabilities.js +6 -2
- package/dist/lib/crabbox/lease.js +2 -2
- package/dist/lib/crabbox/setup-copy.d.ts +4 -4
- package/dist/lib/crabbox/setup-copy.js +4 -4
- package/dist/lib/daemon.d.ts +26 -40
- package/dist/lib/daemon.js +63 -123
- package/dist/lib/devices/fleet-divergence.d.ts +101 -0
- package/dist/lib/devices/fleet-divergence.js +188 -0
- package/dist/lib/devices/fleet-inventory.d.ts +19 -0
- package/dist/lib/devices/fleet-inventory.js +57 -0
- package/dist/lib/devices/fleet.d.ts +1 -1
- package/dist/lib/devices/fleet.js +1 -1
- package/dist/lib/devices/health-report.d.ts +10 -2
- package/dist/lib/devices/health-report.js +32 -1
- package/dist/lib/doctor-diff.d.ts +20 -0
- package/dist/lib/doctor-diff.js +6 -1
- package/dist/lib/drift.d.ts +31 -11
- package/dist/lib/drift.js +58 -7
- package/dist/lib/events.d.ts +8 -1
- package/dist/lib/events.js +10 -1
- package/dist/lib/exec.js +35 -8
- package/dist/lib/feed-post.d.ts +28 -1
- package/dist/lib/feed-post.js +110 -2
- package/dist/lib/fleet/apply.js +8 -1
- package/dist/lib/fleet/auth-sync.d.ts +18 -1
- package/dist/lib/fleet/auth-sync.js +25 -11
- package/dist/lib/fleet/remote-login.js +5 -0
- package/dist/lib/git.d.ts +27 -14
- package/dist/lib/git.js +138 -43
- package/dist/lib/hooks.d.ts +42 -0
- package/dist/lib/hooks.js +137 -93
- package/dist/lib/hosts/dispatch.d.ts +28 -1
- package/dist/lib/hosts/dispatch.js +33 -5
- package/dist/lib/hosts/option.js +2 -2
- package/dist/lib/hosts/passthrough.d.ts +22 -1
- package/dist/lib/hosts/passthrough.js +238 -13
- package/dist/lib/hosts/remote-cmd.js +2 -0
- package/dist/lib/hosts/run-target.d.ts +2 -0
- package/dist/lib/hosts/run-target.js +1 -0
- package/dist/lib/hq/floor.d.ts +1 -1
- package/dist/lib/hq/floor.js +6 -1
- package/dist/lib/mcp.js +3 -71
- package/dist/lib/menubar/MenubarHelper.app/Contents/Info.plist +2 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/Resources/AppIcon.icns +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/_CodeSignature/CodeResources +15 -2
- package/dist/lib/menubar/notify-desktop.d.ts +15 -0
- package/dist/lib/menubar/notify-desktop.js +41 -5
- package/dist/lib/merged-resources.d.ts +11 -0
- package/dist/{commands/resources.js → lib/merged-resources.js} +11 -19
- package/dist/lib/permissions.d.ts +0 -32
- package/dist/lib/permissions.js +4 -157
- package/dist/lib/picker.js +1 -1
- package/dist/lib/platform/process.d.ts +16 -5
- package/dist/lib/platform/process.js +54 -0
- package/dist/lib/plugins.d.ts +0 -8
- package/dist/lib/plugins.js +4 -110
- package/dist/lib/project-resources.js +4 -1
- package/dist/lib/refresh.d.ts +5 -3
- package/dist/lib/refresh.js +7 -5
- package/dist/lib/resources/commands.js +4 -1
- package/dist/lib/resources/mcp.js +0 -4
- package/dist/lib/resources/permissions.d.ts +1 -1
- package/dist/lib/resources/permissions.js +1 -5
- package/dist/lib/resources/rules.js +4 -1
- package/dist/lib/resources/skills.js +4 -1
- package/dist/lib/resources/subagents.js +4 -0
- package/dist/lib/resources/types.d.ts +1 -1
- package/dist/lib/routines.d.ts +20 -0
- package/dist/lib/routines.js +6 -0
- package/dist/lib/run-defaults.d.ts +1 -0
- package/dist/lib/run-defaults.js +9 -0
- package/dist/lib/runner.d.ts +0 -12
- package/dist/lib/runner.js +71 -29
- package/dist/lib/sandbox.js +0 -16
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/Info.plist +2 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/Resources/AppIcon.icns +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +13 -1
- package/dist/lib/secrets/agent.d.ts +18 -0
- package/dist/lib/secrets/agent.js +131 -17
- package/dist/lib/secrets/audit.d.ts +46 -0
- package/dist/lib/secrets/audit.js +56 -0
- package/dist/lib/secrets/bundles.d.ts +12 -9
- package/dist/lib/secrets/bundles.js +47 -31
- package/dist/lib/secrets/index.d.ts +2 -1
- package/dist/lib/secrets/index.js +8 -2
- package/dist/lib/secrets/remote.d.ts +75 -2
- package/dist/lib/secrets/remote.js +139 -5
- package/dist/lib/secrets/scope.d.ts +26 -0
- package/dist/lib/secrets/scope.js +29 -0
- package/dist/lib/secrets/session-store.d.ts +10 -0
- package/dist/lib/secrets/session-store.js +64 -11
- package/dist/lib/secrets/sync.js +3 -3
- package/dist/lib/session/active.d.ts +117 -26
- package/dist/lib/session/active.js +199 -53
- package/dist/lib/session/actor-sidecar.d.ts +23 -0
- package/dist/lib/session/actor-sidecar.js +101 -0
- package/dist/lib/session/db.d.ts +41 -1
- package/dist/lib/session/db.js +173 -8
- package/dist/lib/session/digest.js +126 -21
- package/dist/lib/session/discover.d.ts +19 -2
- package/dist/lib/session/discover.js +32 -11
- package/dist/lib/session/hook-sessions.d.ts +9 -0
- package/dist/lib/session/hook-sessions.js +57 -8
- package/dist/lib/session/origin-machine.d.ts +18 -0
- package/dist/lib/session/origin-machine.js +34 -0
- package/dist/lib/session/pid-registry.d.ts +13 -0
- package/dist/lib/session/render.d.ts +2 -0
- package/dist/lib/session/render.js +1 -1
- package/dist/lib/session/state.d.ts +1 -0
- package/dist/lib/session/state.js +1 -1
- package/dist/lib/session/sync/config.js +2 -2
- package/dist/lib/session/types.d.ts +16 -0
- package/dist/lib/smart-launch.d.ts +86 -0
- package/dist/lib/smart-launch.js +172 -0
- package/dist/lib/staleness/detectors/commands.js +2 -2
- package/dist/lib/staleness/detectors/permissions.js +1 -45
- package/dist/lib/staleness/writers/commands.js +4 -5
- package/dist/lib/staleness/writers/hooks.js +1 -1
- package/dist/lib/startup/command-registry.d.ts +1 -2
- package/dist/lib/startup/command-registry.js +2 -4
- package/dist/lib/subagents-registry.js +5 -12
- package/dist/lib/subagents.d.ts +0 -10
- package/dist/lib/subagents.js +0 -12
- package/dist/lib/teams/agents.d.ts +0 -2
- package/dist/lib/teams/agents.js +10 -27
- package/dist/lib/teams/index.d.ts +1 -1
- package/dist/lib/teams/index.js +1 -1
- package/dist/lib/types.d.ts +17 -9
- package/dist/lib/usage.d.ts +28 -5
- package/dist/lib/usage.js +271 -8
- package/dist/lib/versions.js +7 -1
- package/dist/lib/watchdog/watchdog.d.ts +1 -1
- package/dist/lib/watchdog/watchdog.js +25 -13
- package/package.json +1 -2
- package/dist/commands/check.d.ts +0 -15
- package/dist/commands/check.js +0 -180
- package/dist/commands/resources.d.ts +0 -5
- package/dist/lib/secrets/account-token.d.ts +0 -20
- package/dist/lib/secrets/account-token.js +0 -64
package/dist/commands/repo.js
CHANGED
|
@@ -36,11 +36,13 @@ import { refresh } from '../lib/refresh.js';
|
|
|
36
36
|
import { capableAgents } from '../lib/capabilities.js';
|
|
37
37
|
import { getGlobalDefault, getVersionHomePath, listInstalledVersions } from '../lib/versions.js';
|
|
38
38
|
import { syncAllMarketplaces } from '../lib/plugin-marketplace.js';
|
|
39
|
+
import { gatherRemoteAgentsJson } from '../lib/remote-agents-json.js';
|
|
40
|
+
import { machineId, normalizeHost } from '../lib/machine-id.js';
|
|
39
41
|
/**
|
|
40
42
|
* After a repo add/remove/enable/disable, reconcile each plugins-capable
|
|
41
43
|
* agent's default version against the new marketplace set. Re-synthesizes
|
|
42
44
|
* catalogs and known_marketplaces.json entries. Source-copy of plugins is
|
|
43
|
-
* out of scope here — full sync still goes through `agents
|
|
45
|
+
* out of scope here — full sync still goes through `agents sync`.
|
|
44
46
|
*/
|
|
45
47
|
function syncMarketplacesForDefaults() {
|
|
46
48
|
for (const agent of capableAgents('plugins')) {
|
|
@@ -432,13 +434,161 @@ function renderCards(rows, cols) {
|
|
|
432
434
|
}
|
|
433
435
|
}
|
|
434
436
|
}
|
|
437
|
+
/**
|
|
438
|
+
* Recursion guard for the fleet fan-out, passed as an env var (not a CLI flag)
|
|
439
|
+
* so an OLDER remote `agents` that predates this feature ignores it harmlessly
|
|
440
|
+
* instead of erroring on an unknown option. A peer new enough to fan out reads
|
|
441
|
+
* it and answers only for itself. Mirrors `NO_FANOUT_ENV` in remote-active.ts.
|
|
442
|
+
*/
|
|
443
|
+
export const NO_REPO_FANOUT_ENV = 'AGENTS_REPO_LOCAL';
|
|
444
|
+
/**
|
|
445
|
+
* Parse one peer's `repo status --json` stdout (a RepoRow[]) into a single
|
|
446
|
+
* device entry tagged with `machine`. Defensive against version skew: non-JSON
|
|
447
|
+
* or a non-array yields no entry, and non-object rows are dropped rather than
|
|
448
|
+
* throwing. Exported for unit testing without a live tailnet.
|
|
449
|
+
*/
|
|
450
|
+
export function parseRemoteRepoRows(stdout, machine) {
|
|
451
|
+
let parsed;
|
|
452
|
+
try {
|
|
453
|
+
parsed = JSON.parse(stdout);
|
|
454
|
+
}
|
|
455
|
+
catch {
|
|
456
|
+
return [];
|
|
457
|
+
}
|
|
458
|
+
if (!Array.isArray(parsed))
|
|
459
|
+
return [];
|
|
460
|
+
const rows = parsed.filter((x) => !!x && typeof x === 'object' && !Array.isArray(x));
|
|
461
|
+
return [{ device: machine, reachable: true, rows }];
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Query repo status on the fleet. With an explicit `hosts` list, fan out to
|
|
465
|
+
* exactly those; otherwise sweep every registered, online peer (excluding this
|
|
466
|
+
* machine). Each peer runs `repo status [alias] --json` locally — never its own
|
|
467
|
+
* fan-out — and unreachable peers are skipped with a gray note, never fatal.
|
|
468
|
+
*/
|
|
469
|
+
async function gatherRemoteRepoStatus(alias, hosts) {
|
|
470
|
+
const args = ['repo', 'status', ...(alias ? [alias] : []), '--json'];
|
|
471
|
+
const { items } = await gatherRemoteAgentsJson({
|
|
472
|
+
args,
|
|
473
|
+
noFanoutEnv: NO_REPO_FANOUT_ENV,
|
|
474
|
+
hosts,
|
|
475
|
+
parse: parseRemoteRepoRows,
|
|
476
|
+
});
|
|
477
|
+
return items;
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Render the fleet repo-status table: one row per (device, repo), with the
|
|
481
|
+
* device name shown once per group. SYNC and CHANGES reuse the same compact
|
|
482
|
+
* cells as the local wide table (`↓N (…) ↑N` / `clean` / `~N edits`), and an
|
|
483
|
+
* unreachable device collapses to a single `unreachable` marker row. Pure — no
|
|
484
|
+
* I/O — so the aggregation/formatting is unit-testable without live devices.
|
|
485
|
+
*/
|
|
486
|
+
export function renderDeviceStatusRows(results) {
|
|
487
|
+
const cells = [];
|
|
488
|
+
for (const r of results) {
|
|
489
|
+
if (!r.reachable || !r.rows) {
|
|
490
|
+
cells.push({ device: r.device, repo: chalk.gray('unreachable'), sync: '', changes: '' });
|
|
491
|
+
continue;
|
|
492
|
+
}
|
|
493
|
+
if (r.rows.length === 0) {
|
|
494
|
+
cells.push({ device: r.device, repo: chalk.gray('no repos'), sync: '', changes: '' });
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
let firstOfDevice = true;
|
|
498
|
+
for (const row of r.rows) {
|
|
499
|
+
const device = firstOfDevice ? r.device : '';
|
|
500
|
+
firstOfDevice = false;
|
|
501
|
+
if (row.raw) {
|
|
502
|
+
// missing / no-git-remote / error — the free-form trailer says it all.
|
|
503
|
+
cells.push({ device, repo: row.alias, sync: row.raw, changes: '' });
|
|
504
|
+
}
|
|
505
|
+
else {
|
|
506
|
+
cells.push({ device, repo: row.alias, sync: syncCompact(row), changes: changesCompact(row) });
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
if (cells.length === 0)
|
|
511
|
+
return [];
|
|
512
|
+
const headers = ['DEVICE', 'REPO', 'SYNC', 'CHANGES'];
|
|
513
|
+
const devW = Math.max(headers[0].length, ...cells.map((c) => visibleWidth(c.device)));
|
|
514
|
+
const repoW = Math.max(headers[1].length, ...cells.map((c) => visibleWidth(c.repo)));
|
|
515
|
+
const syncW = Math.max(headers[2].length, ...cells.map((c) => visibleWidth(c.sync)));
|
|
516
|
+
const lines = [];
|
|
517
|
+
lines.push(` ${chalk.gray(headers[0].padEnd(devW))} ${chalk.gray(headers[1].padEnd(repoW))} ${chalk.gray(headers[2].padEnd(syncW))} ${chalk.gray(headers[3])}`);
|
|
518
|
+
for (const c of cells) {
|
|
519
|
+
lines.push(` ${chalk.cyan(padVisible(c.device, devW))} ${padVisible(c.repo, repoW)} ${padVisible(c.sync, syncW)} ${c.changes}`);
|
|
520
|
+
}
|
|
521
|
+
return lines;
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Read the device-scope flags into an intent, or null for the default local-only
|
|
525
|
+
* run. `--devices-all`/`--hosts-all` (and `--devices all`/`--hosts all`) sweep
|
|
526
|
+
* every reachable peer; `--devices box1,box2` targets an explicit list. A peer
|
|
527
|
+
* carrying {@link NO_REPO_FANOUT_ENV} never fans out again (recursion guard).
|
|
528
|
+
*/
|
|
529
|
+
export function resolveDeviceIntent(opts) {
|
|
530
|
+
if (process.env[NO_REPO_FANOUT_ENV] === '1')
|
|
531
|
+
return null;
|
|
532
|
+
if (opts.devicesAll || opts.hostsAll)
|
|
533
|
+
return { all: true };
|
|
534
|
+
const val = opts.devices ?? opts.hosts;
|
|
535
|
+
if (val === undefined)
|
|
536
|
+
return null;
|
|
537
|
+
const v = val.trim();
|
|
538
|
+
if (v === '' || v.toLowerCase() === 'all')
|
|
539
|
+
return { all: true };
|
|
540
|
+
const hosts = v.split(',').map((s) => s.trim()).filter(Boolean);
|
|
541
|
+
return hosts.length ? { hosts } : { all: true };
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* `repo status`/`list` across the fleet: read THIS machine's rows locally, fan
|
|
545
|
+
* out to peers, and render one aggregated table (device · repo · sync · changes).
|
|
546
|
+
* The local box is always shown first; unreachable peers were already noted by
|
|
547
|
+
* the fan-out and are simply absent from the table.
|
|
548
|
+
*/
|
|
549
|
+
async function listReposAcrossDevices(alias, opts, intent) {
|
|
550
|
+
const localTargets = collectRepoTargets(alias);
|
|
551
|
+
if (!localTargets) {
|
|
552
|
+
process.exitCode = 1;
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
const localRows = await Promise.all(localTargets.map(renderRepoRow));
|
|
556
|
+
const self = machineId();
|
|
557
|
+
const hosts = 'hosts' in intent ? intent.hosts : undefined;
|
|
558
|
+
const remote = (await gatherRemoteRepoStatus(alias, hosts))
|
|
559
|
+
// An explicit host list may name this box; the local rows already cover it.
|
|
560
|
+
.filter((r) => normalizeHost(r.device) !== self);
|
|
561
|
+
const combined = [
|
|
562
|
+
{ device: self, reachable: true, rows: localRows },
|
|
563
|
+
...remote,
|
|
564
|
+
];
|
|
565
|
+
if (opts.json) {
|
|
566
|
+
const clean = combined.map((r) => ({
|
|
567
|
+
device: r.device,
|
|
568
|
+
reachable: r.reachable,
|
|
569
|
+
rows: (r.rows ?? []).map((row) => row.raw !== undefined ? { ...row, raw: stripAnsi(row.raw) } : row),
|
|
570
|
+
}));
|
|
571
|
+
console.log(JSON.stringify(clean, null, 2));
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
console.log('');
|
|
575
|
+
for (const line of renderDeviceStatusRows(combined))
|
|
576
|
+
console.log(line);
|
|
577
|
+
console.log('');
|
|
578
|
+
}
|
|
435
579
|
/**
|
|
436
580
|
* Shared action body for `agents repo list` and the hidden `agents repo status`
|
|
437
581
|
* alias. Responsive: a wide terminal gets an aligned table with compact SYNC /
|
|
438
582
|
* CHANGES cells; a narrow one gets one card per repo with wrapping detail;
|
|
439
|
-
* `--verbose` forces the full-detail table at any width.
|
|
583
|
+
* `--verbose` forces the full-detail table at any width. A device-scope flag
|
|
584
|
+
* (`--devices-all` / `--devices <list>`) instead fans out across the fleet.
|
|
440
585
|
*/
|
|
441
586
|
async function listRepos(alias, opts = {}) {
|
|
587
|
+
const intent = resolveDeviceIntent(opts);
|
|
588
|
+
if (intent) {
|
|
589
|
+
await listReposAcrossDevices(alias, opts, intent);
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
442
592
|
const targets = collectRepoTargets(alias);
|
|
443
593
|
if (!targets) {
|
|
444
594
|
process.exitCode = 1;
|
|
@@ -487,6 +637,20 @@ function formatRepoTarget(alias, dir, branch) {
|
|
|
487
637
|
const ref = branch ? `origin/${branch}` : 'origin';
|
|
488
638
|
return `${alias} (${displayHomePath(dir)} → ${ref})`;
|
|
489
639
|
}
|
|
640
|
+
/**
|
|
641
|
+
* Register the fleet device-scope flags shared by `repo list` / `repo status`.
|
|
642
|
+
* `--devices-all` (alias `--hosts-all`) sweeps every reachable device;
|
|
643
|
+
* `--devices <who>` (alias `--hosts`) takes `all` or a comma-separated list.
|
|
644
|
+
* A single `--host`/`--device` is handled upstream by maybeRunOnHost, which
|
|
645
|
+
* streams that one box's `agents repo status` — these are the aggregating forms.
|
|
646
|
+
*/
|
|
647
|
+
function addDeviceStatusOptions(cmd) {
|
|
648
|
+
return cmd
|
|
649
|
+
.option('--devices-all', 'Report repo sync state across ALL reachable fleet devices (alias: --hosts-all).')
|
|
650
|
+
.option('--hosts-all', 'Alias of --devices-all.')
|
|
651
|
+
.option('--devices <who>', 'Fleet devices to report on: "all", or a comma-separated device list (alias: --hosts).')
|
|
652
|
+
.option('--hosts <who>', 'Alias of --devices.');
|
|
653
|
+
}
|
|
490
654
|
/** Register the `agents repos` command tree (`repo` is a convenience alias). */
|
|
491
655
|
export function registerRepoCommands(program) {
|
|
492
656
|
// addHostOption on the group so --help documents --host/--device; remote
|
|
@@ -512,6 +676,9 @@ export function registerRepoCommands(program) {
|
|
|
512
676
|
# See what's registered
|
|
513
677
|
agents repos list
|
|
514
678
|
|
|
679
|
+
# Report repo sync state across the whole fleet (one box: --device <name>)
|
|
680
|
+
agents repos status --devices-all
|
|
681
|
+
|
|
515
682
|
# View one repo's contents (git state + resource counts); omit the name for a picker
|
|
516
683
|
agents repos view system
|
|
517
684
|
|
|
@@ -714,13 +881,13 @@ export function registerRepoCommands(program) {
|
|
|
714
881
|
console.log(chalk.gray(`\nRegistered as "${alias}". Skills and commands from this repo will be`));
|
|
715
882
|
console.log(chalk.gray(`picked up automatically the next time you launch any agent.`));
|
|
716
883
|
});
|
|
717
|
-
repoCmd
|
|
884
|
+
const listCmd = repoCmd
|
|
718
885
|
.command('list [alias]')
|
|
719
886
|
.alias('ls')
|
|
720
887
|
.description('Show all repos with resource-level sync (skills/commands/plugins to pull or push) and local changes.')
|
|
721
888
|
.option('-v, --verbose', 'Full per-resource detail in a table, regardless of terminal width')
|
|
722
|
-
.option('--json', 'machine-readable JSON output')
|
|
723
|
-
|
|
889
|
+
.option('--json', 'machine-readable JSON output');
|
|
890
|
+
addDeviceStatusOptions(listCmd).action(async (alias, options) => {
|
|
724
891
|
await listRepos(alias, options);
|
|
725
892
|
});
|
|
726
893
|
repoCmd
|
|
@@ -856,6 +1023,10 @@ export function registerRepoCommands(program) {
|
|
|
856
1023
|
}
|
|
857
1024
|
else {
|
|
858
1025
|
spinner.fail(`${formatRepoTarget(t.alias, t.dir)}: ${result.error}`);
|
|
1026
|
+
// A failed repo must fail the command. Without this, `agents fleet run
|
|
1027
|
+
// "agents repo pull user"` reported 11 ok across a fleet that pulled
|
|
1028
|
+
// nothing — the silence that hid RUSH-2056. Matches commands/sync.ts.
|
|
1029
|
+
process.exitCode = 1;
|
|
859
1030
|
}
|
|
860
1031
|
}
|
|
861
1032
|
// RUSH-1980: a pull rewrites the routine YAML on disk, but the daemon's
|
|
@@ -911,15 +1082,28 @@ export function registerRepoCommands(program) {
|
|
|
911
1082
|
}
|
|
912
1083
|
else {
|
|
913
1084
|
spinner.fail(`${formatRepoTarget(t.alias, t.dir)}: ${result.error}`);
|
|
1085
|
+
// A failed repo must fail the command. Without this, `agents fleet run
|
|
1086
|
+
// "agents repo push user"` reported ok across a fleet that pushed
|
|
1087
|
+
// nothing — the silence that hid RUSH-2056. Matches commands/sync.ts.
|
|
1088
|
+
process.exitCode = 1;
|
|
914
1089
|
}
|
|
915
1090
|
}
|
|
916
1091
|
});
|
|
1092
|
+
// Deprecated: superseded by `agents sync`. `agents sync --local` runs the same
|
|
1093
|
+
// reconcile stage (sync-umbrella.ts calls this exact `refresh()`), and `agents
|
|
1094
|
+
// sync <agent>` targets one agent — with the added reach `refresh` lacks
|
|
1095
|
+
// (all installed versions, no silent skip when there is no global default).
|
|
1096
|
+
// Hidden from help; kept as a warned, functional alias so old muscle-memory and
|
|
1097
|
+
// scripts don't break. Migrate callers to `agents sync`.
|
|
917
1098
|
repoCmd
|
|
918
|
-
.command('refresh [agent]')
|
|
919
|
-
.description('
|
|
1099
|
+
.command('refresh [agent]', { hidden: true })
|
|
1100
|
+
.description('Deprecated — use `agents sync` instead.')
|
|
920
1101
|
.option('-y, --yes', 'Auto-sync everything without prompting')
|
|
921
1102
|
.option('--skip-clis', 'Skip CLI version install/upgrade from agents.yaml')
|
|
922
1103
|
.action(async (arg, options) => {
|
|
1104
|
+
console.warn(chalk.yellow('`agents repo refresh` is deprecated — use `agents sync` instead:'));
|
|
1105
|
+
console.warn(chalk.gray(' all agents: agents sync --local'));
|
|
1106
|
+
console.warn(chalk.gray(' one agent: agents sync <agent>'));
|
|
923
1107
|
let agentFilter;
|
|
924
1108
|
if (arg) {
|
|
925
1109
|
if (!isAgentName(arg)) {
|
|
@@ -947,12 +1131,12 @@ export function registerRepoCommands(program) {
|
|
|
947
1131
|
process.exit(1);
|
|
948
1132
|
}
|
|
949
1133
|
});
|
|
950
|
-
repoCmd
|
|
1134
|
+
const statusCmd = repoCmd
|
|
951
1135
|
.command('status [alias]', { hidden: true })
|
|
952
|
-
.description('Alias of `list` (kept for muscle memory).')
|
|
1136
|
+
.description('Alias of `list` (kept for muscle memory). Add --devices-all to report across the fleet.')
|
|
953
1137
|
.option('-v, --verbose', 'Full per-resource detail in a table, regardless of terminal width')
|
|
954
|
-
.option('--json', 'machine-readable JSON output')
|
|
955
|
-
|
|
1138
|
+
.option('--json', 'machine-readable JSON output');
|
|
1139
|
+
addDeviceStatusOptions(statusCmd).action(async (alias, options) => {
|
|
956
1140
|
await listRepos(alias, options);
|
|
957
1141
|
});
|
|
958
1142
|
}
|
|
@@ -11,6 +11,7 @@ import * as fs from 'fs';
|
|
|
11
11
|
import * as path from 'path';
|
|
12
12
|
import * as yaml from 'yaml';
|
|
13
13
|
import { isDaemonRunning, signalDaemonReload, startDaemon, stopDaemon, readDaemonLog, getDaemonStatus, } from '../lib/daemon.js';
|
|
14
|
+
import { resolveAgentName, isAgentHardDeprecated, hardDeprecationError } from '../lib/agents.js';
|
|
14
15
|
import { humanizeCron, humanizeNextRun, formatRepoLink, REPO_DISPLAY_MAX } from '../lib/routines-format.js';
|
|
15
16
|
import { listJobs as listAllJobs, deleteJob, readJob, validateJob, writeJob, setJobEnabled, listRuns, getLatestRun, getRunDir, getJobPath, parseAtTime, jobRunsOnThisDevice, checkJobDeviceEligibility, normalizeTriggerEvent, parseHostStrategy, resolveHostStrategy, placementRequiresFiringPin, HOST_STRATEGIES, } from '../lib/routines.js';
|
|
16
17
|
import { discoverProjectRoutinesAt, enableProjectRoutines, disableProjectRoutines, syncProjectRoutines, syncAllProjectRoutines, listEnabledProjectRoots, resolveProjectRoot, displayProjectPath, listProjectRoutineFiles, } from '../lib/routines-project.js';
|
|
@@ -280,7 +281,7 @@ export function registerRoutinesCommands(program) {
|
|
|
280
281
|
`,
|
|
281
282
|
notes: `
|
|
282
283
|
A routine is a YAML file that schedules an agent invocation. It specifies:
|
|
283
|
-
- which agent to run (claude, codex,
|
|
284
|
+
- which agent to run (claude, codex, antigravity, ...)
|
|
284
285
|
- when to run (cron schedule or one-shot time)
|
|
285
286
|
- what task to give the agent (the prompt)
|
|
286
287
|
- execution constraints (mode, effort, timeout)
|
|
@@ -297,8 +298,10 @@ export function registerRoutinesCommands(program) {
|
|
|
297
298
|
- Detached/daemon fires use the pre-flight pick only (next tick re-selects).
|
|
298
299
|
- Diagnostic lines log which account was picked, which were skipped, and
|
|
299
300
|
each failover hop: look for "[agents] routine <name>:" in the run log.
|
|
300
|
-
-
|
|
301
|
-
|
|
301
|
+
- Claude auth: a routine authenticates through the pinned account's own
|
|
302
|
+
on-disk login (the same one 'agents run claude' uses on this device).
|
|
303
|
+
The daemon injects no token; if that account's login has expired the
|
|
304
|
+
run is skipped up front with a re-login hint.
|
|
302
305
|
`,
|
|
303
306
|
});
|
|
304
307
|
routinesCmd
|
|
@@ -458,7 +461,7 @@ export function registerRoutinesCommands(program) {
|
|
|
458
461
|
.command('add [nameOrPath]')
|
|
459
462
|
.description('Create a new routine from a YAML file or inline flags. Starts the scheduler automatically if it is not already running.')
|
|
460
463
|
.option('-s, --schedule <cron>', 'Cron schedule in standard format (5 fields: minute hour day month weekday)')
|
|
461
|
-
.option('-a, --agent <agent>', 'Which agent runs this routine: claude, codex,
|
|
464
|
+
.option('-a, --agent <agent>', 'Which agent runs this routine: claude, codex, antigravity, cursor, or opencode')
|
|
462
465
|
.option('--workflow <name>', 'Run an installed workflow (~/.agents/workflows/<name>) via `agents run`. Mutually exclusive with --agent.')
|
|
463
466
|
.option('--command <sh>', 'Run a plain shell command directly (no agent, no auth, no sandbox) — for deterministic housekeeping routines. Mutually exclusive with --agent and --workflow; --prompt is not used.')
|
|
464
467
|
.option('-p, --prompt <prompt>', 'Task instruction for the agent')
|
|
@@ -525,6 +528,15 @@ export function registerRoutinesCommands(program) {
|
|
|
525
528
|
console.error(chalk.red('An agent, workflow, or command is required (use --agent, --workflow, or --command)'));
|
|
526
529
|
process.exit(1);
|
|
527
530
|
}
|
|
531
|
+
// Hard-deprecated harnesses cannot be scheduled — refuse at create time so
|
|
532
|
+
// no new recurring job silently fails against a retired backend.
|
|
533
|
+
if (options.agent) {
|
|
534
|
+
const routineAgentId = resolveAgentName(options.agent);
|
|
535
|
+
if (routineAgentId && isAgentHardDeprecated(routineAgentId)) {
|
|
536
|
+
console.error(chalk.red(hardDeprecationError(routineAgentId)));
|
|
537
|
+
process.exit(1);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
528
540
|
// Command routines run a plain shell and take no prompt; agent/workflow routines require one.
|
|
529
541
|
if (!options.command && !options.prompt) {
|
|
530
542
|
console.error(chalk.red('Prompt is required (use --prompt)'));
|
package/dist/commands/rules.js
CHANGED
|
@@ -4,7 +4,8 @@ import * as fs from 'fs';
|
|
|
4
4
|
import * as os from 'os';
|
|
5
5
|
import * as path from 'path';
|
|
6
6
|
import { select, checkbox } from '@inquirer/prompts';
|
|
7
|
-
import { AGENTS, agentConfigDirName,
|
|
7
|
+
import { AGENTS, agentConfigDirName, MANAGED_AGENT_IDS, resolveAgentName, formatAgentError, agentLabel, } from '../lib/agents.js';
|
|
8
|
+
import { capableAgents } from '../lib/capabilities.js';
|
|
8
9
|
import { cloneRepo } from '../lib/git.js';
|
|
9
10
|
import { discoverInstructionsFromRepo, discoverRuleFilesFromRepo, installInstructionsCentrally, uninstallInstructions, listInstalledInstructionsWithScope, instructionsExists, getInstructionsContent, listCentralRules, } from '../lib/rules/rules.js';
|
|
10
11
|
import { listInstalledVersions, getGlobalDefault, resolveVersionAlias, syncResourcesToVersion, promptAgentVersionSelection, getVersionHomePath, } from '../lib/versions.js';
|
|
@@ -28,7 +29,7 @@ Examples:
|
|
|
28
29
|
agents rules list claude@2.1.112
|
|
29
30
|
|
|
30
31
|
# Install rules from your .agents repo to multiple agents
|
|
31
|
-
agents rules add --agents claude,codex,
|
|
32
|
+
agents rules add --agents claude,codex,cursor
|
|
32
33
|
|
|
33
34
|
# Install a shared rule file from GitHub
|
|
34
35
|
agents rules add gh:anthropics/agent-rules --agents codex@0.116.0
|
|
@@ -44,13 +45,13 @@ When to use:
|
|
|
44
45
|
|
|
45
46
|
Project rules & @-imports:
|
|
46
47
|
Project rules live in <repo>/.agents/rules/. On every agent launch, the shim compiles
|
|
47
|
-
them into <repo>/AGENTS.md (with CLAUDE.md
|
|
48
|
+
them into <repo>/AGENTS.md (with CLAUDE.md and .cursorrules symlinked to it).
|
|
48
49
|
A hand-authored AGENTS.md is preserved — the compile pipeline only overwrites files
|
|
49
50
|
it owns (those starting with the auto-compile header). Delete the file to migrate.
|
|
50
51
|
|
|
51
52
|
@path imports inside AGENTS.md/CLAUDE.md are resolved at session start by the agent
|
|
52
53
|
itself, not by agents-cli. Support is per-agent:
|
|
53
|
-
Inlined natively: claude
|
|
54
|
+
Inlined natively: claude
|
|
54
55
|
Literal text: codex, cursor, opencode, copilot, amp, kiro, goose
|
|
55
56
|
|
|
56
57
|
For rules that need to work across all agents, inline the content rather than using
|
|
@@ -161,7 +162,7 @@ Project rules & @-imports:
|
|
|
161
162
|
return;
|
|
162
163
|
}
|
|
163
164
|
console.log(chalk.bold('Installed Rules\n'));
|
|
164
|
-
for (const aid of
|
|
165
|
+
for (const aid of MANAGED_AGENT_IDS) {
|
|
165
166
|
const agent = AGENTS[aid];
|
|
166
167
|
const installedVersions = listInstalledVersions(aid);
|
|
167
168
|
const defaultVer = getGlobalDefault(aid);
|
|
@@ -200,7 +201,7 @@ Project rules & @-imports:
|
|
|
200
201
|
rulesCmd
|
|
201
202
|
.command('add [source]')
|
|
202
203
|
.description('Install rule files from a source (GitHub, local path) or pick from central storage')
|
|
203
|
-
.option('-a, --agents <list>', 'Targets: claude, codex@0.116.0, or
|
|
204
|
+
.option('-a, --agents <list>', 'Targets: claude, codex@0.116.0, or cursor@default')
|
|
204
205
|
.option('--names <list>', 'Rule file names from ~/.agents/rules/ (comma-separated)')
|
|
205
206
|
.option('-y, --yes', 'Skip all prompts and use defaults')
|
|
206
207
|
.addHelpText('after', `
|
|
@@ -212,7 +213,7 @@ Examples:
|
|
|
212
213
|
agents rules add --names AGENTS.md --agents codex@0.116.0
|
|
213
214
|
|
|
214
215
|
# Install all rules from a GitHub repo to multiple agents
|
|
215
|
-
agents rules add gh:team/agent-rules --agents claude,
|
|
216
|
+
agents rules add gh:team/agent-rules --agents claude,codex
|
|
216
217
|
|
|
217
218
|
# Sync a local rules directory to your active agents
|
|
218
219
|
agents rules add ~/projects/my-rules --agents claude@default
|
|
@@ -317,7 +318,7 @@ Examples:
|
|
|
317
318
|
let selectedAgents;
|
|
318
319
|
let versionSelections;
|
|
319
320
|
if (options.agents) {
|
|
320
|
-
const result = await resolveAgentTargetsAutoInstalling(options.agents,
|
|
321
|
+
const result = await resolveAgentTargetsAutoInstalling(options.agents, capableAgents('rules'), { yes: options.yes });
|
|
321
322
|
if (!result) {
|
|
322
323
|
console.log(chalk.gray('Cancelled.'));
|
|
323
324
|
return;
|
|
@@ -326,7 +327,7 @@ Examples:
|
|
|
326
327
|
versionSelections = result.versionSelections;
|
|
327
328
|
}
|
|
328
329
|
else {
|
|
329
|
-
const result = await promptAgentVersionSelection(
|
|
330
|
+
const result = await promptAgentVersionSelection(capableAgents('rules'), {
|
|
330
331
|
skipPrompts: options.yes,
|
|
331
332
|
});
|
|
332
333
|
selectedAgents = result.selectedAgents;
|
|
@@ -376,7 +377,7 @@ Examples:
|
|
|
376
377
|
agents rules view codex --scope project
|
|
377
378
|
|
|
378
379
|
# View rules for a specific installed version
|
|
379
|
-
agents rules view
|
|
380
|
+
agents rules view codex@0.116.0
|
|
380
381
|
`)
|
|
381
382
|
.action(async (agentArg, options) => {
|
|
382
383
|
const cwd = process.cwd();
|
|
@@ -393,7 +394,7 @@ Examples:
|
|
|
393
394
|
requestedVersion = resolveVersionAlias(agentId, parts[1]) ?? null;
|
|
394
395
|
}
|
|
395
396
|
else {
|
|
396
|
-
const choices =
|
|
397
|
+
const choices = MANAGED_AGENT_IDS.filter((id) => instructionsExists(id, 'user', cwd) || instructionsExists(id, 'project', cwd));
|
|
397
398
|
if (choices.length === 0) {
|
|
398
399
|
console.log(chalk.yellow('No rule files found.'));
|
|
399
400
|
return;
|
|
@@ -460,7 +461,7 @@ Examples:
|
|
|
460
461
|
if (!agentArg) {
|
|
461
462
|
// Only list agents that actually have a rules file installed — avoids
|
|
462
463
|
// suggesting agents the user hasn't touched.
|
|
463
|
-
const candidates =
|
|
464
|
+
const candidates = MANAGED_AGENT_IDS.filter((id) => instructionsExists(id));
|
|
464
465
|
requireDestructiveArg({
|
|
465
466
|
argName: 'agent',
|
|
466
467
|
command: 'agents rules remove',
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { type Command } from 'commander';
|
|
9
9
|
import { SSH_TARGET_RE, assertValidSshTarget } from '../lib/ssh-exec.js';
|
|
10
10
|
import { quoteWin32ExecArg } from '../lib/platform/index.js';
|
|
11
|
-
import { type SecretsBundle, type SecretsPolicy } from '../lib/secrets/bundles.js';
|
|
11
|
+
import { type SecretsBackend, type SecretsBundle, type SecretsPolicy } from '../lib/secrets/bundles.js';
|
|
12
12
|
/** Read all available data from stdin synchronously, trimmed. */
|
|
13
13
|
/**
|
|
14
14
|
* Read the raw `.env` text for `import --from <path|->`. A `-` reads the .env
|
|
@@ -109,4 +109,6 @@ export declare function assertNeverPolicyAcknowledged(policy: SecretsPolicy | un
|
|
|
109
109
|
iUnderstand?: boolean;
|
|
110
110
|
interactive: boolean;
|
|
111
111
|
}): 'ok' | 'prompt';
|
|
112
|
+
/** Validate a --backend value, exiting with a clear message on a bad one. */
|
|
113
|
+
export declare function secretsDefaultBackend(): SecretsBackend;
|
|
112
114
|
export { quoteWin32ExecArg };
|