@phnx-labs/agents-cli 1.20.5 → 1.20.7
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 +13 -0
- package/README.md +1 -1
- package/dist/commands/browser.js +31 -4
- package/dist/commands/computer-actions.d.ts +36 -0
- package/dist/commands/computer-actions.js +328 -0
- package/dist/commands/computer.js +74 -55
- package/dist/commands/defaults.d.ts +7 -0
- package/dist/commands/defaults.js +89 -0
- package/dist/commands/exec.js +24 -6
- package/dist/commands/inspect.d.ts +38 -7
- package/dist/commands/inspect.js +194 -24
- package/dist/commands/rules.js +3 -3
- package/dist/commands/secrets.js +46 -9
- package/dist/commands/sessions.js +9 -12
- package/dist/commands/setup.js +2 -2
- package/dist/commands/teams.js +108 -11
- package/dist/commands/view.d.ts +12 -1
- package/dist/commands/view.js +121 -38
- package/dist/index.js +61 -22
- package/dist/lib/agents.d.ts +10 -6
- package/dist/lib/agents.js +23 -14
- package/dist/lib/browser/chrome.d.ts +10 -0
- package/dist/lib/browser/chrome.js +84 -3
- package/dist/lib/daemon.js +4 -7
- package/dist/lib/exec.d.ts +9 -0
- package/dist/lib/exec.js +85 -9
- package/dist/lib/migrate.js +6 -4
- package/dist/lib/permissions.d.ts +23 -0
- package/dist/lib/permissions.js +89 -7
- package/dist/lib/platform/exec.d.ts +9 -0
- package/dist/lib/platform/exec.js +24 -0
- package/dist/lib/platform/index.d.ts +20 -0
- package/dist/lib/platform/index.js +20 -0
- package/dist/lib/platform/paths.d.ts +22 -0
- package/dist/lib/platform/paths.js +49 -0
- package/dist/lib/platform/process.d.ts +12 -0
- package/dist/lib/platform/process.js +22 -0
- package/dist/lib/plugin-marketplace.js +1 -1
- package/dist/lib/project-launch.d.ts +5 -0
- package/dist/lib/project-launch.js +37 -0
- package/dist/lib/pty-client.js +13 -5
- package/dist/lib/pty-server.d.ts +24 -1
- package/dist/lib/pty-server.js +109 -29
- package/dist/lib/resources/rules.js +1 -1
- package/dist/lib/resources/skills.js +1 -1
- package/dist/lib/resources.d.ts +2 -0
- package/dist/lib/resources.js +2 -1
- package/dist/lib/rotate.js +6 -18
- package/dist/lib/run-config.d.ts +9 -0
- package/dist/lib/run-config.js +35 -0
- package/dist/lib/run-defaults.d.ts +42 -0
- package/dist/lib/run-defaults.js +180 -0
- 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/install-helper.d.ts +11 -3
- package/dist/lib/secrets/install-helper.js +48 -6
- package/dist/lib/secrets/linux.d.ts +12 -0
- package/dist/lib/secrets/linux.js +30 -16
- package/dist/lib/session/artifacts.js +8 -2
- package/dist/lib/shims.d.ts +9 -1
- package/dist/lib/shims.js +80 -3
- package/dist/lib/staleness/detectors/hooks.js +1 -1
- package/dist/lib/staleness/writers/hooks.js +1 -1
- package/dist/lib/teams/agents.js +5 -7
- package/dist/lib/teams/api.d.ts +67 -0
- package/dist/lib/teams/api.js +78 -0
- package/dist/lib/types.d.ts +15 -6
- package/dist/lib/versions.js +4 -4
- package/package.json +5 -2
- package/scripts/postinstall.js +18 -1
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Defaults command tree.
|
|
3
|
+
*
|
|
4
|
+
* `agents defaults run ...` manages selector-based defaults for `agents run`.
|
|
5
|
+
*/
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import { setHelpSections } from '../lib/help.js';
|
|
8
|
+
import { listRunDefaults, setRunDefault, unsetRunDefault, } from '../lib/run-defaults.js';
|
|
9
|
+
function formatRunDefault(entry) {
|
|
10
|
+
const parts = [];
|
|
11
|
+
if (entry.defaults.mode)
|
|
12
|
+
parts.push(`mode ${chalk.white(entry.defaults.mode)}`);
|
|
13
|
+
if (entry.defaults.model)
|
|
14
|
+
parts.push(`model ${chalk.white(entry.defaults.model)}`);
|
|
15
|
+
return `${chalk.cyan(entry.selector.padEnd(22))} ${parts.join(' ')}`;
|
|
16
|
+
}
|
|
17
|
+
export function registerDefaultsCommands(program) {
|
|
18
|
+
const defaults = program
|
|
19
|
+
.command('defaults')
|
|
20
|
+
.description('Manage default options for agents-cli commands');
|
|
21
|
+
const run = defaults
|
|
22
|
+
.command('run')
|
|
23
|
+
.description('Manage selector-based defaults for `agents run`');
|
|
24
|
+
setHelpSections(run, {
|
|
25
|
+
examples: `
|
|
26
|
+
agents defaults run list
|
|
27
|
+
agents defaults run set 'claude:*' --mode auto --model opus
|
|
28
|
+
agents defaults run set claude@2.1.45 --mode plan --model sonnet
|
|
29
|
+
agents defaults run unset 'claude:*'
|
|
30
|
+
`,
|
|
31
|
+
notes: `
|
|
32
|
+
Selectors use <agent>:<version>. Use * for all versions of an agent.
|
|
33
|
+
Exact selectors override wildcard selectors field by field.
|
|
34
|
+
Explicit flags on agents run always win over configured defaults.
|
|
35
|
+
`,
|
|
36
|
+
});
|
|
37
|
+
run
|
|
38
|
+
.command('list')
|
|
39
|
+
.description('List configured run defaults')
|
|
40
|
+
.action(() => {
|
|
41
|
+
const entries = listRunDefaults();
|
|
42
|
+
if (entries.length === 0) {
|
|
43
|
+
console.log(chalk.gray('No run defaults configured.'));
|
|
44
|
+
console.log(chalk.gray("Set one with: agents defaults run set 'claude:*' --mode auto --model opus"));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
console.log(chalk.bold('Run Defaults\n'));
|
|
48
|
+
for (const entry of entries) {
|
|
49
|
+
console.log(` ${formatRunDefault(entry)}`);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
run
|
|
53
|
+
.command('set <selector>')
|
|
54
|
+
.description('Set defaults for an agent/version selector')
|
|
55
|
+
.option('--mode <mode>', "Default mode: plan, edit, auto, skip. 'full' accepted as alias for skip.")
|
|
56
|
+
.option('--model <model>', 'Default model or model alias, forwarded via --model')
|
|
57
|
+
.action((selector, options) => {
|
|
58
|
+
try {
|
|
59
|
+
const entry = setRunDefault(selector, {
|
|
60
|
+
...(options.mode !== undefined ? { mode: options.mode } : {}),
|
|
61
|
+
...(options.model !== undefined ? { model: options.model } : {}),
|
|
62
|
+
});
|
|
63
|
+
console.log(chalk.green('Set run default:'));
|
|
64
|
+
console.log(` ${formatRunDefault(entry)}`);
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
console.error(chalk.red(err.message));
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
run
|
|
72
|
+
.command('unset <selector>')
|
|
73
|
+
.description('Remove defaults for an agent/version selector')
|
|
74
|
+
.action((selector) => {
|
|
75
|
+
try {
|
|
76
|
+
const removed = unsetRunDefault(selector);
|
|
77
|
+
if (removed) {
|
|
78
|
+
console.log(chalk.green(`Removed run default ${selector}`));
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
console.log(chalk.gray(`No run default matched ${selector}`));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
console.error(chalk.red(err.message));
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
package/dist/commands/exec.js
CHANGED
|
@@ -85,7 +85,7 @@ export function registerRunCommand(program) {
|
|
|
85
85
|
`,
|
|
86
86
|
});
|
|
87
87
|
runCmd.action(async (agentSpec, prompt, options) => {
|
|
88
|
-
const [{ buildExecCommand, parseExecEnv, execAgent, runWithFallback, normalizeMode, resolveMode, defaultModeFor }, { ALL_AGENT_IDS }, { profileExists, resolveProfileForRun }, { readAndResolveBundleEnv, describeBundle }, { getConfiguredRunStrategy, normalizeRunStrategy, resolveRunVersion, RUN_STRATEGIES }, { getGlobalDefault, getVersionHomePath, resolveVersionAlias }, { buildDiscoveredPlugin, loadPluginManifest, syncPluginToVersion }, { parseWorkflowFrontmatter, resolveWorkflowRef },] = await Promise.all([
|
|
88
|
+
const [{ buildExecCommand, parseExecEnv, execAgent, runWithFallback, normalizeMode, resolveMode, defaultModeFor }, { ALL_AGENT_IDS }, { profileExists, resolveProfileForRun }, { readAndResolveBundleEnv, describeBundle }, { getConfiguredRunStrategy, normalizeRunStrategy, resolveRunVersion, RUN_STRATEGIES }, { getGlobalDefault, getVersionHomePath, resolveVersion, resolveVersionAlias }, { buildDiscoveredPlugin, loadPluginManifest, syncPluginToVersion }, { parseWorkflowFrontmatter, resolveWorkflowRef }, { resolveRunDefaults },] = await Promise.all([
|
|
89
89
|
import('../lib/exec.js'),
|
|
90
90
|
import('../lib/agents.js'),
|
|
91
91
|
import('../lib/profiles.js'),
|
|
@@ -94,6 +94,7 @@ export function registerRunCommand(program) {
|
|
|
94
94
|
import('../lib/versions.js'),
|
|
95
95
|
import('../lib/plugins.js'),
|
|
96
96
|
import('../lib/workflows.js'),
|
|
97
|
+
import('../lib/run-defaults.js'),
|
|
97
98
|
]);
|
|
98
99
|
const isValidAgent = (agent) => ALL_AGENT_IDS.includes(agent);
|
|
99
100
|
// Parse agent@version
|
|
@@ -102,6 +103,7 @@ export function registerRunCommand(program) {
|
|
|
102
103
|
let version = rawVersion || undefined;
|
|
103
104
|
let profileEnv;
|
|
104
105
|
let fromProfile = false;
|
|
106
|
+
let workflowModel;
|
|
105
107
|
const cwd = options.cwd ?? process.cwd();
|
|
106
108
|
if (isValidAgent(rawAgent)) {
|
|
107
109
|
agent = rawAgent;
|
|
@@ -133,6 +135,10 @@ export function registerRunCommand(program) {
|
|
|
133
135
|
// subagents/*.md ← flat .md files copied to ~/.claude/agents/ for Agent tool discovery
|
|
134
136
|
const workflowDir = resolveWorkflowRef(rawAgent, cwd);
|
|
135
137
|
agent = 'claude';
|
|
138
|
+
const workflowFrontmatter = parseWorkflowFrontmatter(workflowDir);
|
|
139
|
+
if (typeof workflowFrontmatter?.model === 'string' && workflowFrontmatter.model.trim() !== '') {
|
|
140
|
+
workflowModel = workflowFrontmatter.model.trim();
|
|
141
|
+
}
|
|
136
142
|
const resolvedVersion = resolveVersionAlias('claude', version);
|
|
137
143
|
const versionHome = getVersionHomePath('claude', resolvedVersion ?? getGlobalDefault('claude') ?? '');
|
|
138
144
|
const claudeAgentsDir = path.join(versionHome, '.claude', 'agents');
|
|
@@ -179,8 +185,7 @@ export function registerRunCommand(program) {
|
|
|
179
185
|
// Auto-inject secrets bundles declared in the workflow's frontmatter `secrets:` field.
|
|
180
186
|
// Union with any --secrets flags the user passed; dedupe. Skip when --no-auto-secrets is set.
|
|
181
187
|
if (!options.noAutoSecrets) {
|
|
182
|
-
const
|
|
183
|
-
const declared = fm?.secrets ?? [];
|
|
188
|
+
const declared = workflowFrontmatter?.secrets ?? [];
|
|
184
189
|
if (declared.length > 0) {
|
|
185
190
|
const existing = new Set(options.secrets);
|
|
186
191
|
const added = [];
|
|
@@ -262,9 +267,18 @@ export function registerRunCommand(program) {
|
|
|
262
267
|
}
|
|
263
268
|
}
|
|
264
269
|
}
|
|
270
|
+
const defaultVersion = version ?? resolveVersion(agent, cwd);
|
|
271
|
+
const runDefaults = fromProfile
|
|
272
|
+
? { sources: {} }
|
|
273
|
+
: resolveRunDefaults(agent, defaultVersion, cwd);
|
|
265
274
|
// Accept the four canonical modes plus 'full' as a permanent silent
|
|
266
275
|
// alias for 'skip' (rewritten downstream by normalizeMode in exec.ts).
|
|
267
276
|
let mode = options.mode;
|
|
277
|
+
const modeSource = runCmd.getOptionValueSource('mode');
|
|
278
|
+
const modeFromRunDefault = modeSource === 'default' && !!runDefaults.mode;
|
|
279
|
+
if (modeFromRunDefault) {
|
|
280
|
+
mode = runDefaults.mode;
|
|
281
|
+
}
|
|
268
282
|
if (!['plan', 'edit', 'auto', 'skip', 'full'].includes(mode)) {
|
|
269
283
|
console.error(chalk.red(`Invalid mode: ${mode}. Use plan, edit, auto, or skip ('full' accepted as alias for skip).`));
|
|
270
284
|
process.exit(1);
|
|
@@ -276,13 +290,12 @@ export function registerRunCommand(program) {
|
|
|
276
290
|
// user did not ask for read-only, they asked for "just run it." An
|
|
277
291
|
// explicit --mode plan still throws (see resolveMode), because silently
|
|
278
292
|
// elevating an explicit read-only request to edit is unsafe.
|
|
279
|
-
const modeSource = runCmd.getOptionValueSource('mode');
|
|
280
293
|
const modeIsDefault = modeSource === 'default';
|
|
281
294
|
try {
|
|
282
295
|
resolveMode(agent, normalizeMode(mode));
|
|
283
296
|
}
|
|
284
297
|
catch (err) {
|
|
285
|
-
if (modeIsDefault) {
|
|
298
|
+
if (modeIsDefault && !modeFromRunDefault) {
|
|
286
299
|
mode = defaultModeFor(agent);
|
|
287
300
|
if (!options.quiet) {
|
|
288
301
|
process.stderr.write(chalk.gray(`[agents] ${agent} has no '${options.mode}' mode; using '${mode}'\n`));
|
|
@@ -334,6 +347,11 @@ export function registerRunCommand(program) {
|
|
|
334
347
|
const env = hasOverrides
|
|
335
348
|
? { ...(profileEnv ?? {}), ...secretsEnv, ...(userEnv ?? {}) }
|
|
336
349
|
: undefined;
|
|
350
|
+
const modelSource = runCmd.getOptionValueSource('model');
|
|
351
|
+
const model = options.model
|
|
352
|
+
?? (!fromProfile && modelSource === undefined
|
|
353
|
+
? (workflowModel ?? (options.fallback ? undefined : runDefaults.model))
|
|
354
|
+
: undefined);
|
|
337
355
|
const execOptions = {
|
|
338
356
|
agent,
|
|
339
357
|
version,
|
|
@@ -342,7 +360,7 @@ export function registerRunCommand(program) {
|
|
|
342
360
|
mode,
|
|
343
361
|
effort,
|
|
344
362
|
cwd: options.cwd,
|
|
345
|
-
model
|
|
363
|
+
model,
|
|
346
364
|
addDirs: options.addDir,
|
|
347
365
|
json: options.json,
|
|
348
366
|
headless: options.headless ?? true,
|
|
@@ -1,14 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `agents inspect <
|
|
2
|
+
* `agents inspect <target>` — detail view for one agent+version or one DotAgents repo.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* counts, sessions).
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* Agent targets (`claude`, `claude@2.1.170`) show the per-version header (paths,
|
|
5
|
+
* shim, capabilities, resource counts, sessions). Repo targets (`user`, `system`,
|
|
6
|
+
* `project`, a registered extra-repo alias, or a filesystem path to a repo with a
|
|
7
|
+
* `.agents/` dir or to a DotAgents root itself) show the repo root, git state, and
|
|
8
|
+
* per-kind resource counts. Drill-down flags (`--skills`, `--hooks`, `--mcp`, ...)
|
|
9
|
+
* list one resource kind for either target form; passing a positional query to the
|
|
10
|
+
* same flag fuzzy-searches for a single resource and prints its detail. Resource
|
|
11
|
+
* names render as OSC-8 hyperlinks to the marker file (SKILL.md / WORKFLOW.md /
|
|
12
|
+
* AGENT.md / the file itself) so users can click straight to the source.
|
|
10
13
|
*/
|
|
11
14
|
import { Command } from 'commander';
|
|
15
|
+
/** Resource kinds the inspect command can drill into. */
|
|
16
|
+
declare const DRILLABLE_KINDS: readonly ["commands", "skills", "hooks", "mcp", "rules", "plugins", "workflows", "subagents"];
|
|
17
|
+
type DrillableKind = typeof DRILLABLE_KINDS[number];
|
|
18
|
+
interface ResourceItem {
|
|
19
|
+
name: string;
|
|
20
|
+
source: string;
|
|
21
|
+
/** Absolute path to the resource entry (file or directory). */
|
|
22
|
+
path: string;
|
|
23
|
+
/** Path the OSC-8 link should point at — marker file inside bundles, else `path`. */
|
|
24
|
+
linkTarget: string;
|
|
25
|
+
/** One-line description (frontmatter `description:` or first non-frontmatter line). */
|
|
26
|
+
description: string;
|
|
27
|
+
}
|
|
12
28
|
interface InspectOptions {
|
|
13
29
|
brief?: boolean;
|
|
14
30
|
json?: boolean;
|
|
@@ -23,4 +39,19 @@ interface InspectOptions {
|
|
|
23
39
|
}
|
|
24
40
|
export declare function registerInspectCommand(program: Command): void;
|
|
25
41
|
export declare function inspectAction(target: string, options: InspectOptions): Promise<void>;
|
|
42
|
+
export interface RepoTarget {
|
|
43
|
+
/** Display label: 'user' | 'system' | 'project', an extra-repo alias, or a path-derived name. */
|
|
44
|
+
label: string;
|
|
45
|
+
/** Absolute path to the DotAgents root (the dir holding commands/, skills/, ...). */
|
|
46
|
+
root: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Resolve a non-agent target as a DotAgents repo: the built-in layer names,
|
|
50
|
+
* a registered extra-repo alias, or a filesystem path. Paths accept either a
|
|
51
|
+
* DotAgents root itself or a repo whose `.agents/` dir should be inspected.
|
|
52
|
+
* Returns null when the target is none of these.
|
|
53
|
+
*/
|
|
54
|
+
export declare function resolveRepoTarget(target: string, cwd?: string): RepoTarget | null;
|
|
55
|
+
/** List one resource kind from a single repo root — no layering, no overrides. */
|
|
56
|
+
export declare function collectRepoKind(repo: RepoTarget, kind: DrillableKind): ResourceItem[];
|
|
26
57
|
export {};
|
package/dist/commands/inspect.js
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `agents inspect <
|
|
2
|
+
* `agents inspect <target>` — detail view for one agent+version or one DotAgents repo.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* counts, sessions).
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* Agent targets (`claude`, `claude@2.1.170`) show the per-version header (paths,
|
|
5
|
+
* shim, capabilities, resource counts, sessions). Repo targets (`user`, `system`,
|
|
6
|
+
* `project`, a registered extra-repo alias, or a filesystem path to a repo with a
|
|
7
|
+
* `.agents/` dir or to a DotAgents root itself) show the repo root, git state, and
|
|
8
|
+
* per-kind resource counts. Drill-down flags (`--skills`, `--hooks`, `--mcp`, ...)
|
|
9
|
+
* list one resource kind for either target form; passing a positional query to the
|
|
10
|
+
* same flag fuzzy-searches for a single resource and prints its detail. Resource
|
|
11
|
+
* names render as OSC-8 hyperlinks to the marker file (SKILL.md / WORKFLOW.md /
|
|
12
|
+
* AGENT.md / the file itself) so users can click straight to the source.
|
|
10
13
|
*/
|
|
14
|
+
import { execSync } from 'child_process';
|
|
11
15
|
import * as fs from 'fs';
|
|
12
16
|
import * as os from 'os';
|
|
13
17
|
import * as path from 'path';
|
|
@@ -15,7 +19,7 @@ import chalk from 'chalk';
|
|
|
15
19
|
import * as yaml from 'yaml';
|
|
16
20
|
import { AGENTS, getCliState } from '../lib/agents.js';
|
|
17
21
|
import { supports } from '../lib/capabilities.js';
|
|
18
|
-
import { readMeta } from '../lib/state.js';
|
|
22
|
+
import { readMeta, getUserAgentsDir, getSystemAgentsDir, getProjectAgentsDir, getEnabledExtraRepos, } from '../lib/state.js';
|
|
19
23
|
import { getVersionHomePath } from '../lib/versions.js';
|
|
20
24
|
import { getShimsDir, getVersionedAliasPath } from '../lib/shims.js';
|
|
21
25
|
import { getAgentResources, listResources, } from '../lib/resources.js';
|
|
@@ -39,8 +43,8 @@ const CAPABILITY_NAMES = [
|
|
|
39
43
|
// ─── Command registration ────────────────────────────────────────────────────
|
|
40
44
|
export function registerInspectCommand(program) {
|
|
41
45
|
const cmd = program
|
|
42
|
-
.command('inspect <
|
|
43
|
-
.description('Inspect one installed agent at one version — paths, capabilities, resources, drill into any kind.')
|
|
46
|
+
.command('inspect <target>')
|
|
47
|
+
.description('Inspect one installed agent at one version, or a DotAgents repo (user|system|project|alias|path) — paths, capabilities, resources, drill into any kind.')
|
|
44
48
|
.option('--brief', 'header + capabilities only; skip resources/sessions')
|
|
45
49
|
.option('--json', 'machine-readable JSON output');
|
|
46
50
|
for (const kind of DRILLABLE_KINDS) {
|
|
@@ -52,6 +56,20 @@ export function registerInspectCommand(program) {
|
|
|
52
56
|
}
|
|
53
57
|
// ─── Main dispatcher ─────────────────────────────────────────────────────────
|
|
54
58
|
export async function inspectAction(target, options) {
|
|
59
|
+
const agentKey = target.split('@')[0].toLowerCase();
|
|
60
|
+
if (!(agentKey in AGENTS)) {
|
|
61
|
+
const repo = resolveRepoTarget(target);
|
|
62
|
+
if (repo) {
|
|
63
|
+
await inspectRepo(repo, options);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const extras = getEnabledExtraRepos();
|
|
67
|
+
console.error(chalk.red(`Unknown target: ${target}`));
|
|
68
|
+
console.error(chalk.gray(`Agents: ${Object.keys(AGENTS).join(', ')}`));
|
|
69
|
+
const aliases = extras.length > 0 ? `, ${extras.map(e => e.alias).join(', ')}` : '';
|
|
70
|
+
console.error(chalk.gray(`Repos: user, system, project${aliases} — or a path to a repo with a .agents/ dir`));
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
55
73
|
const { agent, version } = parseTarget(target);
|
|
56
74
|
const versionHome = getVersionHomePath(agent, version);
|
|
57
75
|
if (!fs.existsSync(versionHome)) {
|
|
@@ -75,13 +93,7 @@ export async function inspectAction(target, options) {
|
|
|
75
93
|
}
|
|
76
94
|
function parseTarget(target) {
|
|
77
95
|
const [rawAgent, rawVersion] = target.split('@');
|
|
78
|
-
const
|
|
79
|
-
if (!(agentKey in AGENTS)) {
|
|
80
|
-
console.error(chalk.red(`Unknown agent: ${rawAgent}`));
|
|
81
|
-
console.error(chalk.gray(`Known agents: ${Object.keys(AGENTS).join(', ')}`));
|
|
82
|
-
process.exit(1);
|
|
83
|
-
}
|
|
84
|
-
const agent = agentKey;
|
|
96
|
+
const agent = (rawAgent || '').toLowerCase();
|
|
85
97
|
let version = rawVersion;
|
|
86
98
|
if (!version || version === 'default') {
|
|
87
99
|
const meta = readMeta();
|
|
@@ -110,6 +122,160 @@ function pickDrillKind(options) {
|
|
|
110
122
|
}
|
|
111
123
|
return active[0];
|
|
112
124
|
}
|
|
125
|
+
/** Files at a DotAgents root that mark it as one, beyond the per-kind dirs. */
|
|
126
|
+
const REPO_MARKER_FILES = ['agents.yaml', 'hooks.yaml'];
|
|
127
|
+
/**
|
|
128
|
+
* Resolve a non-agent target as a DotAgents repo: the built-in layer names,
|
|
129
|
+
* a registered extra-repo alias, or a filesystem path. Paths accept either a
|
|
130
|
+
* DotAgents root itself or a repo whose `.agents/` dir should be inspected.
|
|
131
|
+
* Returns null when the target is none of these.
|
|
132
|
+
*/
|
|
133
|
+
export function resolveRepoTarget(target, cwd) {
|
|
134
|
+
if (target === 'user')
|
|
135
|
+
return { label: 'user', root: getUserAgentsDir() };
|
|
136
|
+
if (target === 'system')
|
|
137
|
+
return { label: 'system', root: getSystemAgentsDir() };
|
|
138
|
+
if (target === 'project') {
|
|
139
|
+
const dir = getProjectAgentsDir(cwd);
|
|
140
|
+
if (!dir) {
|
|
141
|
+
console.error(chalk.red('No project .agents/ directory found from the current directory.'));
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
return { label: 'project', root: dir };
|
|
145
|
+
}
|
|
146
|
+
for (const extra of getEnabledExtraRepos()) {
|
|
147
|
+
if (extra.alias === target)
|
|
148
|
+
return { label: extra.alias, root: extra.dir };
|
|
149
|
+
}
|
|
150
|
+
const expanded = target.startsWith('~/') ? path.join(os.homedir(), target.slice(2)) : target;
|
|
151
|
+
const abs = path.resolve(cwd ?? process.cwd(), expanded);
|
|
152
|
+
const stat = safeStat(abs);
|
|
153
|
+
if (!stat || !stat.isDirectory())
|
|
154
|
+
return null;
|
|
155
|
+
// A dir that is itself a DotAgents root wins over its nested .agents/ —
|
|
156
|
+
// extra repos like ~/.agents-extras keep resources at the top level and use
|
|
157
|
+
// .agents/ only for worktrees.
|
|
158
|
+
if (isDotAgentsRoot(abs)) {
|
|
159
|
+
const label = path.basename(abs) === '.agents' ? path.basename(path.dirname(abs)) : path.basename(abs);
|
|
160
|
+
return { label, root: abs };
|
|
161
|
+
}
|
|
162
|
+
if (path.basename(abs) !== '.agents') {
|
|
163
|
+
const nested = path.join(abs, '.agents');
|
|
164
|
+
if (safeStat(nested)?.isDirectory()) {
|
|
165
|
+
return { label: path.basename(abs), root: nested };
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
function isDotAgentsRoot(dir) {
|
|
171
|
+
for (const marker of REPO_MARKER_FILES) {
|
|
172
|
+
if (fs.existsSync(path.join(dir, marker)))
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
for (const kind of DRILLABLE_KINDS) {
|
|
176
|
+
if (safeStat(path.join(dir, kind))?.isDirectory())
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
async function inspectRepo(repo, options) {
|
|
182
|
+
const drill = pickDrillKind(options);
|
|
183
|
+
const jsonHead = { repo: repo.label, root: repo.root };
|
|
184
|
+
if (drill) {
|
|
185
|
+
const items = collectRepoKind(repo, drill.kind);
|
|
186
|
+
if (drill.query === true || drill.query === undefined) {
|
|
187
|
+
renderItemList(repo.label, jsonHead, drill.kind, items, options);
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
renderItemDetail(repo.label, jsonHead, drill.kind, String(drill.query), items, options);
|
|
191
|
+
}
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
renderRepoSummary(repo, options);
|
|
195
|
+
}
|
|
196
|
+
/** List one resource kind from a single repo root — no layering, no overrides. */
|
|
197
|
+
export function collectRepoKind(repo, kind) {
|
|
198
|
+
const dir = path.join(repo.root, kind);
|
|
199
|
+
let entries;
|
|
200
|
+
try {
|
|
201
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
return [];
|
|
205
|
+
}
|
|
206
|
+
const items = [];
|
|
207
|
+
for (const entry of entries) {
|
|
208
|
+
if (entry.name.startsWith('.'))
|
|
209
|
+
continue;
|
|
210
|
+
const p = path.join(dir, entry.name);
|
|
211
|
+
items.push({
|
|
212
|
+
name: entry.name.replace(/\.(md|yaml|yml|toml|json)$/, ''),
|
|
213
|
+
source: repo.label,
|
|
214
|
+
path: p,
|
|
215
|
+
linkTarget: linkTarget(p),
|
|
216
|
+
description: readDescription(p),
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
return items.sort((a, b) => a.name.localeCompare(b.name));
|
|
220
|
+
}
|
|
221
|
+
function renderRepoSummary(repo, options) {
|
|
222
|
+
const git = repoGitInfo(repo.root);
|
|
223
|
+
const manifests = REPO_MARKER_FILES.filter(m => fs.existsSync(path.join(repo.root, m)));
|
|
224
|
+
const counts = {};
|
|
225
|
+
if (!options.brief) {
|
|
226
|
+
for (const kind of DRILLABLE_KINDS) {
|
|
227
|
+
const items = collectRepoKind(repo, kind);
|
|
228
|
+
counts[kind] = { total: items.length, bySource: { [repo.label]: items.length } };
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
if (options.json) {
|
|
232
|
+
console.log(JSON.stringify({
|
|
233
|
+
repo: repo.label,
|
|
234
|
+
root: repo.root,
|
|
235
|
+
git,
|
|
236
|
+
manifests,
|
|
237
|
+
resources: options.brief ? null : Object.fromEntries(DRILLABLE_KINDS.map(kind => [kind, counts[kind].total])),
|
|
238
|
+
}, null, 2));
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
console.log('\n' + chalk.bold(repo.label) + ' ' + chalk.gray('[dotagents repo]') + '\n');
|
|
242
|
+
const rows = [['root', termLink(repo.root, repo.root)]];
|
|
243
|
+
if (git) {
|
|
244
|
+
const dirty = git.dirty > 0 ? ` ${chalk.gray('·')} ${chalk.yellow(`${git.dirty} dirty`)}` : '';
|
|
245
|
+
const url = git.url ? ` ${chalk.gray('·')} ${chalk.gray(git.url)}` : '';
|
|
246
|
+
rows.push(['git', `${git.branch}${dirty}${url}`]);
|
|
247
|
+
}
|
|
248
|
+
if (manifests.length > 0)
|
|
249
|
+
rows.push(['manifests', manifests.join(', ')]);
|
|
250
|
+
for (const [k, v] of rows)
|
|
251
|
+
console.log(` ${k.padEnd(10)} ${v}`);
|
|
252
|
+
if (!options.brief) {
|
|
253
|
+
console.log('\n' + chalk.bold('Resources'));
|
|
254
|
+
for (const kind of DRILLABLE_KINDS) {
|
|
255
|
+
console.log(` ${kind.padEnd(10)} ${String(counts[kind].total).padStart(4)}`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
console.log('');
|
|
259
|
+
console.log(chalk.gray(`Drill in: agents inspect ${repo.label} --skills <query>`));
|
|
260
|
+
console.log('');
|
|
261
|
+
}
|
|
262
|
+
function repoGitInfo(root) {
|
|
263
|
+
const git = (args) => {
|
|
264
|
+
try {
|
|
265
|
+
return execSync(`git -C ${JSON.stringify(root)} ${args}`, { stdio: ['ignore', 'pipe', 'ignore'] })
|
|
266
|
+
.toString().trim();
|
|
267
|
+
}
|
|
268
|
+
catch {
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
const branch = git('rev-parse --abbrev-ref HEAD');
|
|
273
|
+
if (branch === null)
|
|
274
|
+
return null;
|
|
275
|
+
const status = git('status --porcelain');
|
|
276
|
+
const dirty = status ? status.split('\n').filter(Boolean).length : 0;
|
|
277
|
+
return { branch, dirty, url: git('remote get-url origin') };
|
|
278
|
+
}
|
|
113
279
|
// ─── Summary mode ────────────────────────────────────────────────────────────
|
|
114
280
|
async function renderSummary(agent, version, versionHome, options) {
|
|
115
281
|
const meta = readMeta();
|
|
@@ -184,17 +350,19 @@ async function renderSummary(agent, version, versionHome, options) {
|
|
|
184
350
|
// ─── List mode ───────────────────────────────────────────────────────────────
|
|
185
351
|
async function renderList(agent, version, versionHome, kind, options) {
|
|
186
352
|
const items = collectKind(agent, versionHome, kind);
|
|
353
|
+
renderItemList(`${agent}@${version}`, { agent, version }, kind, items, options);
|
|
354
|
+
}
|
|
355
|
+
function renderItemList(header, jsonHead, kind, items, options) {
|
|
187
356
|
if (options.json) {
|
|
188
357
|
console.log(JSON.stringify({
|
|
189
|
-
|
|
190
|
-
version,
|
|
358
|
+
...jsonHead,
|
|
191
359
|
kind,
|
|
192
360
|
count: items.length,
|
|
193
361
|
items: items.map(i => ({ name: i.name, source: i.source, path: i.path, description: i.description })),
|
|
194
362
|
}, null, 2));
|
|
195
363
|
return;
|
|
196
364
|
}
|
|
197
|
-
console.log('\n' + chalk.bold(
|
|
365
|
+
console.log('\n' + chalk.bold(header) + ' ' + chalk.gray(`${kind} (${items.length})`) + '\n');
|
|
198
366
|
if (items.length === 0) {
|
|
199
367
|
console.log(chalk.gray(` (none installed)`));
|
|
200
368
|
console.log('');
|
|
@@ -212,11 +380,14 @@ async function renderList(agent, version, versionHome, kind, options) {
|
|
|
212
380
|
// ─── Detail mode (fuzzy) ─────────────────────────────────────────────────────
|
|
213
381
|
async function renderDetail(agent, version, versionHome, kind, query, options) {
|
|
214
382
|
const items = collectKind(agent, versionHome, kind);
|
|
383
|
+
renderItemDetail(`${agent}@${version}`, { agent, version }, kind, query, items, options);
|
|
384
|
+
}
|
|
385
|
+
function renderItemDetail(header, jsonHead, kind, query, items, options) {
|
|
215
386
|
const matches = findMatches(items, query);
|
|
216
387
|
if (matches.length === 0) {
|
|
217
388
|
const suggestions = suggestClosest(items, query, 3);
|
|
218
389
|
if (options.json) {
|
|
219
|
-
console.log(JSON.stringify({
|
|
390
|
+
console.log(JSON.stringify({ ...jsonHead, kind, query, match: null, suggestions: suggestions.map(s => s.name) }, null, 2));
|
|
220
391
|
}
|
|
221
392
|
else {
|
|
222
393
|
console.error(chalk.red(`No ${kind} matching '${query}'.`));
|
|
@@ -231,8 +402,7 @@ async function renderDetail(agent, version, versionHome, kind, query, options) {
|
|
|
231
402
|
if (options.json) {
|
|
232
403
|
const detail = buildDetail(best.item, kind);
|
|
233
404
|
console.log(JSON.stringify({
|
|
234
|
-
|
|
235
|
-
version,
|
|
405
|
+
...jsonHead,
|
|
236
406
|
kind,
|
|
237
407
|
query,
|
|
238
408
|
match: { ...detail, matchKind: best.matchKind },
|
|
@@ -240,7 +410,7 @@ async function renderDetail(agent, version, versionHome, kind, query, options) {
|
|
|
240
410
|
}, null, 2));
|
|
241
411
|
return;
|
|
242
412
|
}
|
|
243
|
-
console.log('\n' + chalk.bold(
|
|
413
|
+
console.log('\n' + chalk.bold(header) + ' ' + chalk.gray(`${kind} matching "${query}"`) + '\n');
|
|
244
414
|
const matchTag = best.matchKind === 'exact' ? 'exact' : best.matchKind === 'substring' ? 'substring' : `~${best.distance}`;
|
|
245
415
|
console.log(` ${chalk.green('✓')} ${termLink(chalk.bold.cyan(best.item.name), best.item.linkTarget)} ${chalk.gray(`[${matchTag}, ${best.item.source}]`)}`);
|
|
246
416
|
if (best.item.description) {
|
package/dist/commands/rules.js
CHANGED
|
@@ -4,7 +4,7 @@ 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, ALL_AGENT_IDS, resolveAgentName, formatAgentError, agentLabel, } from '../lib/agents.js';
|
|
7
|
+
import { AGENTS, agentConfigDirName, 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
10
|
import { listInstalledVersions, getGlobalDefault, resolveVersionAlias, syncResourcesToVersion, promptAgentVersionSelection, getVersionHomePath, } from '../lib/versions.js';
|
|
@@ -426,7 +426,7 @@ Examples:
|
|
|
426
426
|
return;
|
|
427
427
|
}
|
|
428
428
|
const home = getVersionHomePath(agentId, requestedVersion);
|
|
429
|
-
const filePath = path.join(home,
|
|
429
|
+
const filePath = path.join(home, agentConfigDirName(agentId), AGENTS[agentId].instructionsFile);
|
|
430
430
|
if (!fs.existsSync(filePath)) {
|
|
431
431
|
console.log(chalk.yellow(`No user rules found for ${agentLabel(agentId)}@${requestedVersion}`));
|
|
432
432
|
return;
|
|
@@ -486,7 +486,7 @@ Examples:
|
|
|
486
486
|
}
|
|
487
487
|
const home = getVersionHomePath(agentId, requestedVersion);
|
|
488
488
|
const agent = AGENTS[agentId];
|
|
489
|
-
const filePath = path.join(home,
|
|
489
|
+
const filePath = path.join(home, agentConfigDirName(agentId), agent.instructionsFile);
|
|
490
490
|
if (fs.existsSync(filePath)) {
|
|
491
491
|
fs.unlinkSync(filePath);
|
|
492
492
|
console.log(chalk.green(`Removed ${agent.instructionsFile} from ${agentLabel(agent.id)}@${requestedVersion}`));
|
package/dist/commands/secrets.js
CHANGED
|
@@ -1052,15 +1052,15 @@ Examples:
|
|
|
1052
1052
|
password += charClass[charIndex];
|
|
1053
1053
|
}
|
|
1054
1054
|
if (opts.copy) {
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1055
|
+
try {
|
|
1056
|
+
await copyToClipboard(password);
|
|
1057
|
+
console.log(chalk.green(`Password copied to clipboard (${length} chars)`));
|
|
1058
|
+
}
|
|
1059
|
+
catch (err) {
|
|
1060
|
+
console.error(chalk.red(`Clipboard copy failed: ${err.message}`));
|
|
1061
|
+
console.error(chalk.gray('Re-run without --copy to print the password instead.'));
|
|
1062
|
+
process.exitCode = 1;
|
|
1063
|
+
}
|
|
1064
1064
|
}
|
|
1065
1065
|
else {
|
|
1066
1066
|
console.log(password);
|
|
@@ -1069,3 +1069,40 @@ Examples:
|
|
|
1069
1069
|
registerSecretsSyncCommands(cmd);
|
|
1070
1070
|
registerSecretsMigrateAclCommand(cmd);
|
|
1071
1071
|
}
|
|
1072
|
+
/**
|
|
1073
|
+
* Copy text to the system clipboard, cross-platform.
|
|
1074
|
+
* macOS: `pbcopy`. Windows: `clip`. Linux: tries `wl-copy` (Wayland), then
|
|
1075
|
+
* `xclip`, then `xsel` (X11). Throws with an install hint if none are present.
|
|
1076
|
+
*/
|
|
1077
|
+
async function copyToClipboard(text) {
|
|
1078
|
+
const { spawn } = await import('child_process');
|
|
1079
|
+
const candidates = process.platform === 'darwin'
|
|
1080
|
+
? [['pbcopy', []]]
|
|
1081
|
+
: process.platform === 'win32'
|
|
1082
|
+
? [['clip', []]]
|
|
1083
|
+
: [
|
|
1084
|
+
['wl-copy', []],
|
|
1085
|
+
['xclip', ['-selection', 'clipboard']],
|
|
1086
|
+
['xsel', ['--clipboard', '--input']],
|
|
1087
|
+
];
|
|
1088
|
+
let lastErr = null;
|
|
1089
|
+
for (const [cmd, args] of candidates) {
|
|
1090
|
+
try {
|
|
1091
|
+
await new Promise((resolve, reject) => {
|
|
1092
|
+
const proc = spawn(cmd, args, { stdio: ['pipe', 'ignore', 'ignore'] });
|
|
1093
|
+
proc.on('error', reject);
|
|
1094
|
+
proc.on('close', (code) => (code === 0 ? resolve() : reject(new Error(`${cmd} exited ${code}`))));
|
|
1095
|
+
proc.stdin.write(text);
|
|
1096
|
+
proc.stdin.end();
|
|
1097
|
+
});
|
|
1098
|
+
return;
|
|
1099
|
+
}
|
|
1100
|
+
catch (err) {
|
|
1101
|
+
lastErr = err;
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
const hint = process.platform === 'linux'
|
|
1105
|
+
? ' Install one: wl-clipboard (Wayland) or xclip / xsel (X11).'
|
|
1106
|
+
: '';
|
|
1107
|
+
throw new Error(`no clipboard tool available (${lastErr?.message ?? 'none found'}).${hint}`);
|
|
1108
|
+
}
|