@aipm-registry/cli 0.1.8 → 0.2.2

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.cjs CHANGED
@@ -4155,13 +4155,19 @@ var init_zod = __esm({
4155
4155
  });
4156
4156
 
4157
4157
  // ../../packages/schemas/dist/manifest.js
4158
- var AiToolSchema, PackageManifestSchema;
4158
+ function expandTargets(targets) {
4159
+ if (targets.includes("*"))
4160
+ return [...ALL_TOOLS];
4161
+ return targets.filter((t) => t !== "*");
4162
+ }
4163
+ var AiToolSchema, ALL_TOOLS, PackageManifestSchema;
4159
4164
  var init_manifest = __esm({
4160
4165
  "../../packages/schemas/dist/manifest.js"() {
4161
4166
  "use strict";
4162
4167
  init_zod();
4163
4168
  init_scope_name();
4164
- AiToolSchema = external_exports.enum(["cursor", "claude"]);
4169
+ AiToolSchema = external_exports.enum(["cursor", "claude", "*"]);
4170
+ ALL_TOOLS = ["cursor", "claude"];
4165
4171
  PackageManifestSchema = external_exports.object({
4166
4172
  schemaVersion: external_exports.literal("0.1"),
4167
4173
  name: external_exports.string().regex(SCOPE_NAME_REGEX, "name must be @scope/name"),
@@ -4245,26 +4251,36 @@ async function detectToolsInProject(projectRoot) {
4245
4251
  detected.push("claude");
4246
4252
  return detected;
4247
4253
  }
4254
+ function manifestAllowsTool(manifest, tool) {
4255
+ if (manifest.targets.includes("*"))
4256
+ return true;
4257
+ return manifest.targets.includes(tool);
4258
+ }
4248
4259
  async function resolveInstallTools(options) {
4249
4260
  const { manifest } = options;
4250
- const allowed = new Set(manifest.targets);
4261
+ const expandedAllowed = expandTargets(manifest.targets);
4251
4262
  if (options.explicitTarget) {
4252
- if (!allowed.has(options.explicitTarget)) {
4263
+ if (options.explicitTarget === "*") {
4264
+ return [...ALL_TOOLS];
4265
+ }
4266
+ if (!manifestAllowsTool(manifest, options.explicitTarget)) {
4253
4267
  throw new Error(`Package ${manifest.name} does not target "${options.explicitTarget}". Targets: ${manifest.targets.join(", ")}`);
4254
4268
  }
4255
4269
  return [options.explicitTarget];
4256
4270
  }
4257
4271
  const detected = await detectToolsInProject(options.projectRoot);
4258
4272
  if (detected.length > 0) {
4259
- const intersection = detected.filter((t) => allowed.has(t));
4273
+ const intersection = detected.filter((t) => manifestAllowsTool(manifest, t));
4260
4274
  if (intersection.length === 0) {
4261
4275
  throw new Error(`Package ${manifest.name} targets [${manifest.targets.join(", ")}] but project only has [${detected.join(", ")}].`);
4262
4276
  }
4263
4277
  return intersection;
4264
4278
  }
4265
- const preferred = (options.preferredTools ?? []).filter((t) => allowed.has(t));
4279
+ const preferred = expandTargets(options.preferredTools ?? []).filter((t) => manifestAllowsTool(manifest, t));
4266
4280
  if (preferred.length > 0)
4267
4281
  return preferred;
4282
+ if (manifest.targets.includes("*"))
4283
+ return expandedAllowed;
4268
4284
  return [];
4269
4285
  }
4270
4286
  var import_promises, import_node_path2;
@@ -4273,6 +4289,7 @@ var init_detect_tools = __esm({
4273
4289
  "use strict";
4274
4290
  import_promises = require("node:fs/promises");
4275
4291
  import_node_path2 = require("node:path");
4292
+ init_dist();
4276
4293
  }
4277
4294
  });
4278
4295
 
@@ -4331,7 +4348,7 @@ async function installSkillPackage(options) {
4331
4348
  explicitTarget: options.explicitTarget
4332
4349
  });
4333
4350
  if (tools.length === 0) {
4334
- throw new Error("No AI tool detected (.cursor/ or .claude/). Use --target cursor|claude or set preferredTools in aipm.package.json.");
4351
+ throw new Error("No AI tool detected (.cursor/ or .claude/). Use --target cursor|claude|* or set preferredTools in aipm.package.json.");
4335
4352
  }
4336
4353
  const installed = {};
4337
4354
  for (const tool of tools) {
@@ -4375,74 +4392,6 @@ var init_dist4 = __esm({
4375
4392
  }
4376
4393
  });
4377
4394
 
4378
- // src/project-files.ts
4379
- var project_files_exports = {};
4380
- __export(project_files_exports, {
4381
- parseTargetFlag: () => parseTargetFlag,
4382
- readLockfile: () => readLockfile,
4383
- readProjectPackageJson: () => readProjectPackageJson,
4384
- resolveRegistryUrl: () => resolveRegistryUrl,
4385
- upsertLockEntry: () => upsertLockEntry,
4386
- writeLockfile: () => writeLockfile,
4387
- writeProjectPackageJson: () => writeProjectPackageJson
4388
- });
4389
- async function readProjectPackageJson(projectRoot) {
4390
- try {
4391
- const raw = await (0, import_promises7.readFile)((0, import_node_path7.join)(projectRoot, PACKAGE_JSON), "utf8");
4392
- return ProjectPackageJsonSchema.parse(JSON.parse(raw));
4393
- } catch {
4394
- return null;
4395
- }
4396
- }
4397
- async function writeProjectPackageJson(projectRoot, data) {
4398
- await (0, import_promises7.writeFile)(
4399
- (0, import_node_path7.join)(projectRoot, PACKAGE_JSON),
4400
- JSON.stringify(data, null, 2) + "\n",
4401
- "utf8"
4402
- );
4403
- }
4404
- async function readLockfile(projectRoot) {
4405
- try {
4406
- const raw = await (0, import_promises7.readFile)((0, import_node_path7.join)(projectRoot, LOCKFILE), "utf8");
4407
- return LockfileSchema.parse(JSON.parse(raw));
4408
- } catch {
4409
- return null;
4410
- }
4411
- }
4412
- async function writeLockfile(projectRoot, data) {
4413
- await (0, import_promises7.writeFile)((0, import_node_path7.join)(projectRoot, LOCKFILE), JSON.stringify(data, null, 2) + "\n", "utf8");
4414
- }
4415
- function upsertLockEntry(lock, name, entry) {
4416
- return {
4417
- ...lock,
4418
- packages: { ...lock.packages, [name]: entry }
4419
- };
4420
- }
4421
- function resolveRegistryUrl(project, flag, fallback) {
4422
- const fromEnv = process.env.AIPM_REGISTRY_URL ?? process.env.AIPM_REGISTRY;
4423
- if (flag) return flag.replace(/\/$/, "");
4424
- if (project?.registry) return project.registry.replace(/\/$/, "");
4425
- if (fromEnv) return fromEnv.replace(/\/$/, "");
4426
- if (fallback) return fallback.replace(/\/$/, "");
4427
- throw new Error("Registry URL required: set registry in aipm.package.json or AIPM_REGISTRY_URL");
4428
- }
4429
- function parseTargetFlag(target) {
4430
- if (!target) return void 0;
4431
- if (target === "cursor" || target === "claude") return target;
4432
- throw new Error('--target must be "cursor" or "claude"');
4433
- }
4434
- var import_promises7, import_node_path7, PACKAGE_JSON, LOCKFILE;
4435
- var init_project_files = __esm({
4436
- "src/project-files.ts"() {
4437
- "use strict";
4438
- import_promises7 = require("node:fs/promises");
4439
- import_node_path7 = require("node:path");
4440
- init_dist();
4441
- PACKAGE_JSON = "aipm.package.json";
4442
- LOCKFILE = "aipm-lock.json";
4443
- }
4444
- });
4445
-
4446
4395
  // ../../node_modules/.pnpm/commander@15.0.0/node_modules/commander/lib/error.js
4447
4396
  var CommanderError = class extends Error {
4448
4397
  /**
@@ -7814,8 +7763,8 @@ var program = new Command();
7814
7763
  // src/bin.ts
7815
7764
  var import_node_child_process4 = require("node:child_process");
7816
7765
  var import_promises9 = require("node:fs/promises");
7817
- var import_node_path10 = require("node:path");
7818
- var import_node_process3 = require("node:process");
7766
+ var import_node_path11 = require("node:path");
7767
+ var import_node_process5 = require("node:process");
7819
7768
  init_dist();
7820
7769
  init_dist4();
7821
7770
 
@@ -8040,14 +7989,14 @@ async function packStagedFiles(root) {
8040
7989
  }
8041
7990
  async function unpackTarballToBuffer(tarball, entry) {
8042
7991
  const { mkdtemp: mkdtemp2, rm: rm3, writeFile: writeFile6 } = await import("node:fs/promises");
8043
- const { join: join9 } = await import("node:path");
7992
+ const { join: join11 } = await import("node:path");
8044
7993
  const { tmpdir: tmpdir2 } = await import("node:os");
8045
- const tempDir = await mkdtemp2(join9(tmpdir2(), "aipm-install-"));
8046
- const tgzPath = join9(tempDir, "pkg.tgz");
7994
+ const tempDir = await mkdtemp2(join11(tmpdir2(), "aipm-install-"));
7995
+ const tgzPath = join11(tempDir, "pkg.tgz");
8047
7996
  try {
8048
7997
  await writeFile6(tgzPath, tarball);
8049
7998
  await execFileAsync("tar", ["-xzf", tgzPath, "-C", tempDir]);
8050
- return (0, import_promises5.readFile)(join9(tempDir, entry), "utf8");
7999
+ return (0, import_promises5.readFile)(join11(tempDir, entry), "utf8");
8051
8000
  } finally {
8052
8001
  await rm3(tempDir, { recursive: true, force: true });
8053
8002
  }
@@ -8123,7 +8072,70 @@ async function fetchPackageTarball(registry, name, version) {
8123
8072
 
8124
8073
  // src/install-one.ts
8125
8074
  init_dist4();
8126
- init_project_files();
8075
+
8076
+ // src/project-files.ts
8077
+ var import_promises7 = require("node:fs/promises");
8078
+ var import_node_path7 = require("node:path");
8079
+ init_dist();
8080
+ var PACKAGE_JSON = "aipm.package.json";
8081
+ var LOCKFILE = "aipm-lock.json";
8082
+ async function readProjectPackageJson(projectRoot) {
8083
+ try {
8084
+ const raw = await (0, import_promises7.readFile)((0, import_node_path7.join)(projectRoot, PACKAGE_JSON), "utf8");
8085
+ return ProjectPackageJsonSchema.parse(JSON.parse(raw));
8086
+ } catch {
8087
+ return null;
8088
+ }
8089
+ }
8090
+ async function writeProjectPackageJson(projectRoot, data) {
8091
+ await (0, import_promises7.writeFile)(
8092
+ (0, import_node_path7.join)(projectRoot, PACKAGE_JSON),
8093
+ JSON.stringify(data, null, 2) + "\n",
8094
+ "utf8"
8095
+ );
8096
+ }
8097
+ async function readLockfile(projectRoot) {
8098
+ try {
8099
+ const raw = await (0, import_promises7.readFile)((0, import_node_path7.join)(projectRoot, LOCKFILE), "utf8");
8100
+ return LockfileSchema.parse(JSON.parse(raw));
8101
+ } catch {
8102
+ return null;
8103
+ }
8104
+ }
8105
+ async function writeLockfile(projectRoot, data) {
8106
+ await (0, import_promises7.writeFile)((0, import_node_path7.join)(projectRoot, LOCKFILE), JSON.stringify(data, null, 2) + "\n", "utf8");
8107
+ }
8108
+ function upsertLockEntry(lock, name, entry) {
8109
+ return {
8110
+ ...lock,
8111
+ packages: { ...lock.packages, [name]: entry }
8112
+ };
8113
+ }
8114
+ function resolveRegistryUrl(project, flag, fallback) {
8115
+ const fromEnv = process.env.AIPM_REGISTRY_URL ?? process.env.AIPM_REGISTRY;
8116
+ if (flag) return flag.replace(/\/$/, "");
8117
+ if (project?.registry) return project.registry.replace(/\/$/, "");
8118
+ if (fromEnv) return fromEnv.replace(/\/$/, "");
8119
+ if (fallback) return fallback.replace(/\/$/, "");
8120
+ throw new Error("Registry URL required: set registry in aipm.package.json or AIPM_REGISTRY_URL");
8121
+ }
8122
+ function parseTargetFlag(target) {
8123
+ if (!target) return void 0;
8124
+ if (target === "cursor" || target === "claude" || target === "*") return target;
8125
+ throw new Error('--target must be "cursor", "claude", or "*"');
8126
+ }
8127
+ function parseTargetsFlag(value) {
8128
+ const targets = value.split(",").map((target) => target.trim()).filter(Boolean);
8129
+ if (targets.length === 0) throw new Error("At least one target is required.");
8130
+ for (const target of targets) {
8131
+ if (target !== "cursor" && target !== "claude" && target !== "*") {
8132
+ throw new Error('--targets must contain only "cursor", "claude", and/or "*"');
8133
+ }
8134
+ }
8135
+ const unique = [...new Set(targets)];
8136
+ if (unique.includes("*")) return ["*"];
8137
+ return unique;
8138
+ }
8127
8139
 
8128
8140
  // src/prompt.ts
8129
8141
  var readline = __toESM(require("node:readline/promises"), 1);
@@ -8148,6 +8160,8 @@ async function promptForTool() {
8148
8160
 
8149
8161
  // src/install-one.ts
8150
8162
  async function installOnePackage(options) {
8163
+ const configRoot = options.configRoot;
8164
+ const installRoot = options.installRoot ?? configRoot;
8151
8165
  await assertRegistryReachable(options.registry);
8152
8166
  const { manifest, integrity: remoteIntegrity } = await fetchPackageMetadata(
8153
8167
  options.registry,
@@ -8158,7 +8172,7 @@ async function installOnePackage(options) {
8158
8172
  let preferredTools = options.project.preferredTools;
8159
8173
  const { resolveInstallTools: resolveInstallTools2 } = await Promise.resolve().then(() => (init_dist4(), dist_exports));
8160
8174
  let tools = await resolveInstallTools2({
8161
- projectRoot: options.projectRoot,
8175
+ projectRoot: installRoot,
8162
8176
  manifest,
8163
8177
  preferredTools,
8164
8178
  explicitTarget
@@ -8166,14 +8180,14 @@ async function installOnePackage(options) {
8166
8180
  if (tools.length === 0) {
8167
8181
  if (options.ci) {
8168
8182
  throw new Error(
8169
- "No tool detected. Use --target cursor|claude in CI mode."
8183
+ "No tool detected. Use --target cursor|claude|* in CI mode."
8170
8184
  );
8171
8185
  }
8172
8186
  const choice = await promptForTool();
8173
8187
  explicitTarget = choice;
8174
8188
  preferredTools = [choice];
8175
8189
  tools = await resolveInstallTools2({
8176
- projectRoot: options.projectRoot,
8190
+ projectRoot: installRoot,
8177
8191
  manifest,
8178
8192
  preferredTools,
8179
8193
  explicitTarget
@@ -8186,13 +8200,13 @@ async function installOnePackage(options) {
8186
8200
  );
8187
8201
  const skillMarkdown = await unpackTarballToBuffer(tarball, manifest.entry);
8188
8202
  const result = await installSkillPackage({
8189
- projectRoot: options.projectRoot,
8203
+ projectRoot: installRoot,
8190
8204
  manifest,
8191
8205
  skillMarkdown,
8192
8206
  preferredTools,
8193
8207
  explicitTarget
8194
8208
  });
8195
- const lock = await readLockfile(options.projectRoot) ?? {
8209
+ const lock = await readLockfile(configRoot) ?? {
8196
8210
  schemaVersion: "0.1",
8197
8211
  packages: {}
8198
8212
  };
@@ -8202,7 +8216,7 @@ async function installOnePackage(options) {
8202
8216
  if (paths) installed[tool] = paths;
8203
8217
  }
8204
8218
  await writeLockfile(
8205
- options.projectRoot,
8219
+ configRoot,
8206
8220
  upsertLockEntry(lock, options.name, {
8207
8221
  version: options.version,
8208
8222
  integrity: remoteIntegrity,
@@ -8217,9 +8231,31 @@ async function installOnePackage(options) {
8217
8231
  // src/doctor.ts
8218
8232
  var import_node_child_process3 = require("node:child_process");
8219
8233
  var import_promises8 = require("node:fs/promises");
8220
- var import_node_path8 = require("node:path");
8234
+ var import_node_path9 = require("node:path");
8221
8235
  var import_node_util4 = require("node:util");
8222
- init_project_files();
8236
+
8237
+ // src/project-root.ts
8238
+ var import_node_os2 = require("node:os");
8239
+ var import_node_path8 = require("node:path");
8240
+ var import_node_process3 = require("node:process");
8241
+ function globalConfigDir(env2 = process.env) {
8242
+ const fromEnv = env2.AIPM_HOME?.trim();
8243
+ return fromEnv ? fromEnv.replace(/\/$/, "") : (0, import_node_path8.join)((0, import_node_os2.homedir)(), ".aipm");
8244
+ }
8245
+ function resolveConfigRoot(options = {}) {
8246
+ return options.global ? globalConfigDir() : (0, import_node_process3.cwd)();
8247
+ }
8248
+ function resolveInstallRoot(options = {}) {
8249
+ return options.global ? (0, import_node_os2.homedir)() : (0, import_node_process3.cwd)();
8250
+ }
8251
+ function scopeLabel(options) {
8252
+ return options.global ? "global" : "project";
8253
+ }
8254
+ function initRequiredMessage(options) {
8255
+ return options.global ? "Run aipm init -g first" : "Run aipm init first";
8256
+ }
8257
+
8258
+ // src/doctor.ts
8223
8259
  var execFileAsync2 = (0, import_node_util4.promisify)(import_node_child_process3.execFile);
8224
8260
  var MIN_NODE_MAJOR = 20;
8225
8261
  async function commandOutput(command, args) {
@@ -8235,11 +8271,11 @@ async function commandOutput(command, args) {
8235
8271
  }
8236
8272
  function npmGlobalBin(prefix) {
8237
8273
  if (!prefix) return null;
8238
- return process.platform === "win32" ? prefix : `${prefix}${import_node_path8.sep}bin`;
8274
+ return process.platform === "win32" ? prefix : `${prefix}${import_node_path9.sep}bin`;
8239
8275
  }
8240
8276
  function pathIncludes(pathDir) {
8241
8277
  if (!pathDir) return false;
8242
- return (process.env.PATH ?? "").split(import_node_path8.delimiter).includes(pathDir);
8278
+ return (process.env.PATH ?? "").split(import_node_path9.delimiter).includes(pathDir);
8243
8279
  }
8244
8280
  function shellPathHint(binDir) {
8245
8281
  if (!binDir) return "Run npm prefix -g and add its bin folder to PATH.";
@@ -8253,8 +8289,10 @@ async function commandExists(command) {
8253
8289
  return await commandOutput(probe, args) !== null;
8254
8290
  }
8255
8291
  async function runDoctor(options) {
8292
+ const scope = { global: options.global };
8293
+ const configRoot = resolveConfigRoot(scope);
8256
8294
  const root = process.cwd();
8257
- const project = await readProjectPackageJson(root);
8295
+ const project = await readProjectPackageJson(configRoot);
8258
8296
  const registry = resolveRegistryUrl(project, options.registry, "https://api.aipm-registry.com");
8259
8297
  const nodeMajor = Number(process.versions.node.split(".")[0]);
8260
8298
  const npmPrefix = await commandOutput("npm", ["prefix", "-g"]);
@@ -8288,11 +8326,20 @@ async function runDoctor(options) {
8288
8326
  });
8289
8327
  }
8290
8328
  if (!options.publish) {
8329
+ const configLabel = options.global ? "Global config" : "Project config";
8291
8330
  try {
8292
- await (0, import_promises8.access)("aipm.package.json");
8293
- checks.push({ name: "Project config", ok: true, detail: "aipm.package.json exists." });
8331
+ await (0, import_promises8.access)((0, import_node_path9.join)(configRoot, "aipm.package.json"));
8332
+ checks.push({
8333
+ name: configLabel,
8334
+ ok: true,
8335
+ detail: `${(0, import_node_path9.join)(configRoot, "aipm.package.json")} exists (${scopeLabel(scope)}).`
8336
+ });
8294
8337
  } catch {
8295
- checks.push({ name: "Project config", ok: false, detail: "Run aipm init in this project." });
8338
+ checks.push({
8339
+ name: configLabel,
8340
+ ok: false,
8341
+ detail: initRequiredMessage(scope)
8342
+ });
8296
8343
  }
8297
8344
  }
8298
8345
  try {
@@ -8353,18 +8400,95 @@ async function runDoctor(options) {
8353
8400
  }
8354
8401
  }
8355
8402
 
8356
- // src/bin.ts
8357
- init_project_files();
8358
-
8359
8403
  // src/version.ts
8360
8404
  var import_node_fs2 = require("node:fs");
8361
- var import_node_path9 = require("node:path");
8405
+ var import_node_path10 = require("node:path");
8362
8406
  function getCliVersion() {
8363
- const pkgPath = (0, import_node_path9.join)(__dirname, "..", "package.json");
8407
+ const pkgPath = (0, import_node_path10.join)(__dirname, "..", "package.json");
8364
8408
  const pkg = JSON.parse((0, import_node_fs2.readFileSync)(pkgPath, "utf8"));
8365
8409
  return pkg.version;
8366
8410
  }
8367
8411
 
8412
+ // src/cli-update-check.ts
8413
+ var import_node_process4 = require("node:process");
8414
+ var CLI_PACKAGE_NAME = "@aipm-registry/cli";
8415
+ var CLI_UPDATE_COMMAND = `npm install -g ${CLI_PACKAGE_NAME}`;
8416
+ var NPM_REGISTRY = "https://registry.npmjs.org";
8417
+ var FETCH_TIMEOUT_MS = 3e3;
8418
+ function parseSemVer(version) {
8419
+ const match = version.trim().match(/^(\d+)\.(\d+)\.(\d+)/);
8420
+ if (!match) return null;
8421
+ return {
8422
+ major: Number(match[1]),
8423
+ minor: Number(match[2]),
8424
+ patch: Number(match[3])
8425
+ };
8426
+ }
8427
+ function isSemVerNewer(current, latest) {
8428
+ if (latest.major !== current.major) return latest.major > current.major;
8429
+ if (latest.minor !== current.minor) return latest.minor > current.minor;
8430
+ return latest.patch > current.patch;
8431
+ }
8432
+ function classifyCliUpdate(current, latest) {
8433
+ const cur = parseSemVer(current);
8434
+ const lat = parseSemVer(latest);
8435
+ if (!cur || !lat || !isSemVerNewer(cur, lat)) return "none";
8436
+ if (lat.major > cur.major) return "major";
8437
+ return "patch-minor";
8438
+ }
8439
+ async function fetchLatestCliVersion(fetchImpl = fetch) {
8440
+ try {
8441
+ const response = await fetchImpl(
8442
+ `${NPM_REGISTRY}/${encodeURIComponent(CLI_PACKAGE_NAME)}/latest`,
8443
+ {
8444
+ headers: { Accept: "application/json" },
8445
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
8446
+ }
8447
+ );
8448
+ if (!response.ok) return null;
8449
+ const data = await response.json();
8450
+ return data.version ?? null;
8451
+ } catch {
8452
+ return null;
8453
+ }
8454
+ }
8455
+ function formatCliUpdateNotice(options) {
8456
+ const { currentVersion, latestVersion, level } = options;
8457
+ const cmd = CLI_UPDATE_COMMAND;
8458
+ if (level === "major") {
8459
+ return `AIPM CLI ${latestVersion} is available (you have ${currentVersion}). Update now: ${cmd}`;
8460
+ }
8461
+ return `A new AIPM CLI version is available (${currentVersion} \u2192 ${latestVersion}). Run: ${cmd}`;
8462
+ }
8463
+ function printCliUpdateNotice(options) {
8464
+ const useColor2 = options.useColor ?? import_node_process4.stderr.isTTY;
8465
+ const writeInfo = options.writeInfo ?? ((message2) => console.log(message2));
8466
+ const writeWarn = options.writeWarn ?? ((message2) => console.error(message2));
8467
+ const message = formatCliUpdateNotice(options);
8468
+ if (options.level === "major") {
8469
+ const red = useColor2 ? "\x1B[31m" : "";
8470
+ const reset = useColor2 ? "\x1B[0m" : "";
8471
+ writeWarn(`${red}${message}${reset}`);
8472
+ return;
8473
+ }
8474
+ writeInfo(message);
8475
+ }
8476
+ async function notifyCliUpdateIfNeeded(currentVersion, options) {
8477
+ const latestVersion = await (options?.fetchLatest ?? fetchLatestCliVersion)();
8478
+ if (!latestVersion) return "none";
8479
+ const level = classifyCliUpdate(currentVersion, latestVersion);
8480
+ if (level === "none") return "none";
8481
+ printCliUpdateNotice({
8482
+ currentVersion,
8483
+ latestVersion,
8484
+ level,
8485
+ useColor: options?.useColor,
8486
+ writeInfo: options?.writeInfo,
8487
+ writeWarn: options?.writeWarn
8488
+ });
8489
+ return level;
8490
+ }
8491
+
8368
8492
  // src/bin.ts
8369
8493
  var CLI_VERSION = getCliVersion();
8370
8494
  var program2 = new Command();
@@ -8372,6 +8496,8 @@ var DEFAULT_REGISTRY = "https://api.aipm-registry.com";
8372
8496
  var SITE_URL = "https://aipm-registry.com";
8373
8497
  var PUBLISH_DOC_URL = "https://aipm-registry.com/publish";
8374
8498
  var DASHBOARD_URL = "https://aipm-registry.com/dashboard";
8499
+ var GLOBAL_OPTION = "-g, --global";
8500
+ var GLOBAL_OPTION_DESC = "Use global config (~/.aipm) and install skills under your home directory";
8375
8501
  program2.name("aipm").description("AI package manager").enablePositionalOptions().version(CLI_VERSION, "-v, --version", "output the current version").option("--verbose", "Print extra diagnostic output").option("--quiet", "Reduce non-essential output").addHelpText(
8376
8502
  "after",
8377
8503
  `
@@ -8380,8 +8506,10 @@ Examples:
8380
8506
  $ npm install -g @aipm-registry/cli
8381
8507
  $ aipm doctor
8382
8508
  $ aipm init
8509
+ $ aipm init -g
8383
8510
  $ aipm search sentry
8384
8511
  $ aipm add @scope/name@1.0.0 --target cursor --ci
8512
+ $ aipm add @scope/name -g
8385
8513
  $ aipm publish init --name @org/skill
8386
8514
  $ cd skill
8387
8515
  $ aipm publish add .
@@ -8409,7 +8537,7 @@ async function writeStarterFile(path2, content) {
8409
8537
  }
8410
8538
  async function inferEntryFromSource(source, fallback) {
8411
8539
  const sourceInfo = await (0, import_promises9.stat)(source);
8412
- if (sourceInfo.isFile()) return (0, import_node_path10.basename)(source);
8540
+ if (sourceInfo.isFile()) return (0, import_node_path11.basename)(source);
8413
8541
  if (!sourceInfo.isDirectory()) return fallback;
8414
8542
  const entries = await (0, import_promises9.readdir)(source);
8415
8543
  if (entries.includes(fallback)) return fallback;
@@ -8506,16 +8634,6 @@ async function printPublishPreview(root, json) {
8506
8634
  preview.warnings.length > 0 ? "Fix the warnings, then run aipm publish preview again." : "Run aipm publish validate."
8507
8635
  );
8508
8636
  }
8509
- function parseTargets(value) {
8510
- const targets = value.split(",").map((target) => target.trim()).filter(Boolean);
8511
- if (targets.length === 0) throw new Error("At least one target is required.");
8512
- for (const target of targets) {
8513
- if (target !== "cursor" && target !== "claude") {
8514
- throw new Error('--targets must contain only "cursor" and/or "claude"');
8515
- }
8516
- }
8517
- return [...new Set(targets)];
8518
- }
8519
8637
  var SKILL_TEMPLATES = {
8520
8638
  blank: (name) => `# ${name}
8521
8639
 
@@ -8574,42 +8692,41 @@ function parseSkillTemplate(value) {
8574
8692
  }
8575
8693
  async function initSkill(opts) {
8576
8694
  if (!isValidScopeName(opts.name)) throw new Error("Invalid @scope/name");
8577
- const root = opts.here ? (0, import_node_process3.cwd)() : (0, import_node_path10.resolve)((0, import_node_process3.cwd)(), opts.dir ?? skillFolderName(opts.name));
8695
+ const root = opts.here ? (0, import_node_process5.cwd)() : (0, import_node_path11.resolve)((0, import_node_process5.cwd)(), opts.dir ?? skillFolderName(opts.name));
8578
8696
  const cdTarget = opts.here ? null : opts.dir ?? skillFolderName(opts.name);
8579
8697
  await (0, import_promises9.mkdir)(root, { recursive: true });
8580
- const source = opts.from ? (0, import_node_path10.resolve)(opts.from) : null;
8698
+ const source = opts.from ? (0, import_node_path11.resolve)(opts.from) : null;
8581
8699
  const entry = source ? await inferEntryFromSource(source, opts.entry) : opts.entry;
8582
- const manifest = {
8700
+ const manifest = PackageManifestSchema.parse({
8583
8701
  schemaVersion: "0.1",
8584
8702
  name: opts.name,
8585
8703
  version: opts.version,
8586
8704
  type: "skill",
8587
8705
  description: opts.description,
8588
8706
  entry,
8589
- targets: parseTargets(opts.targets),
8707
+ targets: parseTargetsFlag(opts.targets),
8590
8708
  license: "Apache-2.0"
8591
- };
8592
- PackageManifestSchema.parse(manifest);
8709
+ });
8593
8710
  if (source) {
8594
8711
  const sourceInfo = await (0, import_promises9.stat)(source);
8595
8712
  if (sourceInfo.isDirectory()) {
8596
8713
  await (0, import_promises9.cp)(source, root, { recursive: true, force: false, errorOnExist: true });
8597
8714
  } else if (sourceInfo.isFile()) {
8598
- await (0, import_promises9.cp)(source, (0, import_node_path10.join)(root, (0, import_node_path10.basename)(source)), { force: false, errorOnExist: true });
8715
+ await (0, import_promises9.cp)(source, (0, import_node_path11.join)(root, (0, import_node_path11.basename)(source)), { force: false, errorOnExist: true });
8599
8716
  } else {
8600
8717
  throw new Error(`Unsupported source path: ${opts.from}`);
8601
8718
  }
8602
8719
  }
8603
- await writeStarterFile((0, import_node_path10.join)(root, "aipm.manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
8720
+ await writeStarterFile((0, import_node_path11.join)(root, "aipm.manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
8604
8721
  if (!opts.from) {
8605
8722
  const template = parseSkillTemplate(opts.template ?? "blank");
8606
8723
  await writeStarterFile(
8607
- (0, import_node_path10.join)(root, entry),
8724
+ (0, import_node_path11.join)(root, entry),
8608
8725
  SKILL_TEMPLATES[template](opts.name)
8609
8726
  );
8610
8727
  }
8611
8728
  await writeStarterFile(
8612
- (0, import_node_path10.join)(root, ".aipmignore"),
8729
+ (0, import_node_path11.join)(root, ".aipmignore"),
8613
8730
  [
8614
8731
  "# Files that should never be published with this skill",
8615
8732
  ".env*",
@@ -8627,34 +8744,37 @@ async function initSkill(opts) {
8627
8744
  ""
8628
8745
  ].join("\n")
8629
8746
  );
8630
- await (0, import_promises9.mkdir)((0, import_node_path10.join)(root, ".aipm"), { recursive: true });
8747
+ await (0, import_promises9.mkdir)((0, import_node_path11.join)(root, ".aipm"), { recursive: true });
8631
8748
  console.log(`Created ${opts.name} skill folder: ${root}`);
8632
8749
  printPublishGuide(
8633
8750
  opts.from ? `Copied ${opts.from} into ${root} and created aipm.manifest.json, .aipmignore, and the local publish state folder.` : `Created ${root} with aipm.manifest.json, ${entry}, .aipmignore, and the local publish state folder.`,
8634
8751
  cdTarget ? `Run cd ${cdTarget}, edit/review the files, then run aipm publish add .` : "Edit/review the files, then run aipm publish add ."
8635
8752
  );
8636
8753
  }
8637
- program2.command("init").description("Create aipm.package.json in the current project").option("--registry <url>", "Default registry URL").action(async (opts) => {
8638
- const root = (0, import_node_process3.cwd)();
8639
- const existing = await readProjectPackageJson(root);
8754
+ program2.command("init").description("Create aipm.package.json in the current project or globally").option(GLOBAL_OPTION, GLOBAL_OPTION_DESC).option("--registry <url>", "Default registry URL").action(async (opts) => {
8755
+ const scope = { global: opts.global };
8756
+ const configRoot = resolveConfigRoot(scope);
8757
+ const existing = await readProjectPackageJson(configRoot);
8640
8758
  if (existing) {
8641
- console.log("aipm.package.json already exists.");
8759
+ console.log(`aipm.package.json already exists (${scopeLabel(scope)}).`);
8642
8760
  return;
8643
8761
  }
8644
- const detected = await detectToolsInProject(root);
8762
+ if (scope.global) await (0, import_promises9.mkdir)(configRoot, { recursive: true });
8763
+ const installRoot = resolveInstallRoot(scope);
8764
+ const detected = await detectToolsInProject(installRoot);
8645
8765
  let preferredTools = detected;
8646
8766
  if (detected.length === 0) {
8647
8767
  const choice = await promptForTool();
8648
8768
  preferredTools = [choice];
8649
8769
  }
8650
8770
  const registry = registryFromEnvOrDefault(opts.registry);
8651
- await writeProjectPackageJson(root, {
8771
+ await writeProjectPackageJson(configRoot, {
8652
8772
  schemaVersion: "0.1",
8653
8773
  registry,
8654
8774
  preferredTools: preferredTools.length ? preferredTools : void 0,
8655
8775
  packages: {}
8656
8776
  });
8657
- console.log(`Created aipm.package.json (registry: ${registry})`);
8777
+ console.log(`Created aipm.package.json (${scopeLabel(scope)}, registry: ${registry})`);
8658
8778
  });
8659
8779
  program2.command("login").description("Open the AIPM dashboard sign-in page").option("--no-open", "Print the URL without opening a browser").action(async (opts) => {
8660
8780
  const url = `${SITE_URL}/login`;
@@ -8663,17 +8783,22 @@ program2.command("login").description("Open the AIPM dashboard sign-in page").op
8663
8783
  if (opts.open) await openUrl(url);
8664
8784
  else console.log(`Open: ${url}`);
8665
8785
  });
8666
- program2.command("config").description("Show resolved AIPM CLI and project configuration").option("--registry <url>", "Registry base URL").option("--json", "Print machine-readable JSON").action(async (opts) => {
8667
- const root = (0, import_node_process3.cwd)();
8668
- const project = await readProjectPackageJson(root);
8669
- const lock = await readLockfile(root);
8786
+ program2.command("config").description("Show resolved AIPM CLI and project configuration").option(GLOBAL_OPTION, GLOBAL_OPTION_DESC).option("--registry <url>", "Registry base URL").option("--json", "Print machine-readable JSON").action(async (opts) => {
8787
+ const scope = { global: opts.global };
8788
+ const configRoot = resolveConfigRoot(scope);
8789
+ const installRoot = resolveInstallRoot(scope);
8790
+ const project = await readProjectPackageJson(configRoot);
8791
+ const lock = await readLockfile(configRoot);
8670
8792
  const registry = resolveRegistryUrl(project, opts.registry, DEFAULT_REGISTRY);
8671
8793
  const data = {
8672
8794
  cliVersion: CLI_VERSION,
8673
8795
  nodeVersion: process.versions.node,
8674
- cwd: root,
8796
+ scope: scopeLabel(scope),
8797
+ configRoot,
8798
+ installRoot,
8799
+ cwd: (0, import_node_process5.cwd)(),
8675
8800
  registry,
8676
- envRegistry: import_node_process3.env.AIPM_REGISTRY_URL ?? import_node_process3.env.AIPM_REGISTRY ?? null,
8801
+ envRegistry: import_node_process5.env.AIPM_REGISTRY_URL ?? import_node_process5.env.AIPM_REGISTRY ?? null,
8677
8802
  hasProjectConfig: Boolean(project),
8678
8803
  projectPackages: project ? Object.keys(project.packages) : [],
8679
8804
  hasLockfile: Boolean(lock),
@@ -8686,7 +8811,10 @@ program2.command("config").description("Show resolved AIPM CLI and project confi
8686
8811
  }
8687
8812
  console.log(`CLI: ${data.cliVersion}`);
8688
8813
  console.log(`Node: ${data.nodeVersion}`);
8689
- console.log(`Project: ${data.cwd}`);
8814
+ console.log(`Scope: ${data.scope}`);
8815
+ console.log(`Config: ${data.configRoot}`);
8816
+ console.log(`Install root: ${data.installRoot}`);
8817
+ console.log(`Working directory: ${data.cwd}`);
8690
8818
  console.log(`Registry: ${data.registry}`);
8691
8819
  console.log(`Env registry: ${data.envRegistry ?? "not set"}`);
8692
8820
  console.log(`Project config: ${data.hasProjectConfig ? "found" : "not found"}`);
@@ -8695,11 +8823,13 @@ program2.command("config").description("Show resolved AIPM CLI and project confi
8695
8823
  console.log(`Installed packages: ${data.installedPackages.length}`);
8696
8824
  console.log(`Publish guide: ${data.publishDoc}`);
8697
8825
  });
8698
- program2.command("add <package>").description("Add and install a package @scope/name[@version]").option("--registry <url>", "Registry base URL").option("--target <tool>", "cursor or claude").option("--ci", "Non-interactive; fail if prompt needed").action(async (pkgArg, opts) => {
8826
+ program2.command("add <package>").description("Add and install a package @scope/name[@version]").option(GLOBAL_OPTION, GLOBAL_OPTION_DESC).option("--registry <url>", "Registry base URL").option("--target <tool>", "cursor, claude, or *").option("--ci", "Non-interactive; fail if prompt needed").action(async (pkgArg, opts) => {
8699
8827
  const { name, version: requestedVersion } = parsePackageArg(pkgArg);
8700
- const root = (0, import_node_process3.cwd)();
8701
- let project = await readProjectPackageJson(root);
8702
- if (!project) throw new Error("Run aipm init first");
8828
+ const scope = { global: opts.global };
8829
+ const configRoot = resolveConfigRoot(scope);
8830
+ const installRoot = resolveInstallRoot(scope);
8831
+ let project = await readProjectPackageJson(configRoot);
8832
+ if (!project) throw new Error(initRequiredMessage(scope));
8703
8833
  const registry = resolveRegistryUrl(project, opts.registry, DEFAULT_REGISTRY);
8704
8834
  const version = requestedVersion ?? project.packages[name] ?? await latestVersionForPackage(registry, name);
8705
8835
  if (!version) throw new Error("Specify version: aipm add @scope/pkg@1.0.0");
@@ -8707,9 +8837,10 @@ program2.command("add <package>").description("Add and install a package @scope/
8707
8837
  ...project,
8708
8838
  packages: { ...project.packages, [name]: version.replace(/^\^/, "") }
8709
8839
  };
8710
- await writeProjectPackageJson(root, project);
8840
+ await writeProjectPackageJson(configRoot, project);
8711
8841
  await installOnePackage({
8712
- projectRoot: root,
8842
+ configRoot,
8843
+ installRoot,
8713
8844
  registry,
8714
8845
  name,
8715
8846
  version: version.replace(/^\^/, ""),
@@ -8717,6 +8848,9 @@ program2.command("add <package>").description("Add and install a package @scope/
8717
8848
  explicitTarget: parseTargetFlag(opts.target),
8718
8849
  ci: opts.ci
8719
8850
  });
8851
+ if (!opts.ci && !program2.opts().quiet) {
8852
+ await notifyCliUpdateIfNeeded(CLI_VERSION);
8853
+ }
8720
8854
  });
8721
8855
  program2.command("search [query]").description("Search public registry packages").option("--registry <url>", "Registry base URL").option("--limit <number>", "Maximum results", "20").option("--json", "Print machine-readable JSON").action(async (query = "", opts) => {
8722
8856
  const registry = registryFromEnvOrDefault(opts.registry);
@@ -8737,15 +8871,18 @@ program2.command("search [query]").description("Search public registry packages"
8737
8871
  console.log(` ${meta}`);
8738
8872
  }
8739
8873
  });
8740
- program2.command("install").description("Install all packages from aipm.package.json").option("--registry <url>", "Registry base URL").option("--target <tool>", "cursor or claude").option("--ci", "Non-interactive").action(async (opts) => {
8741
- const root = (0, import_node_process3.cwd)();
8742
- const project = await readProjectPackageJson(root);
8743
- if (!project) throw new Error("Run aipm init first");
8874
+ program2.command("install").description("Install all packages from aipm.package.json").option(GLOBAL_OPTION, GLOBAL_OPTION_DESC).option("--registry <url>", "Registry base URL").option("--target <tool>", "cursor, claude, or *").option("--ci", "Non-interactive").action(async (opts) => {
8875
+ const scope = { global: opts.global };
8876
+ const configRoot = resolveConfigRoot(scope);
8877
+ const installRoot = resolveInstallRoot(scope);
8878
+ const project = await readProjectPackageJson(configRoot);
8879
+ if (!project) throw new Error(initRequiredMessage(scope));
8744
8880
  const registry = resolveRegistryUrl(project, opts.registry, DEFAULT_REGISTRY);
8745
8881
  const target = parseTargetFlag(opts.target);
8746
8882
  for (const [name, version] of Object.entries(project.packages)) {
8747
8883
  await installOnePackage({
8748
- projectRoot: root,
8884
+ configRoot,
8885
+ installRoot,
8749
8886
  registry,
8750
8887
  name,
8751
8888
  version: version.replace(/^\^/, ""),
@@ -8763,12 +8900,12 @@ var publish = program2.command("publish [dir]").description("Stage, validate, an
8763
8900
  publish.help();
8764
8901
  return;
8765
8902
  }
8766
- const abs = (0, import_node_path10.resolve)(dir);
8767
- const manifestRaw = await (0, import_promises9.readFile)((0, import_node_path10.join)(abs, "aipm.manifest.json"), "utf8");
8903
+ const abs = (0, import_node_path11.resolve)(dir);
8904
+ const manifestRaw = await (0, import_promises9.readFile)((0, import_node_path11.join)(abs, "aipm.manifest.json"), "utf8");
8768
8905
  const manifest = PackageManifestSchema.parse(JSON.parse(manifestRaw));
8769
8906
  const tarball = await packDirectory(abs);
8770
8907
  const registry = registryFromEnvOrDefault(opts.registry);
8771
- const result = await publishPackage(registry, manifest.name, tarball, opts.token ?? import_node_process3.env.AIPM_TOKEN);
8908
+ const result = await publishPackage(registry, manifest.name, tarball, opts.token ?? import_node_process5.env.AIPM_TOKEN);
8772
8909
  console.log(`Published ${manifest.name}@${result.version}`);
8773
8910
  console.log(`View: ${registry.replace(/\/$/, "")}/packages/${manifest.name.replace(/^@/, "").replace("/", "/")}/${result.version}`);
8774
8911
  console.log(`Install: aipm add ${manifest.name}@${result.version} --target ${manifest.targets[0]} --ci`);
@@ -8782,7 +8919,7 @@ publish.command("import <source>").description("Create a package folder from an
8782
8919
  async (source, opts) => initSkill({ ...opts, from: source })
8783
8920
  );
8784
8921
  publish.command("add [files...]").description("Stage files for publishing").action(async (files) => {
8785
- const state = await addPublishFiles((0, import_node_process3.cwd)(), files);
8922
+ const state = await addPublishFiles((0, import_node_process5.cwd)(), files);
8786
8923
  console.log(`Staged ${state.files.length} file${state.files.length === 1 ? "" : "s"}.`);
8787
8924
  printPublishGuide(
8788
8925
  `Added ${files.length > 0 ? files.join(", ") : "."} to the publish stage, respecting .aipmignore and secret-file exclusions.`,
@@ -8813,7 +8950,7 @@ publish.command("token").description("Open the dashboard page used to generate a
8813
8950
  );
8814
8951
  });
8815
8952
  publish.command("remove <files...>").alias("rm").description("Remove files from the publish stage").action(async (files) => {
8816
- const state = await removePublishFiles((0, import_node_process3.cwd)(), files);
8953
+ const state = await removePublishFiles((0, import_node_process5.cwd)(), files);
8817
8954
  console.log(`Staged ${state.files.length} file${state.files.length === 1 ? "" : "s"}.`);
8818
8955
  printPublishGuide(
8819
8956
  `Removed ${files.join(", ")} from the publish stage.`,
@@ -8821,7 +8958,7 @@ publish.command("remove <files...>").alias("rm").description("Remove files from
8821
8958
  );
8822
8959
  });
8823
8960
  publish.command("reset").description("Clear staged publish files").action(async () => {
8824
- await resetPublishState((0, import_node_process3.cwd)());
8961
+ await resetPublishState((0, import_node_process5.cwd)());
8825
8962
  console.log("Cleared publish stage.");
8826
8963
  printPublishGuide(
8827
8964
  "Cleared all staged publish files.",
@@ -8829,7 +8966,7 @@ publish.command("reset").description("Clear staged publish files").action(async
8829
8966
  );
8830
8967
  });
8831
8968
  publish.command("status").description("Show staged publish files").option("--json", "Print machine-readable JSON").action(async (opts) => {
8832
- const rows = await statusPublishState((0, import_node_process3.cwd)());
8969
+ const rows = await statusPublishState((0, import_node_process5.cwd)());
8833
8970
  if (opts.json) {
8834
8971
  console.log(JSON.stringify({ files: rows }, null, 2));
8835
8972
  return;
@@ -8852,7 +8989,7 @@ publish.command("status").description("Show staged publish files").option("--jso
8852
8989
  );
8853
8990
  });
8854
8991
  publish.command("diff").description("Show staged files that changed since add").action(async () => {
8855
- const rows = await statusPublishState((0, import_node_process3.cwd)());
8992
+ const rows = await statusPublishState((0, import_node_process5.cwd)());
8856
8993
  const changed = rows.filter((row) => row.changed);
8857
8994
  if (changed.length === 0) {
8858
8995
  console.log("No staged files changed.");
@@ -8869,16 +9006,16 @@ publish.command("diff").description("Show staged files that changed since add").
8869
9006
  );
8870
9007
  });
8871
9008
  publish.command("validate").description("Validate staged files before publish").action(async () => {
8872
- const result = await validatePublishState((0, import_node_process3.cwd)());
9009
+ const result = await validatePublishState((0, import_node_process5.cwd)());
8873
9010
  console.log(`Valid ${result.manifest.name}@${result.manifest.version} (${result.size} bytes staged).`);
8874
9011
  printPublishGuide(
8875
9012
  `Validated ${result.manifest.name}@${result.manifest.version}; manifest, entry file, staged hashes, ignored paths, package size, and obvious secrets passed.`,
8876
9013
  "Generate a 5-minute token from the dashboard, then run AIPM_TOKEN=<token> aipm publish push."
8877
9014
  );
8878
9015
  });
8879
- publish.command("preview").description("Preview exactly what will be uploaded").option("--json", "Print machine-readable JSON").action((opts) => printPublishPreview((0, import_node_process3.cwd)(), opts.json));
9016
+ publish.command("preview").description("Preview exactly what will be uploaded").option("--json", "Print machine-readable JSON").action((opts) => printPublishPreview((0, import_node_process5.cwd)(), opts.json));
8880
9017
  publish.command("push").description("Publish staged files").option("--registry <url>", "Registry base URL").option("--token <token>", "Publish token").option("--yes", "Confirm upload without an extra reminder").action(async (opts) => {
8881
- const root = (0, import_node_process3.cwd)();
9018
+ const root = (0, import_node_process5.cwd)();
8882
9019
  const { manifest } = await validatePublishState(root);
8883
9020
  const registry = registryFromEnvOrDefault(opts.registry);
8884
9021
  const tarball = await packStagedFiles(root);
@@ -8889,7 +9026,7 @@ publish.command("push").description("Publish staged files").option("--registry <
8889
9026
  }
8890
9027
  console.log(`Uploading ${staged.length} file${staged.length === 1 ? "" : "s"} to ${registry}.`);
8891
9028
  for (const entry of staged) console.log(` ${entry.path}`);
8892
- const result = await publishPackage(registry, manifest.name, tarball, opts.token ?? import_node_process3.env.AIPM_TOKEN);
9029
+ const result = await publishPackage(registry, manifest.name, tarball, opts.token ?? import_node_process5.env.AIPM_TOKEN);
8893
9030
  const base = registry.replace(/\/$/, "");
8894
9031
  console.log(`Published ${manifest.name}@${result.version}`);
8895
9032
  console.log(`View: ${base}/packages/${manifest.name.replace(/^@/, "")}/${result.version}`);
@@ -8899,9 +9036,9 @@ publish.command("push").description("Publish staged files").option("--registry <
8899
9036
  "Open the registry page, test the install command in a clean project, and share the package link."
8900
9037
  );
8901
9038
  });
8902
- program2.command("list").description("List packages from aipm-lock.json").action(async () => {
8903
- const { readLockfile: readLockfile2 } = await Promise.resolve().then(() => (init_project_files(), project_files_exports));
8904
- const lock = await readLockfile2((0, import_node_process3.cwd)());
9039
+ program2.command("list").description("List packages from aipm-lock.json").option(GLOBAL_OPTION, GLOBAL_OPTION_DESC).action(async (opts) => {
9040
+ const configRoot = resolveConfigRoot({ global: opts.global });
9041
+ const lock = await readLockfile(configRoot);
8905
9042
  if (!lock || Object.keys(lock.packages).length === 0) {
8906
9043
  console.log("No packages installed.");
8907
9044
  return;
@@ -8910,27 +9047,30 @@ program2.command("list").description("List packages from aipm-lock.json").action
8910
9047
  console.log(`${name}@${entry.version} [${entry.resolvedTools.join(", ")}]`);
8911
9048
  }
8912
9049
  });
8913
- program2.command("remove <package>").alias("rm").description("Remove a package from aipm.package.json and aipm-lock.json").action(async (pkgArg) => {
9050
+ program2.command("remove <package>").alias("rm").description("Remove a package from aipm.package.json and aipm-lock.json").option(GLOBAL_OPTION, GLOBAL_OPTION_DESC).action(async (pkgArg, opts) => {
8914
9051
  const { name } = parsePackageArg(pkgArg);
8915
- const root = (0, import_node_process3.cwd)();
8916
- const project = await readProjectPackageJson(root);
8917
- if (!project) throw new Error("Run aipm init first");
9052
+ const scope = { global: opts.global };
9053
+ const configRoot = resolveConfigRoot(scope);
9054
+ const project = await readProjectPackageJson(configRoot);
9055
+ if (!project) throw new Error(initRequiredMessage(scope));
8918
9056
  const packages = { ...project.packages };
8919
9057
  delete packages[name];
8920
- await writeProjectPackageJson(root, { ...project, packages });
8921
- const lock = await readLockfile(root);
9058
+ await writeProjectPackageJson(configRoot, { ...project, packages });
9059
+ const lock = await readLockfile(configRoot);
8922
9060
  if (lock) {
8923
9061
  const lockPackages = { ...lock.packages };
8924
9062
  delete lockPackages[name];
8925
- await writeLockfile(root, { ...lock, packages: lockPackages });
9063
+ await writeLockfile(configRoot, { ...lock, packages: lockPackages });
8926
9064
  }
8927
- console.log(`Removed ${name} from AIPM project files.`);
9065
+ console.log(`Removed ${name} from AIPM ${scopeLabel(scope)} files.`);
8928
9066
  console.log("Adapter-written files are not deleted yet; review your project before committing.");
8929
9067
  });
8930
- program2.command("update [package]").description("Update one installed package, or all installed packages, to the latest registry version").option("--registry <url>", "Registry base URL").option("--target <tool>", "cursor or claude").option("--ci", "Non-interactive").action(async (pkgArg, opts) => {
8931
- const root = (0, import_node_process3.cwd)();
8932
- let project = await readProjectPackageJson(root);
8933
- if (!project) throw new Error("Run aipm init first");
9068
+ program2.command("update [package]").description("Update one installed package, or all installed packages, to the latest registry version").option(GLOBAL_OPTION, GLOBAL_OPTION_DESC).option("--registry <url>", "Registry base URL").option("--target <tool>", "cursor, claude, or *").option("--ci", "Non-interactive").action(async (pkgArg, opts) => {
9069
+ const scope = { global: opts.global };
9070
+ const configRoot = resolveConfigRoot(scope);
9071
+ const installRoot = resolveInstallRoot(scope);
9072
+ let project = await readProjectPackageJson(configRoot);
9073
+ if (!project) throw new Error(initRequiredMessage(scope));
8934
9074
  const registry = resolveRegistryUrl(project, opts.registry, DEFAULT_REGISTRY);
8935
9075
  const names = pkgArg ? [parsePackageArg(pkgArg).name] : Object.keys(project.packages);
8936
9076
  if (names.length === 0) {
@@ -8943,9 +9083,10 @@ program2.command("update [package]").description("Update one installed package,
8943
9083
  ...project,
8944
9084
  packages: { ...project.packages, [name]: version }
8945
9085
  };
8946
- await writeProjectPackageJson(root, project);
9086
+ await writeProjectPackageJson(configRoot, project);
8947
9087
  await installOnePackage({
8948
- projectRoot: root,
9088
+ configRoot,
9089
+ installRoot,
8949
9090
  registry,
8950
9091
  name,
8951
9092
  version,
@@ -8955,7 +9096,7 @@ program2.command("update [package]").description("Update one installed package,
8955
9096
  });
8956
9097
  }
8957
9098
  });
8958
- program2.command("doctor").description("Check PATH, Node, registry, project config, and publish readiness").option("--registry <url>", "Registry base URL").option("--json", "Print machine-readable JSON").option("--publish", "Only show publish-readiness checks").action((opts) => runDoctor(opts));
9099
+ program2.command("doctor").description("Check PATH, Node, registry, project config, and publish readiness").option(GLOBAL_OPTION, GLOBAL_OPTION_DESC).option("--registry <url>", "Registry base URL").option("--json", "Print machine-readable JSON").option("--publish", "Only show publish-readiness checks").action((opts) => runDoctor(opts));
8959
9100
  program2.parseAsync(process.argv).catch((err) => {
8960
9101
  console.error(err.message);
8961
9102
  const message = err.message.toLowerCase();