opencode-agents 1.0.16 → 1.1.0
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/dist/index.js +21777 -20690
- package/dist/index.js.map +4 -4
- package/package.json +7 -5
- package/src/commands/add.ts +73 -43
- package/src/index.ts +9 -2
- package/src/utils/ui.ts +135 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-agents",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "CLI for managing AI coding agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -22,18 +22,20 @@
|
|
|
22
22
|
"author": "",
|
|
23
23
|
"license": "MIT",
|
|
24
24
|
"dependencies": {
|
|
25
|
+
"@clack/prompts": "^1.0.1",
|
|
26
|
+
"chalk": "^5.3.0",
|
|
25
27
|
"commander": "^12.1.0",
|
|
26
28
|
"degit": "^2.8.4",
|
|
27
29
|
"gray-matter": "^4.0.3",
|
|
30
|
+
"inquirer": "^9.2.12",
|
|
28
31
|
"ora": "^5.4.1",
|
|
29
|
-
"
|
|
30
|
-
"yaml": "^2.3.4"
|
|
31
|
-
"inquirer": "^9.2.12"
|
|
32
|
+
"picocolors": "^1.1.1",
|
|
33
|
+
"yaml": "^2.3.4"
|
|
32
34
|
},
|
|
33
35
|
"devDependencies": {
|
|
34
36
|
"@types/node": "^20.10.0",
|
|
35
|
-
"typescript": "^5.3.0",
|
|
36
37
|
"esbuild": "^0.24.0",
|
|
38
|
+
"typescript": "^5.3.0",
|
|
37
39
|
"vitest": "^1.0.0"
|
|
38
40
|
}
|
|
39
41
|
}
|
package/src/commands/add.ts
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import * as p from '@clack/prompts';
|
|
2
|
+
import pc from 'picocolors';
|
|
3
3
|
import { tmpdir } from 'os';
|
|
4
4
|
import { existsSync, rmSync } from 'fs';
|
|
5
5
|
import { installAgent } from '../core/installer.js';
|
|
6
6
|
import { discoverFromDirectory } from '../core/discover.js';
|
|
7
|
-
import { detectPlatforms } from '../utils/filesystem.js';
|
|
8
|
-
import { logger } from '../utils/logger.js';
|
|
9
7
|
import type { AgentPlatform, InstallOptions, AgentFile } from '../types/index.js';
|
|
10
8
|
import { basename, join } from 'path';
|
|
11
9
|
import { mkdtempSync } from 'fs';
|
|
12
10
|
import degit from 'degit';
|
|
11
|
+
import { showLogo, renderTree, renderSkillCard, success, error } from '../utils/ui.js';
|
|
13
12
|
|
|
14
13
|
interface AddCommandOptions {
|
|
15
14
|
global: boolean | undefined;
|
|
@@ -20,38 +19,41 @@ interface AddCommandOptions {
|
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
async function promptInstallLocation(): Promise<boolean> {
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
22
|
+
const location = await p.select({
|
|
23
|
+
message: 'Where would you like to install this agent?',
|
|
24
|
+
options: [
|
|
25
|
+
{ value: false, label: 'Current project', hint: './.opencode/agents/' },
|
|
26
|
+
{ value: true, label: 'Global', hint: '~/.config/opencode/agents/' },
|
|
27
|
+
],
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
if (p.isCancel(location)) {
|
|
31
|
+
p.cancel('Operation cancelled');
|
|
32
|
+
process.exit(0);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return location;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
async function promptSelectAgents(agents: AgentFile[]): Promise<AgentFile[]> {
|
|
39
|
-
const
|
|
40
|
-
name: agent.agent.name || basename(agent.path, '.md'),
|
|
39
|
+
const options = agents.map(agent => ({
|
|
41
40
|
value: agent,
|
|
42
|
-
|
|
41
|
+
label: agent.agent.name || basename(agent.path, '.md'),
|
|
42
|
+
hint: agent.agent.description?.slice(0, 50) + '...',
|
|
43
43
|
}));
|
|
44
44
|
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
45
|
+
const selected = await p.multiselect({
|
|
46
|
+
message: 'Select agents to install (space to select, enter to confirm):',
|
|
47
|
+
options,
|
|
48
|
+
required: true,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
if (p.isCancel(selected)) {
|
|
52
|
+
p.cancel('Operation cancelled');
|
|
53
|
+
process.exit(0);
|
|
54
|
+
}
|
|
53
55
|
|
|
54
|
-
return
|
|
56
|
+
return selected as AgentFile[];
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
async function fetchSource(source: string): Promise<string> {
|
|
@@ -79,39 +81,49 @@ async function fetchSource(source: string): Promise<string> {
|
|
|
79
81
|
}
|
|
80
82
|
|
|
81
83
|
export async function addCommand(source: string, options: AddCommandOptions): Promise<void> {
|
|
84
|
+
console.clear();
|
|
85
|
+
showLogo();
|
|
86
|
+
|
|
82
87
|
let isGlobal = options.global;
|
|
83
88
|
|
|
84
89
|
if (isGlobal === undefined) {
|
|
85
90
|
isGlobal = await promptInstallLocation();
|
|
86
91
|
}
|
|
87
92
|
|
|
88
|
-
|
|
89
|
-
|
|
93
|
+
// 步骤 1: 获取源码
|
|
94
|
+
p.log.step(pc.cyan(`Source: ${pc.dim(`https://github.com/${source}.git`)}`));
|
|
95
|
+
|
|
96
|
+
const s = p.spinner();
|
|
97
|
+
s.start('Cloning repository...');
|
|
98
|
+
|
|
90
99
|
let tempDir: string;
|
|
91
100
|
try {
|
|
92
101
|
tempDir = await fetchSource(source);
|
|
93
|
-
|
|
102
|
+
s.stop('Repository cloned');
|
|
94
103
|
} catch (err) {
|
|
95
|
-
|
|
96
|
-
|
|
104
|
+
s.stop('Failed to clone repository');
|
|
105
|
+
error(`Failed to fetch source: ${err instanceof Error ? err.message : String(err)}`);
|
|
97
106
|
process.exit(1);
|
|
98
107
|
}
|
|
99
108
|
|
|
100
|
-
|
|
109
|
+
// 步骤 2: 发现 agents
|
|
110
|
+
s.start('Discovering agents...');
|
|
101
111
|
let agents: AgentFile[];
|
|
102
112
|
try {
|
|
103
113
|
agents = await discoverFromDirectory(tempDir);
|
|
104
|
-
|
|
114
|
+
s.stop(`Found ${pc.green(String(agents.length))} agent(s)`);
|
|
105
115
|
} catch (err) {
|
|
106
|
-
|
|
116
|
+
s.stop('Failed to discover agents');
|
|
117
|
+
error(`Failed to discover agents: ${err instanceof Error ? err.message : String(err)}`);
|
|
107
118
|
process.exit(1);
|
|
108
119
|
}
|
|
109
120
|
|
|
110
121
|
if (agents.length === 0) {
|
|
111
|
-
|
|
122
|
+
error('No agents found in the source');
|
|
112
123
|
process.exit(1);
|
|
113
124
|
}
|
|
114
125
|
|
|
126
|
+
// 步骤 3: 选择 agents
|
|
115
127
|
let selectedAgents: AgentFile[];
|
|
116
128
|
if (options.yes) {
|
|
117
129
|
selectedAgents = agents;
|
|
@@ -120,11 +132,22 @@ export async function addCommand(source: string, options: AddCommandOptions): Pr
|
|
|
120
132
|
}
|
|
121
133
|
|
|
122
134
|
if (selectedAgents.length === 0) {
|
|
123
|
-
|
|
135
|
+
p.cancel('No agents selected, aborting');
|
|
124
136
|
process.exit(0);
|
|
125
137
|
}
|
|
126
138
|
|
|
127
|
-
|
|
139
|
+
// 显示选中的 agents
|
|
140
|
+
console.log();
|
|
141
|
+
for (const agent of selectedAgents) {
|
|
142
|
+
renderSkillCard(
|
|
143
|
+
agent.agent.name || basename(agent.path, '.md'),
|
|
144
|
+
agent.agent.description || 'No description available',
|
|
145
|
+
0
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// 步骤 4: 安装
|
|
150
|
+
s.start(`Installing ${selectedAgents.length} agent(s)...`);
|
|
128
151
|
|
|
129
152
|
try {
|
|
130
153
|
let platforms: AgentPlatform[];
|
|
@@ -148,10 +171,17 @@ export async function addCommand(source: string, options: AddCommandOptions): Pr
|
|
|
148
171
|
|
|
149
172
|
await installAgent(installOptions);
|
|
150
173
|
|
|
151
|
-
|
|
174
|
+
s.stop(pc.green(`Successfully installed ${selectedAgents.length} agent(s)`));
|
|
175
|
+
|
|
176
|
+
console.log();
|
|
177
|
+
success('Installation complete!');
|
|
178
|
+
console.log();
|
|
179
|
+
console.log(pc.dim(' Try:'));
|
|
180
|
+
console.log(pc.dim(` npx opencode-agents list`));
|
|
181
|
+
console.log();
|
|
152
182
|
} catch (err) {
|
|
153
|
-
|
|
154
|
-
|
|
183
|
+
s.stop('Installation failed');
|
|
184
|
+
error(`Failed to install agent: ${err instanceof Error ? err.message : String(err)}`);
|
|
155
185
|
process.exit(1);
|
|
156
186
|
} finally {
|
|
157
187
|
if (existsSync(tempDir) && tempDir.startsWith(tmpdir())) {
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { Command } from 'commander';
|
|
4
|
-
import
|
|
4
|
+
import pc from 'picocolors';
|
|
5
5
|
import { addCommand } from './commands/add.js';
|
|
6
6
|
import { listCommand } from './commands/list.js';
|
|
7
7
|
import { removeCommand } from './commands/remove.js';
|
|
@@ -9,13 +9,14 @@ import { initCommand } from './commands/init.js';
|
|
|
9
9
|
import { findCommand } from './commands/find.js';
|
|
10
10
|
import { checkCommand } from './commands/check.js';
|
|
11
11
|
import { updateCommand } from './commands/update.js';
|
|
12
|
+
import { showBanner } from './utils/ui.js';
|
|
12
13
|
|
|
13
14
|
const program = new Command();
|
|
14
15
|
|
|
15
16
|
program
|
|
16
17
|
.name('agents')
|
|
17
18
|
.description('CLI for managing AI coding agents')
|
|
18
|
-
.version('1.
|
|
19
|
+
.version('1.1.0');
|
|
19
20
|
|
|
20
21
|
program
|
|
21
22
|
.command('add <source>')
|
|
@@ -62,4 +63,10 @@ program
|
|
|
62
63
|
.description('Update all installed agents to the latest version')
|
|
63
64
|
.action(updateCommand);
|
|
64
65
|
|
|
66
|
+
// 显示 banner 如果没有参数
|
|
67
|
+
if (process.argv.length === 2) {
|
|
68
|
+
showBanner();
|
|
69
|
+
program.help();
|
|
70
|
+
}
|
|
71
|
+
|
|
65
72
|
program.parse();
|
package/src/utils/ui.ts
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import pc from 'picocolors';
|
|
2
|
+
|
|
3
|
+
// ANSI 重置
|
|
4
|
+
export const RESET = '\x1b[0m';
|
|
5
|
+
export const BOLD = '\x1b[1m';
|
|
6
|
+
|
|
7
|
+
// 图标
|
|
8
|
+
export const S_STEP_ACTIVE = pc.green('◆');
|
|
9
|
+
export const S_STEP_SUBMIT = pc.green('◇');
|
|
10
|
+
export const S_STEP_CANCEL = pc.red('■');
|
|
11
|
+
export const S_RADIO_ACTIVE = pc.green('●');
|
|
12
|
+
export const S_RADIO_INACTIVE = pc.dim('○');
|
|
13
|
+
export const S_CHECKBOX_ACTIVE = pc.green('☑');
|
|
14
|
+
export const S_CHECKBOX_INACTIVE = pc.dim('☐');
|
|
15
|
+
export const S_CHECKBOX_LOCKED = pc.green('✓');
|
|
16
|
+
export const S_BULLET = pc.blue('●');
|
|
17
|
+
export const S_BAR = pc.dim('│');
|
|
18
|
+
export const S_BAR_H = pc.dim('─');
|
|
19
|
+
export const S_CORNER_TOP = pc.dim('┌');
|
|
20
|
+
export const S_CORNER_BOTTOM = pc.dim('└');
|
|
21
|
+
export const S_BRANCH = pc.dim('├');
|
|
22
|
+
export const S_BRANCH_END = pc.dim('└');
|
|
23
|
+
|
|
24
|
+
// Logo
|
|
25
|
+
export const LOGO_LINES = [
|
|
26
|
+
' █████╗ ██████╗ ███████╗███╗ ██╗████████╗███████╗',
|
|
27
|
+
'██╔══██╗██╔════╝ ██╔════╝████╗ ██║╚══██╔══╝██╔════╝',
|
|
28
|
+
'███████║██║ ███╗█████╗ ██╔██╗ ██║ ██║ █████╗ ',
|
|
29
|
+
'██╔══██║██║ ██║██╔══╝ ██║╚██╗██║ ██║ ██╔══╝ ',
|
|
30
|
+
'██║ ██║╚██████╔╝███████╗██║ ╚████║ ██║ ███████╗',
|
|
31
|
+
'╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚══════╝',
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
export const LOGO_COLORS = [
|
|
35
|
+
pc.cyan,
|
|
36
|
+
pc.cyan,
|
|
37
|
+
pc.blue,
|
|
38
|
+
pc.blue,
|
|
39
|
+
pc.magenta,
|
|
40
|
+
pc.magenta,
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
export function showLogo(): void {
|
|
44
|
+
console.log();
|
|
45
|
+
LOGO_LINES.forEach((line, i) => {
|
|
46
|
+
console.log(LOGO_COLORS[i](line));
|
|
47
|
+
});
|
|
48
|
+
console.log();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function showBanner(): void {
|
|
52
|
+
showLogo();
|
|
53
|
+
console.log(pc.dim(' AI Agent Management CLI'));
|
|
54
|
+
console.log();
|
|
55
|
+
console.log(` ${pc.dim('$')} ${pc.cyan('npx opencode-agents add')} ${pc.dim('<repo>')} ${pc.dim('Install agents from a repository')}`);
|
|
56
|
+
console.log(` ${pc.dim('$')} ${pc.cyan('npx opencode-agents list')} ${pc.dim('List installed agents')}`);
|
|
57
|
+
console.log(` ${pc.dim('$')} ${pc.cyan('npx opencode-agents remove')} ${pc.dim('Remove installed agents')}`);
|
|
58
|
+
console.log(` ${pc.dim('$')} ${pc.cyan('npx opencode-agents find')} ${pc.dim('Search for agents')}`);
|
|
59
|
+
console.log();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 进度树状结构
|
|
63
|
+
export interface TreeStep {
|
|
64
|
+
label: string;
|
|
65
|
+
status: 'pending' | 'active' | 'done' | 'error';
|
|
66
|
+
value?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function renderTree(steps: TreeStep[]): void {
|
|
70
|
+
console.log();
|
|
71
|
+
steps.forEach((step, index) => {
|
|
72
|
+
const isLast = index === steps.length - 1;
|
|
73
|
+
const prefix = isLast ? S_BRANCH_END : S_BRANCH;
|
|
74
|
+
|
|
75
|
+
let icon: string;
|
|
76
|
+
let color: (s: string) => string;
|
|
77
|
+
|
|
78
|
+
switch (step.status) {
|
|
79
|
+
case 'done':
|
|
80
|
+
icon = pc.green('◆');
|
|
81
|
+
color = pc.green;
|
|
82
|
+
break;
|
|
83
|
+
case 'active':
|
|
84
|
+
icon = pc.cyan('◆');
|
|
85
|
+
color = pc.cyan;
|
|
86
|
+
break;
|
|
87
|
+
case 'error':
|
|
88
|
+
icon = pc.red('■');
|
|
89
|
+
color = pc.red;
|
|
90
|
+
break;
|
|
91
|
+
default:
|
|
92
|
+
icon = pc.dim('○');
|
|
93
|
+
color = pc.dim;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
console.log(` ${prefix} ${icon} ${color(step.label)}`);
|
|
97
|
+
|
|
98
|
+
if (step.value && step.status === 'done') {
|
|
99
|
+
console.log(` ${isLast ? ' ' : `${S_BAR} `} ${pc.dim(step.value)}`);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
console.log();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// 技能卡片
|
|
106
|
+
export function renderSkillCard(name: string, description: string, index: number): void {
|
|
107
|
+
console.log();
|
|
108
|
+
console.log(` ${S_BAR}`);
|
|
109
|
+
console.log(` ${S_BRANCH} ${pc.cyan('Agent:')} ${pc.bold(name)}`);
|
|
110
|
+
console.log(` ${S_BAR}`);
|
|
111
|
+
console.log(` ${S_BRANCH_END} ${pc.dim(description)}`);
|
|
112
|
+
console.log();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// 分隔线
|
|
116
|
+
export function divider(): void {
|
|
117
|
+
console.log(pc.dim(' ' + '─'.repeat(50)));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 成功/错误信息
|
|
121
|
+
export function success(message: string): void {
|
|
122
|
+
console.log(` ${pc.green('✓')} ${message}`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function error(message: string): void {
|
|
126
|
+
console.log(` ${pc.red('✗')} ${message}`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function warning(message: string): void {
|
|
130
|
+
console.log(` ${pc.yellow('⚠')} ${message}`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function info(message: string): void {
|
|
134
|
+
console.log(` ${pc.blue('ℹ')} ${message}`);
|
|
135
|
+
}
|