@guanmu/ccprofile 0.1.6 → 0.1.9
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 +275 -80
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import fs from "node:fs";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
3
4
|
import path from "node:path";
|
|
4
|
-
import {
|
|
5
|
+
import { execFileSync } from "node:child_process";
|
|
5
6
|
import * as p from "@clack/prompts";
|
|
6
7
|
const PROFILES_DIR = path.join(process.env.HOME, ".ccx", "profiles");
|
|
7
8
|
const MARKETPLACES_DIR = path.join(process.env.HOME, ".claude", "plugins", "marketplaces");
|
|
@@ -11,24 +12,77 @@ function ensureProfilesDir() {
|
|
|
11
12
|
function profilePath(name) {
|
|
12
13
|
return path.join(PROFILES_DIR, `${name}.json`);
|
|
13
14
|
}
|
|
15
|
+
function isValidProfileName(name) {
|
|
16
|
+
return /^[A-Za-z0-9._-]+$/.test(name) && name !== "." && name !== "..";
|
|
17
|
+
}
|
|
18
|
+
function normalizeProfileName(name) {
|
|
19
|
+
if (!name)
|
|
20
|
+
return undefined;
|
|
21
|
+
const normalized = name.trim();
|
|
22
|
+
if (!isValidProfileName(normalized)) {
|
|
23
|
+
console.error("Invalid profile name. Use only letters, numbers, dots, underscores, and hyphens.");
|
|
24
|
+
process.exitCode = 1;
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
return normalized;
|
|
28
|
+
}
|
|
29
|
+
function normalizePluginName(plugin) {
|
|
30
|
+
if (!plugin)
|
|
31
|
+
return undefined;
|
|
32
|
+
const normalized = plugin.trim();
|
|
33
|
+
if (!normalized) {
|
|
34
|
+
console.error("Plugin name is required.");
|
|
35
|
+
process.exitCode = 1;
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
return normalized;
|
|
39
|
+
}
|
|
14
40
|
function readProfile(name) {
|
|
15
|
-
const
|
|
41
|
+
const normalized = normalizeProfileName(name);
|
|
42
|
+
if (!normalized)
|
|
43
|
+
process.exit(1);
|
|
44
|
+
const file = profilePath(normalized);
|
|
16
45
|
if (!fs.existsSync(file)) {
|
|
17
|
-
p.log.error(`Profile "${
|
|
46
|
+
p.log.error(`Profile "${normalized}" not found.`);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
let data;
|
|
50
|
+
try {
|
|
51
|
+
data = JSON.parse(fs.readFileSync(file, "utf-8"));
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
p.log.error(`Invalid profile file "${normalized}". Expected valid JSON.`);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
if (!data ||
|
|
58
|
+
typeof data !== "object" ||
|
|
59
|
+
!Array.isArray(data.plugins)) {
|
|
60
|
+
p.log.error(`Invalid profile file "${normalized}". Expected a plugins array.`);
|
|
18
61
|
process.exit(1);
|
|
19
62
|
}
|
|
20
|
-
|
|
63
|
+
const plugins = data.plugins.map((plugin) => typeof plugin === "string" ? plugin.trim() : undefined);
|
|
64
|
+
if (plugins.some((plugin) => !plugin)) {
|
|
65
|
+
p.log.error(`Invalid profile file "${normalized}". Plugin entries must be non-empty strings.`);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
name: normalized,
|
|
70
|
+
plugins: plugins,
|
|
71
|
+
};
|
|
21
72
|
}
|
|
22
73
|
function writeProfile(name, data) {
|
|
74
|
+
const normalized = normalizeProfileName(name);
|
|
75
|
+
if (!normalized)
|
|
76
|
+
return;
|
|
23
77
|
ensureProfilesDir();
|
|
24
|
-
fs.writeFileSync(profilePath(
|
|
78
|
+
fs.writeFileSync(profilePath(normalized), JSON.stringify({ ...data, name: normalized }, null, 2) + "\n");
|
|
25
79
|
}
|
|
26
80
|
function getProfileNames() {
|
|
27
81
|
ensureProfilesDir();
|
|
28
82
|
return fs
|
|
29
83
|
.readdirSync(PROFILES_DIR)
|
|
30
84
|
.filter((f) => f.endsWith(".json"))
|
|
31
|
-
.map((f) => f.
|
|
85
|
+
.map((f) => f.slice(0, -".json".length));
|
|
32
86
|
}
|
|
33
87
|
function getAllPlugins() {
|
|
34
88
|
if (!fs.existsSync(MARKETPLACES_DIR))
|
|
@@ -39,27 +93,69 @@ function getAllPlugins() {
|
|
|
39
93
|
const file = path.join(MARKETPLACES_DIR, dir, ".claude-plugin", "marketplace.json");
|
|
40
94
|
if (!fs.existsSync(file))
|
|
41
95
|
continue;
|
|
42
|
-
|
|
43
|
-
|
|
96
|
+
let data;
|
|
97
|
+
try {
|
|
98
|
+
data = JSON.parse(fs.readFileSync(file, "utf-8"));
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
p.log.warn(`Skipping invalid marketplace "${dir}".`);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (!data ||
|
|
105
|
+
typeof data !== "object" ||
|
|
106
|
+
!Array.isArray(data.plugins)) {
|
|
107
|
+
p.log.warn(`Skipping invalid marketplace "${dir}".`);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const marketplace = typeof data.name === "string"
|
|
111
|
+
? (data.name || dir)
|
|
112
|
+
: dir;
|
|
113
|
+
for (const pl of data.plugins) {
|
|
114
|
+
if (!pl || typeof pl !== "object")
|
|
115
|
+
continue;
|
|
116
|
+
const name = pl.name;
|
|
117
|
+
if (typeof name !== "string" || !name.trim())
|
|
118
|
+
continue;
|
|
119
|
+
const description = pl.description;
|
|
120
|
+
const category = pl.category;
|
|
44
121
|
plugins.push({
|
|
45
|
-
name:
|
|
46
|
-
description:
|
|
47
|
-
category:
|
|
48
|
-
marketplace
|
|
122
|
+
name: name.trim(),
|
|
123
|
+
description: typeof description === "string" ? description : "",
|
|
124
|
+
category: typeof category === "string" ? category : undefined,
|
|
125
|
+
marketplace,
|
|
49
126
|
});
|
|
50
127
|
}
|
|
51
128
|
}
|
|
52
129
|
return plugins;
|
|
53
130
|
}
|
|
131
|
+
function canPrompt() {
|
|
132
|
+
return Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
133
|
+
}
|
|
134
|
+
function printNonInteractiveHelp() {
|
|
135
|
+
console.error("Interactive mode requires a TTY. Run `ccx --help` for command usage.");
|
|
136
|
+
printHelp();
|
|
137
|
+
}
|
|
138
|
+
function missingArg(message, usage) {
|
|
139
|
+
console.error(message);
|
|
140
|
+
console.error(`Usage: ${usage}`);
|
|
141
|
+
process.exitCode = 1;
|
|
142
|
+
}
|
|
54
143
|
// ── Commands ──────────────────────────────────────────────
|
|
55
144
|
async function addProfile(name) {
|
|
56
145
|
if (!name) {
|
|
146
|
+
if (!canPrompt()) {
|
|
147
|
+
missingArg("Profile name is required.", "ccx create <name>");
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
57
150
|
name = (await p.text({
|
|
58
151
|
message: "Profile name:",
|
|
59
152
|
}));
|
|
60
153
|
if (p.isCancel(name))
|
|
61
154
|
return;
|
|
62
155
|
}
|
|
156
|
+
name = normalizeProfileName(name);
|
|
157
|
+
if (!name)
|
|
158
|
+
return;
|
|
63
159
|
const file = profilePath(name);
|
|
64
160
|
if (fs.existsSync(file)) {
|
|
65
161
|
p.log.error(`Profile "${name}" already exists.`);
|
|
@@ -69,12 +165,20 @@ async function addProfile(name) {
|
|
|
69
165
|
p.log.success(`Created profile "${name}".`);
|
|
70
166
|
}
|
|
71
167
|
async function removeProfile(name) {
|
|
168
|
+
if (!name && !canPrompt()) {
|
|
169
|
+
missingArg("Profile name is required.", "ccx delete <name>");
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
72
172
|
const names = getProfileNames();
|
|
73
173
|
if (names.length === 0) {
|
|
74
174
|
p.log.warn("No profiles found.");
|
|
75
175
|
return;
|
|
76
176
|
}
|
|
77
177
|
if (!name) {
|
|
178
|
+
if (!canPrompt()) {
|
|
179
|
+
missingArg("Profile name is required.", "ccx delete <name>");
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
78
182
|
name = (await p.select({
|
|
79
183
|
message: "Select profile to remove:",
|
|
80
184
|
options: names.map((n) => ({ value: n, label: n })),
|
|
@@ -82,6 +186,9 @@ async function removeProfile(name) {
|
|
|
82
186
|
if (p.isCancel(name))
|
|
83
187
|
return;
|
|
84
188
|
}
|
|
189
|
+
name = normalizeProfileName(name);
|
|
190
|
+
if (!name)
|
|
191
|
+
return;
|
|
85
192
|
const file = profilePath(name);
|
|
86
193
|
if (!fs.existsSync(file)) {
|
|
87
194
|
p.log.error(`Profile "${name}" not found.`);
|
|
@@ -91,22 +198,26 @@ async function removeProfile(name) {
|
|
|
91
198
|
p.log.success(`Removed profile "${name}".`);
|
|
92
199
|
}
|
|
93
200
|
async function listProfiles() {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
.readdirSync(PROFILES_DIR)
|
|
97
|
-
.filter((f) => f.endsWith(".json"));
|
|
98
|
-
if (files.length === 0) {
|
|
201
|
+
const names = getProfileNames();
|
|
202
|
+
if (names.length === 0) {
|
|
99
203
|
p.log.warn("No profiles found.");
|
|
100
204
|
return;
|
|
101
205
|
}
|
|
102
|
-
for (const
|
|
103
|
-
const data =
|
|
206
|
+
for (const name of names) {
|
|
207
|
+
const data = readProfile(name);
|
|
104
208
|
p.log.success(`${data.name} (${data.plugins.length} plugins)`);
|
|
105
209
|
}
|
|
106
210
|
}
|
|
107
211
|
async function addPlugin(profileName, plugin) {
|
|
212
|
+
const normalizedProfileName = normalizeProfileName(profileName);
|
|
213
|
+
if (!normalizedProfileName)
|
|
214
|
+
return;
|
|
108
215
|
const data = readProfile(profileName);
|
|
109
216
|
if (!plugin) {
|
|
217
|
+
if (!canPrompt()) {
|
|
218
|
+
missingArg("Plugin name is required.", "ccx add <profile> <plugin>");
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
110
221
|
const allPlugins = getAllPlugins();
|
|
111
222
|
const available = allPlugins.filter((pl) => !data.plugins.includes(pl.name));
|
|
112
223
|
if (available.length === 0) {
|
|
@@ -137,21 +248,31 @@ async function addPlugin(profileName, plugin) {
|
|
|
137
248
|
plugin = selected;
|
|
138
249
|
}
|
|
139
250
|
}
|
|
251
|
+
plugin = normalizePluginName(plugin);
|
|
252
|
+
if (!plugin)
|
|
253
|
+
return;
|
|
140
254
|
if (data.plugins.includes(plugin)) {
|
|
141
255
|
p.log.warn(`Plugin "${plugin}" already in profile.`);
|
|
142
256
|
return;
|
|
143
257
|
}
|
|
144
258
|
data.plugins.push(plugin);
|
|
145
|
-
writeProfile(
|
|
146
|
-
p.log.success(`Added "${plugin}" to profile "${
|
|
259
|
+
writeProfile(normalizedProfileName, data);
|
|
260
|
+
p.log.success(`Added "${plugin}" to profile "${normalizedProfileName}".`);
|
|
147
261
|
}
|
|
148
262
|
async function removePlugin(profileName, plugin) {
|
|
263
|
+
const normalizedProfileName = normalizeProfileName(profileName);
|
|
264
|
+
if (!normalizedProfileName)
|
|
265
|
+
return;
|
|
149
266
|
const data = readProfile(profileName);
|
|
150
267
|
if (data.plugins.length === 0) {
|
|
151
268
|
p.log.warn("No plugins in this profile.");
|
|
152
269
|
return;
|
|
153
270
|
}
|
|
154
271
|
if (!plugin) {
|
|
272
|
+
if (!canPrompt()) {
|
|
273
|
+
missingArg("Plugin name is required.", "ccx remove <profile> <plugin>");
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
155
276
|
plugin = (await p.select({
|
|
156
277
|
message: `Remove plugin from "${profileName}":`,
|
|
157
278
|
options: data.plugins.map((pl) => ({
|
|
@@ -162,19 +283,25 @@ async function removePlugin(profileName, plugin) {
|
|
|
162
283
|
if (p.isCancel(plugin))
|
|
163
284
|
return;
|
|
164
285
|
}
|
|
286
|
+
plugin = normalizePluginName(plugin);
|
|
287
|
+
if (!plugin)
|
|
288
|
+
return;
|
|
165
289
|
const idx = data.plugins.indexOf(plugin);
|
|
166
290
|
if (idx === -1) {
|
|
167
291
|
p.log.error(`Plugin "${plugin}" not found.`);
|
|
168
292
|
return;
|
|
169
293
|
}
|
|
170
294
|
data.plugins.splice(idx, 1);
|
|
171
|
-
writeProfile(
|
|
172
|
-
p.log.success(`Removed "${plugin}" from profile "${
|
|
295
|
+
writeProfile(normalizedProfileName, data);
|
|
296
|
+
p.log.success(`Removed "${plugin}" from profile "${normalizedProfileName}".`);
|
|
173
297
|
}
|
|
174
298
|
async function listPlugins(profileName) {
|
|
175
|
-
const
|
|
299
|
+
const normalizedProfileName = normalizeProfileName(profileName);
|
|
300
|
+
if (!normalizedProfileName)
|
|
301
|
+
return;
|
|
302
|
+
const data = readProfile(normalizedProfileName);
|
|
176
303
|
if (data.plugins.length === 0) {
|
|
177
|
-
p.log.warn(`No plugins in profile "${
|
|
304
|
+
p.log.warn(`No plugins in profile "${normalizedProfileName}".`);
|
|
178
305
|
return;
|
|
179
306
|
}
|
|
180
307
|
for (const pl of data.plugins) {
|
|
@@ -183,6 +310,10 @@ async function listPlugins(profileName) {
|
|
|
183
310
|
}
|
|
184
311
|
async function searchPlugins(keyword) {
|
|
185
312
|
if (!keyword) {
|
|
313
|
+
if (!canPrompt()) {
|
|
314
|
+
missingArg("Search keyword is required.", "ccx search <keyword>");
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
186
317
|
keyword = (await p.text({
|
|
187
318
|
message: "Search plugins:",
|
|
188
319
|
}));
|
|
@@ -217,19 +348,22 @@ async function searchPlugins(keyword) {
|
|
|
217
348
|
}
|
|
218
349
|
}
|
|
219
350
|
async function executeProfile(profileName) {
|
|
220
|
-
const
|
|
351
|
+
const normalizedProfileName = normalizeProfileName(profileName);
|
|
352
|
+
if (!normalizedProfileName)
|
|
353
|
+
return;
|
|
354
|
+
const data = readProfile(normalizedProfileName);
|
|
221
355
|
if (data.plugins.length === 0) {
|
|
222
|
-
p.log.warn(`No plugins to install in profile "${
|
|
356
|
+
p.log.warn(`No plugins to install in profile "${normalizedProfileName}".`);
|
|
223
357
|
return;
|
|
224
358
|
}
|
|
225
359
|
const s = p.spinner();
|
|
226
|
-
s.start(`Installing ${data.plugins.length} plugin(s) from "${
|
|
360
|
+
s.start(`Installing ${data.plugins.length} plugin(s) from "${normalizedProfileName}"...`);
|
|
227
361
|
let installed = 0;
|
|
228
362
|
let failed = 0;
|
|
229
363
|
for (const plugin of data.plugins) {
|
|
230
364
|
s.message(`Installing ${plugin}...`);
|
|
231
365
|
try {
|
|
232
|
-
|
|
366
|
+
execFileSync("claude", ["plugin", "install", plugin, "--scope", "project"], {
|
|
233
367
|
stdio: "pipe",
|
|
234
368
|
});
|
|
235
369
|
installed++;
|
|
@@ -243,6 +377,11 @@ async function executeProfile(profileName) {
|
|
|
243
377
|
p.log.success("Done.");
|
|
244
378
|
}
|
|
245
379
|
async function interactiveMode() {
|
|
380
|
+
if (!canPrompt()) {
|
|
381
|
+
printNonInteractiveHelp();
|
|
382
|
+
process.exitCode = 1;
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
246
385
|
p.intro("ccx — Agent Profile Manager");
|
|
247
386
|
const action = await p.select({
|
|
248
387
|
message: "What do you want to do?",
|
|
@@ -339,60 +478,116 @@ function printHelp() {
|
|
|
339
478
|
console.log(`ccx — Agent Profile Manager for Claude Code
|
|
340
479
|
|
|
341
480
|
Usage:
|
|
342
|
-
ccx
|
|
343
|
-
ccx
|
|
344
|
-
ccx
|
|
345
|
-
ccx
|
|
346
|
-
ccx
|
|
481
|
+
ccx Interactive mode (TTY only)
|
|
482
|
+
ccx ui Interactive mode (TTY only)
|
|
483
|
+
ccx install <profile> Install all plugins from profile
|
|
484
|
+
ccx create <name> Create a new profile
|
|
485
|
+
ccx delete <name> Remove a profile
|
|
486
|
+
ccx profiles List all profiles
|
|
487
|
+
ccx add <profile> <plugin> Add plugin to profile
|
|
488
|
+
ccx remove <profile> <plugin> Remove plugin from profile
|
|
489
|
+
ccx list <profile> List plugins in profile
|
|
347
490
|
ccx search <keyword> Search plugins in marketplaces
|
|
348
|
-
ccx <profile>
|
|
349
|
-
ccx <profile>
|
|
350
|
-
ccx <profile>
|
|
491
|
+
ccx <profile> Install all plugins from profile
|
|
492
|
+
ccx <profile> add [plugin] Add plugin to profile (legacy)
|
|
493
|
+
ccx <profile> remove [plugin] Remove plugin from profile (legacy)
|
|
494
|
+
ccx <profile> list List plugins in profile (legacy)
|
|
495
|
+
ccx add <name> Create a new profile (legacy)
|
|
496
|
+
ccx remove <name> Remove a profile (legacy)
|
|
497
|
+
ccx list List all profiles (legacy)
|
|
498
|
+
ccx -v, --version Show version`);
|
|
351
499
|
}
|
|
352
500
|
// ── Main ──────────────────────────────────────────────────
|
|
353
|
-
|
|
354
|
-
if (args.length === 0) {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
}
|
|
358
|
-
if (args[0] === "--help" || args[0] === "-h") {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
executeProfile(
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
501
|
+
async function main(args) {
|
|
502
|
+
if (args.length === 0) {
|
|
503
|
+
await interactiveMode();
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
if (args[0] === "--help" || args[0] === "-h") {
|
|
507
|
+
printHelp();
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
if (args[0] === "--version" || args[0] === "-v" || args[0] === "-V") {
|
|
511
|
+
const require = createRequire(import.meta.url);
|
|
512
|
+
const pkg = require("../package.json");
|
|
513
|
+
console.log(`ccx v${pkg.version}`);
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
const cmd = args[0];
|
|
517
|
+
switch (cmd) {
|
|
518
|
+
case "ui":
|
|
519
|
+
case "tui":
|
|
520
|
+
case "interactive":
|
|
521
|
+
await interactiveMode();
|
|
522
|
+
break;
|
|
523
|
+
case "install":
|
|
524
|
+
if (!args[1]) {
|
|
525
|
+
missingArg("Profile name is required.", "ccx install <profile>");
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
await executeProfile(args[1]);
|
|
529
|
+
break;
|
|
530
|
+
case "create":
|
|
531
|
+
await addProfile(args[1]);
|
|
532
|
+
break;
|
|
533
|
+
case "delete":
|
|
534
|
+
await removeProfile(args[1]);
|
|
535
|
+
break;
|
|
536
|
+
case "profiles":
|
|
537
|
+
await listProfiles();
|
|
538
|
+
break;
|
|
539
|
+
case "add":
|
|
540
|
+
if (args[2]) {
|
|
541
|
+
await addPlugin(args[1], args[2]);
|
|
542
|
+
}
|
|
543
|
+
else {
|
|
544
|
+
await addProfile(args[1]);
|
|
545
|
+
}
|
|
546
|
+
break;
|
|
547
|
+
case "remove":
|
|
548
|
+
if (args[2]) {
|
|
549
|
+
await removePlugin(args[1], args[2]);
|
|
550
|
+
}
|
|
551
|
+
else {
|
|
552
|
+
await removeProfile(args[1]);
|
|
553
|
+
}
|
|
554
|
+
break;
|
|
555
|
+
case "list":
|
|
556
|
+
if (args[1]) {
|
|
557
|
+
await listPlugins(args[1]);
|
|
558
|
+
}
|
|
559
|
+
else {
|
|
560
|
+
await listProfiles();
|
|
561
|
+
}
|
|
562
|
+
break;
|
|
563
|
+
case "search":
|
|
564
|
+
await searchPlugins(args[1]);
|
|
565
|
+
break;
|
|
566
|
+
default: {
|
|
567
|
+
const profileName = cmd;
|
|
568
|
+
const sub = args[1];
|
|
569
|
+
if (!sub) {
|
|
570
|
+
await executeProfile(profileName);
|
|
571
|
+
}
|
|
572
|
+
else if (sub === "add") {
|
|
573
|
+
await addPlugin(profileName, args[2]);
|
|
574
|
+
}
|
|
575
|
+
else if (sub === "remove") {
|
|
576
|
+
await removePlugin(profileName, args[2]);
|
|
577
|
+
}
|
|
578
|
+
else if (sub === "list") {
|
|
579
|
+
await listPlugins(profileName);
|
|
580
|
+
}
|
|
581
|
+
else {
|
|
582
|
+
console.error(`Unknown command: ccx ${args.join(" ")}`);
|
|
583
|
+
printHelp();
|
|
584
|
+
process.exitCode = 1;
|
|
585
|
+
}
|
|
586
|
+
break;
|
|
395
587
|
}
|
|
396
|
-
break;
|
|
397
588
|
}
|
|
398
589
|
}
|
|
590
|
+
main(process.argv.slice(2)).catch((err) => {
|
|
591
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
592
|
+
process.exitCode = 1;
|
|
593
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@guanmu/ccprofile",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Agent Profile Manager for Claude Code",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"scripts": {
|
|
17
17
|
"build": "tsc",
|
|
18
18
|
"dev": "bun run src/index.ts",
|
|
19
|
+
"test": "pnpm run build && node test/cli.test.mjs",
|
|
19
20
|
"prepublishOnly": "npm run build"
|
|
20
21
|
},
|
|
21
22
|
"dependencies": {
|