@phnx-labs/agents-cli 1.19.2 → 1.20.3
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 +140 -0
- package/README.md +72 -12
- package/dist/browser.js +0 -0
- package/dist/commands/browser.js +88 -16
- package/dist/commands/cli.d.ts +14 -0
- package/dist/commands/cli.js +244 -0
- package/dist/commands/cloud.js +1 -1
- package/dist/commands/commands.js +27 -10
- package/dist/commands/computer.js +18 -1
- package/dist/commands/doctor.d.ts +1 -1
- package/dist/commands/doctor.js +2 -2
- package/dist/commands/exec.js +38 -18
- package/dist/commands/factory.d.ts +3 -14
- package/dist/commands/factory.js +3 -3
- package/dist/commands/feedback.d.ts +7 -0
- package/dist/commands/feedback.js +89 -0
- package/dist/commands/helper.d.ts +12 -0
- package/dist/commands/helper.js +87 -0
- package/dist/commands/hooks.js +89 -10
- package/dist/commands/mcp.js +166 -10
- package/dist/commands/packages.js +196 -27
- package/dist/commands/permissions.js +21 -6
- package/dist/commands/plugins.js +11 -4
- package/dist/commands/profiles.d.ts +8 -0
- package/dist/commands/profiles.js +118 -5
- package/dist/commands/prune.js +39 -160
- package/dist/commands/pull.js +58 -5
- package/dist/commands/routines.js +107 -14
- package/dist/commands/rules.js +8 -4
- package/dist/commands/secrets-migrate.d.ts +24 -0
- package/dist/commands/secrets-migrate.js +198 -0
- package/dist/commands/secrets-sync.d.ts +11 -0
- package/dist/commands/secrets-sync.js +155 -0
- package/dist/commands/secrets.js +79 -46
- package/dist/commands/sessions.d.ts +28 -0
- package/dist/commands/sessions.js +98 -33
- package/dist/commands/setup.d.ts +1 -0
- package/dist/commands/setup.js +37 -28
- package/dist/commands/skills.js +25 -8
- package/dist/commands/subagents.js +69 -49
- package/dist/commands/teams.js +61 -10
- package/dist/commands/utils.d.ts +33 -0
- package/dist/commands/utils.js +139 -0
- package/dist/commands/versions.d.ts +4 -3
- package/dist/commands/versions.js +134 -130
- package/dist/commands/view.d.ts +6 -0
- package/dist/commands/view.js +175 -19
- package/dist/commands/workflows.js +29 -6
- package/dist/computer.js +0 -0
- package/dist/index.js +38 -6
- package/dist/lib/acp/client.js +6 -1
- package/dist/lib/acp/harnesses.js +8 -0
- package/dist/lib/agents.d.ts +4 -0
- package/dist/lib/agents.js +125 -34
- package/dist/lib/auto-pull-worker.js +18 -1
- package/dist/lib/browser/cdp.d.ts +8 -1
- package/dist/lib/browser/cdp.js +40 -3
- package/dist/lib/browser/chrome.d.ts +13 -0
- package/dist/lib/browser/chrome.js +46 -3
- package/dist/lib/browser/domain-skills.d.ts +51 -0
- package/dist/lib/browser/domain-skills.js +157 -0
- package/dist/lib/browser/drivers/local.js +45 -4
- package/dist/lib/browser/drivers/ssh.js +2 -2
- package/dist/lib/browser/ipc.d.ts +8 -1
- package/dist/lib/browser/ipc.js +37 -28
- package/dist/lib/browser/profiles.d.ts +16 -3
- package/dist/lib/browser/profiles.js +44 -4
- package/dist/lib/browser/service.d.ts +3 -0
- package/dist/lib/browser/service.js +40 -5
- package/dist/lib/browser/types.d.ts +11 -4
- package/dist/lib/cli-resources.d.ts +137 -0
- package/dist/lib/cli-resources.js +477 -0
- package/dist/lib/cloud/factory.d.ts +1 -1
- package/dist/lib/cloud/factory.js +1 -1
- package/dist/lib/cloud/rush.js +5 -5
- package/dist/lib/command-skills.js +0 -2
- package/dist/lib/computer-rpc.d.ts +3 -0
- package/dist/lib/computer-rpc.js +53 -0
- package/dist/lib/daemon.js +20 -0
- package/dist/lib/events.d.ts +16 -2
- package/dist/lib/events.js +33 -2
- package/dist/lib/exec.d.ts +42 -13
- package/dist/lib/exec.js +127 -33
- package/dist/lib/help.js +11 -5
- package/dist/lib/hooks/cache.d.ts +38 -0
- package/dist/lib/hooks/cache.js +242 -0
- package/dist/lib/hooks/profile.d.ts +33 -0
- package/dist/lib/hooks/profile.js +129 -0
- package/dist/lib/hooks.d.ts +0 -10
- package/dist/lib/hooks.js +246 -11
- package/dist/lib/mcp.d.ts +15 -0
- package/dist/lib/mcp.js +46 -0
- package/dist/lib/migrate.js +1 -1
- package/dist/lib/overdue.d.ts +26 -0
- package/dist/lib/overdue.js +101 -0
- package/dist/lib/permissions.d.ts +13 -0
- package/dist/lib/permissions.js +55 -1
- package/dist/lib/plugin-marketplace.js +1 -1
- package/dist/lib/plugins.js +15 -1
- package/dist/lib/profiles-presets.d.ts +26 -0
- package/dist/lib/profiles-presets.js +216 -0
- package/dist/lib/profiles.d.ts +34 -0
- package/dist/lib/profiles.js +112 -1
- package/dist/lib/resources/mcp.js +37 -0
- package/dist/lib/resources.d.ts +1 -1
- package/dist/lib/rotate.js +10 -4
- package/dist/lib/routines-format.d.ts +47 -0
- package/dist/lib/routines-format.js +194 -0
- package/dist/lib/routines.d.ts +8 -2
- package/dist/lib/routines.js +34 -14
- package/dist/lib/runner.js +83 -15
- package/dist/lib/scheduler.js +8 -1
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +1 -9
- package/dist/lib/secrets/bundles.d.ts +34 -17
- package/dist/lib/secrets/bundles.js +210 -36
- package/dist/lib/secrets/index.d.ts +49 -30
- package/dist/lib/secrets/index.js +126 -115
- package/dist/lib/secrets/install-helper.d.ts +45 -0
- package/dist/lib/secrets/install-helper.js +165 -0
- package/dist/lib/secrets/linux.js +4 -4
- package/dist/lib/secrets/sync.d.ts +56 -0
- package/dist/lib/secrets/sync.js +180 -0
- package/dist/lib/session/active.d.ts +8 -0
- package/dist/lib/session/active.js +3 -2
- package/dist/lib/session/db.d.ts +0 -4
- package/dist/lib/session/db.js +0 -26
- package/dist/lib/session/parse.d.ts +1 -0
- package/dist/lib/session/parse.js +44 -0
- package/dist/lib/session/render.js +4 -4
- package/dist/lib/session/types.d.ts +2 -2
- package/dist/lib/session/types.js +1 -1
- package/dist/lib/shims.d.ts +5 -2
- package/dist/lib/shims.js +70 -38
- package/dist/lib/state.d.ts +14 -2
- package/dist/lib/state.js +51 -20
- package/dist/lib/teams/agents.d.ts +5 -4
- package/dist/lib/teams/agents.js +48 -22
- package/dist/lib/teams/api.d.ts +2 -1
- package/dist/lib/teams/api.js +4 -3
- package/dist/lib/teams/parsers.d.ts +1 -1
- package/dist/lib/teams/parsers.js +153 -3
- package/dist/lib/teams/summarizer.js +18 -2
- package/dist/lib/teams/worktree.js +14 -3
- package/dist/lib/types.d.ts +63 -4
- package/dist/lib/types.js +8 -3
- package/dist/lib/usage.d.ts +27 -2
- package/dist/lib/usage.js +100 -17
- package/dist/lib/versions.d.ts +45 -3
- package/dist/lib/versions.js +455 -60
- package/package.json +15 -14
- package/scripts/install-helper.js +97 -0
- package/scripts/postinstall.js +16 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/embedded.provisionprofile +0 -0
- package/npm-shrinkwrap.json +0 -3162
package/dist/commands/prune.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* `agents prune cleanup` — destructive cleanup across the install.
|
|
3
3
|
*
|
|
4
4
|
* Cleanup targets:
|
|
5
5
|
* - Resource orphans: command/skill/hook files inside a version home that no
|
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
* into the version install).
|
|
8
8
|
* - Version duplicates: older installed versions of an agent that share an
|
|
9
9
|
* account with a newer installed version of the same agent.
|
|
10
|
-
* - Trash
|
|
11
|
-
*
|
|
10
|
+
* - Trash/session targets are retained as no-op compatibility shims: version
|
|
11
|
+
* homes and session history are durable and must not be hard-deleted by
|
|
12
|
+
* agents-cli.
|
|
12
13
|
* - Runs: routine execution logs, keeping only the last N per job.
|
|
13
14
|
*
|
|
14
15
|
* Sync (additive: copy missing/changed files into version homes) is no longer
|
|
@@ -21,7 +22,6 @@
|
|
|
21
22
|
* to widen orphan cleanup to every installed version.
|
|
22
23
|
*/
|
|
23
24
|
import * as fs from 'fs';
|
|
24
|
-
import * as path from 'path';
|
|
25
25
|
import chalk from 'chalk';
|
|
26
26
|
import { confirm } from '@inquirer/prompts';
|
|
27
27
|
import { diffVersionCommands, iterCommandsCapableVersions, removeCommandFromVersion, } from '../lib/commands.js';
|
|
@@ -34,7 +34,6 @@ import { resolveAgentName, formatAgentError } from '../lib/agents.js';
|
|
|
34
34
|
import { pruneDuplicates } from './view.js';
|
|
35
35
|
import { isInteractiveTerminal, isPromptCancelled } from './utils.js';
|
|
36
36
|
import { getTrashDir } from '../lib/state.js';
|
|
37
|
-
import { countSessionsOlderThan, deleteSessionsOlderThan } from '../lib/session/db.js';
|
|
38
37
|
import { previewRunsPrune, pruneRuns, countAllRuns } from '../lib/routines.js';
|
|
39
38
|
const RESOURCE_TYPES = ['commands', 'skills', 'hooks', 'plugins', 'subagents'];
|
|
40
39
|
const STATE_TYPES = ['trash', 'sessions', 'runs'];
|
|
@@ -125,12 +124,6 @@ function parseTarget(arg) {
|
|
|
125
124
|
console.log(chalk.gray(formatAgentError(arg)));
|
|
126
125
|
process.exit(1);
|
|
127
126
|
}
|
|
128
|
-
function parseDays(value, defaultDays) {
|
|
129
|
-
const match = value.match(/^(\d+)d?$/);
|
|
130
|
-
if (match)
|
|
131
|
-
return parseInt(match[1], 10);
|
|
132
|
-
return defaultDays;
|
|
133
|
-
}
|
|
134
127
|
function formatBytes(bytes) {
|
|
135
128
|
if (bytes < 1024)
|
|
136
129
|
return `${bytes} B`;
|
|
@@ -140,137 +133,25 @@ function formatBytes(bytes) {
|
|
|
140
133
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
141
134
|
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
|
|
142
135
|
}
|
|
143
|
-
function getDirSize(dirPath) {
|
|
144
|
-
if (!fs.existsSync(dirPath))
|
|
145
|
-
return 0;
|
|
146
|
-
let size = 0;
|
|
147
|
-
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
148
|
-
for (const entry of entries) {
|
|
149
|
-
const fullPath = path.join(dirPath, entry.name);
|
|
150
|
-
if (entry.isDirectory()) {
|
|
151
|
-
size += getDirSize(fullPath);
|
|
152
|
-
}
|
|
153
|
-
else {
|
|
154
|
-
try {
|
|
155
|
-
size += fs.statSync(fullPath).size;
|
|
156
|
-
}
|
|
157
|
-
catch { /* ignore */ }
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
return size;
|
|
161
|
-
}
|
|
162
136
|
async function runTrashPrune(options) {
|
|
163
137
|
const trashDir = getTrashDir();
|
|
164
138
|
if (!fs.existsSync(trashDir)) {
|
|
165
139
|
console.log(chalk.green('Trash is empty.'));
|
|
166
140
|
return;
|
|
167
141
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const toPrune = [];
|
|
171
|
-
function scanDir(dir) {
|
|
172
|
-
if (!fs.existsSync(dir))
|
|
173
|
-
return;
|
|
174
|
-
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
175
|
-
const fullPath = path.join(dir, entry.name);
|
|
176
|
-
try {
|
|
177
|
-
const stat = fs.statSync(fullPath);
|
|
178
|
-
if (stat.mtimeMs < cutoffMs) {
|
|
179
|
-
toPrune.push({ path: fullPath, mtime: stat.mtimeMs, size: entry.isDirectory() ? getDirSize(fullPath) : stat.size });
|
|
180
|
-
}
|
|
181
|
-
else if (entry.isDirectory()) {
|
|
182
|
-
scanDir(fullPath);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
catch { /* skip inaccessible */ }
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
scanDir(trashDir);
|
|
189
|
-
if (toPrune.length === 0) {
|
|
190
|
-
console.log(chalk.green(`No trash entries older than ${days} days.`));
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
const totalSize = toPrune.reduce((sum, e) => sum + e.size, 0);
|
|
194
|
-
console.log(chalk.bold(`Trash entries older than ${days} days\n`));
|
|
195
|
-
for (const entry of toPrune.slice(0, 20)) {
|
|
196
|
-
const age = Math.floor((Date.now() - entry.mtime) / (24 * 60 * 60 * 1000));
|
|
197
|
-
console.log(` ${chalk.gray(`${age}d ago`)} ${path.relative(trashDir, entry.path)}`);
|
|
198
|
-
}
|
|
199
|
-
if (toPrune.length > 20) {
|
|
200
|
-
console.log(chalk.gray(` ... and ${toPrune.length - 20} more`));
|
|
201
|
-
}
|
|
202
|
-
console.log();
|
|
203
|
-
if (options.dryRun) {
|
|
204
|
-
console.log(chalk.gray(`${toPrune.length} entries (${formatBytes(totalSize)}). Run without --dry-run to delete.`));
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
if (!options.yes) {
|
|
208
|
-
if (!isInteractiveTerminal()) {
|
|
209
|
-
console.log(chalk.yellow('Non-interactive shell: pass -y to confirm, or --dry-run to preview.'));
|
|
210
|
-
process.exit(1);
|
|
211
|
-
}
|
|
212
|
-
let ok = false;
|
|
213
|
-
try {
|
|
214
|
-
ok = await confirm({ message: `Delete ${toPrune.length} entries (${formatBytes(totalSize)})?`, default: false });
|
|
215
|
-
}
|
|
216
|
-
catch (err) {
|
|
217
|
-
if (isPromptCancelled(err)) {
|
|
218
|
-
console.log(chalk.gray('Cancelled'));
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
|
-
throw err;
|
|
222
|
-
}
|
|
223
|
-
if (!ok) {
|
|
224
|
-
console.log(chalk.gray('Cancelled'));
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
let deleted = 0;
|
|
229
|
-
for (const entry of toPrune) {
|
|
230
|
-
try {
|
|
231
|
-
fs.rmSync(entry.path, { recursive: true, force: true });
|
|
232
|
-
deleted++;
|
|
233
|
-
}
|
|
234
|
-
catch { /* ignore */ }
|
|
142
|
+
if (options.olderThan || options.yes || options.dryRun) {
|
|
143
|
+
console.log(chalk.gray('Trash expiry flags are accepted for compatibility but do not delete data.'));
|
|
235
144
|
}
|
|
236
|
-
console.log(chalk.
|
|
145
|
+
console.log(chalk.yellow('Trash is durable. agents-cli does not hard-delete soft-deleted version data.'));
|
|
146
|
+
console.log(chalk.gray('Inspect recoverable versions with: agents trash list'));
|
|
147
|
+
console.log(chalk.gray(`Trash path: ${trashDir}`));
|
|
237
148
|
}
|
|
238
149
|
async function runSessionsPrune(options) {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
const count = countSessionsOlderThan(cutoffMs);
|
|
242
|
-
if (count === 0) {
|
|
243
|
-
console.log(chalk.green(`No sessions older than ${days} days.`));
|
|
244
|
-
return;
|
|
150
|
+
if (options.olderThan || options.yes || options.dryRun) {
|
|
151
|
+
console.log(chalk.gray('Session prune flags are accepted for compatibility but do not delete data.'));
|
|
245
152
|
}
|
|
246
|
-
console.log(chalk.
|
|
247
|
-
|
|
248
|
-
console.log(chalk.gray(`${count} session(s). Run without --dry-run to delete.`));
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
|
-
if (!options.yes) {
|
|
252
|
-
if (!isInteractiveTerminal()) {
|
|
253
|
-
console.log(chalk.yellow('Non-interactive shell: pass -y to confirm, or --dry-run to preview.'));
|
|
254
|
-
process.exit(1);
|
|
255
|
-
}
|
|
256
|
-
let ok = false;
|
|
257
|
-
try {
|
|
258
|
-
ok = await confirm({ message: `Delete ${count} session records?`, default: false });
|
|
259
|
-
}
|
|
260
|
-
catch (err) {
|
|
261
|
-
if (isPromptCancelled(err)) {
|
|
262
|
-
console.log(chalk.gray('Cancelled'));
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
throw err;
|
|
266
|
-
}
|
|
267
|
-
if (!ok) {
|
|
268
|
-
console.log(chalk.gray('Cancelled'));
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
const deleted = deleteSessionsOlderThan(cutoffMs);
|
|
273
|
-
console.log(chalk.green(`Pruned ${deleted} session records.`));
|
|
153
|
+
console.log(chalk.yellow('Session history is durable. agents-cli does not hard-delete session records.'));
|
|
154
|
+
console.log(chalk.gray('Browse sessions with: agents sessions'));
|
|
274
155
|
}
|
|
275
156
|
async function runRunsPrune(options) {
|
|
276
157
|
const keep = options.keep ? parseInt(options.keep, 10) : 10;
|
|
@@ -380,13 +261,16 @@ async function runOrphanPrune(resourceTypes, options) {
|
|
|
380
261
|
console.log(chalk.green(summary) + (failures > 0 ? chalk.red(`, ${failures} failed`) : '') + '.');
|
|
381
262
|
}
|
|
382
263
|
export function registerPruneCommand(program) {
|
|
383
|
-
program
|
|
384
|
-
.command('prune
|
|
385
|
-
.description('
|
|
264
|
+
const pruneCmd = program.commands.find((cmd) => cmd.name() === 'prune') ?? program
|
|
265
|
+
.command('prune <specs...>')
|
|
266
|
+
.description('Uninstall agent CLI versions. Moves version data to trash for recovery.');
|
|
267
|
+
pruneCmd
|
|
268
|
+
.command('cleanup [target]')
|
|
269
|
+
.description('Remove orphan resources, old versions, or routine runs')
|
|
386
270
|
.option('--all', 'For orphan cleanup: sweep every installed version (default: current default version per agent)')
|
|
387
271
|
.option('--dry-run', 'Show what would be removed without deleting (default for state targets)')
|
|
388
272
|
.option('-y, --yes', 'Skip confirmation prompt')
|
|
389
|
-
.option('--older-than <days>', '
|
|
273
|
+
.option('--older-than <days>', 'Deprecated for trash/sessions; accepted but no data is deleted')
|
|
390
274
|
.option('--keep <n>', 'For runs: keep the last N runs per job (default: 10)')
|
|
391
275
|
.addHelpText('after', `
|
|
392
276
|
Targets:
|
|
@@ -396,46 +280,40 @@ Targets:
|
|
|
396
280
|
hooks Orphan hook scripts only
|
|
397
281
|
versions Older duplicate version installs only
|
|
398
282
|
<agent> Older duplicate versions for one agent (e.g. 'claude')
|
|
399
|
-
trash
|
|
400
|
-
sessions
|
|
283
|
+
trash No-op compatibility target; trash is durable
|
|
284
|
+
sessions No-op compatibility target; session history is durable
|
|
401
285
|
runs Routine execution logs, keeping only --keep per job (default 10)
|
|
402
286
|
|
|
403
287
|
Examples:
|
|
404
288
|
# Full sweep: orphan resources + duplicate versions for current defaults
|
|
405
|
-
agents prune
|
|
289
|
+
agents prune cleanup
|
|
406
290
|
|
|
407
291
|
# Preview what a full sweep would remove
|
|
408
|
-
agents prune --dry-run
|
|
292
|
+
agents prune cleanup --dry-run
|
|
409
293
|
|
|
410
294
|
# Just orphan skills
|
|
411
|
-
agents prune skills
|
|
295
|
+
agents prune cleanup skills
|
|
412
296
|
|
|
413
297
|
# Just version dedup
|
|
414
|
-
agents prune versions
|
|
298
|
+
agents prune cleanup versions
|
|
415
299
|
|
|
416
300
|
# Deduplicate versions for one agent only
|
|
417
|
-
agents prune claude
|
|
301
|
+
agents prune cleanup claude
|
|
418
302
|
|
|
419
303
|
# Sweep every installed version's orphans, not only the defaults
|
|
420
|
-
agents prune --all
|
|
421
|
-
|
|
422
|
-
# Preview trash entries older than 30 days
|
|
423
|
-
agents prune trash --dry-run
|
|
424
|
-
|
|
425
|
-
# Delete trash entries older than 60 days
|
|
426
|
-
agents prune trash --older-than 60 -y
|
|
304
|
+
agents prune cleanup --all
|
|
427
305
|
|
|
428
|
-
#
|
|
429
|
-
agents prune
|
|
306
|
+
# Show the durable-trash notice
|
|
307
|
+
agents prune cleanup trash --dry-run
|
|
430
308
|
|
|
431
|
-
#
|
|
432
|
-
agents prune sessions --
|
|
309
|
+
# Show the durable-session notice
|
|
310
|
+
agents prune cleanup sessions --dry-run
|
|
433
311
|
|
|
434
312
|
# Preview runs cleanup (keeping last 10)
|
|
435
|
-
agents prune runs --dry-run
|
|
313
|
+
agents prune cleanup runs --dry-run
|
|
436
314
|
|
|
437
315
|
# Keep only the last 5 runs per job
|
|
438
|
-
agents prune runs --keep 5 -y
|
|
316
|
+
agents prune cleanup runs --keep 5 -y
|
|
439
317
|
|
|
440
318
|
What's an orphan?
|
|
441
319
|
A command, skill, or hook present inside a version home but missing from every
|
|
@@ -443,10 +321,11 @@ What's an orphan?
|
|
|
443
321
|
repos). Usually leftovers from a resource that was deleted or moved but never
|
|
444
322
|
reconciled into the version install.
|
|
445
323
|
|
|
446
|
-
|
|
447
|
-
Version directories are NEVER hard-deleted.
|
|
448
|
-
~/.agents/.trash/versions/<agent>/<version>/<timestamp>/.
|
|
449
|
-
|
|
324
|
+
Durability:
|
|
325
|
+
Version directories are NEVER hard-deleted by agents-cli. Version prune and
|
|
326
|
+
cleanup move them to ~/.agents/.history/trash/versions/<agent>/<version>/<timestamp>/.
|
|
327
|
+
Session records are also durable; the sessions target remains only as a no-op
|
|
328
|
+
compatibility shim.
|
|
450
329
|
`)
|
|
451
330
|
.action(async (target, options) => {
|
|
452
331
|
const parsed = parseTarget(target);
|
package/dist/commands/pull.js
CHANGED
|
@@ -13,11 +13,12 @@ import { getUserAgentsDir, ensureAgentsDir, getEnabledExtraRepos, } from '../lib
|
|
|
13
13
|
import { isGitRepo, pullRepo, isSystemRepoOrigin, } from '../lib/git.js';
|
|
14
14
|
import * as fs from 'fs';
|
|
15
15
|
import * as path from 'path';
|
|
16
|
-
import { installVersion, listInstalledVersions, getGlobalDefault, setGlobalDefault, getVersionHomePath, syncResourcesToVersion, getAvailableResources, getActuallySyncedResources, getNewResources, hasNewResources, promptNewResourceSelection, promptResourceSelection, resolveConfiguredAgentTargets, } from '../lib/versions.js';
|
|
16
|
+
import { installVersion, listInstalledVersions, getGlobalDefault, setGlobalDefault, getVersionHomePath, syncResourcesToVersion, getAvailableResources, getActuallySyncedResources, getNewResources, getProjectOnlyResources, hasNewResources, promptNewResourceSelection, promptResourceSelection, resolveConfiguredAgentTargets, } from '../lib/versions.js';
|
|
17
|
+
import { listCliStatus, installCli, describeMethod, describeCheck, selectInstallMethod, } from '../lib/cli-resources.js';
|
|
17
18
|
import { ensureShimCurrent, isShimsInPath, addShimsToPath, getPathSetupInstructions, switchConfigSymlink, switchHomeFileSymlinks, } from '../lib/shims.js';
|
|
18
19
|
import { parseHookManifest, registerHooksToSettings } from '../lib/hooks.js';
|
|
19
20
|
import { setHelpSections } from '../lib/help.js';
|
|
20
|
-
import { select } from '@inquirer/prompts';
|
|
21
|
+
import { select, confirm } from '@inquirer/prompts';
|
|
21
22
|
import { isInteractiveTerminal, isPromptCancelled } from './utils.js';
|
|
22
23
|
/**
|
|
23
24
|
* Old repo layout stored promptcuts under claude/promptcuts.yaml (agent-scoped).
|
|
@@ -218,7 +219,7 @@ export function registerPullCommand(program) {
|
|
|
218
219
|
if (!defaultVer)
|
|
219
220
|
continue;
|
|
220
221
|
const actuallySynced = getActuallySyncedResources(agentId, defaultVer);
|
|
221
|
-
const newResources = getNewResources(available, actuallySynced);
|
|
222
|
+
const newResources = getNewResources(available, actuallySynced, getProjectOnlyResources());
|
|
222
223
|
const hasAnySynced = actuallySynced.commands.length > 0 ||
|
|
223
224
|
actuallySynced.skills.length > 0 ||
|
|
224
225
|
actuallySynced.hooks.length > 0 ||
|
|
@@ -244,10 +245,10 @@ export function registerPullCommand(program) {
|
|
|
244
245
|
if (userSelection)
|
|
245
246
|
selection = userSelection;
|
|
246
247
|
}
|
|
247
|
-
else if (hasNewResources(newResources, agentId)) {
|
|
248
|
+
else if (hasNewResources(newResources, agentId, defaultVer)) {
|
|
248
249
|
// Has synced before, but NEW items available
|
|
249
250
|
console.log(chalk.cyan(`\n${agentLabel(agentId)}@${defaultVer}:`));
|
|
250
|
-
const userSelection = await promptNewResourceSelection(agentId, newResources);
|
|
251
|
+
const userSelection = await promptNewResourceSelection(agentId, newResources, defaultVer);
|
|
251
252
|
if (userSelection)
|
|
252
253
|
selection = userSelection;
|
|
253
254
|
}
|
|
@@ -366,6 +367,58 @@ export function registerPullCommand(program) {
|
|
|
366
367
|
console.log(chalk.green(`Set ${agentLabel(agent.id)}@${version} as default`));
|
|
367
368
|
}
|
|
368
369
|
}
|
|
370
|
+
// Report (and optionally install) any declared CLIs that are missing
|
|
371
|
+
// from the host. Skipped under -y so non-interactive pulls don't trigger
|
|
372
|
+
// package-manager prompts.
|
|
373
|
+
try {
|
|
374
|
+
const { statuses, errors } = listCliStatus(process.cwd());
|
|
375
|
+
for (const err of errors) {
|
|
376
|
+
console.log(chalk.yellow(` CLI manifest parse error: ${err.file}: ${err.reason}`));
|
|
377
|
+
}
|
|
378
|
+
const missing = statuses.filter((s) => !s.installed);
|
|
379
|
+
if (missing.length > 0) {
|
|
380
|
+
console.log(chalk.bold('\nDeclared CLIs missing from this host:'));
|
|
381
|
+
for (const s of missing) {
|
|
382
|
+
const method = selectInstallMethod(s.manifest);
|
|
383
|
+
const action = method ? describeMethod(method) : chalk.red('no compatible install method');
|
|
384
|
+
console.log(` ${chalk.cyan(s.manifest.name.padEnd(20))} ${chalk.gray(action)}`);
|
|
385
|
+
}
|
|
386
|
+
console.log('');
|
|
387
|
+
if (!skipPrompts) {
|
|
388
|
+
const proceed = await confirm({ message: `Install ${missing.length} missing CLI(s) now?`, default: true });
|
|
389
|
+
if (proceed) {
|
|
390
|
+
for (const s of missing) {
|
|
391
|
+
console.log(chalk.bold(`\n→ ${s.manifest.name}`));
|
|
392
|
+
const result = installCli(s.manifest);
|
|
393
|
+
if (result.error) {
|
|
394
|
+
console.log(chalk.red(` ${result.error}`));
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
if (result.installed) {
|
|
398
|
+
console.log(chalk.green(` installed`));
|
|
399
|
+
if (s.manifest.postInstall) {
|
|
400
|
+
console.log(chalk.gray(s.manifest.postInstall.trim().split('\n').map((l) => ' ' + l).join('\n')));
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
else {
|
|
404
|
+
console.log(chalk.yellow(` install ran but \`${describeCheck(s.manifest.check)}\` still fails`));
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
console.log(chalk.gray(`Skipped. Run 'agents cli install' later.`));
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
else {
|
|
413
|
+
console.log(chalk.gray(`Run 'agents cli install' to install them.`));
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
catch (err) {
|
|
418
|
+
if (!isPromptCancelled(err)) {
|
|
419
|
+
console.log(chalk.yellow(`CLI install skipped: ${err.message}`));
|
|
420
|
+
}
|
|
421
|
+
}
|
|
369
422
|
console.log(chalk.green('\nPull complete'));
|
|
370
423
|
}
|
|
371
424
|
catch (err) {
|
|
@@ -11,11 +11,13 @@ 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, readDaemonPid, readDaemonLog, } from '../lib/daemon.js';
|
|
14
|
+
import { humanizeCron, humanizeNextRun, formatRepoLink, REPO_DISPLAY_MAX } from '../lib/routines-format.js';
|
|
14
15
|
import { listJobs as listAllJobs, deleteJob, readJob, validateJob, writeJob, setJobEnabled, listRuns, getLatestRun, getRunDir, getJobPath, parseAtTime, } from '../lib/routines.js';
|
|
15
16
|
import { getRoutinesDir } from '../lib/state.js';
|
|
16
17
|
import { safeJoin } from '../lib/paths.js';
|
|
17
|
-
import { executeJob } from '../lib/runner.js';
|
|
18
|
+
import { executeJob, executeJobDetached } from '../lib/runner.js';
|
|
18
19
|
import { JobScheduler } from '../lib/scheduler.js';
|
|
20
|
+
import { detectOverdueJobs } from '../lib/overdue.js';
|
|
19
21
|
import { isInteractiveTerminal, requireInteractiveSelection } from './utils.js';
|
|
20
22
|
import { setHelpSections } from '../lib/help.js';
|
|
21
23
|
/** Start or reload the background scheduler so newly-added jobs fire on time. */
|
|
@@ -60,7 +62,7 @@ async function pickJob(message, filter, alternatives = []) {
|
|
|
60
62
|
message,
|
|
61
63
|
choices: jobs.map((job) => ({
|
|
62
64
|
value: job.name,
|
|
63
|
-
name: `${job.name} ${chalk.gray(`(${job.agent}, ${job.schedule})`)}`,
|
|
65
|
+
name: `${job.name} ${chalk.gray(`(${job.workflow ? `wf:${job.workflow}` : job.agent}, ${job.schedule})`)}`,
|
|
64
66
|
})),
|
|
65
67
|
});
|
|
66
68
|
}
|
|
@@ -120,18 +122,57 @@ export function registerRoutinesCommands(program) {
|
|
|
120
122
|
}
|
|
121
123
|
const scheduler = new JobScheduler(async () => { });
|
|
122
124
|
scheduler.loadAll();
|
|
125
|
+
// Build a quick lookup: which jobs are currently overdue?
|
|
126
|
+
const overdueSet = new Set();
|
|
127
|
+
try {
|
|
128
|
+
for (const j of detectOverdueJobs())
|
|
129
|
+
overdueSet.add(j.name);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// Best-effort indicator; never block the list on detection errors.
|
|
133
|
+
}
|
|
123
134
|
console.log(chalk.bold('Scheduled Jobs\n'));
|
|
124
|
-
|
|
135
|
+
// OSC 8 hyperlink helper — renders as a clickable link in supporting terminals.
|
|
136
|
+
// Guarded on process.stdout.isTTY so that piped/redirected output never
|
|
137
|
+
// contains raw ESC ] 8 ;; ... BEL escape sequences.
|
|
138
|
+
const link = (label, url) => url && process.stdout.isTTY ? `\x1b]8;;${url}\x07${label}\x1b]8;;\x07` : label;
|
|
139
|
+
const now = new Date();
|
|
140
|
+
const NAME_W = 24;
|
|
141
|
+
const AGENT_W = 10;
|
|
142
|
+
const REPO_W = REPO_DISPLAY_MAX;
|
|
143
|
+
const SCHED_W = 22;
|
|
144
|
+
const ENABLED_W = 10;
|
|
145
|
+
const NEXT_W = 22;
|
|
146
|
+
const header = ` ${'Name'.padEnd(NAME_W)} ${'Agent'.padEnd(AGENT_W)} ${'Repo'.padEnd(REPO_W)} ${'Schedule'.padEnd(SCHED_W)} ${'Enabled'.padEnd(ENABLED_W)} ${'Next Run'.padEnd(NEXT_W)} Last Status`;
|
|
125
147
|
console.log(chalk.gray(header));
|
|
126
|
-
console.log(chalk.gray(' ' + '-'.repeat(
|
|
148
|
+
console.log(chalk.gray(' ' + '-'.repeat(NAME_W + AGENT_W + REPO_W + SCHED_W + ENABLED_W + NEXT_W + 20)));
|
|
127
149
|
for (const job of jobs) {
|
|
128
150
|
const nextRun = scheduler.getNextRun(job.name);
|
|
129
|
-
const nextStr = nextRun
|
|
151
|
+
const nextStr = humanizeNextRun(nextRun ?? null, now, job.timezone);
|
|
152
|
+
const schedStr = humanizeCron(job.schedule, job.timezone);
|
|
130
153
|
const latestRun = getLatestRun(job.name);
|
|
131
154
|
const lastStatus = latestRun?.status || '-';
|
|
155
|
+
const repoInfo = formatRepoLink(job.repo);
|
|
156
|
+
const repoCell = link(repoInfo.display, repoInfo.href);
|
|
157
|
+
// Pad based on the display string, not the raw cell (which may include escape codes).
|
|
158
|
+
const repoPadding = Math.max(0, REPO_W - repoInfo.display.length);
|
|
132
159
|
const enabledStr = job.enabled ? chalk.green('yes') : chalk.gray('no');
|
|
133
|
-
|
|
134
|
-
|
|
160
|
+
// chalk adds escape codes; pad the raw word and let chalk wrap it.
|
|
161
|
+
const enabledWord = job.enabled ? 'yes' : 'no';
|
|
162
|
+
const enabledPad = Math.max(0, ENABLED_W - enabledWord.length);
|
|
163
|
+
const statusColor = lastStatus === 'completed' ? chalk.green
|
|
164
|
+
: lastStatus === 'failed' ? chalk.red
|
|
165
|
+
: lastStatus === 'timeout' ? chalk.yellow
|
|
166
|
+
: chalk.gray;
|
|
167
|
+
const overdueTag = overdueSet.has(job.name) ? chalk.yellow(' (overdue)') : '';
|
|
168
|
+
const agentLabelPadded = job.workflow
|
|
169
|
+
? chalk.magenta(`wf:${job.workflow}`.padEnd(10))
|
|
170
|
+
: (job.agent || '').padEnd(10);
|
|
171
|
+
console.log(` ${chalk.cyan(job.name.padEnd(NAME_W))} ${agentLabelPadded} ${repoCell}${' '.repeat(repoPadding)} ${schedStr.padEnd(SCHED_W)} ${enabledStr}${' '.repeat(enabledPad)} ${chalk.gray(nextStr.padEnd(NEXT_W))} ${statusColor(lastStatus)}${overdueTag}`);
|
|
172
|
+
}
|
|
173
|
+
if (overdueSet.size > 0) {
|
|
174
|
+
console.log();
|
|
175
|
+
console.log(chalk.yellow(` ${overdueSet.size} routine(s) overdue — catch up with: agents routines catchup`));
|
|
135
176
|
}
|
|
136
177
|
scheduler.stopAll();
|
|
137
178
|
console.log();
|
|
@@ -141,16 +182,17 @@ export function registerRoutinesCommands(program) {
|
|
|
141
182
|
.description('Create a new routine from a YAML file or inline flags. Starts the scheduler automatically if it is not already running.')
|
|
142
183
|
.option('-s, --schedule <cron>', 'Cron schedule in standard format (5 fields: minute hour day month weekday)')
|
|
143
184
|
.option('-a, --agent <agent>', 'Which agent runs this routine: claude, codex, gemini, cursor, or opencode')
|
|
185
|
+
.option('--workflow <name>', 'Run an installed workflow (~/.agents/workflows/<name>) via `agents run`. Mutually exclusive with --agent.')
|
|
144
186
|
.option('-p, --prompt <prompt>', 'Task instruction for the agent')
|
|
145
|
-
.option('-m, --mode <mode>',
|
|
187
|
+
.option('-m, --mode <mode>', "Execution mode: plan (read-only), edit (can write files), auto (smart classifier), or skip (bypass all permission prompts). 'full' accepted as alias for skip.", 'plan')
|
|
146
188
|
.option('-e, --effort <effort>', 'Reasoning effort: low | medium | high | xhigh | max | auto', 'auto')
|
|
147
|
-
.option('-t, --timeout <timeout>', 'Kill the agent if it runs longer than this (e.g.,
|
|
189
|
+
.option('-t, --timeout <timeout>', 'Kill the agent if it runs longer than this (e.g., 10m, 2h, 3d, 1w; max 1w)', '10m')
|
|
148
190
|
.option('--timezone <tz>', 'Interpret schedule in this timezone (e.g., America/Los_Angeles)')
|
|
149
191
|
.option('--at <time>', 'One-shot mode: run once at this time (e.g., "14:30" or "2026-02-24 09:00"), then disable')
|
|
150
192
|
.option('--disabled', 'Create the routine but keep it paused (enable later with resume)')
|
|
151
193
|
.action(async (nameOrPath, options) => {
|
|
152
194
|
// Check if inline mode (has flags) or file mode
|
|
153
|
-
const hasInlineFlags = options.schedule || options.agent || options.prompt || options.at;
|
|
195
|
+
const hasInlineFlags = options.schedule || options.agent || options.workflow || options.prompt || options.at;
|
|
154
196
|
if (hasInlineFlags) {
|
|
155
197
|
// Inline mode: create job from flags
|
|
156
198
|
if (!nameOrPath) {
|
|
@@ -158,6 +200,11 @@ export function registerRoutinesCommands(program) {
|
|
|
158
200
|
console.log(chalk.gray('Usage: agents routines add <name> --schedule "..." --agent <agent> --prompt "..."'));
|
|
159
201
|
process.exit(1);
|
|
160
202
|
}
|
|
203
|
+
// Validate mutually exclusive --agent / --workflow
|
|
204
|
+
if (options.agent && options.workflow) {
|
|
205
|
+
console.log(chalk.red('--agent and --workflow are mutually exclusive; specify exactly one'));
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
161
208
|
let schedule = options.schedule;
|
|
162
209
|
let runOnce = false;
|
|
163
210
|
// Handle --at for one-shot jobs
|
|
@@ -175,8 +222,8 @@ export function registerRoutinesCommands(program) {
|
|
|
175
222
|
console.log(chalk.red('Schedule is required (use --schedule or --at)'));
|
|
176
223
|
process.exit(1);
|
|
177
224
|
}
|
|
178
|
-
if (!options.agent) {
|
|
179
|
-
console.log(chalk.red('
|
|
225
|
+
if (!options.agent && !options.workflow) {
|
|
226
|
+
console.log(chalk.red('An agent or workflow is required (use --agent or --workflow)'));
|
|
180
227
|
process.exit(1);
|
|
181
228
|
}
|
|
182
229
|
if (!options.prompt) {
|
|
@@ -187,6 +234,7 @@ export function registerRoutinesCommands(program) {
|
|
|
187
234
|
name: nameOrPath,
|
|
188
235
|
schedule,
|
|
189
236
|
agent: options.agent,
|
|
237
|
+
...(options.workflow ? { workflow: options.workflow } : {}),
|
|
190
238
|
mode: options.mode,
|
|
191
239
|
effort: options.effort,
|
|
192
240
|
timeout: options.timeout,
|
|
@@ -245,7 +293,7 @@ export function registerRoutinesCommands(program) {
|
|
|
245
293
|
const config = {
|
|
246
294
|
mode: 'plan',
|
|
247
295
|
effort: 'auto',
|
|
248
|
-
timeout: '
|
|
296
|
+
timeout: '10m',
|
|
249
297
|
enabled: true,
|
|
250
298
|
...parsed,
|
|
251
299
|
};
|
|
@@ -387,7 +435,8 @@ export function registerRoutinesCommands(program) {
|
|
|
387
435
|
console.log(chalk.red(`Job '${name}' not found`));
|
|
388
436
|
process.exit(1);
|
|
389
437
|
}
|
|
390
|
-
|
|
438
|
+
const runLabel = job.workflow ? `workflow: ${job.workflow}` : `agent: ${job.agent}`;
|
|
439
|
+
console.log(chalk.bold(`Running job '${name}' (${runLabel}, mode: ${job.mode})\n`));
|
|
391
440
|
const spinner = ora('Executing...').start();
|
|
392
441
|
try {
|
|
393
442
|
const result = await executeJob(job);
|
|
@@ -413,6 +462,50 @@ export function registerRoutinesCommands(program) {
|
|
|
413
462
|
process.exit(1);
|
|
414
463
|
}
|
|
415
464
|
});
|
|
465
|
+
routinesCmd
|
|
466
|
+
.command('catchup')
|
|
467
|
+
.description('Run any routines that missed their last scheduled fire (e.g. because your laptop was off). Detached — runs in the background under the scheduler.')
|
|
468
|
+
.option('--dry-run', 'List overdue routines without running them')
|
|
469
|
+
.action(async (options) => {
|
|
470
|
+
const overdue = detectOverdueJobs();
|
|
471
|
+
if (overdue.length === 0) {
|
|
472
|
+
console.log(chalk.gray('No overdue routines.'));
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
console.log(chalk.bold(`${overdue.length} overdue routine(s):\n`));
|
|
476
|
+
for (const job of overdue) {
|
|
477
|
+
const last = job.lastRanAt ? job.lastRanAt.toLocaleString() : 'never';
|
|
478
|
+
console.log(` ${chalk.cyan(job.name)} — missed ${chalk.gray(job.expectedAt.toLocaleString())}, last ran ${chalk.gray(last)}`);
|
|
479
|
+
}
|
|
480
|
+
if (options.dryRun) {
|
|
481
|
+
console.log(chalk.gray('\n(dry run — no jobs triggered)'));
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
// Need the daemon alive so spawned jobs are monitored and meta.json is
|
|
485
|
+
// finalized. Start it if it isn't already running.
|
|
486
|
+
if (!isDaemonRunning()) {
|
|
487
|
+
const started = startDaemon();
|
|
488
|
+
if (started.pid) {
|
|
489
|
+
console.log(chalk.gray(`\nStarted scheduler (PID: ${started.pid}) so catchup runs are monitored.`));
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
console.log(chalk.bold('\nTriggering catchup runs...'));
|
|
493
|
+
for (const job of overdue) {
|
|
494
|
+
const config = readJob(job.name);
|
|
495
|
+
if (!config) {
|
|
496
|
+
console.log(` ${job.name} → ${chalk.red('config not found')}`);
|
|
497
|
+
continue;
|
|
498
|
+
}
|
|
499
|
+
try {
|
|
500
|
+
const meta = await executeJobDetached(config);
|
|
501
|
+
console.log(` ${job.name} → ${chalk.green('started')} (run: ${meta.runId}, PID: ${meta.pid ?? 'n/a'})`);
|
|
502
|
+
}
|
|
503
|
+
catch (err) {
|
|
504
|
+
console.log(` ${job.name} → ${chalk.red('failed to start')}: ${err.message}`);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
console.log(chalk.gray('\nTrack progress with: agents routines runs <name>'));
|
|
508
|
+
});
|
|
416
509
|
routinesCmd
|
|
417
510
|
.command('logs [name]')
|
|
418
511
|
.description('Read stdout from the most recent execution. Use --run to see a specific past run.')
|
package/dist/commands/rules.js
CHANGED
|
@@ -7,11 +7,11 @@ import { select, checkbox } from '@inquirer/prompts';
|
|
|
7
7
|
import { AGENTS, ALL_AGENT_IDS, resolveAgentName, formatAgentError, agentLabel, } from '../lib/agents.js';
|
|
8
8
|
import { cloneRepo } from '../lib/git.js';
|
|
9
9
|
import { discoverInstructionsFromRepo, discoverRuleFilesFromRepo, installInstructionsCentrally, uninstallInstructions, listInstalledInstructionsWithScope, instructionsExists, getInstructionsContent, listCentralRules, } from '../lib/rules/rules.js';
|
|
10
|
-
import { listInstalledVersions, getGlobalDefault, resolveVersionAlias, syncResourcesToVersion, promptAgentVersionSelection, getVersionHomePath,
|
|
10
|
+
import { listInstalledVersions, getGlobalDefault, resolveVersionAlias, syncResourcesToVersion, promptAgentVersionSelection, getVersionHomePath, } from '../lib/versions.js';
|
|
11
11
|
import { recordVersionResources, getActiveRulesPreset, setActiveRulesPreset } from '../lib/state.js';
|
|
12
12
|
import { discoverRulesLayers } from '../lib/rules/compose.js';
|
|
13
13
|
import * as yaml from 'yaml';
|
|
14
|
-
import { isPromptCancelled, formatPath, isInteractiveTerminal, parseCommaSeparatedList, printWithPager, requireInteractiveSelection, requireDestructiveArg, } from './utils.js';
|
|
14
|
+
import { isPromptCancelled, formatPath, isInteractiveTerminal, parseCommaSeparatedList, printWithPager, requireInteractiveSelection, requireDestructiveArg, resolveAgentTargetsAutoInstalling, } from './utils.js';
|
|
15
15
|
/** Register the `agents rules` command tree (list, add, view, remove). */
|
|
16
16
|
export function registerRulesCommands(program) {
|
|
17
17
|
const rulesCmd = program
|
|
@@ -317,13 +317,17 @@ Examples:
|
|
|
317
317
|
let selectedAgents;
|
|
318
318
|
let versionSelections;
|
|
319
319
|
if (options.agents) {
|
|
320
|
-
const result =
|
|
320
|
+
const result = await resolveAgentTargetsAutoInstalling(options.agents, ALL_AGENT_IDS, { yes: options.yes });
|
|
321
|
+
if (!result) {
|
|
322
|
+
console.log(chalk.gray('Cancelled.'));
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
321
325
|
selectedAgents = result.selectedAgents;
|
|
322
326
|
versionSelections = result.versionSelections;
|
|
323
327
|
}
|
|
324
328
|
else {
|
|
325
329
|
const result = await promptAgentVersionSelection(ALL_AGENT_IDS, {
|
|
326
|
-
skipPrompts: options.yes
|
|
330
|
+
skipPrompts: options.yes,
|
|
327
331
|
});
|
|
328
332
|
selectedAgents = result.selectedAgents;
|
|
329
333
|
versionSelections = result.versionSelections;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents secrets migrate-acl` — refresh existing keychain items so they pick
|
|
3
|
+
* up the new SecAccess ACL written by the signed Agents CLI.app helper.
|
|
4
|
+
*
|
|
5
|
+
* Items created before 1.19.2 (or by `security add-generic-password` directly)
|
|
6
|
+
* may carry the legacy "this-app-only" ACL that prompts the user for a
|
|
7
|
+
* password on every read. Re-writing them through the helper bakes in the
|
|
8
|
+
* empty trusted-app ACL that suppresses the prompt and lets the helper read
|
|
9
|
+
* them under LocalAuthentication instead.
|
|
10
|
+
*
|
|
11
|
+
* Sequence per item:
|
|
12
|
+
* 1. Read the current value (no auth prompt path — uses the unauthenticated
|
|
13
|
+
* `security` CLI for non-sync items, helper `get` for sync items).
|
|
14
|
+
* 2. Append (item, value, sync) to an encrypted backup before any writes.
|
|
15
|
+
* 3. Delete + rewrite via the helper so macOS hands us a fresh ACL on the
|
|
16
|
+
* new item.
|
|
17
|
+
* 4. Read back via the helper to verify the value round-trips.
|
|
18
|
+
*
|
|
19
|
+
* `--dry-run` (default) reports the planned actions. `--commit` performs the
|
|
20
|
+
* writes and produces the backup.
|
|
21
|
+
*/
|
|
22
|
+
import type { Command } from 'commander';
|
|
23
|
+
/** Register `agents secrets migrate-acl` on the parent secrets Command. */
|
|
24
|
+
export declare function registerSecretsMigrateAclCommand(secrets: Command): void;
|