@cleocode/caamp 0.2.0 → 0.3.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/dist/cli.js CHANGED
@@ -24,6 +24,7 @@ import {
24
24
  listMcpServers,
25
25
  parseSource,
26
26
  readConfig,
27
+ readLockFile,
27
28
  recordMcpInstall,
28
29
  recordSkillInstall,
29
30
  removeMcpFromLock,
@@ -33,9 +34,11 @@ import {
33
34
  resolveConfigPath,
34
35
  scanDirectory,
35
36
  scanFile,
37
+ setQuiet,
38
+ setVerbose,
36
39
  toSarif,
37
40
  validateSkill
38
- } from "./chunk-RW745KDU.js";
41
+ } from "./chunk-PCWTRJV2.js";
39
42
 
40
43
  // src/cli.ts
41
44
  import { Command } from "commander";
@@ -208,7 +211,7 @@ function registerSkillsInstall(parent) {
208
211
  let cleanup;
209
212
  let skillName;
210
213
  let sourceValue;
211
- let sourceType = "github";
214
+ let sourceType;
212
215
  if (isMarketplaceScoped(source)) {
213
216
  console.log(pc2.dim(`Searching marketplace for ${source}...`));
214
217
  const client = new MarketplaceClient();
@@ -228,10 +231,12 @@ function registerSkillsInstall(parent) {
228
231
  cleanup = result.cleanup;
229
232
  skillName = skill.name;
230
233
  sourceValue = skill.githubUrl;
234
+ sourceType = parsed.type;
231
235
  } else {
232
236
  const parsed = parseSource(source);
233
237
  skillName = parsed.inferredName;
234
238
  sourceValue = parsed.value;
239
+ sourceType = parsed.type;
235
240
  if (parsed.type === "github" && parsed.owner && parsed.repo) {
236
241
  const result = await cloneRepo(parsed.owner, parsed.repo, parsed.ref, parsed.path);
237
242
  localPath = result.localPath;
@@ -1059,13 +1064,272 @@ ${provider.toolName} config (${configPath}):
1059
1064
  });
1060
1065
  }
1061
1066
 
1067
+ // src/commands/doctor.ts
1068
+ import pc19 from "picocolors";
1069
+ import { execFileSync } from "child_process";
1070
+ import { existsSync as existsSync5, readdirSync, lstatSync } from "fs";
1071
+ import { homedir } from "os";
1072
+ import { join as join6 } from "path";
1073
+ var CAAMP_VERSION = "0.2.0";
1074
+ function getNodeVersion() {
1075
+ return process.version;
1076
+ }
1077
+ function getNpmVersion() {
1078
+ try {
1079
+ return execFileSync("npm", ["--version"], { stdio: "pipe", encoding: "utf-8" }).trim();
1080
+ } catch {
1081
+ return null;
1082
+ }
1083
+ }
1084
+ function checkEnvironment() {
1085
+ const checks = [];
1086
+ checks.push({ label: `Node.js ${getNodeVersion()}`, status: "pass" });
1087
+ const npmVersion = getNpmVersion();
1088
+ if (npmVersion) {
1089
+ checks.push({ label: `npm ${npmVersion}`, status: "pass" });
1090
+ } else {
1091
+ checks.push({ label: "npm not found", status: "warn" });
1092
+ }
1093
+ checks.push({ label: `CAAMP v${CAAMP_VERSION}`, status: "pass" });
1094
+ checks.push({ label: `${process.platform} ${process.arch}`, status: "pass" });
1095
+ return { name: "Environment", checks };
1096
+ }
1097
+ function checkRegistry() {
1098
+ const checks = [];
1099
+ try {
1100
+ const providers = getAllProviders();
1101
+ const count = getProviderCount();
1102
+ checks.push({ label: `${count} providers loaded`, status: "pass" });
1103
+ const malformed = [];
1104
+ for (const p of providers) {
1105
+ if (!p.id || !p.toolName || !p.configKey || !p.configFormat) {
1106
+ malformed.push(p.id || "(unknown)");
1107
+ }
1108
+ }
1109
+ if (malformed.length === 0) {
1110
+ checks.push({ label: "All entries valid", status: "pass" });
1111
+ } else {
1112
+ checks.push({
1113
+ label: `${malformed.length} malformed entries`,
1114
+ status: "fail",
1115
+ detail: malformed.join(", ")
1116
+ });
1117
+ }
1118
+ } catch (err) {
1119
+ checks.push({
1120
+ label: "Failed to load registry",
1121
+ status: "fail",
1122
+ detail: err instanceof Error ? err.message : String(err)
1123
+ });
1124
+ }
1125
+ return { name: "Registry", checks };
1126
+ }
1127
+ function checkInstalledProviders() {
1128
+ const checks = [];
1129
+ try {
1130
+ const results = detectAllProviders();
1131
+ const installed = results.filter((r) => r.installed);
1132
+ checks.push({ label: `${installed.length} found`, status: "pass" });
1133
+ for (const r of installed) {
1134
+ const methods = r.methods.join(", ");
1135
+ checks.push({ label: `${r.provider.toolName} (${methods})`, status: "pass" });
1136
+ }
1137
+ } catch (err) {
1138
+ checks.push({
1139
+ label: "Detection failed",
1140
+ status: "fail",
1141
+ detail: err instanceof Error ? err.message : String(err)
1142
+ });
1143
+ }
1144
+ return { name: "Installed Providers", checks };
1145
+ }
1146
+ function checkSkillSymlinks() {
1147
+ const checks = [];
1148
+ const canonicalDir = join6(homedir(), ".agents", "skills");
1149
+ if (!existsSync5(canonicalDir)) {
1150
+ checks.push({ label: "0 canonical skills", status: "pass" });
1151
+ checks.push({ label: "No broken symlinks", status: "pass" });
1152
+ return { name: "Skills", checks };
1153
+ }
1154
+ let canonicalCount = 0;
1155
+ try {
1156
+ const entries = readdirSync(canonicalDir);
1157
+ canonicalCount = entries.length;
1158
+ checks.push({ label: `${canonicalCount} canonical skills`, status: "pass" });
1159
+ } catch {
1160
+ checks.push({ label: "Cannot read skills directory", status: "warn" });
1161
+ return { name: "Skills", checks };
1162
+ }
1163
+ const broken = [];
1164
+ const providers = getAllProviders();
1165
+ for (const provider of providers) {
1166
+ const skillDir = provider.pathSkills;
1167
+ if (!existsSync5(skillDir)) continue;
1168
+ try {
1169
+ const entries = readdirSync(skillDir);
1170
+ for (const entry of entries) {
1171
+ const fullPath = join6(skillDir, entry);
1172
+ try {
1173
+ const stat = lstatSync(fullPath);
1174
+ if (stat.isSymbolicLink()) {
1175
+ if (!existsSync5(fullPath)) {
1176
+ broken.push(`${provider.id}/${entry}`);
1177
+ }
1178
+ }
1179
+ } catch {
1180
+ }
1181
+ }
1182
+ } catch {
1183
+ }
1184
+ }
1185
+ if (broken.length === 0) {
1186
+ checks.push({ label: "No broken symlinks", status: "pass" });
1187
+ } else {
1188
+ checks.push({
1189
+ label: `${broken.length} broken symlinks`,
1190
+ status: "warn",
1191
+ detail: broken.join(", ")
1192
+ });
1193
+ }
1194
+ return { name: "Skills", checks };
1195
+ }
1196
+ async function checkLockFile() {
1197
+ const checks = [];
1198
+ try {
1199
+ const lock = await readLockFile();
1200
+ checks.push({ label: "Lock file valid", status: "pass" });
1201
+ let orphaned = 0;
1202
+ for (const [name, entry] of Object.entries(lock.skills)) {
1203
+ if (entry.canonicalPath && !existsSync5(entry.canonicalPath)) {
1204
+ orphaned++;
1205
+ }
1206
+ }
1207
+ if (orphaned === 0) {
1208
+ checks.push({ label: `0 orphaned entries`, status: "pass" });
1209
+ } else {
1210
+ checks.push({
1211
+ label: `${orphaned} orphaned skill entries`,
1212
+ status: "warn",
1213
+ detail: "Skills tracked in lock file but missing from disk"
1214
+ });
1215
+ }
1216
+ } catch (err) {
1217
+ checks.push({
1218
+ label: "Failed to read lock file",
1219
+ status: "fail",
1220
+ detail: err instanceof Error ? err.message : String(err)
1221
+ });
1222
+ }
1223
+ return { name: "Lock File", checks };
1224
+ }
1225
+ async function checkConfigFiles() {
1226
+ const checks = [];
1227
+ const results = detectAllProviders();
1228
+ const installed = results.filter((r) => r.installed);
1229
+ for (const r of installed) {
1230
+ const provider = r.provider;
1231
+ const configPath = provider.configPathGlobal;
1232
+ if (!existsSync5(configPath)) {
1233
+ checks.push({
1234
+ label: `${provider.id}: no config file found`,
1235
+ status: "warn",
1236
+ detail: configPath
1237
+ });
1238
+ continue;
1239
+ }
1240
+ try {
1241
+ await readConfig(configPath, provider.configFormat);
1242
+ const relPath = configPath.replace(homedir(), "~");
1243
+ checks.push({
1244
+ label: `${provider.id}: ${relPath} readable`,
1245
+ status: "pass"
1246
+ });
1247
+ } catch (err) {
1248
+ checks.push({
1249
+ label: `${provider.id}: config parse error`,
1250
+ status: "fail",
1251
+ detail: err instanceof Error ? err.message : String(err)
1252
+ });
1253
+ }
1254
+ }
1255
+ if (installed.length === 0) {
1256
+ checks.push({ label: "No installed providers to check", status: "pass" });
1257
+ }
1258
+ return { name: "Config Files", checks };
1259
+ }
1260
+ function formatSection(section) {
1261
+ const lines = [];
1262
+ lines.push(` ${pc19.bold(section.name)}`);
1263
+ for (const check of section.checks) {
1264
+ const icon = check.status === "pass" ? pc19.green("\u2713") : check.status === "warn" ? pc19.yellow("\u26A0") : pc19.red("\u2717");
1265
+ lines.push(` ${icon} ${check.label}`);
1266
+ if (check.detail) {
1267
+ lines.push(` ${pc19.dim(check.detail)}`);
1268
+ }
1269
+ }
1270
+ return lines.join("\n");
1271
+ }
1272
+ function registerDoctorCommand(program2) {
1273
+ program2.command("doctor").description("Diagnose configuration issues and health").option("--json", "Output as JSON").action(async (opts) => {
1274
+ const sections = [];
1275
+ sections.push(checkEnvironment());
1276
+ sections.push(checkRegistry());
1277
+ sections.push(checkInstalledProviders());
1278
+ sections.push(checkSkillSymlinks());
1279
+ sections.push(await checkLockFile());
1280
+ sections.push(await checkConfigFiles());
1281
+ let passed = 0;
1282
+ let warnings = 0;
1283
+ let errors = 0;
1284
+ for (const section of sections) {
1285
+ for (const check of section.checks) {
1286
+ if (check.status === "pass") passed++;
1287
+ else if (check.status === "warn") warnings++;
1288
+ else errors++;
1289
+ }
1290
+ }
1291
+ if (opts.json) {
1292
+ const output = {
1293
+ version: CAAMP_VERSION,
1294
+ sections: sections.map((s) => ({
1295
+ name: s.name,
1296
+ checks: s.checks
1297
+ })),
1298
+ summary: { passed, warnings, errors }
1299
+ };
1300
+ console.log(JSON.stringify(output, null, 2));
1301
+ return;
1302
+ }
1303
+ console.log(pc19.bold("\ncaamp doctor\n"));
1304
+ for (const section of sections) {
1305
+ console.log(formatSection(section));
1306
+ console.log();
1307
+ }
1308
+ const parts = [];
1309
+ parts.push(pc19.green(`${passed} checks passed`));
1310
+ if (warnings > 0) parts.push(pc19.yellow(`${warnings} warning${warnings !== 1 ? "s" : ""}`));
1311
+ if (errors > 0) parts.push(pc19.red(`${errors} error${errors !== 1 ? "s" : ""}`));
1312
+ console.log(` ${pc19.bold("Summary")}: ${parts.join(", ")}`);
1313
+ console.log();
1314
+ if (errors > 0) {
1315
+ process.exit(1);
1316
+ }
1317
+ });
1318
+ }
1319
+
1062
1320
  // src/cli.ts
1063
1321
  var program = new Command();
1064
- program.name("caamp").description("Central AI Agent Managed Packages - unified provider registry and package manager").version("0.2.0");
1322
+ program.name("caamp").description("Central AI Agent Managed Packages - unified provider registry and package manager").version("0.3.0").option("-v, --verbose", "Show debug output").option("-q, --quiet", "Suppress non-error output");
1323
+ program.hook("preAction", (thisCommand) => {
1324
+ const opts = thisCommand.optsWithGlobals();
1325
+ if (opts.verbose) setVerbose(true);
1326
+ if (opts.quiet) setQuiet(true);
1327
+ });
1065
1328
  registerProvidersCommand(program);
1066
1329
  registerSkillsCommands(program);
1067
1330
  registerMcpCommands(program);
1068
1331
  registerInstructionsCommands(program);
1069
1332
  registerConfigCommand(program);
1333
+ registerDoctorCommand(program);
1070
1334
  program.parse();
1071
1335
  //# sourceMappingURL=cli.js.map