@phnx-labs/agents-cli 1.14.2 → 1.14.4
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/README.md +17 -7
- package/dist/browser.d.ts +2 -0
- package/dist/browser.js +7 -0
- package/dist/commands/browser.d.ts +3 -0
- package/dist/commands/browser.js +392 -0
- package/dist/commands/daemon.js +1 -1
- package/dist/commands/doctor.d.ts +16 -9
- package/dist/commands/doctor.js +248 -12
- package/dist/commands/prune.js +9 -3
- package/dist/commands/refresh-rules.d.ts +15 -0
- package/dist/commands/{refresh-memory.js → refresh-rules.js} +14 -14
- package/dist/commands/routines.js +1 -1
- package/dist/commands/rules.js +100 -4
- package/dist/commands/secrets.js +198 -11
- package/dist/commands/sync.js +19 -0
- package/dist/commands/teams.js +184 -22
- package/dist/commands/trash.d.ts +10 -0
- package/dist/commands/trash.js +187 -0
- package/dist/commands/view.js +47 -14
- package/dist/index.js +62 -4
- package/dist/lib/agents.js +2 -2
- package/dist/lib/browser/cdp.d.ts +24 -0
- package/dist/lib/browser/cdp.js +94 -0
- package/dist/lib/browser/chrome.d.ts +16 -0
- package/dist/lib/browser/chrome.js +157 -0
- package/dist/lib/browser/drivers/local.d.ts +8 -0
- package/dist/lib/browser/drivers/local.js +22 -0
- package/dist/lib/browser/drivers/ssh.d.ts +9 -0
- package/dist/lib/browser/drivers/ssh.js +129 -0
- package/dist/lib/browser/index.d.ts +5 -0
- package/dist/lib/browser/index.js +5 -0
- package/dist/lib/browser/input.d.ts +6 -0
- package/dist/lib/browser/input.js +52 -0
- package/dist/lib/browser/ipc.d.ts +12 -0
- package/dist/lib/browser/ipc.js +223 -0
- package/dist/lib/browser/profiles.d.ts +11 -0
- package/dist/lib/browser/profiles.js +61 -0
- package/dist/lib/browser/refs.d.ts +21 -0
- package/dist/lib/browser/refs.js +88 -0
- package/dist/lib/browser/service.d.ts +45 -0
- package/dist/lib/browser/service.js +404 -0
- package/dist/lib/browser/types.d.ts +73 -0
- package/dist/lib/browser/types.js +7 -0
- package/dist/lib/cloud/codex.js +1 -1
- package/dist/lib/cloud/registry.js +2 -2
- package/dist/lib/cloud/rush.js +2 -2
- package/dist/lib/cloud/store.js +2 -2
- package/dist/lib/daemon.d.ts +1 -1
- package/dist/lib/daemon.js +47 -11
- package/dist/lib/diff-text.d.ts +25 -0
- package/dist/lib/diff-text.js +47 -0
- package/dist/lib/doctor-diff.d.ts +64 -0
- package/dist/lib/doctor-diff.js +497 -0
- package/dist/lib/git.js +3 -3
- package/dist/lib/hooks.d.ts +6 -0
- package/dist/lib/hooks.js +6 -1
- package/dist/lib/migrate.js +123 -0
- package/dist/lib/pty-client.js +3 -3
- package/dist/lib/pty-server.js +36 -7
- package/dist/lib/resources/commands.d.ts +46 -0
- package/dist/lib/resources/commands.js +208 -0
- package/dist/lib/resources/hooks.d.ts +12 -0
- package/dist/lib/resources/hooks.js +136 -0
- package/dist/lib/resources/index.d.ts +36 -0
- package/dist/lib/resources/index.js +69 -0
- package/dist/lib/resources/mcp.d.ts +34 -0
- package/dist/lib/resources/mcp.js +483 -0
- package/dist/lib/resources/permissions.d.ts +13 -0
- package/dist/lib/resources/permissions.js +184 -0
- package/dist/lib/resources/rules.d.ts +43 -0
- package/dist/lib/resources/rules.js +146 -0
- package/dist/lib/resources/skills.d.ts +37 -0
- package/dist/lib/resources/skills.js +238 -0
- package/dist/lib/resources/subagents.d.ts +46 -0
- package/dist/lib/resources/subagents.js +198 -0
- package/dist/lib/resources/types.d.ts +82 -0
- package/dist/lib/resources/types.js +8 -0
- package/dist/lib/resources.js +1 -1
- package/dist/lib/rotate.d.ts +8 -1
- package/dist/lib/rotate.js +17 -4
- package/dist/lib/rules/compile.d.ts +104 -0
- package/dist/lib/{memory-compile.js → rules/compile.js} +160 -21
- package/dist/lib/rules/compose.d.ts +78 -0
- package/dist/lib/rules/compose.js +170 -0
- package/dist/lib/{memory.d.ts → rules/rules.d.ts} +5 -5
- package/dist/lib/{memory.js → rules/rules.js} +10 -10
- package/dist/lib/secrets/AgentsKeychain.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/AgentsKeychain.app/Contents/MacOS/AgentsKeychain +0 -0
- package/dist/lib/secrets/bundles.d.ts +61 -4
- package/dist/lib/secrets/bundles.js +222 -54
- package/dist/lib/secrets/index.d.ts +24 -5
- package/dist/lib/secrets/index.js +70 -41
- package/dist/lib/session/active.js +5 -5
- package/dist/lib/session/db.js +4 -4
- package/dist/lib/session/discover.js +2 -2
- package/dist/lib/session/render.js +21 -7
- package/dist/lib/shims.d.ts +28 -4
- package/dist/lib/shims.js +72 -14
- package/dist/lib/state.d.ts +22 -28
- package/dist/lib/state.js +83 -78
- package/dist/lib/sync-manifest.d.ts +2 -2
- package/dist/lib/sync-manifest.js +5 -5
- package/dist/lib/teams/agents.d.ts +4 -2
- package/dist/lib/teams/agents.js +11 -4
- package/dist/lib/teams/api.d.ts +1 -1
- package/dist/lib/teams/api.js +2 -2
- package/dist/lib/teams/index.d.ts +1 -0
- package/dist/lib/teams/index.js +1 -0
- package/dist/lib/teams/persistence.js +3 -3
- package/dist/lib/teams/registry.d.ts +12 -1
- package/dist/lib/teams/registry.js +12 -2
- package/dist/lib/teams/worktree.d.ts +30 -0
- package/dist/lib/teams/worktree.js +96 -0
- package/dist/lib/types.d.ts +12 -6
- package/dist/lib/types.js +3 -3
- package/dist/lib/versions.d.ts +32 -3
- package/dist/lib/versions.js +147 -119
- package/package.json +3 -2
- package/scripts/postinstall.js +29 -0
- package/dist/commands/refresh-memory.d.ts +0 -15
- package/dist/lib/memory-compile.d.ts +0 -66
package/dist/commands/doctor.js
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { checkAllClis } from '../lib/teams/agents.js';
|
|
3
|
-
import { AGENTS, ALL_AGENT_IDS } from '../lib/agents.js';
|
|
4
|
-
import { getAvailableResources, getGlobalDefault, } from '../lib/versions.js';
|
|
3
|
+
import { AGENTS, ALL_AGENT_IDS, resolveAgentName, formatAgentError } from '../lib/agents.js';
|
|
4
|
+
import { getAvailableResources, getGlobalDefault, getVersionHomePath, isVersionInstalled, listInstalledVersions, parseAgentSpec, } from '../lib/versions.js';
|
|
5
5
|
import { loadSyncManifest, isSyncStale } from '../lib/sync-manifest.js';
|
|
6
6
|
import { diffVersionCommands, iterCommandsCapableVersions } from '../lib/commands.js';
|
|
7
7
|
import { diffVersionSkills, iterSkillsCapableVersions } from '../lib/skills.js';
|
|
8
8
|
import { diffVersionHooks, iterHooksCapableVersions } from '../lib/hooks.js';
|
|
9
|
+
import { diffVersionResources, DOCTOR_ALL_KINDS, } from '../lib/doctor-diff.js';
|
|
10
|
+
import { unifiedDiff, colorizeUnifiedDiff } from '../lib/diff-text.js';
|
|
11
|
+
import * as fs from 'fs';
|
|
9
12
|
const AGENT_NAMES = Object.fromEntries(ALL_AGENT_IDS.map((id) => [id, AGENTS[id].name]));
|
|
13
|
+
// ─── overview mode (no target) ────────────────────────────────────────────────
|
|
10
14
|
function checkSyncStatus(cwd) {
|
|
11
15
|
const rows = [];
|
|
12
16
|
for (const agent of ALL_AGENT_IDS) {
|
|
@@ -58,7 +62,7 @@ function countOrphans() {
|
|
|
58
62
|
}
|
|
59
63
|
return Array.from(byKey.values()).filter((r) => r.commands + r.skills + r.hooks > 0);
|
|
60
64
|
}
|
|
61
|
-
function
|
|
65
|
+
function renderOverviewText(clis, syncRows, orphanRows) {
|
|
62
66
|
console.log(chalk.bold('Agent CLIs'));
|
|
63
67
|
if (Object.keys(clis).length === 0) {
|
|
64
68
|
console.log(chalk.gray(' (no agents reported)'));
|
|
@@ -113,20 +117,252 @@ function renderText(clis, syncRows, orphanRows) {
|
|
|
113
117
|
console.log(chalk.gray(' Run `agents prune` to remove.'));
|
|
114
118
|
}
|
|
115
119
|
}
|
|
120
|
+
function parseTargetArg(arg) {
|
|
121
|
+
const at = arg.indexOf('@');
|
|
122
|
+
const agentPart = at === -1 ? arg : arg.slice(0, at);
|
|
123
|
+
const versionPart = at === -1 ? '' : arg.slice(at + 1);
|
|
124
|
+
const agent = resolveAgentName(agentPart);
|
|
125
|
+
if (!agent)
|
|
126
|
+
return { error: formatAgentError(agentPart) };
|
|
127
|
+
if (!versionPart) {
|
|
128
|
+
const versions = listInstalledVersions(agent);
|
|
129
|
+
if (versions.length === 0)
|
|
130
|
+
return { error: `${AGENTS[agent].name} has no installed versions. Run \`agents add ${agent}@<version>\` first.` };
|
|
131
|
+
return { agent, versions };
|
|
132
|
+
}
|
|
133
|
+
if (versionPart === 'default') {
|
|
134
|
+
const def = getGlobalDefault(agent);
|
|
135
|
+
if (!def)
|
|
136
|
+
return { error: `${AGENTS[agent].name} has no default version pinned. Run \`agents use ${agent}@<version>\`.` };
|
|
137
|
+
return { agent, versions: [def] };
|
|
138
|
+
}
|
|
139
|
+
const spec = parseAgentSpec(`${agent}@${versionPart}`);
|
|
140
|
+
if (!spec)
|
|
141
|
+
return { error: `Invalid version: ${versionPart}` };
|
|
142
|
+
if (!isVersionInstalled(agent, versionPart)) {
|
|
143
|
+
return { error: `${AGENTS[agent].name}@${versionPart} is not installed. Installed: ${listInstalledVersions(agent).join(', ') || '(none)'}` };
|
|
144
|
+
}
|
|
145
|
+
return { agent, versions: [versionPart] };
|
|
146
|
+
}
|
|
147
|
+
function parseKindFilter(arg) {
|
|
148
|
+
if (!arg)
|
|
149
|
+
return DOCTOR_ALL_KINDS;
|
|
150
|
+
const requested = arg.split(',').map((s) => s.trim()).filter(Boolean);
|
|
151
|
+
const valid = new Set(DOCTOR_ALL_KINDS);
|
|
152
|
+
const out = [];
|
|
153
|
+
for (const k of requested) {
|
|
154
|
+
if (!valid.has(k)) {
|
|
155
|
+
return { error: `Unknown kind: ${k}. Valid: ${DOCTOR_ALL_KINDS.join(', ')}` };
|
|
156
|
+
}
|
|
157
|
+
out.push(k);
|
|
158
|
+
}
|
|
159
|
+
return out;
|
|
160
|
+
}
|
|
161
|
+
function statusLabel(status) {
|
|
162
|
+
switch (status) {
|
|
163
|
+
case 'ok': return chalk.green('ok ');
|
|
164
|
+
case 'diff': return chalk.yellow('DIFF ');
|
|
165
|
+
case 'missing': return chalk.red('MISS ');
|
|
166
|
+
case 'extra': return chalk.magenta('EXTRA');
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function sourceLabel(diff, layers) {
|
|
170
|
+
if (!diff.source)
|
|
171
|
+
return '';
|
|
172
|
+
if (diff.source === 'extra') {
|
|
173
|
+
// Find which extra repo this came from.
|
|
174
|
+
const sourcePath = diff.sourcePath;
|
|
175
|
+
if (sourcePath) {
|
|
176
|
+
for (const e of layers.extras) {
|
|
177
|
+
if (sourcePath.startsWith(e.dir + '/') || sourcePath === e.dir) {
|
|
178
|
+
return chalk.gray(`source=extra:${e.alias}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return chalk.gray('source=extra');
|
|
183
|
+
}
|
|
184
|
+
return chalk.gray(`source=${diff.source}`);
|
|
185
|
+
}
|
|
186
|
+
function countByStatus(rows) {
|
|
187
|
+
let ok = 0, diff = 0, missing = 0, extra = 0;
|
|
188
|
+
for (const r of rows) {
|
|
189
|
+
if (r.status === 'ok')
|
|
190
|
+
ok++;
|
|
191
|
+
else if (r.status === 'diff')
|
|
192
|
+
diff++;
|
|
193
|
+
else if (r.status === 'missing')
|
|
194
|
+
missing++;
|
|
195
|
+
else if (r.status === 'extra')
|
|
196
|
+
extra++;
|
|
197
|
+
}
|
|
198
|
+
return { ok, diff, missing, extra };
|
|
199
|
+
}
|
|
200
|
+
function renderKindSection(kind, rows, layers, options) {
|
|
201
|
+
const counts = countByStatus(rows);
|
|
202
|
+
const total = rows.length;
|
|
203
|
+
const summaryParts = [];
|
|
204
|
+
if (counts.ok)
|
|
205
|
+
summaryParts.push(`${counts.ok} ok`);
|
|
206
|
+
if (counts.diff)
|
|
207
|
+
summaryParts.push(chalk.yellow(`${counts.diff} diff`));
|
|
208
|
+
if (counts.missing)
|
|
209
|
+
summaryParts.push(chalk.red(`${counts.missing} missing`));
|
|
210
|
+
if (counts.extra)
|
|
211
|
+
summaryParts.push(chalk.magenta(`${counts.extra} extra`));
|
|
212
|
+
const summary = total === 0 ? chalk.gray('(none)') : summaryParts.join(', ');
|
|
213
|
+
console.log(` ${chalk.bold(kind.padEnd(11))} ${chalk.gray(`${total} item${total === 1 ? '' : 's'}`)} ${summary}`);
|
|
214
|
+
if (total === 0)
|
|
215
|
+
return;
|
|
216
|
+
// Hide ok rows by default for big lists; show them only with --diff so the
|
|
217
|
+
// operator can verify presence; otherwise keep output focused on problems.
|
|
218
|
+
const visible = options.showDiff ? rows : rows.filter((r) => r.status !== 'ok');
|
|
219
|
+
if (visible.length === 0) {
|
|
220
|
+
console.log(` ${chalk.gray('all ok')}`);
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
for (const r of visible) {
|
|
224
|
+
const src = sourceLabel(r, layers);
|
|
225
|
+
console.log(` ${statusLabel(r.status)} ${r.name.padEnd(28)} ${src}`);
|
|
226
|
+
if (options.showDiff && r.status === 'diff' && r.sourcePath && r.homePath) {
|
|
227
|
+
const expected = readExpectedForDiff(kind, r);
|
|
228
|
+
const actual = safeRead(r.homePath);
|
|
229
|
+
if (expected != null && actual != null) {
|
|
230
|
+
const patch = unifiedDiff(expected, actual, {
|
|
231
|
+
fromLabel: r.sourcePath,
|
|
232
|
+
toLabel: r.homePath,
|
|
233
|
+
context: 2,
|
|
234
|
+
});
|
|
235
|
+
if (patch)
|
|
236
|
+
console.log(colorizeUnifiedDiff(patch, ' '));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
function safeRead(p) {
|
|
242
|
+
try {
|
|
243
|
+
return fs.readFileSync(p, 'utf-8');
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
function readExpectedForDiff(kind, row) {
|
|
250
|
+
// Skills are directories; per-file diffs would need recursive walking.
|
|
251
|
+
// Keep the v1 behaviour minimal: the row already says DIFF, the user can
|
|
252
|
+
// open the source path to inspect.
|
|
253
|
+
if (kind === 'skills')
|
|
254
|
+
return null;
|
|
255
|
+
if (!row.sourcePath)
|
|
256
|
+
return null;
|
|
257
|
+
return safeRead(row.sourcePath);
|
|
258
|
+
}
|
|
259
|
+
function renderTargetText(report, options) {
|
|
260
|
+
const label = `${AGENT_NAMES[report.agent] || report.agent}@${report.version}`;
|
|
261
|
+
console.log(chalk.bold(label));
|
|
262
|
+
console.log(chalk.gray(` home: ${report.home}`));
|
|
263
|
+
console.log(chalk.gray(` cwd: ${report.cwd}`));
|
|
264
|
+
const layerStr = [
|
|
265
|
+
report.layers.project ? `project=${report.layers.project}` : null,
|
|
266
|
+
`user=${report.layers.user}`,
|
|
267
|
+
`system=${report.layers.system}`,
|
|
268
|
+
report.layers.extras.length > 0
|
|
269
|
+
? `extras=[${report.layers.extras.map((e) => e.alias).join(',')}]`
|
|
270
|
+
: null,
|
|
271
|
+
].filter(Boolean).join(' ');
|
|
272
|
+
console.log(chalk.gray(` layers: ${layerStr}`));
|
|
273
|
+
console.log();
|
|
274
|
+
for (const kind of DOCTOR_ALL_KINDS) {
|
|
275
|
+
const rows = report.kinds[kind];
|
|
276
|
+
// Skip kinds that weren't requested (kind filter narrowed the report).
|
|
277
|
+
// We detect this by checking whether the kind has any rows AND was in
|
|
278
|
+
// scope; absent kinds with empty arrays still render so the operator
|
|
279
|
+
// sees what was checked. options.requestedKinds drives this.
|
|
280
|
+
if (options.requestedKinds && !options.requestedKinds.has(kind))
|
|
281
|
+
continue;
|
|
282
|
+
renderKindSection(kind, rows, report.layers, options);
|
|
283
|
+
}
|
|
284
|
+
console.log();
|
|
285
|
+
const { ok, diff, missing, extra } = report.summary;
|
|
286
|
+
const verdictParts = [];
|
|
287
|
+
if (diff)
|
|
288
|
+
verdictParts.push(chalk.yellow(`${diff} divergent`));
|
|
289
|
+
if (missing)
|
|
290
|
+
verdictParts.push(chalk.red(`${missing} missing`));
|
|
291
|
+
if (extra)
|
|
292
|
+
verdictParts.push(chalk.magenta(`${extra} extra`));
|
|
293
|
+
if (verdictParts.length === 0) {
|
|
294
|
+
console.log(chalk.green(` Verdict: ${ok} resource${ok === 1 ? '' : 's'} reconciled. Version home matches resolved sources.`));
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
console.log(` Verdict: ${verdictParts.join(', ')}.`);
|
|
298
|
+
console.log(chalk.gray(` Run \`agents sync --agent ${report.agent} --agent-version ${report.version}\` to reconcile, or \`agents prune\` to drop extras.`));
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
// ─── command registration ────────────────────────────────────────────────────
|
|
116
302
|
export function registerDoctorCommand(program) {
|
|
117
303
|
program
|
|
118
|
-
.command('doctor')
|
|
119
|
-
.description('Diagnose CLI availability, sync status, and
|
|
304
|
+
.command('doctor [target]')
|
|
305
|
+
.description('Diagnose CLI availability, sync status, and resource divergence (optionally for a specific agent[@version])')
|
|
120
306
|
.option('--json', 'Output machine-readable JSON')
|
|
121
|
-
.
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
307
|
+
.option('--diff', 'In target mode, include unified diffs for divergent files')
|
|
308
|
+
.option('--kind <kinds>', 'Restrict to comma-separated resource kinds (commands,skills,hooks,rules,mcp,permissions,subagents,plugins,promptcuts)')
|
|
309
|
+
.option('--cwd <path>', 'Resolution cwd for project layer detection (default: process.cwd())')
|
|
310
|
+
.addHelpText('after', `
|
|
311
|
+
Examples:
|
|
312
|
+
# Overview across default versions (CLI availability + sync + orphans)
|
|
313
|
+
agents doctor
|
|
314
|
+
|
|
315
|
+
# Full per-resource report for the active default
|
|
316
|
+
agents doctor claude@default
|
|
317
|
+
|
|
318
|
+
# Pin to a specific installed version
|
|
319
|
+
agents doctor codex@0.117.0
|
|
320
|
+
|
|
321
|
+
# All installed versions for one agent
|
|
322
|
+
agents doctor gemini
|
|
323
|
+
|
|
324
|
+
# Only inspect rules and hooks, with full diffs
|
|
325
|
+
agents doctor claude@default --kind rules,hooks --diff
|
|
326
|
+
`)
|
|
327
|
+
.action((target, opts) => {
|
|
328
|
+
const cwd = opts.cwd ? opts.cwd : process.cwd();
|
|
329
|
+
if (!target) {
|
|
330
|
+
const clis = checkAllClis();
|
|
331
|
+
const syncRows = checkSyncStatus(cwd);
|
|
332
|
+
const orphanRows = countOrphans();
|
|
333
|
+
if (opts.json) {
|
|
334
|
+
console.log(JSON.stringify({ clis, sync: syncRows, orphans: orphanRows }, null, 2));
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
renderOverviewText(clis, syncRows, orphanRows);
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
const parsed = parseTargetArg(target);
|
|
341
|
+
if ('error' in parsed) {
|
|
342
|
+
console.error(chalk.red(parsed.error));
|
|
343
|
+
process.exit(1);
|
|
344
|
+
}
|
|
345
|
+
const kinds = parseKindFilter(opts.kind);
|
|
346
|
+
if (!Array.isArray(kinds)) {
|
|
347
|
+
console.error(chalk.red(kinds.error));
|
|
348
|
+
process.exit(1);
|
|
349
|
+
}
|
|
350
|
+
const reports = parsed.versions.map((v) => diffVersionResources(parsed.agent, v, { cwd, kinds }));
|
|
126
351
|
if (opts.json) {
|
|
127
|
-
console.log(JSON.stringify(
|
|
352
|
+
console.log(JSON.stringify(reports.length === 1 ? reports[0] : reports, null, 2));
|
|
128
353
|
return;
|
|
129
354
|
}
|
|
130
|
-
|
|
355
|
+
const showDiff = !!opts.diff;
|
|
356
|
+
const requestedKinds = opts.kind ? new Set(kinds) : undefined;
|
|
357
|
+
reports.forEach((r, i) => {
|
|
358
|
+
if (i > 0)
|
|
359
|
+
console.log();
|
|
360
|
+
const home = getVersionHomePath(r.agent, r.version);
|
|
361
|
+
if (!fs.existsSync(home)) {
|
|
362
|
+
console.log(chalk.red(`${AGENT_NAMES[r.agent] || r.agent}@${r.version}: version home not found at ${home}`));
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
renderTargetText(r, { showDiff, requestedKinds });
|
|
366
|
+
});
|
|
131
367
|
});
|
|
132
368
|
}
|
package/dist/commands/prune.js
CHANGED
|
@@ -133,7 +133,7 @@ async function runOrphanPrune(resourceTypes, options) {
|
|
|
133
133
|
export function registerPruneCommand(program) {
|
|
134
134
|
program
|
|
135
135
|
.command('prune [target]')
|
|
136
|
-
.description('Remove orphan resources (commands/skills/hooks) and/or older duplicate version installs')
|
|
136
|
+
.description('Remove orphan resources (commands/skills/hooks) and/or older duplicate version installs (versions soft-deleted to ~/.agents-system/trash/)')
|
|
137
137
|
.option('--all', 'For orphan cleanup: sweep every installed version (default: current default version per agent)')
|
|
138
138
|
.option('--dry-run', 'Show what would be removed without deleting')
|
|
139
139
|
.option('-y, --yes', 'Skip confirmation prompt')
|
|
@@ -172,8 +172,14 @@ What's an orphan?
|
|
|
172
172
|
reconciled into the version install.
|
|
173
173
|
|
|
174
174
|
What this does NOT do:
|
|
175
|
-
|
|
176
|
-
|
|
175
|
+
Adds and updates flow through the auto-sync that runs when you launch the
|
|
176
|
+
agent — there is no manual sync verb.
|
|
177
|
+
|
|
178
|
+
Soft-delete:
|
|
179
|
+
Version directories are NEVER hard-deleted. \`prune\` moves them to
|
|
180
|
+
~/.agents-system/trash/versions/<agent>/<version>/<timestamp>/. Recover
|
|
181
|
+
with \`agents trash list\` and \`agents trash restore <agent>@<version>\`.
|
|
182
|
+
The trash never auto-expires; \`rm -rf\` it manually when you're sure.
|
|
177
183
|
`)
|
|
178
184
|
.action(async (target, options) => {
|
|
179
185
|
const parsed = parseTarget(target);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal rules refresh command.
|
|
3
|
+
*
|
|
4
|
+
* Registers the hidden `agents refresh-rules` command invoked by shims for
|
|
5
|
+
* agents that do not natively resolve @-imports in their rules file.
|
|
6
|
+
* Recompiles only when source files have changed.
|
|
7
|
+
*/
|
|
8
|
+
import { Command } from 'commander';
|
|
9
|
+
/**
|
|
10
|
+
* Hidden command invoked by shims for agents that don't natively resolve
|
|
11
|
+
* @-imports in their rules file. Fast-path check first (sha256 of tracked
|
|
12
|
+
* source files); only recompiles if a source has changed since the last
|
|
13
|
+
* sync. Typical cost: 10-20ms when rules are fresh.
|
|
14
|
+
*/
|
|
15
|
+
export declare function registerRefreshRulesCommand(program: Command): void;
|
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Internal
|
|
2
|
+
* Internal rules refresh command.
|
|
3
3
|
*
|
|
4
|
-
* Registers the hidden `agents refresh-
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Registers the hidden `agents refresh-rules` command invoked by shims for
|
|
5
|
+
* agents that do not natively resolve @-imports in their rules file.
|
|
6
|
+
* Recompiles only when source files have changed.
|
|
7
7
|
*/
|
|
8
8
|
import chalk from 'chalk';
|
|
9
9
|
import { AGENTS } from '../lib/agents.js';
|
|
10
10
|
import { isVersionInstalled } from '../lib/versions.js';
|
|
11
|
-
import {
|
|
11
|
+
import { ensureRulesFresh, supportsRulesImports } from '../lib/rules/compile.js';
|
|
12
12
|
/**
|
|
13
13
|
* Hidden command invoked by shims for agents that don't natively resolve
|
|
14
|
-
* @-imports in their
|
|
14
|
+
* @-imports in their rules file. Fast-path check first (sha256 of tracked
|
|
15
15
|
* source files); only recompiles if a source has changed since the last
|
|
16
|
-
* sync. Typical cost: 10-20ms when
|
|
16
|
+
* sync. Typical cost: 10-20ms when rules are fresh.
|
|
17
17
|
*/
|
|
18
|
-
export function
|
|
18
|
+
export function registerRefreshRulesCommand(program) {
|
|
19
19
|
program
|
|
20
|
-
.command('refresh-
|
|
21
|
-
.description('Internal: recompile
|
|
20
|
+
.command('refresh-rules', { hidden: true })
|
|
21
|
+
.description('Internal: recompile rules for an agent if sources have changed. Called by shims.')
|
|
22
22
|
.requiredOption('--agent <agent>', 'Agent identifier (codex, opencode, cursor, etc.)')
|
|
23
|
-
.requiredOption('--agent-version <version>', 'Installed version whose
|
|
23
|
+
.requiredOption('--agent-version <version>', 'Installed version whose rules file should be refreshed')
|
|
24
24
|
.option('--quiet', 'Suppress all output (exit code indicates success)', false)
|
|
25
25
|
.action((opts) => {
|
|
26
26
|
const agentId = opts.agent;
|
|
@@ -32,7 +32,7 @@ export function registerRefreshMemoryCommand(program) {
|
|
|
32
32
|
process.exitCode = 1;
|
|
33
33
|
return;
|
|
34
34
|
}
|
|
35
|
-
if (
|
|
35
|
+
if (supportsRulesImports(agentId)) {
|
|
36
36
|
// Nothing to do — agent resolves @-imports natively.
|
|
37
37
|
return;
|
|
38
38
|
}
|
|
@@ -43,9 +43,9 @@ export function registerRefreshMemoryCommand(program) {
|
|
|
43
43
|
process.exitCode = 1;
|
|
44
44
|
return;
|
|
45
45
|
}
|
|
46
|
-
const recompiled =
|
|
46
|
+
const recompiled = ensureRulesFresh(agentId, version);
|
|
47
47
|
if (!quiet && recompiled) {
|
|
48
|
-
console.log(chalk.gray(`Refreshed
|
|
48
|
+
console.log(chalk.gray(`Refreshed rules for ${agentId}@${version}`));
|
|
49
49
|
}
|
|
50
50
|
});
|
|
51
51
|
}
|
|
@@ -572,7 +572,7 @@ Examples:
|
|
|
572
572
|
if (options.follow) {
|
|
573
573
|
const { exec: execCb } = await import('child_process');
|
|
574
574
|
const { getAgentsDir } = await import('../lib/state.js');
|
|
575
|
-
const logPath = path.join(getAgentsDir(), 'daemon.
|
|
575
|
+
const logPath = path.join(getAgentsDir(), 'helpers/daemon/logs.jsonl');
|
|
576
576
|
const child = execCb(`tail -f "${logPath}"`);
|
|
577
577
|
child.stdout?.pipe(process.stdout);
|
|
578
578
|
child.stderr?.pipe(process.stderr);
|
package/dist/commands/rules.js
CHANGED
|
@@ -6,9 +6,11 @@ import * as path from 'path';
|
|
|
6
6
|
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
|
-
import { discoverInstructionsFromRepo,
|
|
9
|
+
import { discoverInstructionsFromRepo, discoverRuleFilesFromRepo, installInstructionsCentrally, uninstallInstructions, listInstalledInstructionsWithScope, instructionsExists, getInstructionsContent, listCentralRules, } from '../lib/rules/rules.js';
|
|
10
10
|
import { listInstalledVersions, getGlobalDefault, resolveVersionAlias, syncResourcesToVersion, promptAgentVersionSelection, getVersionHomePath, resolveAgentVersionTargets, } from '../lib/versions.js';
|
|
11
|
-
import { recordVersionResources } from '../lib/state.js';
|
|
11
|
+
import { recordVersionResources, getActiveRulesPreset, setActiveRulesPreset } from '../lib/state.js';
|
|
12
|
+
import { discoverRulesLayers } from '../lib/rules/compose.js';
|
|
13
|
+
import * as yaml from 'yaml';
|
|
12
14
|
import { isPromptCancelled, formatPath, isInteractiveTerminal, parseCommaSeparatedList, printWithPager, requireInteractiveSelection, requireDestructiveArg, } from './utils.js';
|
|
13
15
|
/** Register the `agents rules` command tree (list, add, view, remove). */
|
|
14
16
|
export function registerRulesCommands(program) {
|
|
@@ -205,7 +207,7 @@ Examples:
|
|
|
205
207
|
try {
|
|
206
208
|
let ruleNames;
|
|
207
209
|
if (!source) {
|
|
208
|
-
const centralRules =
|
|
210
|
+
const centralRules = listCentralRules();
|
|
209
211
|
if (centralRules.length === 0) {
|
|
210
212
|
console.log(chalk.yellow('No rule files in ~/.agents/rules/'));
|
|
211
213
|
console.log(chalk.gray('\nTo add rule files from a repo:'));
|
|
@@ -271,7 +273,7 @@ Examples:
|
|
|
271
273
|
spinner.succeed('Using local path');
|
|
272
274
|
}
|
|
273
275
|
const agentInstructions = discoverInstructionsFromRepo(localPath);
|
|
274
|
-
const ruleFiles =
|
|
276
|
+
const ruleFiles = discoverRuleFilesFromRepo(localPath);
|
|
275
277
|
const totalFiles = agentInstructions.length + ruleFiles.length;
|
|
276
278
|
console.log(chalk.bold(`\nFound ${totalFiles} rule file(s):`));
|
|
277
279
|
if (totalFiles === 0) {
|
|
@@ -484,6 +486,100 @@ Examples:
|
|
|
484
486
|
console.log(chalk.yellow(`No rule file found for ${agentLabel(agentId)}`));
|
|
485
487
|
}
|
|
486
488
|
});
|
|
489
|
+
rulesCmd
|
|
490
|
+
.command('switch <target>')
|
|
491
|
+
.description('Choose the active rule preset for an agent version (persists in agents.yaml)')
|
|
492
|
+
.option('-p, --preset <name>', 'Preset to activate; omit for an interactive picker')
|
|
493
|
+
.addHelpText('after', `
|
|
494
|
+
Targets are <agent>@<version>. Use 'default' for the alias of the global default version.
|
|
495
|
+
|
|
496
|
+
Examples:
|
|
497
|
+
# Persist the cautious preset for claude 2.1.111
|
|
498
|
+
agents rules switch claude@2.1.111 --preset cautious
|
|
499
|
+
|
|
500
|
+
# Reset back to the literal default preset
|
|
501
|
+
agents rules switch claude@default --preset default
|
|
502
|
+
|
|
503
|
+
# Pick interactively
|
|
504
|
+
agents rules switch codex@0.116.0
|
|
505
|
+
`)
|
|
506
|
+
.action(async (target, options) => {
|
|
507
|
+
try {
|
|
508
|
+
const [rawAgent, rawVersion] = target.split('@');
|
|
509
|
+
const agentId = resolveAgentName(rawAgent);
|
|
510
|
+
if (!agentId) {
|
|
511
|
+
console.log(chalk.red(formatAgentError(rawAgent)));
|
|
512
|
+
process.exit(1);
|
|
513
|
+
}
|
|
514
|
+
const version = resolveVersionAlias(agentId, rawVersion);
|
|
515
|
+
if (!version) {
|
|
516
|
+
console.log(chalk.red(`Pass a version: ${agentId}@<version>. Try 'default' or 'agents list ${agentId}'.`));
|
|
517
|
+
process.exit(1);
|
|
518
|
+
}
|
|
519
|
+
const installed = listInstalledVersions(agentId);
|
|
520
|
+
if (!installed.includes(version)) {
|
|
521
|
+
console.log(chalk.red(`Version ${version} not installed for ${agentLabel(agentId)}.`));
|
|
522
|
+
console.log(chalk.gray(`Installed: ${installed.join(', ') || 'none'}`));
|
|
523
|
+
process.exit(1);
|
|
524
|
+
}
|
|
525
|
+
// Discover available presets across layers (highest-priority defines, lowers union in).
|
|
526
|
+
const layers = discoverRulesLayers();
|
|
527
|
+
const presetSet = new Set();
|
|
528
|
+
for (const layer of layers) {
|
|
529
|
+
const yamlPath = path.join(layer.rulesDir, 'rules.yaml');
|
|
530
|
+
if (!fs.existsSync(yamlPath))
|
|
531
|
+
continue;
|
|
532
|
+
try {
|
|
533
|
+
const parsed = yaml.parse(fs.readFileSync(yamlPath, 'utf-8'));
|
|
534
|
+
for (const name of Object.keys(parsed?.presets || {}))
|
|
535
|
+
presetSet.add(name);
|
|
536
|
+
}
|
|
537
|
+
catch { /* malformed yaml — skip */ }
|
|
538
|
+
}
|
|
539
|
+
const presets = Array.from(presetSet).sort();
|
|
540
|
+
if (presets.length === 0) {
|
|
541
|
+
console.log(chalk.red('No presets found. Define presets in ~/.agents-system/rules/rules.yaml or ~/.agents/rules/rules.yaml.'));
|
|
542
|
+
process.exit(1);
|
|
543
|
+
}
|
|
544
|
+
let chosen = options.preset;
|
|
545
|
+
if (chosen) {
|
|
546
|
+
if (!presets.includes(chosen)) {
|
|
547
|
+
console.log(chalk.red(`Unknown preset: ${chosen}`));
|
|
548
|
+
console.log(chalk.gray(`Available: ${presets.join(', ')}`));
|
|
549
|
+
process.exit(1);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
else {
|
|
553
|
+
if (!isInteractiveTerminal()) {
|
|
554
|
+
requireInteractiveSelection('Selecting a rule preset', [
|
|
555
|
+
`agents rules switch ${agentId}@${version} --preset default`,
|
|
556
|
+
`agents rules switch ${agentId}@${version} --preset cautious`,
|
|
557
|
+
]);
|
|
558
|
+
}
|
|
559
|
+
const current = getActiveRulesPreset(agentId, version);
|
|
560
|
+
chosen = await select({
|
|
561
|
+
message: `Active rule preset for ${agentLabel(agentId)}@${version}`,
|
|
562
|
+
default: current,
|
|
563
|
+
choices: presets.map((p) => ({ name: p === current ? `${p} (current)` : p, value: p })),
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
setActiveRulesPreset(agentId, version, chosen);
|
|
567
|
+
const spinner = ora(`Re-syncing ${agentLabel(agentId)}@${version} with preset '${chosen}'`).start();
|
|
568
|
+
const result = syncResourcesToVersion(agentId, version);
|
|
569
|
+
spinner.succeed(`Switched ${agentLabel(agentId)}@${version} to '${chosen}'`);
|
|
570
|
+
if (result.memory.length > 0) {
|
|
571
|
+
console.log(chalk.gray(` Wrote ${result.memory[0]} from preset '${chosen}'.`));
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
catch (err) {
|
|
575
|
+
if (isPromptCancelled(err)) {
|
|
576
|
+
console.log(chalk.gray('\nCancelled'));
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
console.error(chalk.red(`Failed to switch preset: ${err.message}`));
|
|
580
|
+
process.exit(1);
|
|
581
|
+
}
|
|
582
|
+
});
|
|
487
583
|
rulesCmd
|
|
488
584
|
.command('show [agent]', { hidden: true })
|
|
489
585
|
.action(async (agentArg) => {
|