@guanmu/ccprofile 0.1.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.
Files changed (2) hide show
  1. package/dist/index.js +205 -0
  2. package/package.json +25 -0
package/dist/index.js ADDED
@@ -0,0 +1,205 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { execSync } from "node:child_process";
5
+ const PROFILES_DIR = path.join(process.env.HOME, ".ccx", "profiles");
6
+ function ensureProfilesDir() {
7
+ fs.mkdirSync(PROFILES_DIR, { recursive: true });
8
+ }
9
+ function profilePath(name) {
10
+ return path.join(PROFILES_DIR, `${name}.json`);
11
+ }
12
+ function readProfile(name) {
13
+ const file = profilePath(name);
14
+ if (!fs.existsSync(file)) {
15
+ console.error(`Profile "${name}" not found.`);
16
+ process.exit(1);
17
+ }
18
+ return JSON.parse(fs.readFileSync(file, "utf-8"));
19
+ }
20
+ function writeProfile(name, data) {
21
+ ensureProfilesDir();
22
+ fs.writeFileSync(profilePath(name), JSON.stringify(data, null, 2) + "\n");
23
+ }
24
+ function addProfile(name) {
25
+ const file = profilePath(name);
26
+ if (fs.existsSync(file)) {
27
+ console.error(`Profile "${name}" already exists.`);
28
+ process.exit(1);
29
+ }
30
+ writeProfile(name, { name, plugins: [] });
31
+ console.log(`Created profile "${name}".`);
32
+ }
33
+ function removeProfile(name) {
34
+ const file = profilePath(name);
35
+ if (!fs.existsSync(file)) {
36
+ console.error(`Profile "${name}" not found.`);
37
+ process.exit(1);
38
+ }
39
+ fs.unlinkSync(file);
40
+ console.log(`Removed profile "${name}".`);
41
+ }
42
+ function listProfiles() {
43
+ ensureProfilesDir();
44
+ const files = fs.readdirSync(PROFILES_DIR).filter((f) => f.endsWith(".json"));
45
+ if (files.length === 0) {
46
+ console.log("No profiles found.");
47
+ return;
48
+ }
49
+ for (const f of files) {
50
+ const data = JSON.parse(fs.readFileSync(path.join(PROFILES_DIR, f), "utf-8"));
51
+ console.log(` ${data.name} (${data.plugins.length} plugins)`);
52
+ }
53
+ }
54
+ function addPlugin(profileName, plugin) {
55
+ const data = readProfile(profileName);
56
+ if (data.plugins.includes(plugin)) {
57
+ console.error(`Plugin "${plugin}" already in profile "${profileName}".`);
58
+ process.exit(1);
59
+ }
60
+ data.plugins.push(plugin);
61
+ writeProfile(profileName, data);
62
+ console.log(`Added "${plugin}" to profile "${profileName}".`);
63
+ }
64
+ function removePlugin(profileName, plugin) {
65
+ const data = readProfile(profileName);
66
+ const idx = data.plugins.indexOf(plugin);
67
+ if (idx === -1) {
68
+ console.error(`Plugin "${plugin}" not found in profile "${profileName}".`);
69
+ process.exit(1);
70
+ }
71
+ data.plugins.splice(idx, 1);
72
+ writeProfile(profileName, data);
73
+ console.log(`Removed "${plugin}" from profile "${profileName}".`);
74
+ }
75
+ function listPlugins(profileName) {
76
+ const data = readProfile(profileName);
77
+ if (data.plugins.length === 0) {
78
+ console.log(`No plugins in profile "${profileName}".`);
79
+ return;
80
+ }
81
+ for (const p of data.plugins) {
82
+ console.log(` ${p}`);
83
+ }
84
+ }
85
+ function executeProfile(profileName) {
86
+ const data = readProfile(profileName);
87
+ if (data.plugins.length === 0) {
88
+ console.log(`No plugins to install in profile "${profileName}".`);
89
+ return;
90
+ }
91
+ console.log(`Installing ${data.plugins.length} plugins from profile "${profileName}"...`);
92
+ for (const plugin of data.plugins) {
93
+ console.log(` Installing ${plugin}...`);
94
+ try {
95
+ execSync(`claude plugin install ${plugin} --scope project`, {
96
+ stdio: "inherit",
97
+ });
98
+ }
99
+ catch {
100
+ console.error(` Failed to install ${plugin}.`);
101
+ }
102
+ }
103
+ console.log("Done.");
104
+ }
105
+ function searchPlugins(keyword) {
106
+ const marketplacesDir = path.join(process.env.HOME, ".claude", "plugins", "marketplaces");
107
+ if (!fs.existsSync(marketplacesDir)) {
108
+ console.log("No marketplaces found.");
109
+ return;
110
+ }
111
+ const lower = keyword.toLowerCase();
112
+ const dirs = fs.readdirSync(marketplacesDir);
113
+ let found = 0;
114
+ for (const dir of dirs) {
115
+ const file = path.join(marketplacesDir, dir, ".claude-plugin", "marketplace.json");
116
+ if (!fs.existsSync(file))
117
+ continue;
118
+ const data = JSON.parse(fs.readFileSync(file, "utf-8"));
119
+ for (const p of data.plugins || []) {
120
+ if (p.name?.toLowerCase().includes(lower) ||
121
+ p.description?.toLowerCase().includes(lower) ||
122
+ p.category?.toLowerCase().includes(lower)) {
123
+ found++;
124
+ console.log(` ${p.name} (${data.name}) ${p.description?.slice(0, 80) || ""}`);
125
+ }
126
+ }
127
+ }
128
+ if (found === 0) {
129
+ console.log(`No plugins matching "${keyword}".`);
130
+ }
131
+ }
132
+ function printHelp() {
133
+ console.log(`Usage:
134
+ ccx add <profile> Create a new profile
135
+ ccx remove <profile> Remove a profile
136
+ ccx list List all profiles
137
+ ccx search <keyword> Search plugins in all marketplaces
138
+ ccx <profile> Install all plugins from profile
139
+ ccx <profile> add <plugin|url> Add plugin to profile
140
+ ccx <profile> remove <plugin|url> Remove plugin from profile
141
+ ccx <profile> list List plugins in profile`);
142
+ }
143
+ const args = process.argv.slice(2);
144
+ if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
145
+ printHelp();
146
+ process.exit(0);
147
+ }
148
+ const cmd = args[0];
149
+ switch (cmd) {
150
+ case "add":
151
+ if (!args[1]) {
152
+ console.error("Usage: ccx add <profile>");
153
+ process.exit(1);
154
+ }
155
+ addProfile(args[1]);
156
+ break;
157
+ case "remove":
158
+ if (!args[1]) {
159
+ console.error("Usage: ccx remove <profile>");
160
+ process.exit(1);
161
+ }
162
+ removeProfile(args[1]);
163
+ break;
164
+ case "list":
165
+ listProfiles();
166
+ break;
167
+ case "search":
168
+ if (!args[1]) {
169
+ console.error("Usage: ccx search <keyword>");
170
+ process.exit(1);
171
+ }
172
+ searchPlugins(args[1]);
173
+ break;
174
+ default: {
175
+ // cmd is a profile name
176
+ const profileName = cmd;
177
+ const sub = args[1];
178
+ if (!sub) {
179
+ executeProfile(profileName);
180
+ }
181
+ else if (sub === "add") {
182
+ if (!args[2]) {
183
+ console.error("Usage: ccx <profile> add <plugin|url>");
184
+ process.exit(1);
185
+ }
186
+ addPlugin(profileName, args[2]);
187
+ }
188
+ else if (sub === "remove") {
189
+ if (!args[2]) {
190
+ console.error("Usage: ccx <profile> remove <plugin|url>");
191
+ process.exit(1);
192
+ }
193
+ removePlugin(profileName, args[2]);
194
+ }
195
+ else if (sub === "list") {
196
+ listPlugins(profileName);
197
+ }
198
+ else {
199
+ console.error(`Unknown command: ccx ${args.join(" ")}`);
200
+ printHelp();
201
+ process.exit(1);
202
+ }
203
+ break;
204
+ }
205
+ }
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@guanmu/ccprofile",
3
+ "version": "0.1.4",
4
+ "description": "Agent Profile Manager for Claude Code",
5
+ "type": "module",
6
+ "bin": {
7
+ "ccx": "./dist/index.js"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/ZeroZ-lab/ccpr"
12
+ },
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "scripts": {
17
+ "build": "tsc",
18
+ "dev": "bun run src/index.ts",
19
+ "prepublishOnly": "npm run build"
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^22.0.0",
23
+ "typescript": "^5.7.0"
24
+ }
25
+ }