@aipm-registry/cli 0.3.5 → 0.4.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.cjs CHANGED
@@ -4178,8 +4178,8 @@ var init_manifest = __esm({
4178
4178
  "use strict";
4179
4179
  init_zod();
4180
4180
  init_scope_name();
4181
- AiToolSchema = external_exports.enum(["cursor", "claude", "*"]);
4182
- ALL_TOOLS = ["cursor", "claude"];
4181
+ AiToolSchema = external_exports.enum(["cursor", "claude", "codex", "*"]);
4182
+ ALL_TOOLS = ["cursor", "claude", "codex"];
4183
4183
  PackageExampleSchema = external_exports.object({
4184
4184
  title: external_exports.string().trim().min(1).max(80),
4185
4185
  description: external_exports.string().trim().min(1).max(240).optional(),
@@ -4237,7 +4237,7 @@ var init_manifest = __esm({
4237
4237
  });
4238
4238
 
4239
4239
  // ../../packages/schemas/dist/package-json.js
4240
- var scopedPackageKey, ProjectPackageJsonSchema;
4240
+ var scopedPackageKey, promptAliasKey, ProjectPackageJsonSchema;
4241
4241
  var init_package_json = __esm({
4242
4242
  "../../packages/schemas/dist/package-json.js"() {
4243
4243
  "use strict";
@@ -4245,17 +4245,19 @@ var init_package_json = __esm({
4245
4245
  init_scope_name();
4246
4246
  init_manifest();
4247
4247
  scopedPackageKey = external_exports.string().regex(SCOPE_NAME_REGEX);
4248
+ promptAliasKey = external_exports.string().trim().min(1).max(120);
4248
4249
  ProjectPackageJsonSchema = external_exports.object({
4249
4250
  schemaVersion: external_exports.literal("0.1"),
4250
4251
  registry: external_exports.string().url(),
4251
4252
  preferredTools: external_exports.array(AiToolSchema).optional(),
4252
- packages: external_exports.record(scopedPackageKey, external_exports.string().min(1))
4253
+ packages: external_exports.record(scopedPackageKey, external_exports.string().min(1)),
4254
+ prompts: external_exports.record(promptAliasKey, external_exports.string().url()).default({})
4253
4255
  });
4254
4256
  }
4255
4257
  });
4256
4258
 
4257
4259
  // ../../packages/schemas/dist/lockfile.js
4258
- var scopedPackageKey2, InstalledPathsSchema, LockfilePackageEntrySchema, LockfileSchema;
4260
+ var scopedPackageKey2, InstalledPathsSchema, LockfilePromptEntrySchema, LockfilePackageEntrySchema, LockfileSchema;
4259
4261
  var init_lockfile = __esm({
4260
4262
  "../../packages/schemas/dist/lockfile.js"() {
4261
4263
  "use strict";
@@ -4264,6 +4266,15 @@ var init_lockfile = __esm({
4264
4266
  init_manifest();
4265
4267
  scopedPackageKey2 = external_exports.string().regex(SCOPE_NAME_REGEX);
4266
4268
  InstalledPathsSchema = external_exports.record(AiToolSchema, external_exports.array(external_exports.string()));
4269
+ LockfilePromptEntrySchema = external_exports.object({
4270
+ id: external_exports.string().min(1),
4271
+ url: external_exports.string().url(),
4272
+ publisher: external_exports.string().min(1),
4273
+ slug: external_exports.string().min(1),
4274
+ contentHash: external_exports.string().min(1),
4275
+ updatedAt: external_exports.string().min(1),
4276
+ installedPath: external_exports.string().min(1)
4277
+ });
4267
4278
  LockfilePackageEntrySchema = external_exports.object({
4268
4279
  version: external_exports.string(),
4269
4280
  integrity: external_exports.string(),
@@ -4283,7 +4294,8 @@ var init_lockfile = __esm({
4283
4294
  });
4284
4295
  LockfileSchema = external_exports.object({
4285
4296
  schemaVersion: external_exports.literal("0.1"),
4286
- packages: external_exports.record(scopedPackageKey2, LockfilePackageEntrySchema)
4297
+ packages: external_exports.record(scopedPackageKey2, LockfilePackageEntrySchema),
4298
+ prompts: external_exports.record(external_exports.string().min(1), LockfilePromptEntrySchema).default({})
4287
4299
  });
4288
4300
  }
4289
4301
  });
@@ -4314,6 +4326,8 @@ async function detectToolsInProject(projectRoot) {
4314
4326
  detected.push("cursor");
4315
4327
  if (await pathExists((0, import_node_path2.join)(projectRoot, ".claude")))
4316
4328
  detected.push("claude");
4329
+ if (await pathExists((0, import_node_path2.join)(projectRoot, ".codex")))
4330
+ detected.push("codex");
4317
4331
  return detected;
4318
4332
  }
4319
4333
  function manifestAllowsTool(manifest, tool) {
@@ -4404,6 +4418,29 @@ var init_dist3 = __esm({
4404
4418
  }
4405
4419
  });
4406
4420
 
4421
+ // ../../packages/adapter-codex/dist/index.js
4422
+ var import_promises4, import_node_path5, CodexSkillAdapter, codexSkillAdapter;
4423
+ var init_dist4 = __esm({
4424
+ "../../packages/adapter-codex/dist/index.js"() {
4425
+ "use strict";
4426
+ import_promises4 = require("node:fs/promises");
4427
+ import_node_path5 = require("node:path");
4428
+ init_dist();
4429
+ CodexSkillAdapter = class {
4430
+ tool = "codex";
4431
+ async installSkill(input2) {
4432
+ const short = shortNameFromScopeName(input2.packageName);
4433
+ const skillDir = (0, import_node_path5.join)(input2.projectRoot, ".agents", "skills", short);
4434
+ await (0, import_promises4.mkdir)(skillDir, { recursive: true });
4435
+ const filePath = (0, import_node_path5.join)(skillDir, "SKILL.md");
4436
+ await (0, import_promises4.writeFile)(filePath, input2.skillMarkdown, "utf8");
4437
+ return { writtenPaths: [filePath] };
4438
+ }
4439
+ };
4440
+ codexSkillAdapter = new CodexSkillAdapter();
4441
+ }
4442
+ });
4443
+
4407
4444
  // ../../packages/engine/dist/install-skill.js
4408
4445
  async function installSkillPackage(options) {
4409
4446
  const tools = await resolveInstallTools({
@@ -4413,7 +4450,7 @@ async function installSkillPackage(options) {
4413
4450
  explicitTarget: options.explicitTarget
4414
4451
  });
4415
4452
  if (tools.length === 0) {
4416
- throw new Error("No AI tool detected (.cursor/ or .claude/). Use --target cursor|claude|* or set preferredTools in aipm.package.json.");
4453
+ throw new Error("No AI tool detected (.cursor/, .claude/, or .codex/). Use --target cursor|claude|codex|* or set preferredTools in aipm.package.json.");
4417
4454
  }
4418
4455
  const installed = {};
4419
4456
  for (const tool of tools) {
@@ -4434,10 +4471,12 @@ var init_install_skill = __esm({
4434
4471
  "use strict";
4435
4472
  init_dist2();
4436
4473
  init_dist3();
4474
+ init_dist4();
4437
4475
  init_detect_tools();
4438
4476
  adapters = {
4439
4477
  cursor: cursorSkillAdapter,
4440
- claude: claudeSkillAdapter
4478
+ claude: claudeSkillAdapter,
4479
+ codex: codexSkillAdapter
4441
4480
  };
4442
4481
  }
4443
4482
  });
@@ -4449,7 +4488,7 @@ __export(dist_exports, {
4449
4488
  installSkillPackage: () => installSkillPackage,
4450
4489
  resolveInstallTools: () => resolveInstallTools
4451
4490
  });
4452
- var init_dist4 = __esm({
4491
+ var init_dist5 = __esm({
4453
4492
  "../../packages/engine/dist/index.js"() {
4454
4493
  "use strict";
4455
4494
  init_detect_tools();
@@ -7826,28 +7865,28 @@ function useColor() {
7826
7865
  var program = new Command();
7827
7866
 
7828
7867
  // src/bin.ts
7829
- var import_node_crypto2 = require("node:crypto");
7868
+ var import_node_crypto3 = require("node:crypto");
7830
7869
  var import_node_child_process4 = require("node:child_process");
7831
7870
  var import_node_http = require("node:http");
7832
- var import_promises10 = require("node:fs/promises");
7833
- var import_node_path13 = require("node:path");
7871
+ var import_promises12 = require("node:fs/promises");
7872
+ var import_node_path15 = require("node:path");
7834
7873
  var import_node_process5 = require("node:process");
7835
7874
  init_dist();
7836
- init_dist4();
7875
+ init_dist5();
7837
7876
 
7838
7877
  // src/pack.ts
7839
7878
  var import_node_child_process2 = require("node:child_process");
7840
7879
  var import_node_util3 = require("node:util");
7841
- var import_promises5 = require("node:fs/promises");
7842
- var import_node_path6 = require("node:path");
7880
+ var import_promises6 = require("node:fs/promises");
7881
+ var import_node_path7 = require("node:path");
7843
7882
  var import_node_os = require("node:os");
7844
7883
 
7845
7884
  // src/publish-state.ts
7846
7885
  var import_node_crypto = require("node:crypto");
7847
- var import_promises4 = require("node:fs/promises");
7848
- var import_node_path5 = require("node:path");
7886
+ var import_promises5 = require("node:fs/promises");
7887
+ var import_node_path6 = require("node:path");
7849
7888
  init_dist();
7850
- var STATE_PATH = (0, import_node_path5.join)(".aipm", "publish-state.json");
7889
+ var STATE_PATH = (0, import_node_path6.join)(".aipm", "publish-state.json");
7851
7890
  var IGNORE_FILE = ".aipmignore";
7852
7891
  var MAX_PACKAGE_BYTES = 50 * 1024 * 1024;
7853
7892
  var EXCLUDED_SEGMENTS = /* @__PURE__ */ new Set([".aipm", ".git", "node_modules", "dist", ".next"]);
@@ -7862,11 +7901,11 @@ var SECRET_PATTERNS = [
7862
7901
  /<publishData/i
7863
7902
  ];
7864
7903
  function publishStatePath(root) {
7865
- return (0, import_node_path5.join)(root, STATE_PATH);
7904
+ return (0, import_node_path6.join)(root, STATE_PATH);
7866
7905
  }
7867
7906
  function normalizePublishPath(root, filePath) {
7868
- const abs = (0, import_node_path5.resolve)(root, filePath);
7869
- const rel = (0, import_node_path5.normalize)((0, import_node_path5.relative)(root, abs)).split(import_node_path5.sep).join("/");
7907
+ const abs = (0, import_node_path6.resolve)(root, filePath);
7908
+ const rel = (0, import_node_path6.normalize)((0, import_node_path6.relative)(root, abs)).split(import_node_path6.sep).join("/");
7870
7909
  if (!rel || rel === ".") return ".";
7871
7910
  if (rel.startsWith("../") || rel === ".." || rel.startsWith("/")) {
7872
7911
  throw new Error(`Path is outside the skill folder: ${filePath}`);
@@ -7883,7 +7922,7 @@ function isExcludedPublishPath(rel) {
7883
7922
  }
7884
7923
  async function readAipmIgnore(root) {
7885
7924
  try {
7886
- const raw = await (0, import_promises4.readFile)((0, import_node_path5.join)(root, IGNORE_FILE), "utf8");
7925
+ const raw = await (0, import_promises5.readFile)((0, import_node_path6.join)(root, IGNORE_FILE), "utf8");
7887
7926
  return raw.split(/\r?\n/).map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
7888
7927
  } catch {
7889
7928
  return [];
@@ -7904,7 +7943,7 @@ async function isIgnoredByAipmIgnore(root, rel) {
7904
7943
  return rules.some((rule) => matchesIgnoreRule(rel, rule));
7905
7944
  }
7906
7945
  async function assertNoObviousSecrets(path2, rel) {
7907
- const data = await (0, import_promises4.readFile)(path2);
7946
+ const data = await (0, import_promises5.readFile)(path2);
7908
7947
  if (data.includes(0)) return;
7909
7948
  const text = data.toString("utf8");
7910
7949
  if (SECRET_PATTERNS.some((pattern) => pattern.test(text))) {
@@ -7912,7 +7951,7 @@ async function assertNoObviousSecrets(path2, rel) {
7912
7951
  }
7913
7952
  }
7914
7953
  async function fileHash(path2) {
7915
- const data = await (0, import_promises4.readFile)(path2);
7954
+ const data = await (0, import_promises5.readFile)(path2);
7916
7955
  return {
7917
7956
  hash: (0, import_node_crypto.createHash)("sha256").update(data).digest("hex"),
7918
7957
  size: data.length
@@ -7920,7 +7959,7 @@ async function fileHash(path2) {
7920
7959
  }
7921
7960
  async function readPublishState(root) {
7922
7961
  try {
7923
- const raw = await (0, import_promises4.readFile)(publishStatePath(root), "utf8");
7962
+ const raw = await (0, import_promises5.readFile)(publishStatePath(root), "utf8");
7924
7963
  const parsed = JSON.parse(raw);
7925
7964
  return {
7926
7965
  schemaVersion: "0.1",
@@ -7932,18 +7971,18 @@ async function readPublishState(root) {
7932
7971
  }
7933
7972
  async function writePublishState(root, state) {
7934
7973
  const path2 = publishStatePath(root);
7935
- await (0, import_promises4.mkdir)((0, import_node_path5.dirname)(path2), { recursive: true });
7974
+ await (0, import_promises5.mkdir)((0, import_node_path6.dirname)(path2), { recursive: true });
7936
7975
  const files = [...state.files].sort((a, b) => a.path.localeCompare(b.path));
7937
- await (0, import_promises4.writeFile)(path2, JSON.stringify({ schemaVersion: "0.1", files }, null, 2) + "\n", "utf8");
7976
+ await (0, import_promises5.writeFile)(path2, JSON.stringify({ schemaVersion: "0.1", files }, null, 2) + "\n", "utf8");
7938
7977
  }
7939
7978
  async function expandPublishPath(root, rel) {
7940
7979
  if (isExcludedPublishPath(rel)) return [];
7941
7980
  if (await isIgnoredByAipmIgnore(root, rel)) return [];
7942
- const abs = (0, import_node_path5.join)(root, rel);
7943
- const info = await (0, import_promises4.stat)(abs);
7981
+ const abs = (0, import_node_path6.join)(root, rel);
7982
+ const info = await (0, import_promises5.stat)(abs);
7944
7983
  if (info.isFile()) return [rel];
7945
7984
  if (!info.isDirectory()) return [];
7946
- const entries = await (0, import_promises4.readdir)(abs);
7985
+ const entries = await (0, import_promises5.readdir)(abs);
7947
7986
  const nested = await Promise.all(
7948
7987
  entries.map((entry) => expandPublishPath(root, `${rel === "." ? "" : `${rel}/`}${entry}`))
7949
7988
  );
@@ -7959,7 +7998,7 @@ async function addPublishFiles(root, paths) {
7959
7998
  for (const rel of files) {
7960
7999
  if (isExcludedPublishPath(rel)) continue;
7961
8000
  if (await isIgnoredByAipmIgnore(root, rel)) continue;
7962
- const hashed = await fileHash((0, import_node_path5.join)(root, rel));
8001
+ const hashed = await fileHash((0, import_node_path6.join)(root, rel));
7963
8002
  byPath.set(rel, { path: rel, ...hashed });
7964
8003
  }
7965
8004
  }
@@ -7978,11 +8017,11 @@ async function removePublishFiles(root, paths) {
7978
8017
  return next;
7979
8018
  }
7980
8019
  async function resetPublishState(root) {
7981
- await (0, import_promises4.rm)(publishStatePath(root), { force: true });
8020
+ await (0, import_promises5.rm)(publishStatePath(root), { force: true });
7982
8021
  }
7983
8022
  async function readManifest(root) {
7984
8023
  try {
7985
- const raw = await (0, import_promises4.readFile)((0, import_node_path5.join)(root, "aipm.manifest.json"), "utf8");
8024
+ const raw = await (0, import_promises5.readFile)((0, import_node_path6.join)(root, "aipm.manifest.json"), "utf8");
7986
8025
  return PackageManifestSchema.parse(JSON.parse(raw));
7987
8026
  } catch (error) {
7988
8027
  if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
@@ -8015,8 +8054,8 @@ async function validatePublishState(root) {
8015
8054
  if (await isIgnoredByAipmIgnore(root, entry.path)) {
8016
8055
  throw new Error(`Refusing to publish ignored path: ${entry.path}`);
8017
8056
  }
8018
- const abs = (0, import_node_path5.join)(root, entry.path);
8019
- const info = await (0, import_promises4.stat)(abs).catch(() => null);
8057
+ const abs = (0, import_node_path6.join)(root, entry.path);
8058
+ const info = await (0, import_promises5.stat)(abs).catch(() => null);
8020
8059
  if (!info?.isFile()) throw new Error(`Staged file is missing: ${entry.path}`);
8021
8060
  const hashed = await fileHash(abs);
8022
8061
  if (hashed.hash !== entry.hash) {
@@ -8032,7 +8071,7 @@ async function statusPublishState(root) {
8032
8071
  const state = await readPublishState(root);
8033
8072
  const rows = [];
8034
8073
  for (const entry of state.files) {
8035
- const current = await fileHash((0, import_node_path5.join)(root, entry.path)).catch(() => null);
8074
+ const current = await fileHash((0, import_node_path6.join)(root, entry.path)).catch(() => null);
8036
8075
  rows.push({ ...entry, changed: !current || current.hash !== entry.hash });
8037
8076
  }
8038
8077
  return rows;
@@ -8040,9 +8079,9 @@ async function statusPublishState(root) {
8040
8079
  async function copyStagedFiles(root, destination) {
8041
8080
  const state = await readPublishState(root);
8042
8081
  for (const entry of state.files) {
8043
- const target = (0, import_node_path5.join)(destination, entry.path);
8044
- await (0, import_promises4.mkdir)((0, import_node_path5.dirname)(target), { recursive: true });
8045
- await (0, import_promises4.cp)((0, import_node_path5.join)(root, entry.path), target);
8082
+ const target = (0, import_node_path6.join)(destination, entry.path);
8083
+ await (0, import_promises5.mkdir)((0, import_node_path6.dirname)(target), { recursive: true });
8084
+ await (0, import_promises5.cp)((0, import_node_path6.join)(root, entry.path), target);
8046
8085
  }
8047
8086
  }
8048
8087
 
@@ -8056,21 +8095,21 @@ async function packDirectory(dir) {
8056
8095
  return Buffer.isBuffer(stdout) ? stdout : Buffer.from(stdout);
8057
8096
  }
8058
8097
  async function packStagedFiles(root) {
8059
- const tempDir = await (0, import_promises5.mkdtemp)((0, import_node_path6.join)((0, import_node_os.tmpdir)(), "aipm-publish-stage-"));
8098
+ const tempDir = await (0, import_promises6.mkdtemp)((0, import_node_path7.join)((0, import_node_os.tmpdir)(), "aipm-publish-stage-"));
8060
8099
  try {
8061
8100
  await copyStagedFiles(root, tempDir);
8062
8101
  return await packDirectory(tempDir);
8063
8102
  } finally {
8064
- await (0, import_promises5.rm)(tempDir, { recursive: true, force: true });
8103
+ await (0, import_promises6.rm)(tempDir, { recursive: true, force: true });
8065
8104
  }
8066
8105
  }
8067
8106
  async function unpackTarballToDirectory(tarball) {
8068
- const { mkdtemp: mkdtemp2, writeFile: writeFile7 } = await import("node:fs/promises");
8069
- const { join: join13 } = await import("node:path");
8107
+ const { mkdtemp: mkdtemp2, writeFile: writeFile9 } = await import("node:fs/promises");
8108
+ const { join: join15 } = await import("node:path");
8070
8109
  const { tmpdir: tmpdir2 } = await import("node:os");
8071
- const tempDir = await mkdtemp2(join13(tmpdir2(), "aipm-install-"));
8072
- const tgzPath = join13(tempDir, "pkg.tgz");
8073
- await writeFile7(tgzPath, tarball);
8110
+ const tempDir = await mkdtemp2(join15(tmpdir2(), "aipm-install-"));
8111
+ const tgzPath = join15(tempDir, "pkg.tgz");
8112
+ await writeFile9(tgzPath, tarball);
8074
8113
  await execFileAsync("tar", ["-xzf", tgzPath, "-C", tempDir]);
8075
8114
  return tempDir;
8076
8115
  }
@@ -8229,18 +8268,53 @@ async function recordPackageInstall(registry, name, token) {
8229
8268
  }
8230
8269
  return res.json();
8231
8270
  }
8271
+ async function fetchPromptDetail(registry, publisher, slug) {
8272
+ const base = registry.replace(/\/$/, "");
8273
+ const url = `${base}/v1/prompts/${encodeURIComponent(publisher)}/${encodeURIComponent(slug)}`;
8274
+ const res = await registryFetch(url, base);
8275
+ if (!res.ok) throw new Error(`Prompt not found: @${publisher}/${slug} (${res.status})`);
8276
+ return res.json();
8277
+ }
8278
+ async function recordPromptCopy(registry, publisher, slug) {
8279
+ const base = registry.replace(/\/$/, "");
8280
+ const url = `${base}/v1/prompts/${encodeURIComponent(publisher)}/${encodeURIComponent(slug)}/copy`;
8281
+ const res = await registryFetch(url, base, { method: "POST" });
8282
+ if (!res.ok) throw new Error(`Prompt copy recording failed: ${res.status}`);
8283
+ }
8284
+ async function publishPrompt(registry, input2, accessToken, sampleImage) {
8285
+ const base = registry.replace(/\/$/, "");
8286
+ const form = new FormData();
8287
+ form.append("data", JSON.stringify(input2));
8288
+ if (sampleImage) {
8289
+ form.append(
8290
+ "sampleImage",
8291
+ new Blob([sampleImage.data], { type: sampleImage.contentType }),
8292
+ sampleImage.filename
8293
+ );
8294
+ }
8295
+ const res = await registryFetch(`${base}/v1/prompts`, base, {
8296
+ method: "POST",
8297
+ headers: registryAuthHeaders(accessToken),
8298
+ body: form
8299
+ });
8300
+ if (!res.ok) {
8301
+ const error = await res.json().catch(() => ({}));
8302
+ throw new Error(error.error ?? `Prompt publish failed: ${res.status}`);
8303
+ }
8304
+ return res.json();
8305
+ }
8232
8306
 
8233
8307
  // src/auth-store.ts
8234
- var import_promises6 = require("node:fs/promises");
8308
+ var import_promises7 = require("node:fs/promises");
8235
8309
  var import_node_os2 = require("node:os");
8236
- var import_node_path7 = require("node:path");
8237
- var AUTH_FILE = (0, import_node_path7.join)((0, import_node_os2.homedir)(), ".aipm", "auth.json");
8310
+ var import_node_path8 = require("node:path");
8311
+ var AUTH_FILE = (0, import_node_path8.join)((0, import_node_os2.homedir)(), ".aipm", "auth.json");
8238
8312
  function normalizeRegistry(registry) {
8239
8313
  return registry.replace(/\/$/, "");
8240
8314
  }
8241
8315
  async function readAuthStore() {
8242
8316
  try {
8243
- const raw = await (0, import_promises6.readFile)(AUTH_FILE, "utf8");
8317
+ const raw = await (0, import_promises7.readFile)(AUTH_FILE, "utf8");
8244
8318
  const parsed = JSON.parse(raw);
8245
8319
  return { registries: parsed.registries ?? {} };
8246
8320
  } catch {
@@ -8248,9 +8322,9 @@ async function readAuthStore() {
8248
8322
  }
8249
8323
  }
8250
8324
  async function writeAuthStore(store) {
8251
- await (0, import_promises6.mkdir)((0, import_node_path7.dirname)(AUTH_FILE), { recursive: true });
8252
- await (0, import_promises6.writeFile)(AUTH_FILE, JSON.stringify(store, null, 2) + "\n", "utf8");
8253
- await (0, import_promises6.chmod)(AUTH_FILE, 384).catch(() => void 0);
8325
+ await (0, import_promises7.mkdir)((0, import_node_path8.dirname)(AUTH_FILE), { recursive: true });
8326
+ await (0, import_promises7.writeFile)(AUTH_FILE, JSON.stringify(store, null, 2) + "\n", "utf8");
8327
+ await (0, import_promises7.chmod)(AUTH_FILE, 384).catch(() => void 0);
8254
8328
  }
8255
8329
  async function getStoredRegistryAuth(registry) {
8256
8330
  const store = await readAuthStore();
@@ -8279,41 +8353,41 @@ function isAccessTokenFresh(auth, skewMs = 6e4) {
8279
8353
  }
8280
8354
 
8281
8355
  // src/install-one.ts
8282
- init_dist4();
8283
- var import_promises8 = require("node:fs/promises");
8284
- var import_node_path9 = require("node:path");
8356
+ init_dist5();
8357
+ var import_promises9 = require("node:fs/promises");
8358
+ var import_node_path10 = require("node:path");
8285
8359
 
8286
8360
  // src/project-files.ts
8287
- var import_promises7 = require("node:fs/promises");
8288
- var import_node_path8 = require("node:path");
8361
+ var import_promises8 = require("node:fs/promises");
8362
+ var import_node_path9 = require("node:path");
8289
8363
  init_dist();
8290
8364
  var PACKAGE_JSON = "aipm.package.json";
8291
8365
  var LOCKFILE = "aipm-lock.json";
8292
8366
  async function readProjectPackageJson(projectRoot) {
8293
8367
  try {
8294
- const raw = await (0, import_promises7.readFile)((0, import_node_path8.join)(projectRoot, PACKAGE_JSON), "utf8");
8368
+ const raw = await (0, import_promises8.readFile)((0, import_node_path9.join)(projectRoot, PACKAGE_JSON), "utf8");
8295
8369
  return ProjectPackageJsonSchema.parse(JSON.parse(raw));
8296
8370
  } catch {
8297
8371
  return null;
8298
8372
  }
8299
8373
  }
8300
8374
  async function writeProjectPackageJson(projectRoot, data) {
8301
- await (0, import_promises7.writeFile)(
8302
- (0, import_node_path8.join)(projectRoot, PACKAGE_JSON),
8375
+ await (0, import_promises8.writeFile)(
8376
+ (0, import_node_path9.join)(projectRoot, PACKAGE_JSON),
8303
8377
  JSON.stringify(data, null, 2) + "\n",
8304
8378
  "utf8"
8305
8379
  );
8306
8380
  }
8307
8381
  async function readLockfile(projectRoot) {
8308
8382
  try {
8309
- const raw = await (0, import_promises7.readFile)((0, import_node_path8.join)(projectRoot, LOCKFILE), "utf8");
8383
+ const raw = await (0, import_promises8.readFile)((0, import_node_path9.join)(projectRoot, LOCKFILE), "utf8");
8310
8384
  return LockfileSchema.parse(JSON.parse(raw));
8311
8385
  } catch {
8312
8386
  return null;
8313
8387
  }
8314
8388
  }
8315
8389
  async function writeLockfile(projectRoot, data) {
8316
- await (0, import_promises7.writeFile)((0, import_node_path8.join)(projectRoot, LOCKFILE), JSON.stringify(data, null, 2) + "\n", "utf8");
8390
+ await (0, import_promises8.writeFile)((0, import_node_path9.join)(projectRoot, LOCKFILE), JSON.stringify(data, null, 2) + "\n", "utf8");
8317
8391
  }
8318
8392
  function upsertLockEntry(lock, name, entry) {
8319
8393
  return {
@@ -8321,6 +8395,12 @@ function upsertLockEntry(lock, name, entry) {
8321
8395
  packages: { ...lock.packages, [name]: entry }
8322
8396
  };
8323
8397
  }
8398
+ function upsertPromptLockEntry(lock, name, entry) {
8399
+ return {
8400
+ ...lock,
8401
+ prompts: { ...lock.prompts, [name]: entry }
8402
+ };
8403
+ }
8324
8404
  function resolveRegistryUrl(project, flag, fallback) {
8325
8405
  const fromEnv = process.env.AIPM_REGISTRY_URL ?? process.env.AIPM_REGISTRY;
8326
8406
  if (flag) return flag.replace(/\/$/, "");
@@ -8331,15 +8411,17 @@ function resolveRegistryUrl(project, flag, fallback) {
8331
8411
  }
8332
8412
  function parseTargetFlag(target) {
8333
8413
  if (!target) return void 0;
8334
- if (target === "cursor" || target === "claude" || target === "*") return target;
8335
- throw new Error('--target must be "cursor", "claude", or "*"');
8414
+ if (target === "cursor" || target === "claude" || target === "codex" || target === "*") {
8415
+ return target;
8416
+ }
8417
+ throw new Error('--target must be "cursor", "claude", "codex", or "*"');
8336
8418
  }
8337
8419
  function parseTargetsFlag(value) {
8338
8420
  const targets = value.split(",").map((target) => target.trim()).filter(Boolean);
8339
8421
  if (targets.length === 0) throw new Error("At least one target is required.");
8340
8422
  for (const target of targets) {
8341
- if (target !== "cursor" && target !== "claude" && target !== "*") {
8342
- throw new Error('--targets must contain only "cursor", "claude", and/or "*"');
8423
+ if (target !== "cursor" && target !== "claude" && target !== "codex" && target !== "*") {
8424
+ throw new Error('--targets must contain only "cursor", "claude", "codex", and/or "*"');
8343
8425
  }
8344
8426
  }
8345
8427
  const unique = [...new Set(targets)];
@@ -8355,13 +8437,13 @@ async function promptForTool() {
8355
8437
  try {
8356
8438
  while (true) {
8357
8439
  const answer = await rl.question(
8358
- "Which AI tool should this skill be installed for? (cursor/claude): "
8440
+ "Which AI tool should this skill be installed for? (cursor/claude/codex): "
8359
8441
  );
8360
8442
  const normalized = answer.trim().toLowerCase();
8361
- if (normalized === "cursor" || normalized === "claude") {
8443
+ if (normalized === "cursor" || normalized === "claude" || normalized === "codex") {
8362
8444
  return normalized;
8363
8445
  }
8364
- console.log('Please enter "cursor" or "claude".');
8446
+ console.log('Please enter "cursor", "claude", or "codex".');
8365
8447
  }
8366
8448
  } finally {
8367
8449
  rl.close();
@@ -8380,7 +8462,7 @@ async function promptForConfirmation(question) {
8380
8462
 
8381
8463
  // src/install-one.ts
8382
8464
  function normalizeRelativePath(path2) {
8383
- return (0, import_node_path9.normalize)(path2).split(import_node_path9.sep).join("/");
8465
+ return (0, import_node_path10.normalize)(path2).split(import_node_path10.sep).join("/");
8384
8466
  }
8385
8467
  function assertSafeRelativePath(path2) {
8386
8468
  const rel = normalizeRelativePath(path2.trim());
@@ -8391,8 +8473,8 @@ function assertSafeRelativePath(path2) {
8391
8473
  }
8392
8474
  function safeJoin(root, relPath) {
8393
8475
  const rel = assertSafeRelativePath(relPath);
8394
- const target = (0, import_node_path9.resolve)(root, rel);
8395
- const fromRoot = normalizeRelativePath((0, import_node_path9.relative)(root, target));
8476
+ const target = (0, import_node_path10.resolve)(root, rel);
8477
+ const fromRoot = normalizeRelativePath((0, import_node_path10.relative)(root, target));
8396
8478
  if (fromRoot === ".." || fromRoot.startsWith("../") || fromRoot.startsWith("/")) {
8397
8479
  throw new Error(`Unsafe install path: ${relPath}`);
8398
8480
  }
@@ -8403,12 +8485,12 @@ function packageHelperSlug(name) {
8403
8485
  return `${scope}__${pkg}`;
8404
8486
  }
8405
8487
  function helperRootFor(configRoot, packageName, version) {
8406
- const base = (0, import_node_path9.basename)(configRoot) === ".aipm" ? (0, import_node_path9.join)(configRoot, "helpers") : (0, import_node_path9.join)(configRoot, ".aipm", "helpers");
8407
- return (0, import_node_path9.join)(base, packageHelperSlug(packageName), version);
8488
+ const base = (0, import_node_path10.basename)(configRoot) === ".aipm" ? (0, import_node_path10.join)(configRoot, "helpers") : (0, import_node_path10.join)(configRoot, ".aipm", "helpers");
8489
+ return (0, import_node_path10.join)(base, packageHelperSlug(packageName), version);
8408
8490
  }
8409
8491
  async function assertSourceFile(packageRoot, relPath) {
8410
8492
  const source = safeJoin(packageRoot, relPath);
8411
- const info = await (0, import_promises8.stat)(source).catch(() => null);
8493
+ const info = await (0, import_promises9.stat)(source).catch(() => null);
8412
8494
  if (!info?.isFile()) throw new Error(`Package install file not found: ${relPath}`);
8413
8495
  return source;
8414
8496
  }
@@ -8416,20 +8498,20 @@ async function copyMainFile(input2) {
8416
8498
  const source = await assertSourceFile(input2.packageRoot, input2.file.from);
8417
8499
  const target = safeJoin(input2.installRoot, input2.file.to);
8418
8500
  const overwrite = input2.file.overwrite ?? "fail";
8419
- const exists = await (0, import_promises8.stat)(target).catch(() => null);
8501
+ const exists = await (0, import_promises9.stat)(target).catch(() => null);
8420
8502
  if (exists) {
8421
8503
  if (overwrite === "skip") return null;
8422
8504
  if (overwrite === "fail") throw new Error(`Install target already exists: ${target}`);
8423
8505
  }
8424
- await (0, import_promises8.mkdir)((0, import_node_path9.dirname)(target), { recursive: true });
8425
- await (0, import_promises8.cp)(source, target, { force: overwrite === "replace" });
8506
+ await (0, import_promises9.mkdir)((0, import_node_path10.dirname)(target), { recursive: true });
8507
+ await (0, import_promises9.cp)(source, target, { force: overwrite === "replace" });
8426
8508
  return target;
8427
8509
  }
8428
8510
  async function copyHelperFile(input2) {
8429
8511
  const source = await assertSourceFile(input2.packageRoot, input2.file.from);
8430
- const target = safeJoin(input2.helperRoot, input2.file.to ?? (0, import_node_path9.basename)(input2.file.from));
8431
- await (0, import_promises8.mkdir)((0, import_node_path9.dirname)(target), { recursive: true });
8432
- await (0, import_promises8.cp)(source, target, { force: true });
8512
+ const target = safeJoin(input2.helperRoot, input2.file.to ?? (0, import_node_path10.basename)(input2.file.from));
8513
+ await (0, import_promises9.mkdir)((0, import_node_path10.dirname)(target), { recursive: true });
8514
+ await (0, import_promises9.cp)(source, target, { force: true });
8433
8515
  return target;
8434
8516
  }
8435
8517
  async function installPackageAssets(options) {
@@ -8446,7 +8528,7 @@ async function installPackageAssets(options) {
8446
8528
  }
8447
8529
  const helperRoot = helperRootFor(options.configRoot, options.packageName, options.version);
8448
8530
  if ((install.helperFiles ?? []).length > 0) {
8449
- await (0, import_promises8.rm)(helperRoot, { recursive: true, force: true });
8531
+ await (0, import_promises9.rm)(helperRoot, { recursive: true, force: true });
8450
8532
  }
8451
8533
  for (const file of install.helperFiles ?? []) {
8452
8534
  assets.helper.push(await copyHelperFile({ packageRoot: options.packageRoot, helperRoot, file }));
@@ -8491,7 +8573,7 @@ async function installOnePackage(options) {
8491
8573
  }
8492
8574
  let explicitTarget = options.explicitTarget;
8493
8575
  let preferredTools = options.project.preferredTools;
8494
- const { resolveInstallTools: resolveInstallTools2 } = await Promise.resolve().then(() => (init_dist4(), dist_exports));
8576
+ const { resolveInstallTools: resolveInstallTools2 } = await Promise.resolve().then(() => (init_dist5(), dist_exports));
8495
8577
  let tools = await resolveInstallTools2({
8496
8578
  projectRoot: installRoot,
8497
8579
  manifest,
@@ -8501,7 +8583,7 @@ async function installOnePackage(options) {
8501
8583
  if (tools.length === 0) {
8502
8584
  if (options.ci) {
8503
8585
  throw new Error(
8504
- "No tool detected. Use --target cursor|claude|* in CI mode."
8586
+ "No tool detected. Use --target cursor|claude|codex|* in CI mode."
8505
8587
  );
8506
8588
  }
8507
8589
  const choice = await promptForTool();
@@ -8522,7 +8604,7 @@ async function installOnePackage(options) {
8522
8604
  );
8523
8605
  const packageRoot = await unpackTarballToDirectory(tarball);
8524
8606
  try {
8525
- const skillMarkdown = await (0, import_promises8.readFile)(safeJoin(packageRoot, manifest.entry), "utf8");
8607
+ const skillMarkdown = await (0, import_promises9.readFile)(safeJoin(packageRoot, manifest.entry), "utf8");
8526
8608
  const result = await installSkillPackage({
8527
8609
  projectRoot: installRoot,
8528
8610
  manifest,
@@ -8540,7 +8622,8 @@ async function installOnePackage(options) {
8540
8622
  });
8541
8623
  const lock = await readLockfile(configRoot) ?? {
8542
8624
  schemaVersion: "0.1",
8543
- packages: {}
8625
+ packages: {},
8626
+ prompts: {}
8544
8627
  };
8545
8628
  const installed = {};
8546
8629
  for (const tool of result.resolvedTools) {
@@ -8562,23 +8645,23 @@ async function installOnePackage(options) {
8562
8645
  console.log(`Installed ${options.name}@${options.version} \u2192 ${result.resolvedTools.join(", ")}`);
8563
8646
  printPostInstallNotice(options.name, postInstall);
8564
8647
  } finally {
8565
- await (0, import_promises8.rm)(packageRoot, { recursive: true, force: true });
8648
+ await (0, import_promises9.rm)(packageRoot, { recursive: true, force: true });
8566
8649
  }
8567
8650
  }
8568
8651
 
8569
8652
  // src/doctor.ts
8570
8653
  var import_node_child_process3 = require("node:child_process");
8571
- var import_promises9 = require("node:fs/promises");
8572
- var import_node_path11 = require("node:path");
8654
+ var import_promises10 = require("node:fs/promises");
8655
+ var import_node_path12 = require("node:path");
8573
8656
  var import_node_util4 = require("node:util");
8574
8657
 
8575
8658
  // src/project-root.ts
8576
8659
  var import_node_os3 = require("node:os");
8577
- var import_node_path10 = require("node:path");
8660
+ var import_node_path11 = require("node:path");
8578
8661
  var import_node_process3 = require("node:process");
8579
8662
  function globalConfigDir(env2 = process.env) {
8580
8663
  const fromEnv = env2.AIPM_HOME?.trim();
8581
- return fromEnv ? fromEnv.replace(/\/$/, "") : (0, import_node_path10.join)((0, import_node_os3.homedir)(), ".aipm");
8664
+ return fromEnv ? fromEnv.replace(/\/$/, "") : (0, import_node_path11.join)((0, import_node_os3.homedir)(), ".aipm");
8582
8665
  }
8583
8666
  function resolveConfigRoot(options = {}) {
8584
8667
  return options.global ? globalConfigDir() : (0, import_node_process3.cwd)();
@@ -8609,11 +8692,11 @@ async function commandOutput(command, args) {
8609
8692
  }
8610
8693
  function npmGlobalBin(prefix) {
8611
8694
  if (!prefix) return null;
8612
- return process.platform === "win32" ? prefix : `${prefix}${import_node_path11.sep}bin`;
8695
+ return process.platform === "win32" ? prefix : `${prefix}${import_node_path12.sep}bin`;
8613
8696
  }
8614
8697
  function pathIncludes(pathDir) {
8615
8698
  if (!pathDir) return false;
8616
- return (process.env.PATH ?? "").split(import_node_path11.delimiter).includes(pathDir);
8699
+ return (process.env.PATH ?? "").split(import_node_path12.delimiter).includes(pathDir);
8617
8700
  }
8618
8701
  function shellPathHint(binDir) {
8619
8702
  if (!binDir) return "Run npm prefix -g and add its bin folder to PATH.";
@@ -8666,11 +8749,11 @@ async function runDoctor(options) {
8666
8749
  if (!options.publish) {
8667
8750
  const configLabel = options.global ? "Global config" : "Project config";
8668
8751
  try {
8669
- await (0, import_promises9.access)((0, import_node_path11.join)(configRoot, "aipm.package.json"));
8752
+ await (0, import_promises10.access)((0, import_node_path12.join)(configRoot, "aipm.package.json"));
8670
8753
  checks.push({
8671
8754
  name: configLabel,
8672
8755
  ok: true,
8673
- detail: `${(0, import_node_path11.join)(configRoot, "aipm.package.json")} exists (${scopeLabel(scope)}).`
8756
+ detail: `${(0, import_node_path12.join)(configRoot, "aipm.package.json")} exists (${scopeLabel(scope)}).`
8674
8757
  });
8675
8758
  } catch {
8676
8759
  checks.push({
@@ -8738,11 +8821,181 @@ async function runDoctor(options) {
8738
8821
  }
8739
8822
  }
8740
8823
 
8824
+ // src/prompt-install.ts
8825
+ var import_node_crypto2 = require("node:crypto");
8826
+ var import_promises11 = require("node:fs/promises");
8827
+ var import_node_path13 = require("node:path");
8828
+ var PROMPT_SITE_ORIGIN = "https://www.aipm-registry.com";
8829
+ function parsePromptUrl(value) {
8830
+ let url;
8831
+ try {
8832
+ url = new URL(value);
8833
+ } catch {
8834
+ return null;
8835
+ }
8836
+ if (url.origin !== PROMPT_SITE_ORIGIN) return null;
8837
+ const parts = url.pathname.split("/").filter(Boolean);
8838
+ if (parts.length !== 3 || parts[0] !== "prompts") return null;
8839
+ const publisher = decodeURIComponent(parts[1] ?? "").trim().toLowerCase();
8840
+ const slug = decodeURIComponent(parts[2] ?? "").trim().toLowerCase();
8841
+ if (!publisher || !slug || !/^[a-z0-9][a-z0-9-]*$/.test(publisher) || !/^[a-z0-9][a-z0-9-]*$/.test(slug)) {
8842
+ return null;
8843
+ }
8844
+ return {
8845
+ url: `${PROMPT_SITE_ORIGIN}/prompts/${encodeURIComponent(publisher)}/${encodeURIComponent(slug)}`,
8846
+ publisher,
8847
+ slug,
8848
+ alias: slug
8849
+ };
8850
+ }
8851
+ function promptDirectory(configRoot) {
8852
+ return (0, import_node_path13.basename)(configRoot) === ".aipm" ? (0, import_node_path13.join)(configRoot, "prompts") : (0, import_node_path13.join)(configRoot, ".aipm", "prompts");
8853
+ }
8854
+ function promptSnapshotPath(configRoot, reference) {
8855
+ return (0, import_node_path13.join)(promptDirectory(configRoot), `${reference.publisher}--${reference.slug}.md`);
8856
+ }
8857
+ function canonicalPromptContent(prompt) {
8858
+ return JSON.stringify({
8859
+ promptText: prompt.promptText,
8860
+ variables: prompt.variables,
8861
+ exampleInput: prompt.exampleInput,
8862
+ exampleOutput: prompt.exampleOutput,
8863
+ usageNotes: prompt.usageNotes,
8864
+ license: prompt.license
8865
+ });
8866
+ }
8867
+ function promptContentHash(prompt) {
8868
+ return (0, import_node_crypto2.createHash)("sha256").update(canonicalPromptContent(prompt)).digest("hex");
8869
+ }
8870
+ function section(title, value) {
8871
+ return value?.trim() ? `
8872
+ ## ${title}
8873
+
8874
+ ${value.trim()}
8875
+ ` : "";
8876
+ }
8877
+ function renderPromptMarkdown(prompt, sourceUrl) {
8878
+ const variables = prompt.variables.length ? `
8879
+ ## Variables
8880
+
8881
+ ${prompt.variables.map(
8882
+ (variable) => `- \`{{${variable.name}}}\`${variable.required ? " \u2014 required" : " \u2014 optional"}: ${variable.description}${variable.example ? ` Example: ${variable.example}` : ""}`
8883
+ ).join("\n")}
8884
+ ` : "";
8885
+ const publisher = prompt.publisher.org?.name ?? prompt.publisher.user.name ?? `@${prompt.publisher.scope}`;
8886
+ return [
8887
+ `# ${prompt.title}`,
8888
+ "",
8889
+ prompt.summary,
8890
+ "",
8891
+ `- Source: ${sourceUrl}`,
8892
+ `- Publisher: ${publisher} (@${prompt.publisher.scope})`,
8893
+ `- Category: ${prompt.category}`,
8894
+ `- Inputs: ${prompt.inputTypes.join(", ") || "none"}`,
8895
+ `- Outputs: ${prompt.outputTypes.join(", ") || "none"}`,
8896
+ `- Effort: ${prompt.effort}`,
8897
+ `- Language: ${prompt.language}`,
8898
+ `- License: ${prompt.license}`,
8899
+ `- Updated: ${prompt.updatedAt}`,
8900
+ "",
8901
+ "## Prompt",
8902
+ "",
8903
+ prompt.promptText.trim(),
8904
+ variables,
8905
+ section("Example input", prompt.exampleInput),
8906
+ section("Example output", prompt.exampleOutput),
8907
+ section("Usage notes", prompt.usageNotes)
8908
+ ].join("\n").replace(/\n{3,}/g, "\n\n").trimEnd().concat("\n");
8909
+ }
8910
+ function emptyLock() {
8911
+ return { schemaVersion: "0.1", packages: {}, prompts: {} };
8912
+ }
8913
+ function installedRelativePath(configRoot, absolutePath) {
8914
+ return (0, import_node_path13.relative)(configRoot, absolutePath).split("\\").join("/");
8915
+ }
8916
+ async function installTrackedPrompt(input2) {
8917
+ const prompt = await fetchPromptDetail(
8918
+ input2.registry,
8919
+ input2.reference.publisher,
8920
+ input2.reference.slug
8921
+ );
8922
+ const canonicalReference = {
8923
+ ...input2.reference,
8924
+ publisher: prompt.publisher.scope,
8925
+ slug: prompt.slug,
8926
+ url: `${PROMPT_SITE_ORIGIN}/prompts/${encodeURIComponent(prompt.publisher.scope)}/${encodeURIComponent(prompt.slug)}`
8927
+ };
8928
+ const outputPath = promptSnapshotPath(input2.configRoot, canonicalReference);
8929
+ const contentHash = promptContentHash(prompt);
8930
+ const lock = await readLockfile(input2.configRoot) ?? emptyLock();
8931
+ const existing = lock.prompts[input2.reference.alias];
8932
+ const fileExists = Boolean(await (0, import_promises11.stat)(outputPath).catch(() => null));
8933
+ const changed = !existing || existing.contentHash !== contentHash || !fileExists;
8934
+ if (!input2.updateOnly || changed) {
8935
+ await (0, import_promises11.mkdir)((0, import_node_path13.dirname)(outputPath), { recursive: true });
8936
+ await (0, import_promises11.writeFile)(outputPath, renderPromptMarkdown(prompt, canonicalReference.url), "utf8");
8937
+ }
8938
+ if (input2.track) {
8939
+ const currentUrl = input2.project.prompts[input2.reference.alias];
8940
+ if (currentUrl && currentUrl !== canonicalReference.url) {
8941
+ throw new Error(
8942
+ `Prompt alias "${input2.reference.alias}" already tracks ${currentUrl}. Remove it first or use a unique slug.`
8943
+ );
8944
+ }
8945
+ await writeProjectPackageJson(input2.configRoot, {
8946
+ ...input2.project,
8947
+ prompts: { ...input2.project.prompts, [input2.reference.alias]: canonicalReference.url }
8948
+ });
8949
+ }
8950
+ const entry = {
8951
+ id: prompt.id,
8952
+ url: canonicalReference.url,
8953
+ publisher: canonicalReference.publisher,
8954
+ slug: canonicalReference.slug,
8955
+ contentHash,
8956
+ updatedAt: prompt.updatedAt,
8957
+ installedPath: installedRelativePath(input2.configRoot, outputPath)
8958
+ };
8959
+ await writeLockfile(input2.configRoot, upsertPromptLockEntry(lock, input2.reference.alias, entry));
8960
+ if (input2.recordCopy) {
8961
+ await recordPromptCopy(input2.registry, canonicalReference.publisher, canonicalReference.slug).catch(
8962
+ () => void 0
8963
+ );
8964
+ }
8965
+ return { changed, path: outputPath, prompt };
8966
+ }
8967
+ function resolveTrackedPrompt(project, value) {
8968
+ const direct = parsePromptUrl(value);
8969
+ if (direct) {
8970
+ const match = Object.entries(project.prompts).find(([, url2]) => url2 === direct.url);
8971
+ return match ? { ...direct, alias: match[0] } : direct;
8972
+ }
8973
+ const url = project.prompts[value];
8974
+ const parsed = url ? parsePromptUrl(url) : null;
8975
+ return parsed ? { ...parsed, alias: value } : null;
8976
+ }
8977
+ async function readInstalledPrompt(configRoot, entry) {
8978
+ return (0, import_promises11.readFile)((0, import_node_path13.join)(configRoot, entry.installedPath), "utf8");
8979
+ }
8980
+ async function removeTrackedPrompt(input2) {
8981
+ const lock = await readLockfile(input2.configRoot);
8982
+ const entry = lock?.prompts[input2.reference.alias];
8983
+ if (entry) await (0, import_promises11.rm)((0, import_node_path13.join)(input2.configRoot, entry.installedPath), { force: true });
8984
+ const prompts = { ...input2.project.prompts };
8985
+ delete prompts[input2.reference.alias];
8986
+ await writeProjectPackageJson(input2.configRoot, { ...input2.project, prompts });
8987
+ if (lock) {
8988
+ const lockedPrompts = { ...lock.prompts };
8989
+ delete lockedPrompts[input2.reference.alias];
8990
+ await writeLockfile(input2.configRoot, { ...lock, prompts: lockedPrompts });
8991
+ }
8992
+ }
8993
+
8741
8994
  // src/version.ts
8742
8995
  var import_node_fs2 = require("node:fs");
8743
- var import_node_path12 = require("node:path");
8996
+ var import_node_path14 = require("node:path");
8744
8997
  function getCliVersion() {
8745
- const pkgPath = (0, import_node_path12.join)(__dirname, "..", "package.json");
8998
+ const pkgPath = (0, import_node_path14.join)(__dirname, "..", "package.json");
8746
8999
  const pkg = JSON.parse((0, import_node_fs2.readFileSync)(pkgPath, "utf8"));
8747
9000
  return pkg.version;
8748
9001
  }
@@ -8831,11 +9084,14 @@ async function notifyCliUpdateIfNeeded(currentVersion, options) {
8831
9084
  var CLI_VERSION = getCliVersion();
8832
9085
  var program2 = new Command();
8833
9086
  var DEFAULT_REGISTRY = "https://api.aipm-registry.com";
8834
- var SITE_URL = "https://aipm-registry.com";
8835
- var PUBLISH_DOC_URL = "https://aipm-registry.com/publish";
8836
- var DASHBOARD_URL = "https://aipm-registry.com/dashboard";
9087
+ var SITE_URL = "https://www.aipm-registry.com";
9088
+ var PUBLISH_DOC_URL = `${SITE_URL}/publish`;
9089
+ var DASHBOARD_URL = `${SITE_URL}/dashboard`;
8837
9090
  var GLOBAL_OPTION = "-g, --global";
8838
9091
  var GLOBAL_OPTION_DESC = "Use global config (~/.aipm) and install skills under your home directory";
9092
+ var normalizedArgv = process.argv.map(
9093
+ (arg) => arg === "-prompt" ? "--prompt" : arg === "-skill" ? "--skill" : arg
9094
+ );
8839
9095
  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(
8840
9096
  "after",
8841
9097
  `
@@ -8863,6 +9119,10 @@ function packagePageUrl(packageName, version) {
8863
9119
  const [scope, name] = packageName.replace(/^@/, "").split("/");
8864
9120
  return `${SITE_URL}/packages/${encodeURIComponent(scope ?? "")}/${encodeURIComponent(name ?? "")}/${encodeURIComponent(version)}`;
8865
9121
  }
9122
+ function packageBadgeMarkdown(packageName, version) {
9123
+ const pageUrl = packagePageUrl(packageName, version);
9124
+ return `[![Install with AIPM](${SITE_URL}/install-with-aipm.svg)](${pageUrl})`;
9125
+ }
8866
9126
  function parsePackageArg(value) {
8867
9127
  const match = value.match(/^(@[^@]+)(?:@(.+))?$/);
8868
9128
  if (!match) throw new Error("Package must be @scope/name or @scope/name@version");
@@ -8877,23 +9137,23 @@ function skillFolderName(name) {
8877
9137
  return folder;
8878
9138
  }
8879
9139
  async function writeStarterFile(path2, content) {
8880
- await (0, import_promises10.writeFile)(path2, content, { encoding: "utf8", flag: "wx" });
9140
+ await (0, import_promises12.writeFile)(path2, content, { encoding: "utf8", flag: "wx" });
8881
9141
  }
8882
9142
  async function inferEntryFromSource(source, fallback) {
8883
- const sourceInfo = await (0, import_promises10.stat)(source);
8884
- if (sourceInfo.isFile()) return (0, import_node_path13.basename)(source);
9143
+ const sourceInfo = await (0, import_promises12.stat)(source);
9144
+ if (sourceInfo.isFile()) return (0, import_node_path15.basename)(source);
8885
9145
  if (!sourceInfo.isDirectory()) return fallback;
8886
- const entries = await (0, import_promises10.readdir)(source);
9146
+ const entries = await (0, import_promises12.readdir)(source);
8887
9147
  if (entries.includes(fallback)) return fallback;
8888
9148
  return entries.find((entry) => entry.endsWith(".md")) ?? entries.find((entry) => entry.endsWith(".mdc")) ?? fallback;
8889
9149
  }
8890
9150
  async function copySourceIntoSkillRoot(source, root, sourceLabel) {
8891
- const sourceInfo = await (0, import_promises10.stat)(source);
8892
- await (0, import_promises10.mkdir)(root, { recursive: true });
9151
+ const sourceInfo = await (0, import_promises12.stat)(source);
9152
+ await (0, import_promises12.mkdir)(root, { recursive: true });
8893
9153
  if (sourceInfo.isDirectory()) {
8894
- const entries = await (0, import_promises10.readdir)(source);
9154
+ const entries = await (0, import_promises12.readdir)(source);
8895
9155
  for (const entry of entries) {
8896
- await (0, import_promises10.cp)((0, import_node_path13.join)(source, entry), (0, import_node_path13.join)(root, entry), {
9156
+ await (0, import_promises12.cp)((0, import_node_path15.join)(source, entry), (0, import_node_path15.join)(root, entry), {
8897
9157
  recursive: true,
8898
9158
  force: false,
8899
9159
  errorOnExist: true
@@ -8902,7 +9162,7 @@ async function copySourceIntoSkillRoot(source, root, sourceLabel) {
8902
9162
  return;
8903
9163
  }
8904
9164
  if (sourceInfo.isFile()) {
8905
- await (0, import_promises10.cp)(source, (0, import_node_path13.join)(root, (0, import_node_path13.basename)(source)), { force: false, errorOnExist: true });
9165
+ await (0, import_promises12.cp)(source, (0, import_node_path15.join)(root, (0, import_node_path15.basename)(source)), { force: false, errorOnExist: true });
8906
9166
  return;
8907
9167
  }
8908
9168
  throw new Error(`Unsupported source path: ${sourceLabel}`);
@@ -8936,11 +9196,11 @@ function packageDashboardUrl(name) {
8936
9196
  return `${DASHBOARD_URL}/packages/${name.replace(/^@/, "")}`;
8937
9197
  }
8938
9198
  function helperRootFromTrackedPath(path2) {
8939
- const parts = path2.split(import_node_path13.sep).filter(Boolean);
9199
+ const parts = path2.split(import_node_path15.sep).filter(Boolean);
8940
9200
  const helpersIndex = parts.lastIndexOf("helpers");
8941
9201
  if (helpersIndex === -1 || parts.length <= helpersIndex + 2) return null;
8942
- const prefix = path2.startsWith(import_node_path13.sep) ? import_node_path13.sep : "";
8943
- return (0, import_node_path13.join)(prefix, ...parts.slice(0, helpersIndex + 3));
9202
+ const prefix = path2.startsWith(import_node_path15.sep) ? import_node_path15.sep : "";
9203
+ return (0, import_node_path15.join)(prefix, ...parts.slice(0, helpersIndex + 3));
8944
9204
  }
8945
9205
  async function showInstalledPrompt(configRoot, packageArg) {
8946
9206
  const { name } = parsePackageArg(packageArg);
@@ -8953,7 +9213,7 @@ async function showInstalledPrompt(configRoot, packageArg) {
8953
9213
  if (entry.postInstall.status === "cleaned") {
8954
9214
  throw new Error(`Setup helper files were cleaned for ${name}. Reinstall the package to restore them.`);
8955
9215
  }
8956
- const content = await (0, import_promises10.readFile)(entry.postInstall.promptFile, "utf8").catch(() => {
9216
+ const content = await (0, import_promises12.readFile)(entry.postInstall.promptFile, "utf8").catch(() => {
8957
9217
  throw new Error(`Setup prompt file is missing: ${entry.postInstall?.promptFile}`);
8958
9218
  });
8959
9219
  console.log(`Prompt file: ${entry.postInstall.promptFile}`);
@@ -8986,9 +9246,9 @@ async function cleanupInstalledHelpers(options) {
8986
9246
  entry.installedAssets.helper.map((path2) => helperRootFromTrackedPath(path2)).filter((path2) => Boolean(path2))
8987
9247
  );
8988
9248
  if (roots.size > 0) {
8989
- for (const root of roots) await (0, import_promises10.rm)(root, { recursive: true, force: true });
9249
+ for (const root of roots) await (0, import_promises12.rm)(root, { recursive: true, force: true });
8990
9250
  } else {
8991
- for (const helperPath of entry.installedAssets.helper) await (0, import_promises10.rm)(helperPath, { force: true });
9251
+ for (const helperPath of entry.installedAssets.helper) await (0, import_promises12.rm)(helperPath, { force: true });
8992
9252
  }
8993
9253
  await writeLockfile(options.configRoot, {
8994
9254
  ...lock,
@@ -9011,10 +9271,10 @@ async function openUrl(url) {
9011
9271
  console.log(`Open: ${url}`);
9012
9272
  }
9013
9273
  function randomBase64Url(bytes = 32) {
9014
- return (0, import_node_crypto2.randomBytes)(bytes).toString("base64url");
9274
+ return (0, import_node_crypto3.randomBytes)(bytes).toString("base64url");
9015
9275
  }
9016
9276
  function sha256Base64Url(value) {
9017
- return (0, import_node_crypto2.createHash)("sha256").update(value).digest("base64url");
9277
+ return (0, import_node_crypto3.createHash)("sha256").update(value).digest("base64url");
9018
9278
  }
9019
9279
  function deviceName() {
9020
9280
  return `AIPM CLI on ${process.platform}`;
@@ -9282,9 +9542,9 @@ function buildStarterQualityMetadata(options) {
9282
9542
  }
9283
9543
  async function initSkill(opts) {
9284
9544
  if (!isValidScopeName(opts.name)) throw new Error("Invalid @scope/name");
9285
- const root = opts.here ? (0, import_node_process5.cwd)() : (0, import_node_path13.resolve)((0, import_node_process5.cwd)(), opts.dir ?? skillFolderName(opts.name));
9545
+ const root = opts.here ? (0, import_node_process5.cwd)() : (0, import_node_path15.resolve)((0, import_node_process5.cwd)(), opts.dir ?? skillFolderName(opts.name));
9286
9546
  const cdTarget = opts.here ? null : opts.dir ?? skillFolderName(opts.name);
9287
- const source = opts.from ? (0, import_node_path13.resolve)(opts.from) : null;
9547
+ const source = opts.from ? (0, import_node_path15.resolve)(opts.from) : null;
9288
9548
  const entry = source ? await inferEntryFromSource(source, opts.entry) : opts.entry;
9289
9549
  const template = parseSkillTemplate(opts.template ?? "blank");
9290
9550
  const manifest = PackageManifestSchema.parse({
@@ -9305,17 +9565,17 @@ async function initSkill(opts) {
9305
9565
  if (source) {
9306
9566
  await copySourceIntoSkillRoot(source, root, source);
9307
9567
  } else {
9308
- await (0, import_promises10.mkdir)(root, { recursive: true });
9568
+ await (0, import_promises12.mkdir)(root, { recursive: true });
9309
9569
  }
9310
- await writeStarterFile((0, import_node_path13.join)(root, "aipm.manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
9570
+ await writeStarterFile((0, import_node_path15.join)(root, "aipm.manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
9311
9571
  if (!opts.from) {
9312
9572
  await writeStarterFile(
9313
- (0, import_node_path13.join)(root, entry),
9573
+ (0, import_node_path15.join)(root, entry),
9314
9574
  SKILL_TEMPLATES[template](opts.name)
9315
9575
  );
9316
9576
  }
9317
9577
  await writeStarterFile(
9318
- (0, import_node_path13.join)(root, ".aipmignore"),
9578
+ (0, import_node_path15.join)(root, ".aipmignore"),
9319
9579
  [
9320
9580
  "# Files that should never be published with this skill",
9321
9581
  ".env*",
@@ -9333,14 +9593,14 @@ async function initSkill(opts) {
9333
9593
  ""
9334
9594
  ].join("\n")
9335
9595
  );
9336
- await (0, import_promises10.mkdir)((0, import_node_path13.join)(root, ".aipm"), { recursive: true });
9596
+ await (0, import_promises12.mkdir)((0, import_node_path15.join)(root, ".aipm"), { recursive: true });
9337
9597
  console.log(`Created ${opts.name} skill folder: ${root}`);
9338
9598
  printPublishGuide(
9339
9599
  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.`,
9340
9600
  cdTarget ? `Run cd ${cdTarget}, edit/review the files, then run aipm publish add .` : "Edit/review the files, then run aipm publish add ."
9341
9601
  );
9342
9602
  }
9343
- 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").option("--target <tool>", "Preferred install target: cursor, claude, or *").action(async (opts) => {
9603
+ 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").option("--target <tool>", "Preferred install target: cursor, claude, codex, or *").action(async (opts) => {
9344
9604
  const scope = { global: opts.global };
9345
9605
  const configRoot = resolveConfigRoot(scope);
9346
9606
  const existing = await readProjectPackageJson(configRoot);
@@ -9348,7 +9608,7 @@ program2.command("init").description("Create aipm.package.json in the current pr
9348
9608
  console.log(`aipm.package.json already exists (${scopeLabel(scope)}).`);
9349
9609
  return;
9350
9610
  }
9351
- if (scope.global) await (0, import_promises10.mkdir)(configRoot, { recursive: true });
9611
+ if (scope.global) await (0, import_promises12.mkdir)(configRoot, { recursive: true });
9352
9612
  const installRoot = resolveInstallRoot(scope);
9353
9613
  const detected = await detectToolsInProject(installRoot);
9354
9614
  const parsedTarget = parseTargetFlag(opts.target);
@@ -9362,7 +9622,8 @@ program2.command("init").description("Create aipm.package.json in the current pr
9362
9622
  schemaVersion: "0.1",
9363
9623
  registry,
9364
9624
  preferredTools: preferredTools.length ? preferredTools : void 0,
9365
- packages: {}
9625
+ packages: {},
9626
+ prompts: {}
9366
9627
  });
9367
9628
  console.log(`Created aipm.package.json (${scopeLabel(scope)}, registry: ${registry})`);
9368
9629
  });
@@ -9450,14 +9711,27 @@ program2.command("config").description("Show resolved AIPM CLI and project confi
9450
9711
  console.log(`Auth file: ${data.authFile}`);
9451
9712
  console.log(`Publish guide: ${data.publishDoc}`);
9452
9713
  });
9453
- 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("--token <token>", "Install token for private packages").option("--ci", "Non-interactive; fail if prompt needed").action(async (pkgArg, opts) => {
9454
- const { name, version: requestedVersion } = parsePackageArg(pkgArg);
9714
+ program2.command("add <package-or-prompt>").description("Add a skill package or an AIPM prompt URL").option(GLOBAL_OPTION, GLOBAL_OPTION_DESC).option("--registry <url>", "Registry base URL").option("--target <tool>", "cursor, claude, codex, or *").option("--token <token>", "Install token for private packages").option("--ci", "Non-interactive; fail if prompt needed").action(async (pkgArg, opts) => {
9455
9715
  const scope = { global: opts.global };
9456
9716
  const configRoot = resolveConfigRoot(scope);
9457
9717
  const installRoot = resolveInstallRoot(scope);
9458
9718
  let project = await readProjectPackageJson(configRoot);
9459
9719
  if (!project) throw new Error(initRequiredMessage(scope));
9460
9720
  const registry = resolveRegistryUrl(project, opts.registry, DEFAULT_REGISTRY);
9721
+ const promptReference = parsePromptUrl(pkgArg);
9722
+ if (promptReference) {
9723
+ const result = await installTrackedPrompt({
9724
+ configRoot,
9725
+ registry,
9726
+ project,
9727
+ reference: promptReference,
9728
+ track: true,
9729
+ recordCopy: true
9730
+ });
9731
+ console.log(`Installed prompt ${result.prompt.title} \u2192 ${result.path}`);
9732
+ return;
9733
+ }
9734
+ const { name, version: requestedVersion } = parsePackageArg(pkgArg);
9461
9735
  const token = await tokenForRead(registry, opts.token);
9462
9736
  const version = requestedVersion ?? project.packages[name] ?? await latestVersionForPackage(registry, name, token);
9463
9737
  if (!version) throw new Error("Specify version: aipm add @scope/pkg@1.0.0");
@@ -9509,7 +9783,7 @@ program2.command("search [query]").description("Search public registry packages"
9509
9783
  console.log(` ${meta}`);
9510
9784
  }
9511
9785
  });
9512
- 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("--token <token>", "Install token for private packages").option("--ci", "Non-interactive").action(async (opts) => {
9786
+ 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, codex, or *").option("--token <token>", "Install token for private packages").option("--ci", "Non-interactive").action(async (opts) => {
9513
9787
  const scope = { global: opts.global };
9514
9788
  const configRoot = resolveConfigRoot(scope);
9515
9789
  const installRoot = resolveInstallRoot(scope);
@@ -9531,10 +9805,75 @@ program2.command("install").description("Install all packages from aipm.package.
9531
9805
  token
9532
9806
  });
9533
9807
  }
9808
+ for (const [alias, url] of Object.entries(project.prompts)) {
9809
+ const reference = parsePromptUrl(url);
9810
+ if (!reference) throw new Error(`Invalid tracked prompt URL for ${alias}: ${url}`);
9811
+ const result = await installTrackedPrompt({
9812
+ configRoot,
9813
+ registry,
9814
+ project,
9815
+ reference: { ...reference, alias },
9816
+ track: false
9817
+ });
9818
+ console.log(`Restored prompt ${result.prompt.title} \u2192 ${result.path}`);
9819
+ }
9820
+ });
9821
+ program2.command("show <package-or-prompt>").description("Show an installed prompt snapshot or tracked skill details").option(GLOBAL_OPTION, GLOBAL_OPTION_DESC).action(async (value, opts) => {
9822
+ const configRoot = resolveConfigRoot({ global: opts.global });
9823
+ const project = await readProjectPackageJson(configRoot);
9824
+ if (!project) throw new Error(initRequiredMessage({ global: opts.global }));
9825
+ const reference = resolveTrackedPrompt(project, value);
9826
+ if (reference && project.prompts[reference.alias] === reference.url) {
9827
+ const lock2 = await readLockfile(configRoot);
9828
+ const entry2 = lock2?.prompts[reference.alias];
9829
+ if (!entry2) throw new Error(`Prompt is not installed: ${reference.url}. Run aipm install.`);
9830
+ console.log(await readInstalledPrompt(configRoot, entry2));
9831
+ return;
9832
+ }
9833
+ const { name } = parsePackageArg(value);
9834
+ const lock = await readLockfile(configRoot);
9835
+ const entry = lock?.packages[name];
9836
+ if (!entry) throw new Error(`Package is not installed: ${name}`);
9837
+ console.log(`${name}@${entry.version} [${entry.resolvedTools.join(", ")}]`);
9534
9838
  });
9535
9839
  program2.command("show-prompt <package>").description("Show the manual setup prompt installed with a package").option(GLOBAL_OPTION, GLOBAL_OPTION_DESC).action(async (pkgArg, opts) => {
9536
9840
  await showInstalledPrompt(resolveConfigRoot({ global: opts.global }), pkgArg);
9537
9841
  });
9842
+ function promptImageContentType(path2) {
9843
+ const extension = (0, import_node_path15.extname)(path2).toLowerCase();
9844
+ if (extension === ".png") return "image/png";
9845
+ if (extension === ".jpg" || extension === ".jpeg") return "image/jpeg";
9846
+ if (extension === ".webp") return "image/webp";
9847
+ throw new Error(`Unsupported prompt sample image: ${path2}`);
9848
+ }
9849
+ var promptCommand = program2.command("prompt").description("Publish prompt listings from structured JSON");
9850
+ promptCommand.command("publish <file>").description("Publish one prompt or a batch of prompts from JSON").option("--registry <url>", "Registry API base URL").option("--yes", "Confirm public publishing without an extra reminder").action(async (file, opts) => {
9851
+ const registry = registryFromEnvOrDefault(opts.registry);
9852
+ const accessToken = await authTokenForRegistry(registry, { throwOnFailure: true });
9853
+ if (!accessToken) throw new Error("Run aipm login before publishing prompts.");
9854
+ const absoluteFile = (0, import_node_path15.resolve)(file);
9855
+ const parsed = JSON.parse(await (0, import_promises12.readFile)(absoluteFile, "utf8"));
9856
+ const rows = Array.isArray(parsed) ? parsed : "prompts" in parsed ? parsed.prompts : [parsed];
9857
+ if (rows.length === 0) throw new Error("Prompt publish file contains no prompts.");
9858
+ if (!opts.yes) {
9859
+ console.log(`Reminder: this will publish ${rows.length} public prompt${rows.length === 1 ? "" : "s"}.`);
9860
+ console.log("Use --yes to skip this reminder in automation.");
9861
+ }
9862
+ for (const row of rows) {
9863
+ const { sampleImagePath, ...input2 } = row;
9864
+ let sampleImage;
9865
+ if (sampleImagePath) {
9866
+ const imagePath = (0, import_node_path15.resolve)((0, import_node_path15.dirname)(absoluteFile), sampleImagePath);
9867
+ sampleImage = {
9868
+ data: await (0, import_promises12.readFile)(imagePath),
9869
+ filename: (0, import_node_path15.basename)(imagePath),
9870
+ contentType: promptImageContentType(imagePath)
9871
+ };
9872
+ }
9873
+ const saved = await publishPrompt(registry, input2, accessToken, sampleImage);
9874
+ console.log(`Published ${saved.title} \u2192 ${SITE_URL}${saved.path}`);
9875
+ }
9876
+ });
9538
9877
  program2.command("cleanup <package>").description("Delete temporary helper files installed with a package").option(GLOBAL_OPTION, GLOBAL_OPTION_DESC).option("--yes", "Delete helper files without asking for confirmation").action(async (pkgArg, opts) => {
9539
9878
  await cleanupInstalledHelpers({
9540
9879
  configRoot: resolveConfigRoot({ global: opts.global }),
@@ -9550,8 +9889,8 @@ var publish = program2.command("publish [dir]").description("Stage, validate, an
9550
9889
  publish.help();
9551
9890
  return;
9552
9891
  }
9553
- const abs = (0, import_node_path13.resolve)(dir);
9554
- const manifestRaw = await (0, import_promises10.readFile)((0, import_node_path13.join)(abs, "aipm.manifest.json"), "utf8");
9892
+ const abs = (0, import_node_path15.resolve)(dir);
9893
+ const manifestRaw = await (0, import_promises12.readFile)((0, import_node_path15.join)(abs, "aipm.manifest.json"), "utf8");
9555
9894
  const manifest = PackageManifestSchema.parse(JSON.parse(manifestRaw));
9556
9895
  const tarball = await packDirectory(abs);
9557
9896
  const registry = registryFromEnvOrDefault(opts.registry);
@@ -9559,6 +9898,7 @@ var publish = program2.command("publish [dir]").description("Stage, validate, an
9559
9898
  console.log(`Published ${manifest.name}@${result.version}`);
9560
9899
  console.log(`View: ${packagePageUrl(manifest.name, result.version)}`);
9561
9900
  console.log(`Install: aipm add ${manifest.name}@${result.version} --target ${manifest.targets[0]} --ci`);
9901
+ console.log(`README badge: ${packageBadgeMarkdown(manifest.name, result.version)}`);
9562
9902
  printPublishGuide(
9563
9903
  `Published ${manifest.name}@${result.version} from ${abs}.`,
9564
9904
  "Share the install command or open the package page to confirm the listing."
@@ -9684,28 +10024,46 @@ publish.command("push").description("Publish staged files").option("--registry <
9684
10024
  console.log(`Published ${manifest.name}@${result.version}`);
9685
10025
  console.log(`View: ${packagePageUrl(manifest.name, result.version)}`);
9686
10026
  console.log(`Install: aipm add ${manifest.name}@${result.version} --target ${manifest.targets[0]} --ci`);
10027
+ console.log(`README badge: ${packageBadgeMarkdown(manifest.name, result.version)}`);
9687
10028
  printPublishGuide(
9688
10029
  `Published ${manifest.name}@${result.version} to ${registry}.`,
9689
10030
  "Open the registry page, test the install command in a clean project, and share the package link."
9690
10031
  );
9691
10032
  });
9692
- program2.command("list").description("List packages from aipm-lock.json").option(GLOBAL_OPTION, GLOBAL_OPTION_DESC).action(async (opts) => {
10033
+ program2.command("list").description("List installed skills and prompts from aipm-lock.json").option(GLOBAL_OPTION, GLOBAL_OPTION_DESC).option("-p, --prompt", "List prompts only").option("-s, --skill", "List skills only").action(async (opts) => {
9693
10034
  const configRoot = resolveConfigRoot({ global: opts.global });
9694
10035
  const lock = await readLockfile(configRoot);
9695
- if (!lock || Object.keys(lock.packages).length === 0) {
9696
- console.log("No packages installed.");
10036
+ const showSkills = !opts.prompt || Boolean(opts.skill);
10037
+ const showPrompts = !opts.skill || Boolean(opts.prompt);
10038
+ const skillRows = lock ? Object.entries(lock.packages) : [];
10039
+ const promptRows = lock ? Object.entries(lock.prompts) : [];
10040
+ if ((!showSkills || skillRows.length === 0) && (!showPrompts || promptRows.length === 0)) {
10041
+ console.log(opts.prompt && !opts.skill ? "No prompts installed." : opts.skill && !opts.prompt ? "No skills installed." : "No skills or prompts installed.");
9697
10042
  return;
9698
10043
  }
9699
- for (const [name, entry] of Object.entries(lock.packages)) {
9700
- console.log(`${name}@${entry.version} [${entry.resolvedTools.join(", ")}]`);
10044
+ if (showSkills) {
10045
+ for (const [name, entry] of skillRows) {
10046
+ console.log(`skill ${name}@${entry.version} [${entry.resolvedTools.join(", ")}]`);
10047
+ }
10048
+ }
10049
+ if (showPrompts) {
10050
+ for (const [name, entry] of promptRows) {
10051
+ console.log(`prompt ${name} ${entry.url} ${entry.installedPath}`);
10052
+ }
9701
10053
  }
9702
10054
  });
9703
- 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) => {
9704
- const { name } = parsePackageArg(pkgArg);
10055
+ program2.command("remove <package-or-prompt>").alias("rm").description("Remove a tracked skill or prompt and its installed files").option(GLOBAL_OPTION, GLOBAL_OPTION_DESC).action(async (pkgArg, opts) => {
9705
10056
  const scope = { global: opts.global };
9706
10057
  const configRoot = resolveConfigRoot(scope);
9707
10058
  const project = await readProjectPackageJson(configRoot);
9708
10059
  if (!project) throw new Error(initRequiredMessage(scope));
10060
+ const promptReference = resolveTrackedPrompt(project, pkgArg);
10061
+ if (promptReference && project.prompts[promptReference.alias] === promptReference.url) {
10062
+ await removeTrackedPrompt({ configRoot, project, reference: promptReference });
10063
+ console.log(`Removed prompt ${promptReference.url} and its local snapshot.`);
10064
+ return;
10065
+ }
10066
+ const { name } = parsePackageArg(pkgArg);
9709
10067
  const packages = { ...project.packages };
9710
10068
  delete packages[name];
9711
10069
  await writeProjectPackageJson(configRoot, { ...project, packages });
@@ -9718,19 +10076,31 @@ program2.command("remove <package>").alias("rm").description("Remove a package f
9718
10076
  console.log(`Removed ${name} from AIPM ${scopeLabel(scope)} files.`);
9719
10077
  console.log("Adapter-written files are not deleted yet; review your project before committing.");
9720
10078
  });
9721
- 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("--token <token>", "Install token for private packages").option("--ci", "Non-interactive").action(async (pkgArg, opts) => {
10079
+ 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, codex, or *").option("--token <token>", "Install token for private packages").option("--ci", "Non-interactive").action(async (pkgArg, opts) => {
9722
10080
  const scope = { global: opts.global };
9723
10081
  const configRoot = resolveConfigRoot(scope);
9724
10082
  const installRoot = resolveInstallRoot(scope);
9725
10083
  let project = await readProjectPackageJson(configRoot);
9726
10084
  if (!project) throw new Error(initRequiredMessage(scope));
9727
10085
  const registry = resolveRegistryUrl(project, opts.registry, DEFAULT_REGISTRY);
9728
- const token = await tokenForRead(registry, opts.token);
9729
- const names = pkgArg ? [parsePackageArg(pkgArg).name] : Object.keys(project.packages);
9730
- if (names.length === 0) {
9731
- console.log("No packages configured.");
10086
+ const requestedPrompt = pkgArg ? resolveTrackedPrompt(project, pkgArg) : null;
10087
+ if (requestedPrompt) {
10088
+ if (project.prompts[requestedPrompt.alias] !== requestedPrompt.url) {
10089
+ throw new Error(`Prompt is not tracked: ${requestedPrompt.url}`);
10090
+ }
10091
+ const result = await installTrackedPrompt({
10092
+ configRoot,
10093
+ registry,
10094
+ project,
10095
+ reference: requestedPrompt,
10096
+ track: false,
10097
+ updateOnly: true
10098
+ });
10099
+ console.log(result.changed ? `Updated prompt ${result.prompt.title} \u2192 ${result.path}` : `Prompt unchanged: ${result.prompt.title}`);
9732
10100
  return;
9733
10101
  }
10102
+ const token = await tokenForRead(registry, opts.token);
10103
+ const names = pkgArg ? [parsePackageArg(pkgArg).name] : Object.keys(project.packages);
9734
10104
  for (const name of names) {
9735
10105
  const version = await latestVersionForPackage(registry, name, token);
9736
10106
  project = {
@@ -9750,9 +10120,27 @@ program2.command("update [package]").description("Update one installed package,
9750
10120
  token
9751
10121
  });
9752
10122
  }
10123
+ if (!pkgArg) {
10124
+ for (const [alias, url] of Object.entries(project.prompts)) {
10125
+ const reference = parsePromptUrl(url);
10126
+ if (!reference) throw new Error(`Invalid tracked prompt URL for ${alias}: ${url}`);
10127
+ const result = await installTrackedPrompt({
10128
+ configRoot,
10129
+ registry,
10130
+ project,
10131
+ reference: { ...reference, alias },
10132
+ track: false,
10133
+ updateOnly: true
10134
+ });
10135
+ console.log(result.changed ? `Updated prompt ${result.prompt.title} \u2192 ${result.path}` : `Prompt unchanged: ${result.prompt.title}`);
10136
+ }
10137
+ }
10138
+ if (names.length === 0 && Object.keys(project.prompts).length === 0) {
10139
+ console.log("No skills or prompts configured.");
10140
+ }
9753
10141
  });
9754
10142
  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));
9755
- program2.parseAsync(process.argv).catch((err) => {
10143
+ program2.parseAsync(normalizedArgv).catch((err) => {
9756
10144
  console.error(err.message);
9757
10145
  const message = err.message.toLowerCase();
9758
10146
  if (message.includes("token") || message.includes("unauthorized") || message.includes("forbidden")) {