claude-prime 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/bin/cli.js +63 -0
- package/package.json +33 -0
- package/src/init.js +280 -0
- package/src/install.js +180 -0
- package/src/utils.js +70 -0
package/bin/cli.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { version } = require("../package.json");
|
|
4
|
+
|
|
5
|
+
const HELP = `
|
|
6
|
+
claude-prime v${version}
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
claude-prime install [options] Install Claude Prime into the current directory
|
|
10
|
+
claude-prime init Configure/reconfigure Claude Prime in current directory
|
|
11
|
+
claude-prime --help Show this help message
|
|
12
|
+
claude-prime --version Show CLI version
|
|
13
|
+
|
|
14
|
+
Options (install):
|
|
15
|
+
--version <version> Install a specific version (e.g., 1.0.0)
|
|
16
|
+
|
|
17
|
+
Environment variables:
|
|
18
|
+
CLAUDE_PRIME_VERSION Install a specific version (alternative to --version flag)
|
|
19
|
+
|
|
20
|
+
Examples:
|
|
21
|
+
npx claude-prime install
|
|
22
|
+
npx claude-prime install --version 1.0.0
|
|
23
|
+
npx claude-prime init
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
const args = process.argv.slice(2);
|
|
27
|
+
const command = args[0];
|
|
28
|
+
|
|
29
|
+
if (args.includes("--help") || args.includes("-h") || !command) {
|
|
30
|
+
console.log(HELP.trim());
|
|
31
|
+
process.exit(0);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (command === "--version" || command === "-V") {
|
|
35
|
+
console.log(version);
|
|
36
|
+
process.exit(0);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const [major] = process.versions.node.split(".").map(Number);
|
|
40
|
+
if (major < 18) {
|
|
41
|
+
console.error(
|
|
42
|
+
`Error: claude-prime requires Node.js >= 18 (current: ${process.version})`
|
|
43
|
+
);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (command === "install") {
|
|
48
|
+
require("../src/install")(args.slice(1)).catch((err) => {
|
|
49
|
+
const { log } = require("@clack/prompts");
|
|
50
|
+
log.error(err.message);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
});
|
|
53
|
+
} else if (command === "init") {
|
|
54
|
+
require("../src/init")().catch((err) => {
|
|
55
|
+
const { log } = require("@clack/prompts");
|
|
56
|
+
log.error(err.message);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
});
|
|
59
|
+
} else {
|
|
60
|
+
console.error(`Unknown command: ${command}\n`);
|
|
61
|
+
console.log(HELP.trim());
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "claude-prime",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Supercharge Claude Code with skills, commands, and workflows for agent-skill-driven development",
|
|
5
|
+
"bin": {
|
|
6
|
+
"claude-prime": "./bin/cli.js"
|
|
7
|
+
},
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=18"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"bin/",
|
|
13
|
+
"src/"
|
|
14
|
+
],
|
|
15
|
+
"keywords": [
|
|
16
|
+
"claude",
|
|
17
|
+
"claude-code",
|
|
18
|
+
"ai",
|
|
19
|
+
"agent",
|
|
20
|
+
"skills",
|
|
21
|
+
"workflows"
|
|
22
|
+
],
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "https://github.com/avibebuilder/claude-prime.git"
|
|
27
|
+
},
|
|
28
|
+
"author": "Peter Truong <peter.tr99@gmail.com>",
|
|
29
|
+
"homepage": "https://github.com/avibebuilder/claude-prime",
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@clack/prompts": "^1.1.0"
|
|
32
|
+
}
|
|
33
|
+
}
|
package/src/init.js
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
const fs = require("node:fs");
|
|
2
|
+
const path = require("node:path");
|
|
3
|
+
const { execSync } = require("node:child_process");
|
|
4
|
+
const {
|
|
5
|
+
intro,
|
|
6
|
+
outro,
|
|
7
|
+
log,
|
|
8
|
+
text,
|
|
9
|
+
select,
|
|
10
|
+
confirm,
|
|
11
|
+
spinner,
|
|
12
|
+
cancel,
|
|
13
|
+
isCancel,
|
|
14
|
+
} = require("@clack/prompts");
|
|
15
|
+
const { banner } = require("./utils");
|
|
16
|
+
|
|
17
|
+
const GITIGNORE_ENTRIES = [
|
|
18
|
+
"repomix-output.xml",
|
|
19
|
+
"plans/**/*",
|
|
20
|
+
"!plans/templates/*",
|
|
21
|
+
"screenshots/*",
|
|
22
|
+
"docs/screenshots/*",
|
|
23
|
+
"docs/assets/*",
|
|
24
|
+
"docs/research/*",
|
|
25
|
+
"docs/session-reports/*",
|
|
26
|
+
"claude-prime-*.zip",
|
|
27
|
+
".mcp.json",
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
const CLAUDE_GITIGNORE_ENTRIES = [
|
|
31
|
+
"touch-skill",
|
|
32
|
+
"optimus-prime",
|
|
33
|
+
"convo-analysis",
|
|
34
|
+
"prime-sync",
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
function handleCancel(value) {
|
|
38
|
+
if (isCancel(value)) {
|
|
39
|
+
cancel("Initialization cancelled.");
|
|
40
|
+
process.exit(0);
|
|
41
|
+
}
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function ensureGitignoreEntries(filePath, entries, label) {
|
|
46
|
+
const exists = fs.existsSync(filePath);
|
|
47
|
+
let content = exists ? fs.readFileSync(filePath, "utf-8") : "";
|
|
48
|
+
const lines = content.split("\n").map((l) => l.trim());
|
|
49
|
+
let added = 0;
|
|
50
|
+
|
|
51
|
+
if (exists && content.length > 0 && !content.endsWith("\n")) {
|
|
52
|
+
content += "\n";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
for (const entry of entries) {
|
|
56
|
+
if (!lines.includes(entry)) {
|
|
57
|
+
content += entry + "\n";
|
|
58
|
+
added++;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (added > 0) {
|
|
63
|
+
fs.writeFileSync(filePath, content);
|
|
64
|
+
log.success(`Updated ${label} (${added} entries added)`);
|
|
65
|
+
} else {
|
|
66
|
+
log.success(`${label} already configured`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function stageGitignore(installDir) {
|
|
71
|
+
log.step("Stage: .gitignore configuration");
|
|
72
|
+
|
|
73
|
+
const gitignorePath = path.join(installDir, ".gitignore");
|
|
74
|
+
ensureGitignoreEntries(gitignorePath, GITIGNORE_ENTRIES, ".gitignore");
|
|
75
|
+
|
|
76
|
+
const claudeGitignorePath = path.join(installDir, ".claude", ".gitignore");
|
|
77
|
+
if (fs.existsSync(path.join(installDir, ".claude"))) {
|
|
78
|
+
ensureGitignoreEntries(
|
|
79
|
+
claudeGitignorePath,
|
|
80
|
+
CLAUDE_GITIGNORE_ENTRIES,
|
|
81
|
+
".claude/.gitignore"
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function stageSkillEnvFiles(installDir) {
|
|
87
|
+
log.step("Stage: Skill environment files");
|
|
88
|
+
|
|
89
|
+
const mediaProcessorDir = path.join(
|
|
90
|
+
installDir,
|
|
91
|
+
".claude",
|
|
92
|
+
"skills",
|
|
93
|
+
"media-processor"
|
|
94
|
+
);
|
|
95
|
+
const envExample = path.join(mediaProcessorDir, ".env.example");
|
|
96
|
+
const envFile = path.join(mediaProcessorDir, ".env");
|
|
97
|
+
|
|
98
|
+
if (!fs.existsSync(envExample)) {
|
|
99
|
+
log.warn("media-processor skill not found (skipping)");
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (fs.existsSync(envFile)) {
|
|
104
|
+
const action = handleCancel(
|
|
105
|
+
await select({
|
|
106
|
+
message: "media-processor .env already exists",
|
|
107
|
+
options: [
|
|
108
|
+
{ value: "backup", label: "Backup and reconfigure", hint: "moves to .env.bk" },
|
|
109
|
+
{ value: "override", label: "Override" },
|
|
110
|
+
{ value: "skip", label: "Skip" },
|
|
111
|
+
],
|
|
112
|
+
})
|
|
113
|
+
);
|
|
114
|
+
if (action === "skip") {
|
|
115
|
+
log.warn("Skipping media-processor configuration");
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (action === "backup") {
|
|
119
|
+
fs.copyFileSync(envFile, envFile + ".bk");
|
|
120
|
+
log.success("Backed up .env → .env.bk");
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
log.info(
|
|
125
|
+
"Google Gemini API Configuration\n" +
|
|
126
|
+
" ◌ Used by media-processor skill for audio/video/image processing.\n" +
|
|
127
|
+
" ◌ Stored at: .claude/skills/media-processor/.env\n" +
|
|
128
|
+
" ◌ Get your API key: https://aistudio.google.com/apikey"
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
const apiKey = handleCancel(
|
|
132
|
+
await text({
|
|
133
|
+
message: "Enter Gemini API key (press Enter to skip)",
|
|
134
|
+
placeholder: "AIza...",
|
|
135
|
+
validate: () => undefined,
|
|
136
|
+
})
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
if (apiKey && apiKey.trim()) {
|
|
140
|
+
const template = fs.readFileSync(envExample, "utf-8");
|
|
141
|
+
fs.writeFileSync(
|
|
142
|
+
envFile,
|
|
143
|
+
template.replace("your_api_key_here", apiKey.trim())
|
|
144
|
+
);
|
|
145
|
+
log.success(`Created .env for media-processor\n Stored at: ${envFile}`);
|
|
146
|
+
} else {
|
|
147
|
+
log.warn("Skipped - Gemini API key is required for this skill");
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async function stageMcpServers(installDir) {
|
|
152
|
+
log.step("Stage: MCP servers");
|
|
153
|
+
|
|
154
|
+
const mcpExample = path.join(installDir, ".claude", ".mcp.json.example");
|
|
155
|
+
const mcpFile = path.join(installDir, ".mcp.json");
|
|
156
|
+
|
|
157
|
+
if (!fs.existsSync(mcpExample)) {
|
|
158
|
+
log.warn(".mcp.json.example not found (skipping)");
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (fs.existsSync(mcpFile)) {
|
|
163
|
+
const action = handleCancel(
|
|
164
|
+
await select({
|
|
165
|
+
message: ".mcp.json already exists",
|
|
166
|
+
options: [
|
|
167
|
+
{ value: "backup", label: "Backup and reconfigure", hint: "moves to .mcp.json.bk" },
|
|
168
|
+
{ value: "override", label: "Override" },
|
|
169
|
+
{ value: "skip", label: "Skip" },
|
|
170
|
+
],
|
|
171
|
+
})
|
|
172
|
+
);
|
|
173
|
+
if (action === "skip") {
|
|
174
|
+
log.warn("Skipping .mcp.json configuration");
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (action === "backup") {
|
|
178
|
+
fs.copyFileSync(mcpFile, mcpFile + ".bk");
|
|
179
|
+
log.success("Backed up .mcp.json → .mcp.json.bk");
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
log.info(
|
|
184
|
+
"Figma MCP Server Configuration (optional)\n" +
|
|
185
|
+
" ◌ Only needed if you use Figma for design-to-code workflows.\n" +
|
|
186
|
+
" ◌ Stored at: .mcp.json\n" +
|
|
187
|
+
" ◌ Get your API key: https://help.figma.com/hc/en-us/articles/8085703771159-Manage-personal-access-tokens"
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
const figmaKey = handleCancel(
|
|
191
|
+
await text({
|
|
192
|
+
message: "Enter Figma API key (press Enter to skip)",
|
|
193
|
+
placeholder: "figd_...",
|
|
194
|
+
validate: () => undefined,
|
|
195
|
+
})
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
const template = fs.readFileSync(mcpExample, "utf-8");
|
|
199
|
+
|
|
200
|
+
if (figmaKey && figmaKey.trim()) {
|
|
201
|
+
fs.writeFileSync(mcpFile, template.replace("YOUR-KEY", figmaKey.trim()));
|
|
202
|
+
log.success(`Created .mcp.json with Figma API key\n Stored at: ${mcpFile}`);
|
|
203
|
+
} else {
|
|
204
|
+
fs.writeFileSync(mcpFile, template);
|
|
205
|
+
log.warn("Created .mcp.json with placeholder (configure later)");
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
async function stageBrowserAutomation() {
|
|
210
|
+
log.step("Stage: Browser automation");
|
|
211
|
+
|
|
212
|
+
let installed = false;
|
|
213
|
+
try {
|
|
214
|
+
execSync("command -v agent-browser", { stdio: "pipe" });
|
|
215
|
+
installed = true;
|
|
216
|
+
} catch {}
|
|
217
|
+
|
|
218
|
+
if (installed) {
|
|
219
|
+
log.success("agent-browser is already installed globally");
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const shouldInstall = handleCancel(
|
|
224
|
+
await confirm({
|
|
225
|
+
message: "Install agent-browser globally?",
|
|
226
|
+
initialValue: false,
|
|
227
|
+
})
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
if (!shouldInstall) {
|
|
231
|
+
log.warn("Skipped (use npx agent-browser instead)");
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const s = spinner();
|
|
236
|
+
s.start("Installing agent-browser...");
|
|
237
|
+
try {
|
|
238
|
+
execSync("npm install -g agent-browser", { stdio: "pipe" });
|
|
239
|
+
s.stop("agent-browser installed");
|
|
240
|
+
|
|
241
|
+
s.start("Downloading Chromium...");
|
|
242
|
+
execSync("agent-browser install", { stdio: "pipe" });
|
|
243
|
+
s.stop("Chromium downloaded");
|
|
244
|
+
|
|
245
|
+
log.success("agent-browser installed globally");
|
|
246
|
+
} catch (err) {
|
|
247
|
+
s.stop("Installation failed");
|
|
248
|
+
log.error(`Failed to install agent-browser: ${err.message}`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
async function init(installDir, opts = {}) {
|
|
253
|
+
const dir = installDir || process.cwd();
|
|
254
|
+
const standalone = opts.standalone !== false;
|
|
255
|
+
|
|
256
|
+
if (standalone) {
|
|
257
|
+
banner();
|
|
258
|
+
intro("Claude Prime - Project Initialization");
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
await stageGitignore(dir);
|
|
262
|
+
await stageSkillEnvFiles(dir);
|
|
263
|
+
await stageMcpServers(dir);
|
|
264
|
+
await stageBrowserAutomation();
|
|
265
|
+
|
|
266
|
+
if (standalone) {
|
|
267
|
+
const BLUE = "\x1b[0;34m";
|
|
268
|
+
const BOLD = "\x1b[1m";
|
|
269
|
+
const NC = "\x1b[0m";
|
|
270
|
+
|
|
271
|
+
outro("Initialization complete!");
|
|
272
|
+
|
|
273
|
+
console.log(` ${BOLD}Next steps:${NC}`);
|
|
274
|
+
console.log(` 1. Review generated configuration files`);
|
|
275
|
+
console.log(` 2. Start using Claude Code with: ${BLUE}claude${NC}`);
|
|
276
|
+
console.log("");
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
module.exports = init;
|
package/src/install.js
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
const fs = require("node:fs");
|
|
2
|
+
const path = require("node:path");
|
|
3
|
+
const os = require("node:os");
|
|
4
|
+
const { execSync } = require("node:child_process");
|
|
5
|
+
const {
|
|
6
|
+
intro,
|
|
7
|
+
outro,
|
|
8
|
+
spinner,
|
|
9
|
+
select,
|
|
10
|
+
log,
|
|
11
|
+
cancel,
|
|
12
|
+
isCancel,
|
|
13
|
+
} = require("@clack/prompts");
|
|
14
|
+
const { banner, resolveVersion } = require("./utils");
|
|
15
|
+
|
|
16
|
+
async function checkExistingInstallation(installDir) {
|
|
17
|
+
const hasClaudeDir = fs.existsSync(path.join(installDir, ".claude"));
|
|
18
|
+
const hasClaudeMd = fs.existsSync(path.join(installDir, "CLAUDE.md"));
|
|
19
|
+
|
|
20
|
+
if (!hasClaudeDir && !hasClaudeMd) return;
|
|
21
|
+
|
|
22
|
+
const existing = [];
|
|
23
|
+
if (hasClaudeDir) existing.push(".claude/");
|
|
24
|
+
if (hasClaudeMd) existing.push("CLAUDE.md");
|
|
25
|
+
log.warn(`Existing configuration detected: ${existing.join(", ")}`);
|
|
26
|
+
|
|
27
|
+
if (!process.stdin.isTTY) {
|
|
28
|
+
log.error(
|
|
29
|
+
"Existing files found. Run interactively to choose backup/override, or remove them manually."
|
|
30
|
+
);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const action = await select({
|
|
35
|
+
message: "What would you like to do?",
|
|
36
|
+
options: [
|
|
37
|
+
{
|
|
38
|
+
value: "backup",
|
|
39
|
+
label: "Backup existing files and continue",
|
|
40
|
+
hint: "moves to .claude.bk / CLAUDE.md.bk",
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
value: "override",
|
|
44
|
+
label: "Override",
|
|
45
|
+
hint: "removes existing files",
|
|
46
|
+
},
|
|
47
|
+
{ value: "cancel", label: "Cancel" },
|
|
48
|
+
],
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
if (isCancel(action) || action === "cancel") {
|
|
52
|
+
cancel("Installation cancelled.");
|
|
53
|
+
process.exit(0);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (action === "backup") {
|
|
57
|
+
if (hasClaudeDir) {
|
|
58
|
+
fs.rmSync(path.join(installDir, ".claude.bk"), {
|
|
59
|
+
recursive: true,
|
|
60
|
+
force: true,
|
|
61
|
+
});
|
|
62
|
+
fs.renameSync(
|
|
63
|
+
path.join(installDir, ".claude"),
|
|
64
|
+
path.join(installDir, ".claude.bk")
|
|
65
|
+
);
|
|
66
|
+
log.success("Backed up .claude/ → .claude.bk/");
|
|
67
|
+
}
|
|
68
|
+
if (hasClaudeMd) {
|
|
69
|
+
fs.rmSync(path.join(installDir, "CLAUDE.md.bk"), { force: true });
|
|
70
|
+
fs.renameSync(
|
|
71
|
+
path.join(installDir, "CLAUDE.md"),
|
|
72
|
+
path.join(installDir, "CLAUDE.md.bk")
|
|
73
|
+
);
|
|
74
|
+
log.success("Backed up CLAUDE.md → CLAUDE.md.bk");
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (action === "override") {
|
|
79
|
+
if (hasClaudeDir) {
|
|
80
|
+
fs.rmSync(path.join(installDir, ".claude"), {
|
|
81
|
+
recursive: true,
|
|
82
|
+
force: true,
|
|
83
|
+
});
|
|
84
|
+
log.success("Removed existing .claude/");
|
|
85
|
+
}
|
|
86
|
+
if (hasClaudeMd) {
|
|
87
|
+
fs.rmSync(path.join(installDir, "CLAUDE.md"), { force: true });
|
|
88
|
+
log.success("Removed existing CLAUDE.md");
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function downloadAndInstall(version, installDir) {
|
|
94
|
+
const tag = `v${version}`;
|
|
95
|
+
const assetUrl = `https://github.com/avibebuilder/claude-prime/releases/download/${tag}/claude-prime.zip`;
|
|
96
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "claude-prime-"));
|
|
97
|
+
const zipPath = path.join(tmpDir, "claude-prime.zip");
|
|
98
|
+
|
|
99
|
+
const s = spinner();
|
|
100
|
+
s.start(`Downloading claude-prime ${tag}`);
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
let response;
|
|
104
|
+
try {
|
|
105
|
+
response = await fetch(assetUrl, { redirect: "follow" });
|
|
106
|
+
} catch {
|
|
107
|
+
s.stop("Download failed");
|
|
108
|
+
log.error(
|
|
109
|
+
`Failed to download release asset for ${tag}.\nURL: ${assetUrl}\nMake sure this version has a release with the claude-prime.zip asset.`
|
|
110
|
+
);
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (!response.ok) {
|
|
115
|
+
s.stop("Download failed");
|
|
116
|
+
log.error(
|
|
117
|
+
`Failed to download release asset for ${tag}.\nURL: ${assetUrl}\nMake sure this version has a release with the claude-prime.zip asset.`
|
|
118
|
+
);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
123
|
+
fs.writeFileSync(zipPath, buffer);
|
|
124
|
+
|
|
125
|
+
s.stop(`Downloaded ${tag}`);
|
|
126
|
+
|
|
127
|
+
execSync(`unzip -qo "${zipPath}" -d "${installDir}"`, {
|
|
128
|
+
stdio: "pipe",
|
|
129
|
+
});
|
|
130
|
+
log.success(`Installed .claude/ into ${installDir}`);
|
|
131
|
+
|
|
132
|
+
fs.writeFileSync(
|
|
133
|
+
path.join(installDir, ".claude", ".prime-version"),
|
|
134
|
+
version
|
|
135
|
+
);
|
|
136
|
+
} finally {
|
|
137
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function runInit(installDir) {
|
|
142
|
+
const initModule = require("./init");
|
|
143
|
+
await initModule(installDir, { standalone: false });
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async function init(args) {
|
|
147
|
+
const installDir = process.cwd();
|
|
148
|
+
const versionIdx = args.indexOf("--version");
|
|
149
|
+
const explicitVersion =
|
|
150
|
+
versionIdx !== -1 ? args[versionIdx + 1] : undefined;
|
|
151
|
+
|
|
152
|
+
if (versionIdx !== -1 && !explicitVersion) {
|
|
153
|
+
log.error("--version requires a version argument (e.g., --version 1.0.0)");
|
|
154
|
+
process.exit(1);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
banner();
|
|
158
|
+
intro("Claude Prime Installer");
|
|
159
|
+
|
|
160
|
+
await checkExistingInstallation(installDir);
|
|
161
|
+
|
|
162
|
+
const version = await resolveVersion(explicitVersion);
|
|
163
|
+
await downloadAndInstall(version, installDir);
|
|
164
|
+
|
|
165
|
+
await runInit(installDir);
|
|
166
|
+
|
|
167
|
+
const BLUE = "\x1b[0;34m";
|
|
168
|
+
const BOLD = "\x1b[1m";
|
|
169
|
+
const NC = "\x1b[0m";
|
|
170
|
+
|
|
171
|
+
outro("Done!");
|
|
172
|
+
|
|
173
|
+
console.log(` ${BOLD}Next steps:${NC}`);
|
|
174
|
+
console.log(` 1. Start Claude Code: ${BLUE}claude${NC}`);
|
|
175
|
+
console.log(` 2. Prime your project: ${BLUE}/optimus-prime${NC}`);
|
|
176
|
+
console.log(` 3. Start building: ${BLUE}/cook Add user authentication${NC}`);
|
|
177
|
+
console.log("");
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
module.exports = init;
|
package/src/utils.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
const { spinner } = require("@clack/prompts");
|
|
2
|
+
|
|
3
|
+
const YELLOW = "\x1b[1;33m";
|
|
4
|
+
const NC = "\x1b[0m";
|
|
5
|
+
|
|
6
|
+
function banner() {
|
|
7
|
+
const claude = [
|
|
8
|
+
" ██████╗██╗ █████╗ ██╗ ██╗██████╗ ███████╗",
|
|
9
|
+
"██╔════╝██║ ██╔══██╗██║ ██║██╔══██╗██╔════╝",
|
|
10
|
+
"██║ ██║ ███████║██║ ██║██║ ██║█████╗ ",
|
|
11
|
+
"██║ ██║ ██╔══██║██║ ██║██║ ██║██╔══╝ ",
|
|
12
|
+
"╚██████╗███████╗██║ ██║╚██████╔╝██████╔╝███████╗",
|
|
13
|
+
" ╚═════╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝",
|
|
14
|
+
];
|
|
15
|
+
const prime = [
|
|
16
|
+
"██████╗ ██████╗ ██╗███╗ ███╗███████╗",
|
|
17
|
+
"██╔══██╗██╔══██╗██║████╗ ████║██╔════╝",
|
|
18
|
+
"██████╔╝██████╔╝██║██╔████╔██║█████╗ ",
|
|
19
|
+
"██╔═══╝ ██╔══██╗██║██║╚██╔╝██║██╔══╝ ",
|
|
20
|
+
"██║ ██║ ██║██║██║ ╚═╝ ██║███████╗",
|
|
21
|
+
"╚═╝ ╚═╝ ╚═╝╚═╝╚═╝ ╚═╝╚══════╝",
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
console.log("");
|
|
25
|
+
for (let i = 0; i < claude.length; i++) {
|
|
26
|
+
console.log(`${claude[i]} ${YELLOW}${prime[i]}${NC}`);
|
|
27
|
+
}
|
|
28
|
+
console.log("");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function resolveVersion(explicitVersion) {
|
|
32
|
+
let version = explicitVersion || process.env.CLAUDE_PRIME_VERSION;
|
|
33
|
+
|
|
34
|
+
if (version) {
|
|
35
|
+
return version.replace(/^v/, "");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const s = spinner();
|
|
39
|
+
s.start("Fetching latest version from GitHub");
|
|
40
|
+
|
|
41
|
+
let response;
|
|
42
|
+
try {
|
|
43
|
+
response = await fetch(
|
|
44
|
+
"https://api.github.com/repos/avibebuilder/claude-prime/releases/latest",
|
|
45
|
+
{ headers: { Accept: "application/vnd.github.v3+json" } }
|
|
46
|
+
);
|
|
47
|
+
} catch {
|
|
48
|
+
s.stop("Failed to fetch version");
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!response.ok) {
|
|
53
|
+
s.stop("Failed to fetch version");
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const data = await response.json();
|
|
58
|
+
version = data.tag_name;
|
|
59
|
+
|
|
60
|
+
if (!version) {
|
|
61
|
+
s.stop("Could not determine latest version");
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
version = version.replace(/^v/, "");
|
|
66
|
+
s.stop(`Resolved version: ${version}`);
|
|
67
|
+
return version;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
module.exports = { banner, resolveVersion };
|