@guanmu/ccprofile 0.1.7 → 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 +273 -86
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import path from "node:path";
|
|
5
|
-
import {
|
|
5
|
+
import { execFileSync } from "node:child_process";
|
|
6
6
|
import * as p from "@clack/prompts";
|
|
7
7
|
const PROFILES_DIR = path.join(process.env.HOME, ".ccx", "profiles");
|
|
8
8
|
const MARKETPLACES_DIR = path.join(process.env.HOME, ".claude", "plugins", "marketplaces");
|
|
@@ -12,24 +12,77 @@ function ensureProfilesDir() {
|
|
|
12
12
|
function profilePath(name) {
|
|
13
13
|
return path.join(PROFILES_DIR, `${name}.json`);
|
|
14
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
|
+
}
|
|
15
40
|
function readProfile(name) {
|
|
16
|
-
const
|
|
41
|
+
const normalized = normalizeProfileName(name);
|
|
42
|
+
if (!normalized)
|
|
43
|
+
process.exit(1);
|
|
44
|
+
const file = profilePath(normalized);
|
|
17
45
|
if (!fs.existsSync(file)) {
|
|
18
|
-
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.`);
|
|
19
61
|
process.exit(1);
|
|
20
62
|
}
|
|
21
|
-
|
|
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
|
+
};
|
|
22
72
|
}
|
|
23
73
|
function writeProfile(name, data) {
|
|
74
|
+
const normalized = normalizeProfileName(name);
|
|
75
|
+
if (!normalized)
|
|
76
|
+
return;
|
|
24
77
|
ensureProfilesDir();
|
|
25
|
-
fs.writeFileSync(profilePath(
|
|
78
|
+
fs.writeFileSync(profilePath(normalized), JSON.stringify({ ...data, name: normalized }, null, 2) + "\n");
|
|
26
79
|
}
|
|
27
80
|
function getProfileNames() {
|
|
28
81
|
ensureProfilesDir();
|
|
29
82
|
return fs
|
|
30
83
|
.readdirSync(PROFILES_DIR)
|
|
31
84
|
.filter((f) => f.endsWith(".json"))
|
|
32
|
-
.map((f) => f.
|
|
85
|
+
.map((f) => f.slice(0, -".json".length));
|
|
33
86
|
}
|
|
34
87
|
function getAllPlugins() {
|
|
35
88
|
if (!fs.existsSync(MARKETPLACES_DIR))
|
|
@@ -40,27 +93,69 @@ function getAllPlugins() {
|
|
|
40
93
|
const file = path.join(MARKETPLACES_DIR, dir, ".claude-plugin", "marketplace.json");
|
|
41
94
|
if (!fs.existsSync(file))
|
|
42
95
|
continue;
|
|
43
|
-
|
|
44
|
-
|
|
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;
|
|
45
121
|
plugins.push({
|
|
46
|
-
name:
|
|
47
|
-
description:
|
|
48
|
-
category:
|
|
49
|
-
marketplace
|
|
122
|
+
name: name.trim(),
|
|
123
|
+
description: typeof description === "string" ? description : "",
|
|
124
|
+
category: typeof category === "string" ? category : undefined,
|
|
125
|
+
marketplace,
|
|
50
126
|
});
|
|
51
127
|
}
|
|
52
128
|
}
|
|
53
129
|
return plugins;
|
|
54
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
|
+
}
|
|
55
143
|
// ── Commands ──────────────────────────────────────────────
|
|
56
144
|
async function addProfile(name) {
|
|
57
145
|
if (!name) {
|
|
146
|
+
if (!canPrompt()) {
|
|
147
|
+
missingArg("Profile name is required.", "ccx create <name>");
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
58
150
|
name = (await p.text({
|
|
59
151
|
message: "Profile name:",
|
|
60
152
|
}));
|
|
61
153
|
if (p.isCancel(name))
|
|
62
154
|
return;
|
|
63
155
|
}
|
|
156
|
+
name = normalizeProfileName(name);
|
|
157
|
+
if (!name)
|
|
158
|
+
return;
|
|
64
159
|
const file = profilePath(name);
|
|
65
160
|
if (fs.existsSync(file)) {
|
|
66
161
|
p.log.error(`Profile "${name}" already exists.`);
|
|
@@ -70,12 +165,20 @@ async function addProfile(name) {
|
|
|
70
165
|
p.log.success(`Created profile "${name}".`);
|
|
71
166
|
}
|
|
72
167
|
async function removeProfile(name) {
|
|
168
|
+
if (!name && !canPrompt()) {
|
|
169
|
+
missingArg("Profile name is required.", "ccx delete <name>");
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
73
172
|
const names = getProfileNames();
|
|
74
173
|
if (names.length === 0) {
|
|
75
174
|
p.log.warn("No profiles found.");
|
|
76
175
|
return;
|
|
77
176
|
}
|
|
78
177
|
if (!name) {
|
|
178
|
+
if (!canPrompt()) {
|
|
179
|
+
missingArg("Profile name is required.", "ccx delete <name>");
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
79
182
|
name = (await p.select({
|
|
80
183
|
message: "Select profile to remove:",
|
|
81
184
|
options: names.map((n) => ({ value: n, label: n })),
|
|
@@ -83,6 +186,9 @@ async function removeProfile(name) {
|
|
|
83
186
|
if (p.isCancel(name))
|
|
84
187
|
return;
|
|
85
188
|
}
|
|
189
|
+
name = normalizeProfileName(name);
|
|
190
|
+
if (!name)
|
|
191
|
+
return;
|
|
86
192
|
const file = profilePath(name);
|
|
87
193
|
if (!fs.existsSync(file)) {
|
|
88
194
|
p.log.error(`Profile "${name}" not found.`);
|
|
@@ -92,22 +198,26 @@ async function removeProfile(name) {
|
|
|
92
198
|
p.log.success(`Removed profile "${name}".`);
|
|
93
199
|
}
|
|
94
200
|
async function listProfiles() {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
.readdirSync(PROFILES_DIR)
|
|
98
|
-
.filter((f) => f.endsWith(".json"));
|
|
99
|
-
if (files.length === 0) {
|
|
201
|
+
const names = getProfileNames();
|
|
202
|
+
if (names.length === 0) {
|
|
100
203
|
p.log.warn("No profiles found.");
|
|
101
204
|
return;
|
|
102
205
|
}
|
|
103
|
-
for (const
|
|
104
|
-
const data =
|
|
206
|
+
for (const name of names) {
|
|
207
|
+
const data = readProfile(name);
|
|
105
208
|
p.log.success(`${data.name} (${data.plugins.length} plugins)`);
|
|
106
209
|
}
|
|
107
210
|
}
|
|
108
211
|
async function addPlugin(profileName, plugin) {
|
|
212
|
+
const normalizedProfileName = normalizeProfileName(profileName);
|
|
213
|
+
if (!normalizedProfileName)
|
|
214
|
+
return;
|
|
109
215
|
const data = readProfile(profileName);
|
|
110
216
|
if (!plugin) {
|
|
217
|
+
if (!canPrompt()) {
|
|
218
|
+
missingArg("Plugin name is required.", "ccx add <profile> <plugin>");
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
111
221
|
const allPlugins = getAllPlugins();
|
|
112
222
|
const available = allPlugins.filter((pl) => !data.plugins.includes(pl.name));
|
|
113
223
|
if (available.length === 0) {
|
|
@@ -138,21 +248,31 @@ async function addPlugin(profileName, plugin) {
|
|
|
138
248
|
plugin = selected;
|
|
139
249
|
}
|
|
140
250
|
}
|
|
251
|
+
plugin = normalizePluginName(plugin);
|
|
252
|
+
if (!plugin)
|
|
253
|
+
return;
|
|
141
254
|
if (data.plugins.includes(plugin)) {
|
|
142
255
|
p.log.warn(`Plugin "${plugin}" already in profile.`);
|
|
143
256
|
return;
|
|
144
257
|
}
|
|
145
258
|
data.plugins.push(plugin);
|
|
146
|
-
writeProfile(
|
|
147
|
-
p.log.success(`Added "${plugin}" to profile "${
|
|
259
|
+
writeProfile(normalizedProfileName, data);
|
|
260
|
+
p.log.success(`Added "${plugin}" to profile "${normalizedProfileName}".`);
|
|
148
261
|
}
|
|
149
262
|
async function removePlugin(profileName, plugin) {
|
|
263
|
+
const normalizedProfileName = normalizeProfileName(profileName);
|
|
264
|
+
if (!normalizedProfileName)
|
|
265
|
+
return;
|
|
150
266
|
const data = readProfile(profileName);
|
|
151
267
|
if (data.plugins.length === 0) {
|
|
152
268
|
p.log.warn("No plugins in this profile.");
|
|
153
269
|
return;
|
|
154
270
|
}
|
|
155
271
|
if (!plugin) {
|
|
272
|
+
if (!canPrompt()) {
|
|
273
|
+
missingArg("Plugin name is required.", "ccx remove <profile> <plugin>");
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
156
276
|
plugin = (await p.select({
|
|
157
277
|
message: `Remove plugin from "${profileName}":`,
|
|
158
278
|
options: data.plugins.map((pl) => ({
|
|
@@ -163,19 +283,25 @@ async function removePlugin(profileName, plugin) {
|
|
|
163
283
|
if (p.isCancel(plugin))
|
|
164
284
|
return;
|
|
165
285
|
}
|
|
286
|
+
plugin = normalizePluginName(plugin);
|
|
287
|
+
if (!plugin)
|
|
288
|
+
return;
|
|
166
289
|
const idx = data.plugins.indexOf(plugin);
|
|
167
290
|
if (idx === -1) {
|
|
168
291
|
p.log.error(`Plugin "${plugin}" not found.`);
|
|
169
292
|
return;
|
|
170
293
|
}
|
|
171
294
|
data.plugins.splice(idx, 1);
|
|
172
|
-
writeProfile(
|
|
173
|
-
p.log.success(`Removed "${plugin}" from profile "${
|
|
295
|
+
writeProfile(normalizedProfileName, data);
|
|
296
|
+
p.log.success(`Removed "${plugin}" from profile "${normalizedProfileName}".`);
|
|
174
297
|
}
|
|
175
298
|
async function listPlugins(profileName) {
|
|
176
|
-
const
|
|
299
|
+
const normalizedProfileName = normalizeProfileName(profileName);
|
|
300
|
+
if (!normalizedProfileName)
|
|
301
|
+
return;
|
|
302
|
+
const data = readProfile(normalizedProfileName);
|
|
177
303
|
if (data.plugins.length === 0) {
|
|
178
|
-
p.log.warn(`No plugins in profile "${
|
|
304
|
+
p.log.warn(`No plugins in profile "${normalizedProfileName}".`);
|
|
179
305
|
return;
|
|
180
306
|
}
|
|
181
307
|
for (const pl of data.plugins) {
|
|
@@ -184,6 +310,10 @@ async function listPlugins(profileName) {
|
|
|
184
310
|
}
|
|
185
311
|
async function searchPlugins(keyword) {
|
|
186
312
|
if (!keyword) {
|
|
313
|
+
if (!canPrompt()) {
|
|
314
|
+
missingArg("Search keyword is required.", "ccx search <keyword>");
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
187
317
|
keyword = (await p.text({
|
|
188
318
|
message: "Search plugins:",
|
|
189
319
|
}));
|
|
@@ -218,19 +348,22 @@ async function searchPlugins(keyword) {
|
|
|
218
348
|
}
|
|
219
349
|
}
|
|
220
350
|
async function executeProfile(profileName) {
|
|
221
|
-
const
|
|
351
|
+
const normalizedProfileName = normalizeProfileName(profileName);
|
|
352
|
+
if (!normalizedProfileName)
|
|
353
|
+
return;
|
|
354
|
+
const data = readProfile(normalizedProfileName);
|
|
222
355
|
if (data.plugins.length === 0) {
|
|
223
|
-
p.log.warn(`No plugins to install in profile "${
|
|
356
|
+
p.log.warn(`No plugins to install in profile "${normalizedProfileName}".`);
|
|
224
357
|
return;
|
|
225
358
|
}
|
|
226
359
|
const s = p.spinner();
|
|
227
|
-
s.start(`Installing ${data.plugins.length} plugin(s) from "${
|
|
360
|
+
s.start(`Installing ${data.plugins.length} plugin(s) from "${normalizedProfileName}"...`);
|
|
228
361
|
let installed = 0;
|
|
229
362
|
let failed = 0;
|
|
230
363
|
for (const plugin of data.plugins) {
|
|
231
364
|
s.message(`Installing ${plugin}...`);
|
|
232
365
|
try {
|
|
233
|
-
|
|
366
|
+
execFileSync("claude", ["plugin", "install", plugin, "--scope", "project"], {
|
|
234
367
|
stdio: "pipe",
|
|
235
368
|
});
|
|
236
369
|
installed++;
|
|
@@ -244,6 +377,11 @@ async function executeProfile(profileName) {
|
|
|
244
377
|
p.log.success("Done.");
|
|
245
378
|
}
|
|
246
379
|
async function interactiveMode() {
|
|
380
|
+
if (!canPrompt()) {
|
|
381
|
+
printNonInteractiveHelp();
|
|
382
|
+
process.exitCode = 1;
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
247
385
|
p.intro("ccx — Agent Profile Manager");
|
|
248
386
|
const action = await p.select({
|
|
249
387
|
message: "What do you want to do?",
|
|
@@ -340,67 +478,116 @@ function printHelp() {
|
|
|
340
478
|
console.log(`ccx — Agent Profile Manager for Claude Code
|
|
341
479
|
|
|
342
480
|
Usage:
|
|
343
|
-
ccx
|
|
344
|
-
ccx
|
|
345
|
-
ccx
|
|
346
|
-
ccx
|
|
347
|
-
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
|
|
348
490
|
ccx search <keyword> Search plugins in marketplaces
|
|
349
|
-
ccx <profile>
|
|
350
|
-
ccx <profile>
|
|
351
|
-
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)
|
|
352
498
|
ccx -v, --version Show version`);
|
|
353
499
|
}
|
|
354
500
|
// ── Main ──────────────────────────────────────────────────
|
|
355
|
-
|
|
356
|
-
if (args.length === 0) {
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
}
|
|
360
|
-
if (args[0] === "--help" || args[0] === "-h") {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
}
|
|
364
|
-
if (args[0] === "--version" || args[0] === "-v" || args[0] === "-V") {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
}
|
|
370
|
-
const cmd = args[0];
|
|
371
|
-
switch (cmd) {
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
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;
|
|
403
587
|
}
|
|
404
|
-
break;
|
|
405
588
|
}
|
|
406
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": {
|