ai-builder 0.1.6 → 0.1.8
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/README.md +1 -13
- package/dist/index.js +156 -163
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,7 +19,6 @@
|
|
|
19
19
|
## Features
|
|
20
20
|
|
|
21
21
|
- **Install artifacts** - Add skills, agents, and commands from the registry
|
|
22
|
-
- **Install stacks** - Bundle multiple artifacts together for specific workflows
|
|
23
22
|
- **Search the registry** - Find artifacts by name, description, or task category
|
|
24
23
|
- **Manage installed artifacts** - List and remove artifacts from your project
|
|
25
24
|
- **Self-update** - Keep the CLI up to date with a single command
|
|
@@ -72,7 +71,7 @@ Install an artifact from the registry:
|
|
|
72
71
|
npx ai-builder add <type> <author/slug>
|
|
73
72
|
```
|
|
74
73
|
|
|
75
|
-
**Types:** `skill`, `agent`, `command
|
|
74
|
+
**Types:** `skill`, `agent`, `command`
|
|
76
75
|
|
|
77
76
|
**Examples:**
|
|
78
77
|
|
|
@@ -90,16 +89,6 @@ npx ai-builder add skill anthropic/mcp-builder
|
|
|
90
89
|
npx ai-builder add agent git-town/code_reviewer --force
|
|
91
90
|
```
|
|
92
91
|
|
|
93
|
-
**Stacks** bundle multiple artifacts together:
|
|
94
|
-
|
|
95
|
-
```bash
|
|
96
|
-
# Install a stack (prompts for confirmation)
|
|
97
|
-
npx ai-builder add stack my-stack
|
|
98
|
-
|
|
99
|
-
# Skip confirmation
|
|
100
|
-
npx ai-builder add stack my-stack --yes
|
|
101
|
-
```
|
|
102
|
-
|
|
103
92
|
### Remove
|
|
104
93
|
|
|
105
94
|
Remove an installed artifact:
|
|
@@ -219,7 +208,6 @@ ai-builder completion zsh >> ~/.zshrc
|
|
|
219
208
|
| **skill** | Background capabilities that enhance Claude's abilities | `.claude/skills/{name}/` |
|
|
220
209
|
| **agent** | Specialized agents invoked with `@agent-name` | `.claude/agents/{name}.md` |
|
|
221
210
|
| **command** | Slash commands invoked with `/command-name` | `.claude/commands/{name}.md` |
|
|
222
|
-
| **stack** | Bundles of artifacts for specific workflows | Various locations |
|
|
223
211
|
|
|
224
212
|
---
|
|
225
213
|
|
package/dist/index.js
CHANGED
|
@@ -5,10 +5,85 @@ import chalk8 from "chalk";
|
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
|
|
7
7
|
// src/commands/add.ts
|
|
8
|
-
import * as readline from "readline";
|
|
9
8
|
import chalk from "chalk";
|
|
10
9
|
import ora from "ora";
|
|
11
10
|
|
|
11
|
+
// src/services/metadata.ts
|
|
12
|
+
import * as os2 from "os";
|
|
13
|
+
|
|
14
|
+
// src/services/anonymous-id.ts
|
|
15
|
+
import * as crypto from "crypto";
|
|
16
|
+
import * as fs from "fs";
|
|
17
|
+
import * as os from "os";
|
|
18
|
+
import * as path from "path";
|
|
19
|
+
var CONFIG_DIR = path.join(os.homedir(), ".config", "ai-builder");
|
|
20
|
+
var ANONYMOUS_ID_FILE = path.join(CONFIG_DIR, "anonymous-id");
|
|
21
|
+
function isValidUuid(str) {
|
|
22
|
+
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
23
|
+
return uuidRegex.test(str);
|
|
24
|
+
}
|
|
25
|
+
function getAnonymousId() {
|
|
26
|
+
if (fs.existsSync(ANONYMOUS_ID_FILE)) {
|
|
27
|
+
try {
|
|
28
|
+
const id = fs.readFileSync(ANONYMOUS_ID_FILE, "utf-8").trim();
|
|
29
|
+
if (isValidUuid(id)) {
|
|
30
|
+
return id;
|
|
31
|
+
}
|
|
32
|
+
} catch {
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const newId = crypto.randomUUID();
|
|
36
|
+
try {
|
|
37
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
38
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
39
|
+
}
|
|
40
|
+
fs.writeFileSync(ANONYMOUS_ID_FILE, `${newId}
|
|
41
|
+
`, { mode: 384 });
|
|
42
|
+
} catch {
|
|
43
|
+
}
|
|
44
|
+
return newId;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/services/metadata.ts
|
|
48
|
+
function getShell() {
|
|
49
|
+
const shell = process.env.SHELL || process.env.COMSPEC;
|
|
50
|
+
if (!shell) return null;
|
|
51
|
+
const parts = shell.split(/[/\\]/);
|
|
52
|
+
return parts[parts.length - 1] || null;
|
|
53
|
+
}
|
|
54
|
+
function getTerminal() {
|
|
55
|
+
return process.env.TERM_PROGRAM || // iTerm.app, Apple_Terminal, vscode
|
|
56
|
+
process.env.TERMINAL_EMULATOR || // Some Linux terminals
|
|
57
|
+
(process.env.WT_SESSION ? "windows-terminal" : null);
|
|
58
|
+
}
|
|
59
|
+
function detectCi() {
|
|
60
|
+
return !!(process.env.CI || process.env.CONTINUOUS_INTEGRATION || process.env.BUILD_NUMBER || process.env.GITHUB_ACTIONS || process.env.GITLAB_CI || process.env.CIRCLECI || process.env.TRAVIS || process.env.JENKINS_URL);
|
|
61
|
+
}
|
|
62
|
+
function getLocale() {
|
|
63
|
+
return process.env.LC_ALL || process.env.LC_MESSAGES || process.env.LANG || process.env.LANGUAGE || null;
|
|
64
|
+
}
|
|
65
|
+
function getTimezone() {
|
|
66
|
+
try {
|
|
67
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
68
|
+
} catch {
|
|
69
|
+
return process.env.TZ || null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function collectMetadata() {
|
|
73
|
+
return {
|
|
74
|
+
anonymousId: getAnonymousId(),
|
|
75
|
+
osType: os2.platform(),
|
|
76
|
+
osVersion: os2.release(),
|
|
77
|
+
osArch: os2.arch(),
|
|
78
|
+
nodeVersion: process.version,
|
|
79
|
+
shell: getShell(),
|
|
80
|
+
terminal: getTerminal(),
|
|
81
|
+
isCi: detectCi(),
|
|
82
|
+
locale: getLocale(),
|
|
83
|
+
timezone: getTimezone()
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
12
87
|
// src/services/api.ts
|
|
13
88
|
var API_BASE = process.env.AI_BUILDER_API_URL || "https://aibuilder.sh/api";
|
|
14
89
|
var isDebug = process.env.DEBUG?.includes("ai-builder");
|
|
@@ -67,49 +142,47 @@ async function searchArtifacts(options) {
|
|
|
67
142
|
}
|
|
68
143
|
async function trackInstall(type, slug, cliVersion) {
|
|
69
144
|
try {
|
|
145
|
+
const metadata = collectMetadata();
|
|
70
146
|
const response = await fetch(`${API_BASE}/install-events`, {
|
|
71
147
|
method: "POST",
|
|
72
148
|
headers: { "Content-Type": "application/json" },
|
|
73
|
-
body: JSON.stringify({ type, slug, cliVersion })
|
|
149
|
+
body: JSON.stringify({ type, slug, cliVersion, metadata })
|
|
74
150
|
});
|
|
75
151
|
const result = await response.json();
|
|
76
|
-
logger.debug(
|
|
152
|
+
logger.debug(
|
|
153
|
+
{
|
|
154
|
+
type,
|
|
155
|
+
slug,
|
|
156
|
+
cliVersion,
|
|
157
|
+
anonymousId: metadata.anonymousId,
|
|
158
|
+
status: response.status,
|
|
159
|
+
result
|
|
160
|
+
},
|
|
161
|
+
"Install tracked"
|
|
162
|
+
);
|
|
77
163
|
} catch (error) {
|
|
78
164
|
logger.debug({ err: error, type, slug }, "Install tracking failed (non-critical)");
|
|
79
165
|
}
|
|
80
166
|
}
|
|
81
|
-
async function resolveStack(slug) {
|
|
82
|
-
const url = `${API_BASE}/stacks/${encodeURIComponent(slug)}`;
|
|
83
|
-
const timer = logger.startTimer("api-resolve-stack");
|
|
84
|
-
const response = await fetch(url);
|
|
85
|
-
if (!response.ok) {
|
|
86
|
-
const errorBody = await response.json().catch(() => ({ error: "Unknown error" }));
|
|
87
|
-
timer.done({ slug, status: response.status, success: false });
|
|
88
|
-
throw new Error(errorBody.error || `Failed to resolve stack: ${response.status}`);
|
|
89
|
-
}
|
|
90
|
-
const result = await response.json();
|
|
91
|
-
timer.done({ slug, artifactCount: result.artifactCount, status: response.status, success: true });
|
|
92
|
-
return result;
|
|
93
|
-
}
|
|
94
167
|
|
|
95
168
|
// src/services/installer.ts
|
|
96
|
-
import * as
|
|
97
|
-
import * as
|
|
169
|
+
import * as fs3 from "fs";
|
|
170
|
+
import * as path3 from "path";
|
|
98
171
|
|
|
99
172
|
// src/services/lock-file.ts
|
|
100
|
-
import * as
|
|
101
|
-
import * as
|
|
173
|
+
import * as fs2 from "fs";
|
|
174
|
+
import * as path2 from "path";
|
|
102
175
|
var LOCK_FILE_NAME = "ai-builder.lock.json";
|
|
103
176
|
function getLockFilePath() {
|
|
104
|
-
return
|
|
177
|
+
return path2.join(process.cwd(), ".claude", LOCK_FILE_NAME);
|
|
105
178
|
}
|
|
106
179
|
function readLockFile() {
|
|
107
180
|
const lockPath = getLockFilePath();
|
|
108
|
-
if (!
|
|
181
|
+
if (!fs2.existsSync(lockPath)) {
|
|
109
182
|
return { version: 1, artifacts: {} };
|
|
110
183
|
}
|
|
111
184
|
try {
|
|
112
|
-
const content =
|
|
185
|
+
const content = fs2.readFileSync(lockPath, "utf-8");
|
|
113
186
|
return JSON.parse(content);
|
|
114
187
|
} catch {
|
|
115
188
|
return { version: 1, artifacts: {} };
|
|
@@ -117,11 +190,11 @@ function readLockFile() {
|
|
|
117
190
|
}
|
|
118
191
|
function writeLockFile(lockFile) {
|
|
119
192
|
const lockPath = getLockFilePath();
|
|
120
|
-
const lockDir =
|
|
121
|
-
if (!
|
|
122
|
-
|
|
193
|
+
const lockDir = path2.dirname(lockPath);
|
|
194
|
+
if (!fs2.existsSync(lockDir)) {
|
|
195
|
+
fs2.mkdirSync(lockDir, { recursive: true });
|
|
123
196
|
}
|
|
124
|
-
|
|
197
|
+
fs2.writeFileSync(lockPath, `${JSON.stringify(lockFile, null, 2)}
|
|
125
198
|
`);
|
|
126
199
|
}
|
|
127
200
|
function addToLockFile(entry) {
|
|
@@ -148,82 +221,82 @@ function isInstalled(type, author, slug) {
|
|
|
148
221
|
|
|
149
222
|
// src/services/installer.ts
|
|
150
223
|
function getInstallPath(type, artifactName) {
|
|
151
|
-
const basePath =
|
|
224
|
+
const basePath = path3.join(process.cwd(), ".claude");
|
|
152
225
|
switch (type) {
|
|
153
226
|
case "skill":
|
|
154
|
-
return
|
|
227
|
+
return path3.join(basePath, "skills", artifactName);
|
|
155
228
|
case "agent":
|
|
156
|
-
return
|
|
229
|
+
return path3.join(basePath, "agents", `${artifactName}.md`);
|
|
157
230
|
case "command":
|
|
158
|
-
return
|
|
231
|
+
return path3.join(basePath, "commands", `${artifactName}.md`);
|
|
159
232
|
default:
|
|
160
233
|
throw new Error(`Unknown artifact type: ${type}`);
|
|
161
234
|
}
|
|
162
235
|
}
|
|
163
236
|
function installArtifact(artifact, options = {}) {
|
|
164
|
-
const artifactName = artifact.
|
|
237
|
+
const artifactName = artifact.name;
|
|
165
238
|
const installPath = getInstallPath(artifact.type, artifactName);
|
|
166
239
|
const files = [];
|
|
167
240
|
if (!options.force && isInstalled(artifact.type, artifact.author, artifactName || artifact.name)) {
|
|
168
241
|
return { installed: false, files: [] };
|
|
169
242
|
}
|
|
170
|
-
const dir = artifact.type === "skill" ? installPath :
|
|
171
|
-
if (!
|
|
172
|
-
|
|
243
|
+
const dir = artifact.type === "skill" ? installPath : path3.dirname(installPath);
|
|
244
|
+
if (!fs3.existsSync(dir)) {
|
|
245
|
+
fs3.mkdirSync(dir, { recursive: true });
|
|
173
246
|
}
|
|
174
247
|
if (artifact.type === "skill") {
|
|
175
|
-
const skillMdPath =
|
|
176
|
-
|
|
248
|
+
const skillMdPath = path3.join(installPath, "SKILL.md");
|
|
249
|
+
fs3.writeFileSync(skillMdPath, artifact.content);
|
|
177
250
|
files.push(skillMdPath);
|
|
178
251
|
if (artifact.bundleFiles) {
|
|
179
252
|
for (const [filename, content] of Object.entries(artifact.bundleFiles)) {
|
|
180
|
-
const filePath =
|
|
181
|
-
|
|
182
|
-
|
|
253
|
+
const filePath = path3.join(installPath, filename);
|
|
254
|
+
fs3.mkdirSync(path3.dirname(filePath), { recursive: true });
|
|
255
|
+
fs3.writeFileSync(filePath, content);
|
|
183
256
|
files.push(filePath);
|
|
184
257
|
}
|
|
185
258
|
}
|
|
186
259
|
} else {
|
|
187
|
-
|
|
260
|
+
fs3.writeFileSync(installPath, artifact.content);
|
|
188
261
|
files.push(installPath);
|
|
189
262
|
}
|
|
190
263
|
addToLockFile({
|
|
191
264
|
type: artifact.type,
|
|
192
|
-
slug:
|
|
265
|
+
slug: artifact.name,
|
|
193
266
|
author: artifact.author,
|
|
194
267
|
name: artifact.name,
|
|
195
268
|
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
196
|
-
files: files.map((f) =>
|
|
269
|
+
files: files.map((f) => path3.relative(process.cwd(), f))
|
|
197
270
|
});
|
|
198
271
|
return { installed: true, files };
|
|
199
272
|
}
|
|
200
273
|
function uninstallArtifact(type, author, slug) {
|
|
201
274
|
const installPath = getInstallPath(type, slug);
|
|
202
|
-
if (!
|
|
275
|
+
if (!fs3.existsSync(installPath)) {
|
|
203
276
|
return false;
|
|
204
277
|
}
|
|
205
278
|
if (type === "skill") {
|
|
206
|
-
|
|
279
|
+
fs3.rmSync(installPath, { recursive: true, force: true });
|
|
207
280
|
} else {
|
|
208
|
-
|
|
281
|
+
fs3.unlinkSync(installPath);
|
|
209
282
|
}
|
|
210
283
|
removeFromLockFile(type, author, slug);
|
|
211
284
|
return true;
|
|
212
285
|
}
|
|
213
286
|
|
|
214
287
|
// src/commands/add.ts
|
|
215
|
-
var VALID_TYPES = ["skill", "agent", "command"
|
|
288
|
+
var VALID_TYPES = ["skill", "agent", "command"];
|
|
216
289
|
var CLI_VERSION = "0.1.0";
|
|
217
290
|
async function addCommand(type, slug, options) {
|
|
218
291
|
if (!type) {
|
|
219
292
|
console.log(chalk.red("Error: Missing artifact type."));
|
|
220
293
|
console.log("Usage: ai-builder add <type> <author/slug>");
|
|
221
|
-
console.log("Types: skill, agent, command
|
|
294
|
+
console.log("Types: skill, agent, command");
|
|
222
295
|
process.exit(1);
|
|
223
296
|
}
|
|
224
297
|
if (!VALID_TYPES.includes(type)) {
|
|
225
298
|
console.log(chalk.red(`Error: Invalid type "${type}".`));
|
|
226
|
-
console.log("Valid types: skill, agent, command
|
|
299
|
+
console.log("Valid types: skill, agent, command");
|
|
227
300
|
process.exit(1);
|
|
228
301
|
}
|
|
229
302
|
if (!slug) {
|
|
@@ -232,33 +305,28 @@ async function addCommand(type, slug, options) {
|
|
|
232
305
|
console.log("Example: ai-builder add agent anthropic/frontend-tester");
|
|
233
306
|
process.exit(1);
|
|
234
307
|
}
|
|
235
|
-
if (
|
|
308
|
+
if (!slug.includes("/")) {
|
|
236
309
|
console.log(chalk.red("Error: Invalid slug format."));
|
|
237
310
|
console.log("Expected format: author/artifact-name");
|
|
238
311
|
console.log("Example: anthropic/frontend-tester");
|
|
239
312
|
process.exit(1);
|
|
240
313
|
}
|
|
241
|
-
if (type === "stack") {
|
|
242
|
-
await installStack(slug, options);
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
245
314
|
const [author, artifactSlug] = slug.split("/");
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
|
-
const spinner = ora(`Resolving ${type} ${chalk.cyan(slug)}...`).start();
|
|
315
|
+
const isUpdate = isInstalled(type, author, artifactSlug);
|
|
316
|
+
const actionVerb = isUpdate ? "Updating" : "Resolving";
|
|
317
|
+
const spinner = ora(`${actionVerb} ${type} ${chalk.cyan(slug)}...`).start();
|
|
252
318
|
try {
|
|
253
319
|
const artifact = await resolveArtifact(type, slug);
|
|
254
|
-
|
|
255
|
-
|
|
320
|
+
const installVerb = isUpdate ? "Updating" : "Installing";
|
|
321
|
+
spinner.text = `${installVerb} ${artifact.name}...`;
|
|
322
|
+
const { installed } = installArtifact(artifact, { force: isUpdate || options.force });
|
|
256
323
|
if (!installed) {
|
|
257
|
-
spinner.warn(`${artifact.name}
|
|
324
|
+
spinner.warn(`${artifact.name} could not be installed.`);
|
|
258
325
|
return;
|
|
259
326
|
}
|
|
327
|
+
const successVerb = isUpdate ? "Updated" : "Installed";
|
|
260
328
|
spinner.succeed(
|
|
261
|
-
|
|
329
|
+
`${successVerb} ${chalk.green(artifact.name)} to ${chalk.dim(artifact.installPath)}`
|
|
262
330
|
);
|
|
263
331
|
console.log();
|
|
264
332
|
printUsageHint(type, artifactSlug);
|
|
@@ -284,85 +352,11 @@ function printUsageHint(type, slug) {
|
|
|
284
352
|
break;
|
|
285
353
|
}
|
|
286
354
|
}
|
|
287
|
-
async function installStack(stackSlug, options) {
|
|
288
|
-
const spinner = ora(`Resolving stack ${chalk.cyan(stackSlug)}...`).start();
|
|
289
|
-
try {
|
|
290
|
-
const stack = await resolveStack(stackSlug);
|
|
291
|
-
spinner.stop();
|
|
292
|
-
console.log();
|
|
293
|
-
console.log(chalk.bold(`Stack: ${stack.title}`));
|
|
294
|
-
if (stack.summary) {
|
|
295
|
-
console.log(chalk.dim(stack.summary));
|
|
296
|
-
}
|
|
297
|
-
console.log();
|
|
298
|
-
console.log(`This will install ${chalk.cyan(stack.artifactCount)} artifacts:`);
|
|
299
|
-
console.log();
|
|
300
|
-
for (const artifact of stack.artifacts) {
|
|
301
|
-
const typeColor = artifact.type === "skill" ? chalk.blue : artifact.type === "agent" ? chalk.magenta : chalk.green;
|
|
302
|
-
console.log(` ${typeColor(`[${artifact.type}]`)} ${artifact.name}`);
|
|
303
|
-
console.log(` ${chalk.dim(artifact.slug)}`);
|
|
304
|
-
if (artifact.installNotes) {
|
|
305
|
-
console.log(` ${chalk.yellow(`Note: ${artifact.installNotes}`)}`);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
console.log();
|
|
309
|
-
if (!options.yes) {
|
|
310
|
-
const confirmed = await confirm("Proceed with installation?");
|
|
311
|
-
if (!confirmed) {
|
|
312
|
-
console.log("Cancelled.");
|
|
313
|
-
return;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
console.log();
|
|
317
|
-
let installed = 0;
|
|
318
|
-
let skipped = 0;
|
|
319
|
-
for (const artifact of stack.artifacts) {
|
|
320
|
-
const artifactSpinner = ora(`Installing ${artifact.name}...`).start();
|
|
321
|
-
try {
|
|
322
|
-
const resolved = await resolveArtifact(artifact.type, artifact.slug);
|
|
323
|
-
const result = installArtifact(resolved, { force: options.force });
|
|
324
|
-
if (result.installed) {
|
|
325
|
-
artifactSpinner.succeed(`Installed ${chalk.green(artifact.name)}`);
|
|
326
|
-
await trackInstall(artifact.type, artifact.slug, CLI_VERSION);
|
|
327
|
-
installed++;
|
|
328
|
-
} else {
|
|
329
|
-
artifactSpinner.info(`${artifact.name} already installed`);
|
|
330
|
-
skipped++;
|
|
331
|
-
}
|
|
332
|
-
} catch (error) {
|
|
333
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
334
|
-
artifactSpinner.fail(`Failed to install ${artifact.name}: ${chalk.dim(message)}`);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
console.log();
|
|
338
|
-
console.log(
|
|
339
|
-
`${chalk.green("Done!")} Installed ${installed} artifact${installed === 1 ? "" : "s"}${skipped > 0 ? `, skipped ${skipped}` : ""}`
|
|
340
|
-
);
|
|
341
|
-
console.log();
|
|
342
|
-
console.log(chalk.dim(`Learn more: https://aibuilder.sh/stacks/${stackSlug}`));
|
|
343
|
-
} catch (error) {
|
|
344
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
345
|
-
spinner.fail(chalk.red(`Failed to resolve stack: ${message}`));
|
|
346
|
-
process.exit(1);
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
async function confirm(question) {
|
|
350
|
-
const rl = readline.createInterface({
|
|
351
|
-
input: process.stdin,
|
|
352
|
-
output: process.stdout
|
|
353
|
-
});
|
|
354
|
-
return new Promise((resolve) => {
|
|
355
|
-
rl.question(`${question} ${chalk.dim("[y/N]")} `, (answer) => {
|
|
356
|
-
rl.close();
|
|
357
|
-
resolve(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
|
|
358
|
-
});
|
|
359
|
-
});
|
|
360
|
-
}
|
|
361
355
|
|
|
362
356
|
// src/commands/completion.ts
|
|
363
|
-
import * as
|
|
364
|
-
import * as
|
|
365
|
-
import * as
|
|
357
|
+
import * as fs4 from "fs";
|
|
358
|
+
import * as os3 from "os";
|
|
359
|
+
import * as path4 from "path";
|
|
366
360
|
import chalk2 from "chalk";
|
|
367
361
|
var BASH_COMPLETION = `
|
|
368
362
|
# ai-builder bash completion
|
|
@@ -371,7 +365,7 @@ _ai_builder_completions() {
|
|
|
371
365
|
_get_comp_words_by_ref -n : cur prev words cword
|
|
372
366
|
|
|
373
367
|
local commands="add remove rm list ls search status update completion"
|
|
374
|
-
local types="skill agent command
|
|
368
|
+
local types="skill agent command"
|
|
375
369
|
|
|
376
370
|
case "\${prev}" in
|
|
377
371
|
ai-builder)
|
|
@@ -425,7 +419,7 @@ _ai_builder() {
|
|
|
425
419
|
local -a commands types
|
|
426
420
|
|
|
427
421
|
commands=(
|
|
428
|
-
'add:Install an artifact
|
|
422
|
+
'add:Install an artifact'
|
|
429
423
|
'remove:Remove an installed artifact'
|
|
430
424
|
'rm:Remove an installed artifact'
|
|
431
425
|
'list:List installed artifacts'
|
|
@@ -436,7 +430,7 @@ _ai_builder() {
|
|
|
436
430
|
'completion:Generate shell completions'
|
|
437
431
|
)
|
|
438
432
|
|
|
439
|
-
types=(skill agent command
|
|
433
|
+
types=(skill agent command)
|
|
440
434
|
|
|
441
435
|
_arguments -C \\
|
|
442
436
|
'1:command:->command' \\
|
|
@@ -450,10 +444,9 @@ _ai_builder() {
|
|
|
450
444
|
case "$words[1]" in
|
|
451
445
|
add|remove|rm)
|
|
452
446
|
_arguments \\
|
|
453
|
-
'1:type:(skill agent command
|
|
447
|
+
'1:type:(skill agent command)' \\
|
|
454
448
|
'2:slug:' \\
|
|
455
449
|
'--force[Overwrite existing artifacts]' \\
|
|
456
|
-
'--yes[Skip confirmation]' \\
|
|
457
450
|
'--help[Show help]'
|
|
458
451
|
;;
|
|
459
452
|
list|ls)
|
|
@@ -488,14 +481,14 @@ _ai_builder() {
|
|
|
488
481
|
_ai_builder "$@"
|
|
489
482
|
`.trim();
|
|
490
483
|
function getShellConfigPath(shell) {
|
|
491
|
-
const home =
|
|
484
|
+
const home = os3.homedir();
|
|
492
485
|
if (shell === "bash") {
|
|
493
|
-
const bashrc =
|
|
494
|
-
const bashProfile =
|
|
495
|
-
return
|
|
486
|
+
const bashrc = path4.join(home, ".bashrc");
|
|
487
|
+
const bashProfile = path4.join(home, ".bash_profile");
|
|
488
|
+
return fs4.existsSync(bashrc) ? bashrc : bashProfile;
|
|
496
489
|
}
|
|
497
490
|
if (shell === "zsh") {
|
|
498
|
-
return
|
|
491
|
+
return path4.join(home, ".zshrc");
|
|
499
492
|
}
|
|
500
493
|
return null;
|
|
501
494
|
}
|
|
@@ -523,14 +516,14 @@ function completionCommand(shell, options) {
|
|
|
523
516
|
}
|
|
524
517
|
const completion = detectedShell === "zsh" ? ZSH_COMPLETION : BASH_COMPLETION;
|
|
525
518
|
const marker = "# ai-builder completion";
|
|
526
|
-
const existingContent =
|
|
519
|
+
const existingContent = fs4.existsSync(configPath) ? fs4.readFileSync(configPath, "utf-8") : "";
|
|
527
520
|
if (existingContent.includes(marker)) {
|
|
528
521
|
console.log(chalk2.yellow(`
|
|
529
522
|
Completion already installed in ${configPath}`));
|
|
530
523
|
console.log(chalk2.dim(" Restart your terminal to apply changes\n"));
|
|
531
524
|
return;
|
|
532
525
|
}
|
|
533
|
-
|
|
526
|
+
fs4.appendFileSync(configPath, `
|
|
534
527
|
${completion}
|
|
535
528
|
`);
|
|
536
529
|
console.log(chalk2.green(`
|
|
@@ -634,7 +627,7 @@ function formatDate(isoDate) {
|
|
|
634
627
|
}
|
|
635
628
|
|
|
636
629
|
// src/commands/remove.ts
|
|
637
|
-
import * as
|
|
630
|
+
import * as readline from "readline";
|
|
638
631
|
import chalk4 from "chalk";
|
|
639
632
|
var VALID_TYPES2 = ["skill", "agent", "command"];
|
|
640
633
|
async function removeCommand(type, slug, options) {
|
|
@@ -667,7 +660,7 @@ async function removeCommand(type, slug, options) {
|
|
|
667
660
|
return;
|
|
668
661
|
}
|
|
669
662
|
if (!options.yes) {
|
|
670
|
-
const confirmed = await
|
|
663
|
+
const confirmed = await confirm(`Remove ${type} ${chalk4.cyan(slug)}?`);
|
|
671
664
|
if (!confirmed) {
|
|
672
665
|
console.log("Cancelled.");
|
|
673
666
|
return;
|
|
@@ -681,8 +674,8 @@ async function removeCommand(type, slug, options) {
|
|
|
681
674
|
process.exit(1);
|
|
682
675
|
}
|
|
683
676
|
}
|
|
684
|
-
async function
|
|
685
|
-
const rl =
|
|
677
|
+
async function confirm(question) {
|
|
678
|
+
const rl = readline.createInterface({
|
|
686
679
|
input: process.stdin,
|
|
687
680
|
output: process.stdout
|
|
688
681
|
});
|
|
@@ -766,8 +759,8 @@ function formatNumber(num) {
|
|
|
766
759
|
}
|
|
767
760
|
|
|
768
761
|
// src/commands/status.ts
|
|
769
|
-
import * as
|
|
770
|
-
import * as
|
|
762
|
+
import * as fs5 from "fs";
|
|
763
|
+
import * as path5 from "path";
|
|
771
764
|
import chalk6 from "chalk";
|
|
772
765
|
|
|
773
766
|
// src/version.ts
|
|
@@ -781,8 +774,8 @@ var API_BASE2 = process.env.AI_BUILDER_API_URL || "https://aibuilder.sh/api";
|
|
|
781
774
|
async function statusCommand() {
|
|
782
775
|
console.log(chalk6.cyan.bold("\n ai-builder status\n"));
|
|
783
776
|
console.log(` ${chalk6.dim("Version:")} ${chalk6.white(version)}`);
|
|
784
|
-
const claudeDir =
|
|
785
|
-
const claudeDirExists =
|
|
777
|
+
const claudeDir = path5.join(process.cwd(), ".claude");
|
|
778
|
+
const claudeDirExists = fs5.existsSync(claudeDir);
|
|
786
779
|
console.log(
|
|
787
780
|
` ${chalk6.dim("Claude dir:")} ${claudeDirExists ? chalk6.green(".claude/ found") : chalk6.yellow(".claude/ not found")}`
|
|
788
781
|
);
|
|
@@ -883,7 +876,7 @@ async function updateCommand(options) {
|
|
|
883
876
|
// src/index.ts
|
|
884
877
|
var program = new Command();
|
|
885
878
|
program.name("ai-builder").description("CLI for installing Claude Code artifacts from aibuilder.sh").version(version);
|
|
886
|
-
program.command("add").description("Install an artifact
|
|
879
|
+
program.command("add").description("Install an artifact").argument("[type]", "Artifact type: skill, agent, or command").argument("[slug]", "Artifact slug in author/name format").option("-f, --force", "Overwrite existing artifacts").action(addCommand);
|
|
887
880
|
program.command("remove").alias("rm").description("Remove an installed artifact").argument("[type]", "Artifact type: skill, agent, or command").argument("[slug]", "Artifact slug in author/name format").option("-y, --yes", "Skip confirmation").action(removeCommand);
|
|
888
881
|
program.command("list").alias("ls").description("List installed artifacts").option("-t, --type <type>", "Filter by type: skill, agent, or command").action(listCommand);
|
|
889
882
|
program.command("search").description("Search the registry for artifacts").argument("<query>", "Search query").option("-t, --type <type>", "Filter by type: skill, agent, or command").option("--task <task>", "Filter by task category").option("-l, --limit <number>", "Number of results", "10").action(searchCommand);
|
|
@@ -904,7 +897,7 @@ program.action(() => {
|
|
|
904
897
|
console.log(" ai-builder update Update CLI");
|
|
905
898
|
console.log("\n Examples:");
|
|
906
899
|
console.log(`${chalk8.green(" $ ")}ai-builder add agent anthropic/frontend-tester`);
|
|
907
|
-
console.log(`${chalk8.green(" $ ")}ai-builder add
|
|
900
|
+
console.log(`${chalk8.green(" $ ")}ai-builder add skill anthropic/code-reviewer`);
|
|
908
901
|
console.log(`${chalk8.green(" $ ")}ai-builder search "test generator"`);
|
|
909
902
|
console.log(`
|
|
910
903
|
Learn more at ${chalk8.cyan("https://aibuilder.sh")}
|