@learnrudi/cli 1.9.1 → 1.9.3

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.cjs +523 -86
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -3228,34 +3228,15 @@ var import_os2 = __toESM(require("os"), 1);
3228
3228
  var import_env6 = require("@learnrudi/env");
3229
3229
  var import_mcp4 = require("@learnrudi/mcp");
3230
3230
  var HOME = import_os2.default.homedir();
3231
- var SHIM_PATH = path9.join(import_env6.PATHS.home, "shims", "rudi-mcp");
3232
- function getInstalledStacks() {
3233
- const stacksDir = import_env6.PATHS.stacks;
3234
- if (!fs11.existsSync(stacksDir)) return [];
3235
- return fs11.readdirSync(stacksDir, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith(".")).filter((d) => fs11.existsSync(path9.join(stacksDir, d.name, "manifest.json"))).map((d) => d.name);
3236
- }
3237
- function ensureShim() {
3238
- const shimsDir = path9.dirname(SHIM_PATH);
3239
- if (!fs11.existsSync(shimsDir)) {
3240
- fs11.mkdirSync(shimsDir, { recursive: true });
3241
- }
3242
- const shimContent = `#!/usr/bin/env bash
3243
- set -euo pipefail
3244
- # Try rudi in PATH first, fall back to npx
3245
- if command -v rudi &> /dev/null; then
3246
- exec rudi mcp "$1"
3247
- else
3248
- exec npx --yes @learnrudi/cli mcp "$1"
3249
- fi
3250
- `;
3251
- if (fs11.existsSync(SHIM_PATH)) {
3252
- const existing = fs11.readFileSync(SHIM_PATH, "utf-8");
3253
- if (existing === shimContent) {
3254
- return { created: false, path: SHIM_PATH };
3255
- }
3231
+ var ROUTER_SHIM_PATH = path9.join(import_env6.PATHS.home, "shims", "rudi-router");
3232
+ function checkRouterShim() {
3233
+ if (!fs11.existsSync(ROUTER_SHIM_PATH)) {
3234
+ throw new Error(
3235
+ `Router shim not found at ${ROUTER_SHIM_PATH}
3236
+ Run: npm install -g @learnrudi/cli@latest`
3237
+ );
3256
3238
  }
3257
- fs11.writeFileSync(SHIM_PATH, shimContent, { mode: 493 });
3258
- return { created: true, path: SHIM_PATH };
3239
+ return ROUTER_SHIM_PATH;
3259
3240
  }
3260
3241
  function backupConfig(configPath) {
3261
3242
  if (!fs11.existsSync(configPath)) return null;
@@ -3280,24 +3261,23 @@ function writeJsonConfig(configPath, config) {
3280
3261
  }
3281
3262
  fs11.writeFileSync(configPath, JSON.stringify(config, null, 2));
3282
3263
  }
3283
- function buildMcpEntry(stackName, agentId) {
3264
+ function buildRouterEntry(agentId) {
3284
3265
  const base = {
3285
- command: SHIM_PATH,
3286
- args: [stackName]
3266
+ command: ROUTER_SHIM_PATH,
3267
+ args: []
3287
3268
  };
3288
3269
  if (agentId === "claude-desktop" || agentId === "claude-code") {
3289
3270
  return { type: "stdio", ...base };
3290
3271
  }
3291
3272
  return base;
3292
3273
  }
3293
- async function integrateAgent(agentId, stacks, flags) {
3274
+ async function integrateAgent(agentId, flags) {
3294
3275
  const agentConfig = import_mcp4.AGENT_CONFIGS.find((a) => a.id === agentId);
3295
3276
  if (!agentConfig) {
3296
3277
  console.error(`Unknown agent: ${agentId}`);
3297
3278
  return { success: false, error: "Unknown agent" };
3298
3279
  }
3299
3280
  const configPath = (0, import_mcp4.findAgentConfig)(agentConfig);
3300
- const configDir = configPath ? path9.dirname(configPath) : null;
3301
3281
  const targetPath = configPath || path9.join(HOME, agentConfig.paths[process.platform]?.[0] || agentConfig.paths.darwin[0]);
3302
3282
  console.log(`
3303
3283
  ${agentConfig.name}:`);
@@ -3313,32 +3293,54 @@ ${agentConfig.name}:`);
3313
3293
  if (!config[key]) {
3314
3294
  config[key] = {};
3315
3295
  }
3316
- let added = 0;
3317
- let updated = 0;
3318
- for (const stackName of stacks) {
3319
- const entry = buildMcpEntry(stackName, agentId);
3320
- const existing = config[key][stackName];
3321
- if (!existing) {
3322
- config[key][stackName] = entry;
3323
- added++;
3324
- } else if (existing.command !== entry.command || JSON.stringify(existing.args) !== JSON.stringify(entry.args)) {
3325
- config[key][stackName] = entry;
3326
- updated++;
3327
- }
3328
- }
3329
- if (added > 0 || updated > 0) {
3296
+ const rudiMcpShimPath = path9.join(import_env6.PATHS.home, "shims", "rudi-mcp");
3297
+ const removedEntries = [];
3298
+ for (const [serverName, serverConfig] of Object.entries(config[key])) {
3299
+ if (serverConfig.command === rudiMcpShimPath) {
3300
+ delete config[key][serverName];
3301
+ removedEntries.push(serverName);
3302
+ }
3303
+ }
3304
+ if (removedEntries.length > 0) {
3305
+ console.log(` Removed old entries: ${removedEntries.join(", ")}`);
3306
+ }
3307
+ const routerEntry = buildRouterEntry(agentId);
3308
+ const existing = config[key]["rudi"];
3309
+ let action = "none";
3310
+ if (!existing) {
3311
+ config[key]["rudi"] = routerEntry;
3312
+ action = "added";
3313
+ } else if (existing.command !== routerEntry.command || JSON.stringify(existing.args) !== JSON.stringify(routerEntry.args)) {
3314
+ config[key]["rudi"] = routerEntry;
3315
+ action = "updated";
3316
+ }
3317
+ if (action !== "none" || removedEntries.length > 0) {
3330
3318
  writeJsonConfig(targetPath, config);
3331
- console.log(` Added: ${added}, Updated: ${updated}`);
3319
+ if (action !== "none") {
3320
+ console.log(` ${action === "added" ? "\u2713 Added" : "\u2713 Updated"} rudi router`);
3321
+ }
3332
3322
  } else {
3333
- console.log(` Already up to date`);
3323
+ console.log(` \u2713 Already configured`);
3334
3324
  }
3335
- return { success: true, added, updated };
3325
+ return { success: true, action, removed: removedEntries };
3336
3326
  }
3337
3327
  async function cmdIntegrate(args, flags) {
3338
3328
  const target = args[0];
3329
+ if (flags.list || target === "list") {
3330
+ const installed = (0, import_mcp4.getInstalledAgents)();
3331
+ console.log("\nDetected agents:");
3332
+ for (const agent of installed) {
3333
+ console.log(` \u2713 ${agent.name}`);
3334
+ console.log(` ${agent.configFile}`);
3335
+ }
3336
+ if (installed.length === 0) {
3337
+ console.log(" (none detected)");
3338
+ }
3339
+ return;
3340
+ }
3339
3341
  if (!target) {
3340
3342
  console.log(`
3341
- rudi integrate - Wire RUDI stacks into agent configs
3343
+ rudi integrate - Wire RUDI router into agent configs
3342
3344
 
3343
3345
  USAGE
3344
3346
  rudi integrate <agent> Integrate with specific agent
@@ -3364,29 +3366,14 @@ EXAMPLES
3364
3366
  `);
3365
3367
  return;
3366
3368
  }
3367
- if (flags.list || target === "list") {
3368
- const installed = (0, import_mcp4.getInstalledAgents)();
3369
- console.log("\nDetected agents:");
3370
- for (const agent of installed) {
3371
- console.log(` \u2713 ${agent.name}`);
3372
- console.log(` ${agent.configFile}`);
3373
- }
3374
- if (installed.length === 0) {
3375
- console.log(" (none detected)");
3376
- }
3377
- return;
3378
- }
3379
- const stacks = getInstalledStacks();
3380
- if (stacks.length === 0) {
3381
- console.log("No stacks installed. Install with: rudi install <stack>");
3369
+ try {
3370
+ checkRouterShim();
3371
+ } catch (err) {
3372
+ console.error(err.message);
3382
3373
  return;
3383
3374
  }
3384
3375
  console.log(`
3385
- Integrating ${stacks.length} stack(s)...`);
3386
- const shimResult = ensureShim();
3387
- if (shimResult.created) {
3388
- console.log(`Created shim: ${shimResult.path}`);
3389
- }
3376
+ Wiring up RUDI router...`);
3390
3377
  let targetAgents = [];
3391
3378
  if (target === "all") {
3392
3379
  targetAgents = (0, import_mcp4.getInstalledAgents)().map((a) => a.id);
@@ -3416,25 +3403,25 @@ Integrating ${stacks.length} stack(s)...`);
3416
3403
  targetAgents = [agentId];
3417
3404
  }
3418
3405
  if (flags["dry-run"]) {
3419
- console.log("\nDry run - would integrate:");
3406
+ console.log("\nDry run - would add RUDI router to:");
3420
3407
  for (const agentId of targetAgents) {
3421
3408
  const agent = import_mcp4.AGENT_CONFIGS.find((a) => a.id === agentId);
3422
- console.log(` ${agent?.name || agentId}:`);
3423
- for (const stack of stacks) {
3424
- console.log(` - ${stack}`);
3425
- }
3409
+ console.log(` ${agent?.name || agentId}`);
3426
3410
  }
3427
3411
  return;
3428
3412
  }
3429
3413
  const results = [];
3430
3414
  for (const agentId of targetAgents) {
3431
- const result = await integrateAgent(agentId, stacks, flags);
3415
+ const result = await integrateAgent(agentId, flags);
3432
3416
  results.push({ agent: agentId, ...result });
3433
3417
  }
3434
3418
  const successful = results.filter((r) => r.success);
3435
3419
  console.log(`
3436
3420
  \u2713 Integrated with ${successful.length} agent(s)`);
3437
- console.log("\nRestart your agent(s) to use the new stacks.");
3421
+ console.log("\nRestart your agent(s) to access all installed stacks.");
3422
+ console.log("\nManage stacks:");
3423
+ console.log(" rudi install <stack> # Install a new stack");
3424
+ console.log(" rudi index # Rebuild tool cache");
3438
3425
  }
3439
3426
 
3440
3427
  // src/commands/migrate.js
@@ -3445,7 +3432,7 @@ var import_env7 = require("@learnrudi/env");
3445
3432
  var import_mcp5 = require("@learnrudi/mcp");
3446
3433
  var HOME2 = import_os3.default.homedir();
3447
3434
  var OLD_PROMPT_STACK = path10.join(HOME2, ".prompt-stack");
3448
- var SHIM_PATH2 = path10.join(import_env7.PATHS.home, "shims", "rudi-mcp");
3435
+ var SHIM_PATH = path10.join(import_env7.PATHS.home, "shims", "rudi-mcp");
3449
3436
  function getOldStacks() {
3450
3437
  const stacksDir = path10.join(OLD_PROMPT_STACK, "stacks");
3451
3438
  if (!fs12.existsSync(stacksDir)) return [];
@@ -3481,8 +3468,8 @@ function copyRecursive(src, dest) {
3481
3468
  fs12.copyFileSync(src, dest);
3482
3469
  }
3483
3470
  }
3484
- function ensureShim2() {
3485
- const shimsDir = path10.dirname(SHIM_PATH2);
3471
+ function ensureShim() {
3472
+ const shimsDir = path10.dirname(SHIM_PATH);
3486
3473
  if (!fs12.existsSync(shimsDir)) {
3487
3474
  fs12.mkdirSync(shimsDir, { recursive: true });
3488
3475
  }
@@ -3494,11 +3481,11 @@ else
3494
3481
  exec npx --yes @learnrudi/cli mcp "$1"
3495
3482
  fi
3496
3483
  `;
3497
- fs12.writeFileSync(SHIM_PATH2, shimContent, { mode: 493 });
3484
+ fs12.writeFileSync(SHIM_PATH, shimContent, { mode: 493 });
3498
3485
  }
3499
3486
  function buildNewEntry(stackName, agentId) {
3500
3487
  const base = {
3501
- command: SHIM_PATH2,
3488
+ command: SHIM_PATH,
3502
3489
  args: [stackName]
3503
3490
  };
3504
3491
  if (agentId === "claude-desktop" || agentId === "claude-code") {
@@ -3676,8 +3663,8 @@ Stacks migrated to: ${import_env7.PATHS.stacks}`);
3676
3663
  async function migrateConfigs(flags) {
3677
3664
  console.log("=== Updating Agent Configs ===\n");
3678
3665
  if (!flags.dryRun) {
3679
- ensureShim2();
3680
- console.log(`Shim ready: ${SHIM_PATH2}
3666
+ ensureShim();
3667
+ console.log(`Shim ready: ${SHIM_PATH}
3681
3668
  `);
3682
3669
  }
3683
3670
  let installedStacks = [];
@@ -3847,6 +3834,452 @@ After configuring secrets, run: rudi index`);
3847
3834
  }
3848
3835
  }
3849
3836
 
3837
+ // src/commands/status.js
3838
+ var import_core12 = require("@learnrudi/core");
3839
+ var import_child_process7 = require("child_process");
3840
+ var import_fs10 = __toESM(require("fs"), 1);
3841
+ var import_path7 = __toESM(require("path"), 1);
3842
+ var import_os4 = __toESM(require("os"), 1);
3843
+ var AGENTS = [
3844
+ {
3845
+ id: "claude",
3846
+ name: "Claude Code",
3847
+ npmPackage: "@anthropic-ai/claude-code",
3848
+ credentialType: "keychain",
3849
+ keychainService: "Claude Code-credentials"
3850
+ },
3851
+ {
3852
+ id: "codex",
3853
+ name: "OpenAI Codex",
3854
+ npmPackage: "@openai/codex",
3855
+ credentialType: "file",
3856
+ credentialPath: "~/.codex/auth.json"
3857
+ },
3858
+ {
3859
+ id: "gemini",
3860
+ name: "Gemini CLI",
3861
+ npmPackage: "@google/gemini-cli",
3862
+ credentialType: "file",
3863
+ credentialPath: "~/.gemini/google_accounts.json"
3864
+ },
3865
+ {
3866
+ id: "copilot",
3867
+ name: "GitHub Copilot",
3868
+ npmPackage: "@githubnext/github-copilot-cli",
3869
+ credentialType: "file",
3870
+ credentialPath: "~/.config/github-copilot/hosts.json"
3871
+ }
3872
+ ];
3873
+ var RUNTIMES = [
3874
+ { id: "node", name: "Node.js", command: "node", versionFlag: "--version" },
3875
+ { id: "python", name: "Python", command: "python3", versionFlag: "--version" },
3876
+ { id: "deno", name: "Deno", command: "deno", versionFlag: "--version" },
3877
+ { id: "bun", name: "Bun", command: "bun", versionFlag: "--version" }
3878
+ ];
3879
+ var BINARIES = [
3880
+ { id: "ffmpeg", name: "FFmpeg", command: "ffmpeg", versionFlag: "-version" },
3881
+ { id: "ripgrep", name: "ripgrep", command: "rg", versionFlag: "--version" },
3882
+ { id: "git", name: "Git", command: "git", versionFlag: "--version" },
3883
+ { id: "pandoc", name: "Pandoc", command: "pandoc", versionFlag: "--version" },
3884
+ { id: "jq", name: "jq", command: "jq", versionFlag: "--version" }
3885
+ ];
3886
+ function fileExists(filePath) {
3887
+ const resolved = filePath.replace("~", import_os4.default.homedir());
3888
+ return import_fs10.default.existsSync(resolved);
3889
+ }
3890
+ function checkKeychain(service) {
3891
+ if (process.platform !== "darwin") return false;
3892
+ try {
3893
+ (0, import_child_process7.execSync)(`security find-generic-password -s "${service}"`, {
3894
+ stdio: ["pipe", "pipe", "pipe"]
3895
+ });
3896
+ return true;
3897
+ } catch {
3898
+ return false;
3899
+ }
3900
+ }
3901
+ function getVersion(command, versionFlag) {
3902
+ try {
3903
+ const output = (0, import_child_process7.execSync)(`${command} ${versionFlag} 2>&1`, {
3904
+ encoding: "utf-8",
3905
+ timeout: 5e3,
3906
+ stdio: ["pipe", "pipe", "pipe"]
3907
+ });
3908
+ const match = output.match(/(\d+\.\d+\.?\d*)/);
3909
+ return match ? match[1] : output.trim().split("\n")[0].slice(0, 50);
3910
+ } catch {
3911
+ return null;
3912
+ }
3913
+ }
3914
+ function findBinary(command, kind = "binary") {
3915
+ const rudiPaths = [
3916
+ import_path7.default.join(import_core12.PATHS.agents, command, "node_modules", ".bin", command),
3917
+ import_path7.default.join(import_core12.PATHS.runtimes, command, "bin", command),
3918
+ import_path7.default.join(import_core12.PATHS.binaries, command, command),
3919
+ import_path7.default.join(import_core12.PATHS.binaries, command)
3920
+ ];
3921
+ for (const p of rudiPaths) {
3922
+ if (import_fs10.default.existsSync(p)) {
3923
+ return { found: true, path: p, source: "rudi" };
3924
+ }
3925
+ }
3926
+ try {
3927
+ const output = (0, import_child_process7.execSync)(`which ${command} 2>/dev/null`, {
3928
+ encoding: "utf-8",
3929
+ timeout: 3e3
3930
+ });
3931
+ const globalPath = output.trim();
3932
+ if (globalPath) {
3933
+ return { found: true, path: globalPath, source: "global" };
3934
+ }
3935
+ } catch {
3936
+ }
3937
+ return { found: false, path: null, source: null };
3938
+ }
3939
+ function getAgentStatus(agent) {
3940
+ const binaryPath = import_path7.default.join(import_core12.PATHS.agents, agent.id, "node_modules", ".bin", agent.id);
3941
+ const installed = import_fs10.default.existsSync(binaryPath);
3942
+ let authenticated = false;
3943
+ if (agent.credentialType === "keychain") {
3944
+ authenticated = checkKeychain(agent.keychainService);
3945
+ } else if (agent.credentialType === "file") {
3946
+ authenticated = fileExists(agent.credentialPath);
3947
+ }
3948
+ let version = null;
3949
+ if (installed) {
3950
+ version = getVersion(binaryPath, "--version");
3951
+ }
3952
+ return {
3953
+ id: agent.id,
3954
+ name: agent.name,
3955
+ installed,
3956
+ authenticated,
3957
+ version,
3958
+ path: installed ? binaryPath : null,
3959
+ ready: installed && authenticated
3960
+ };
3961
+ }
3962
+ function getRuntimeStatus(runtime) {
3963
+ const location = findBinary(runtime.command, "runtime");
3964
+ const version = location.found ? getVersion(location.path, runtime.versionFlag) : null;
3965
+ return {
3966
+ id: runtime.id,
3967
+ name: runtime.name,
3968
+ installed: location.found,
3969
+ version,
3970
+ path: location.path,
3971
+ source: location.source
3972
+ };
3973
+ }
3974
+ function getBinaryStatus(binary) {
3975
+ const location = findBinary(binary.command, "binary");
3976
+ const version = location.found ? getVersion(location.path, binary.versionFlag) : null;
3977
+ return {
3978
+ id: binary.id,
3979
+ name: binary.name,
3980
+ installed: location.found,
3981
+ version,
3982
+ path: location.path,
3983
+ source: location.source
3984
+ };
3985
+ }
3986
+ async function getFullStatus() {
3987
+ const agents = AGENTS.map(getAgentStatus);
3988
+ const runtimes = RUNTIMES.map(getRuntimeStatus);
3989
+ const binaries = BINARIES.map(getBinaryStatus);
3990
+ let stacks = [];
3991
+ let prompts = [];
3992
+ try {
3993
+ stacks = (0, import_core12.getInstalledPackages)("stack").map((s) => ({
3994
+ id: s.id,
3995
+ name: s.name,
3996
+ version: s.version
3997
+ }));
3998
+ prompts = (0, import_core12.getInstalledPackages)("prompt").map((p) => ({
3999
+ id: p.id,
4000
+ name: p.name,
4001
+ category: p.category
4002
+ }));
4003
+ } catch {
4004
+ }
4005
+ const directories = {
4006
+ home: { path: import_core12.PATHS.home, exists: import_fs10.default.existsSync(import_core12.PATHS.home) },
4007
+ stacks: { path: import_core12.PATHS.stacks, exists: import_fs10.default.existsSync(import_core12.PATHS.stacks) },
4008
+ agents: { path: import_core12.PATHS.agents, exists: import_fs10.default.existsSync(import_core12.PATHS.agents) },
4009
+ runtimes: { path: import_core12.PATHS.runtimes, exists: import_fs10.default.existsSync(import_core12.PATHS.runtimes) },
4010
+ binaries: { path: import_core12.PATHS.binaries, exists: import_fs10.default.existsSync(import_core12.PATHS.binaries) },
4011
+ db: { path: import_core12.PATHS.db, exists: import_fs10.default.existsSync(import_core12.PATHS.db) }
4012
+ };
4013
+ const summary = {
4014
+ agentsInstalled: agents.filter((a) => a.installed).length,
4015
+ agentsReady: agents.filter((a) => a.ready).length,
4016
+ agentsTotal: agents.length,
4017
+ runtimesInstalled: runtimes.filter((r) => r.installed).length,
4018
+ runtimesTotal: runtimes.length,
4019
+ binariesInstalled: binaries.filter((b) => b.installed).length,
4020
+ binariesTotal: binaries.length,
4021
+ stacksInstalled: stacks.length,
4022
+ promptsInstalled: prompts.length
4023
+ };
4024
+ return {
4025
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4026
+ platform: `${process.platform}-${process.arch}`,
4027
+ rudiHome: import_core12.PATHS.home,
4028
+ summary,
4029
+ agents,
4030
+ runtimes,
4031
+ binaries,
4032
+ stacks,
4033
+ prompts,
4034
+ directories
4035
+ };
4036
+ }
4037
+ function printStatus(status, filter) {
4038
+ console.log("RUDI Status");
4039
+ console.log("=".repeat(50));
4040
+ console.log(`Platform: ${status.platform}`);
4041
+ console.log(`RUDI Home: ${status.rudiHome}`);
4042
+ console.log("");
4043
+ if (!filter || filter === "agents") {
4044
+ console.log(`AGENTS (${status.summary.agentsReady}/${status.summary.agentsTotal} ready)`);
4045
+ console.log("-".repeat(50));
4046
+ for (const agent of status.agents) {
4047
+ const installIcon = agent.installed ? "\x1B[32m\u2713\x1B[0m" : "\x1B[31m\u2717\x1B[0m";
4048
+ const authIcon = agent.authenticated ? "\x1B[32m\u2713\x1B[0m" : "\x1B[33m\u25CB\x1B[0m";
4049
+ const version = agent.version ? `v${agent.version}` : "";
4050
+ console.log(` ${installIcon} ${agent.name} ${version}`);
4051
+ console.log(` Installed: ${agent.installed ? "yes" : "no"}, Auth: ${agent.authenticated ? "yes" : "no"}`);
4052
+ }
4053
+ console.log("");
4054
+ }
4055
+ if (!filter || filter === "runtimes") {
4056
+ console.log(`RUNTIMES (${status.summary.runtimesInstalled}/${status.summary.runtimesTotal})`);
4057
+ console.log("-".repeat(50));
4058
+ for (const rt of status.runtimes) {
4059
+ const icon = rt.installed ? "\x1B[32m\u2713\x1B[0m" : "\x1B[90m\u25CB\x1B[0m";
4060
+ const version = rt.version ? `v${rt.version}` : "";
4061
+ const source = rt.source ? `(${rt.source})` : "";
4062
+ console.log(` ${icon} ${rt.name} ${version} ${source}`);
4063
+ }
4064
+ console.log("");
4065
+ }
4066
+ if (!filter || filter === "binaries") {
4067
+ console.log(`BINARIES (${status.summary.binariesInstalled}/${status.summary.binariesTotal})`);
4068
+ console.log("-".repeat(50));
4069
+ for (const bin of status.binaries) {
4070
+ const icon = bin.installed ? "\x1B[32m\u2713\x1B[0m" : "\x1B[90m\u25CB\x1B[0m";
4071
+ const version = bin.version ? `v${bin.version}` : "";
4072
+ const source = bin.source ? `(${bin.source})` : "";
4073
+ console.log(` ${icon} ${bin.name} ${version} ${source}`);
4074
+ }
4075
+ console.log("");
4076
+ }
4077
+ if (!filter || filter === "stacks") {
4078
+ console.log(`STACKS (${status.summary.stacksInstalled})`);
4079
+ console.log("-".repeat(50));
4080
+ if (status.stacks.length === 0) {
4081
+ console.log(" No stacks installed");
4082
+ } else {
4083
+ for (const stack of status.stacks) {
4084
+ console.log(` ${stack.id} v${stack.version || "?"}`);
4085
+ }
4086
+ }
4087
+ console.log("");
4088
+ }
4089
+ console.log("SUMMARY");
4090
+ console.log("-".repeat(50));
4091
+ console.log(` Agents ready: ${status.summary.agentsReady}/${status.summary.agentsTotal}`);
4092
+ console.log(` Runtimes: ${status.summary.runtimesInstalled}/${status.summary.runtimesTotal}`);
4093
+ console.log(` Binaries: ${status.summary.binariesInstalled}/${status.summary.binariesTotal}`);
4094
+ console.log(` Stacks: ${status.summary.stacksInstalled}`);
4095
+ console.log(` Prompts: ${status.summary.promptsInstalled}`);
4096
+ }
4097
+ async function cmdStatus(args, flags) {
4098
+ const filter = args[0];
4099
+ const status = await getFullStatus();
4100
+ if (flags.json) {
4101
+ if (filter) {
4102
+ const filtered = {
4103
+ timestamp: status.timestamp,
4104
+ platform: status.platform,
4105
+ [filter]: status[filter]
4106
+ };
4107
+ console.log(JSON.stringify(filtered, null, 2));
4108
+ } else {
4109
+ console.log(JSON.stringify(status, null, 2));
4110
+ }
4111
+ } else {
4112
+ printStatus(status, filter);
4113
+ }
4114
+ }
4115
+
4116
+ // src/commands/check.js
4117
+ var import_core13 = require("@learnrudi/core");
4118
+ var import_child_process8 = require("child_process");
4119
+ var import_fs11 = __toESM(require("fs"), 1);
4120
+ var import_path8 = __toESM(require("path"), 1);
4121
+ var import_os5 = __toESM(require("os"), 1);
4122
+ var AGENT_CREDENTIALS = {
4123
+ claude: { type: "keychain", service: "Claude Code-credentials" },
4124
+ codex: { type: "file", path: "~/.codex/auth.json" },
4125
+ gemini: { type: "file", path: "~/.gemini/google_accounts.json" },
4126
+ copilot: { type: "file", path: "~/.config/github-copilot/hosts.json" }
4127
+ };
4128
+ function fileExists2(filePath) {
4129
+ const resolved = filePath.replace("~", import_os5.default.homedir());
4130
+ return import_fs11.default.existsSync(resolved);
4131
+ }
4132
+ function checkKeychain2(service) {
4133
+ if (process.platform !== "darwin") return false;
4134
+ try {
4135
+ (0, import_child_process8.execSync)(`security find-generic-password -s "${service}"`, {
4136
+ stdio: ["pipe", "pipe", "pipe"]
4137
+ });
4138
+ return true;
4139
+ } catch {
4140
+ return false;
4141
+ }
4142
+ }
4143
+ function getVersion2(binaryPath, versionFlag = "--version") {
4144
+ try {
4145
+ const output = (0, import_child_process8.execSync)(`"${binaryPath}" ${versionFlag} 2>&1`, {
4146
+ encoding: "utf-8",
4147
+ timeout: 5e3
4148
+ });
4149
+ const match = output.match(/(\d+\.\d+\.?\d*)/);
4150
+ return match ? match[1] : null;
4151
+ } catch {
4152
+ return null;
4153
+ }
4154
+ }
4155
+ async function cmdCheck(args, flags) {
4156
+ const packageId = args[0];
4157
+ if (!packageId) {
4158
+ console.error("Usage: rudi check <package-id>");
4159
+ console.error("Examples:");
4160
+ console.error(" rudi check agent:claude");
4161
+ console.error(" rudi check runtime:python");
4162
+ console.error(" rudi check binary:ffmpeg");
4163
+ console.error(" rudi check stack:slack");
4164
+ process.exit(1);
4165
+ }
4166
+ let kind, name;
4167
+ if (packageId.includes(":")) {
4168
+ [kind, name] = packageId.split(":");
4169
+ } else {
4170
+ if (["claude", "codex", "gemini", "copilot"].includes(packageId)) {
4171
+ kind = "agent";
4172
+ name = packageId;
4173
+ } else if (["node", "python", "deno", "bun"].includes(packageId)) {
4174
+ kind = "runtime";
4175
+ name = packageId;
4176
+ } else {
4177
+ kind = "stack";
4178
+ name = packageId;
4179
+ }
4180
+ }
4181
+ const result = {
4182
+ id: `${kind}:${name}`,
4183
+ kind,
4184
+ name,
4185
+ installed: false,
4186
+ authenticated: null,
4187
+ // Only for agents
4188
+ ready: false,
4189
+ path: null,
4190
+ version: null
4191
+ };
4192
+ switch (kind) {
4193
+ case "agent": {
4194
+ const binaryPath = import_path8.default.join(import_core13.PATHS.agents, name, "node_modules", ".bin", name);
4195
+ result.installed = import_fs11.default.existsSync(binaryPath);
4196
+ result.path = result.installed ? binaryPath : null;
4197
+ if (result.installed) {
4198
+ result.version = getVersion2(binaryPath);
4199
+ }
4200
+ const cred = AGENT_CREDENTIALS[name];
4201
+ if (cred) {
4202
+ if (cred.type === "keychain") {
4203
+ result.authenticated = checkKeychain2(cred.service);
4204
+ } else if (cred.type === "file") {
4205
+ result.authenticated = fileExists2(cred.path);
4206
+ }
4207
+ }
4208
+ result.ready = result.installed && result.authenticated;
4209
+ break;
4210
+ }
4211
+ case "runtime": {
4212
+ const rudiPath = import_path8.default.join(import_core13.PATHS.runtimes, name, "bin", name);
4213
+ if (import_fs11.default.existsSync(rudiPath)) {
4214
+ result.installed = true;
4215
+ result.path = rudiPath;
4216
+ result.version = getVersion2(rudiPath);
4217
+ } else {
4218
+ try {
4219
+ const globalPath = (0, import_child_process8.execSync)(`which ${name} 2>/dev/null`, { encoding: "utf-8" }).trim();
4220
+ if (globalPath) {
4221
+ result.installed = true;
4222
+ result.path = globalPath;
4223
+ result.version = getVersion2(globalPath);
4224
+ }
4225
+ } catch {
4226
+ }
4227
+ }
4228
+ result.ready = result.installed;
4229
+ break;
4230
+ }
4231
+ case "binary": {
4232
+ const rudiPath = import_path8.default.join(import_core13.PATHS.binaries, name, name);
4233
+ if (import_fs11.default.existsSync(rudiPath)) {
4234
+ result.installed = true;
4235
+ result.path = rudiPath;
4236
+ } else {
4237
+ try {
4238
+ const globalPath = (0, import_child_process8.execSync)(`which ${name} 2>/dev/null`, { encoding: "utf-8" }).trim();
4239
+ if (globalPath) {
4240
+ result.installed = true;
4241
+ result.path = globalPath;
4242
+ }
4243
+ } catch {
4244
+ }
4245
+ }
4246
+ result.ready = result.installed;
4247
+ break;
4248
+ }
4249
+ case "stack": {
4250
+ result.installed = (0, import_core13.isPackageInstalled)(`stack:${name}`);
4251
+ if (result.installed) {
4252
+ result.path = (0, import_core13.getPackagePath)(`stack:${name}`);
4253
+ }
4254
+ result.ready = result.installed;
4255
+ break;
4256
+ }
4257
+ default:
4258
+ console.error(`Unknown package kind: ${kind}`);
4259
+ process.exit(1);
4260
+ }
4261
+ if (flags.json) {
4262
+ console.log(JSON.stringify(result, null, 2));
4263
+ } else {
4264
+ const installIcon = result.installed ? "\x1B[32m\u2713\x1B[0m" : "\x1B[31m\u2717\x1B[0m";
4265
+ console.log(`${installIcon} ${result.id}`);
4266
+ console.log(` Installed: ${result.installed}`);
4267
+ if (result.path) console.log(` Path: ${result.path}`);
4268
+ if (result.version) console.log(` Version: ${result.version}`);
4269
+ if (result.authenticated !== null) {
4270
+ console.log(` Authenticated: ${result.authenticated}`);
4271
+ }
4272
+ console.log(` Ready: ${result.ready}`);
4273
+ }
4274
+ if (!result.installed) {
4275
+ process.exit(1);
4276
+ } else if (result.authenticated === false) {
4277
+ process.exit(2);
4278
+ } else {
4279
+ process.exit(0);
4280
+ }
4281
+ }
4282
+
3850
4283
  // src/index.js
3851
4284
  var VERSION = "2.0.0";
3852
4285
  async function main() {
@@ -3894,7 +4327,6 @@ async function main() {
3894
4327
  await cmdImport(args, flags);
3895
4328
  break;
3896
4329
  case "doctor":
3897
- case "check":
3898
4330
  await cmdDoctor(args, flags);
3899
4331
  break;
3900
4332
  case "init":
@@ -3932,9 +4364,14 @@ async function main() {
3932
4364
  await cmdIndex(args, flags);
3933
4365
  break;
3934
4366
  case "home":
3935
- case "status":
3936
4367
  await cmdHome(args, flags);
3937
4368
  break;
4369
+ case "status":
4370
+ await cmdStatus(args, flags);
4371
+ break;
4372
+ case "check":
4373
+ await cmdCheck(args, flags);
4374
+ break;
3938
4375
  // Shortcuts for listing specific package types
3939
4376
  case "stacks":
3940
4377
  await cmdList(["stacks"], flags);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@learnrudi/cli",
3
- "version": "1.9.1",
3
+ "version": "1.9.3",
4
4
  "description": "RUDI CLI - Install and manage MCP stacks, runtimes, and AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",