@caliber-ai/cli 0.15.0 → 0.16.1
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/bin.js +79 -46
- package/dist/bin.js.map +1 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -2318,7 +2318,21 @@ function logoutCommand() {
|
|
|
2318
2318
|
import chalk8 from "chalk";
|
|
2319
2319
|
import ora5 from "ora";
|
|
2320
2320
|
import { mkdirSync, writeFileSync } from "fs";
|
|
2321
|
-
import { join } from "path";
|
|
2321
|
+
import { join, dirname as dirname2 } from "path";
|
|
2322
|
+
function detectLocalPlatforms() {
|
|
2323
|
+
const items = scanLocalState(process.cwd());
|
|
2324
|
+
const platforms = /* @__PURE__ */ new Set();
|
|
2325
|
+
for (const item of items) {
|
|
2326
|
+
platforms.add(item.platform);
|
|
2327
|
+
}
|
|
2328
|
+
return platforms.size > 0 ? Array.from(platforms) : ["claude"];
|
|
2329
|
+
}
|
|
2330
|
+
function getSkillPath(platform, slug) {
|
|
2331
|
+
if (platform === "cursor") {
|
|
2332
|
+
return join(".cursor", "rules", `${slug}.mdc`);
|
|
2333
|
+
}
|
|
2334
|
+
return join(".claude", "skills", `${slug}.md`);
|
|
2335
|
+
}
|
|
2322
2336
|
async function recommendCommand(options) {
|
|
2323
2337
|
const auth2 = getStoredAuth();
|
|
2324
2338
|
if (!auth2) {
|
|
@@ -2336,44 +2350,57 @@ async function recommendCommand(options) {
|
|
|
2336
2350
|
throw new Error("__exit__");
|
|
2337
2351
|
}
|
|
2338
2352
|
const projectId = match.project.id;
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
spinner.fail("Failed to generate recommendations");
|
|
2350
|
-
throw err;
|
|
2353
|
+
const platforms = detectLocalPlatforms();
|
|
2354
|
+
if (options.status) {
|
|
2355
|
+
const recs2 = await apiRequest(
|
|
2356
|
+
`/api/recommendations/project/${projectId}?status=${options.status}`
|
|
2357
|
+
);
|
|
2358
|
+
if (!recs2?.length) {
|
|
2359
|
+
console.log(chalk8.dim(`
|
|
2360
|
+
No ${options.status} recommendations.
|
|
2361
|
+
`));
|
|
2362
|
+
return;
|
|
2351
2363
|
}
|
|
2364
|
+
printRecommendations(recs2);
|
|
2365
|
+
return;
|
|
2366
|
+
}
|
|
2367
|
+
if (options.generate) {
|
|
2368
|
+
const recs2 = await generateRecommendations(projectId);
|
|
2352
2369
|
if (recs2?.length) {
|
|
2353
|
-
const
|
|
2354
|
-
if (
|
|
2355
|
-
await installSkills(
|
|
2370
|
+
const selected2 = await interactiveSelect(recs2);
|
|
2371
|
+
if (selected2?.length) {
|
|
2372
|
+
await installSkills(selected2, platforms);
|
|
2356
2373
|
}
|
|
2357
2374
|
}
|
|
2358
2375
|
return;
|
|
2359
2376
|
}
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
`/api/recommendations/project/${projectId}?status=${statusFilter}`
|
|
2377
|
+
let recs = await apiRequest(
|
|
2378
|
+
`/api/recommendations/project/${projectId}?status=pending`
|
|
2363
2379
|
);
|
|
2364
2380
|
if (!recs?.length) {
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2381
|
+
recs = await generateRecommendations(projectId);
|
|
2382
|
+
}
|
|
2383
|
+
if (!recs?.length) {
|
|
2384
|
+
console.log(chalk8.dim("\nNo recommendations found for this project.\n"));
|
|
2368
2385
|
return;
|
|
2369
2386
|
}
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2387
|
+
const selected = await interactiveSelect(recs);
|
|
2388
|
+
if (selected?.length) {
|
|
2389
|
+
await installSkills(selected, platforms);
|
|
2390
|
+
}
|
|
2391
|
+
}
|
|
2392
|
+
async function generateRecommendations(projectId) {
|
|
2393
|
+
const spinner = ora5("Detecting technologies and searching for skills...").start();
|
|
2394
|
+
try {
|
|
2395
|
+
const recs = await apiRequest(
|
|
2396
|
+
`/api/recommendations/project/${projectId}/generate`,
|
|
2397
|
+
{ method: "POST" }
|
|
2398
|
+
);
|
|
2399
|
+
spinner.succeed(`Found ${recs?.length || 0} recommendations`);
|
|
2400
|
+
return recs;
|
|
2401
|
+
} catch (err) {
|
|
2402
|
+
spinner.fail("Failed to generate recommendations");
|
|
2403
|
+
throw err;
|
|
2377
2404
|
}
|
|
2378
2405
|
}
|
|
2379
2406
|
async function interactiveSelect(recs) {
|
|
@@ -2466,24 +2493,32 @@ async function interactiveSelect(recs) {
|
|
|
2466
2493
|
stdin.on("data", onData);
|
|
2467
2494
|
});
|
|
2468
2495
|
}
|
|
2469
|
-
async function installSkills(recs) {
|
|
2496
|
+
async function installSkills(recs, platforms) {
|
|
2470
2497
|
const spinner = ora5(`Installing ${recs.length} skill${recs.length > 1 ? "s" : ""}...`).start();
|
|
2471
|
-
const skillsDir = join(process.cwd(), ".claude", "skills");
|
|
2472
|
-
mkdirSync(skillsDir, { recursive: true });
|
|
2473
2498
|
const installed = [];
|
|
2474
2499
|
const warnings = [];
|
|
2475
2500
|
for (const rec of recs) {
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2501
|
+
let accepted = false;
|
|
2502
|
+
for (const platform of platforms) {
|
|
2503
|
+
try {
|
|
2504
|
+
const result = await apiRequest(
|
|
2505
|
+
`/api/recommendations/${rec.id}/content?platform=${platform}`
|
|
2506
|
+
);
|
|
2507
|
+
if (!result?.content) {
|
|
2508
|
+
warnings.push(`[${platform}] No content available for ${rec.skill_name}`);
|
|
2509
|
+
continue;
|
|
2510
|
+
}
|
|
2511
|
+
const skillPath = getSkillPath(platform, rec.skill_slug);
|
|
2512
|
+
const fullPath = join(process.cwd(), skillPath);
|
|
2513
|
+
mkdirSync(dirname2(fullPath), { recursive: true });
|
|
2514
|
+
writeFileSync(fullPath, result.content, "utf-8");
|
|
2515
|
+
installed.push(`[${platform}] ${skillPath}`);
|
|
2516
|
+
accepted = true;
|
|
2517
|
+
} catch {
|
|
2518
|
+
warnings.push(`[${platform}] Failed to fetch ${rec.skill_name}`);
|
|
2483
2519
|
}
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
installed.push(join(".claude", "skills", filename));
|
|
2520
|
+
}
|
|
2521
|
+
if (accepted) {
|
|
2487
2522
|
try {
|
|
2488
2523
|
await apiRequest(`/api/recommendations/${rec.id}/status`, {
|
|
2489
2524
|
method: "PUT",
|
|
@@ -2491,12 +2526,10 @@ async function installSkills(recs) {
|
|
|
2491
2526
|
});
|
|
2492
2527
|
} catch {
|
|
2493
2528
|
}
|
|
2494
|
-
} catch {
|
|
2495
|
-
warnings.push(`Failed to install ${rec.skill_name}`);
|
|
2496
2529
|
}
|
|
2497
2530
|
}
|
|
2498
2531
|
if (installed.length > 0) {
|
|
2499
|
-
spinner.succeed(`Installed ${installed.length}
|
|
2532
|
+
spinner.succeed(`Installed ${installed.length} file${installed.length > 1 ? "s" : ""}`);
|
|
2500
2533
|
for (const p of installed) {
|
|
2501
2534
|
console.log(chalk8.green(` \u2713 ${p}`));
|
|
2502
2535
|
}
|
|
@@ -3297,7 +3330,7 @@ program.command("status").description("Show current Caliber setup status").optio
|
|
|
3297
3330
|
program.command("regenerate").alias("regen").alias("re").alias("update").description("Re-analyze project and regenerate setup").option("--dry-run", "Preview changes without writing files").action(regenerateCommand);
|
|
3298
3331
|
program.command("login").description("Authenticate with Caliber").action(loginCommand);
|
|
3299
3332
|
program.command("logout").description("Clear stored credentials").action(logoutCommand);
|
|
3300
|
-
program.command("recommend").description("Discover and manage skill recommendations").option("--generate", "
|
|
3333
|
+
program.command("recommend").description("Discover and manage skill recommendations").option("--generate", "Force fresh recommendation generation").option("--status <status>", "View recommendations by status: pending, accepted, dismissed").action(recommendCommand);
|
|
3301
3334
|
program.command("health").description("Analyze context health and quality").option("--fix", "Generate and execute a fix plan").option("--json", "Output as JSON").action(healthCommand);
|
|
3302
3335
|
program.command("sync").description("Sync local config with server state").option("--platform <platform>", "Target platform: claude, cursor, or both").option("--dry-run", "Preview changes without writing files").action(syncCommand);
|
|
3303
3336
|
program.command("diff").description("Compare local config with server state").option("--platform <platform>", "Target platform: claude, cursor, or both").action(diffCommand);
|