@aipm-registry/cli 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.cjs CHANGED
@@ -6,8 +6,13 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __getProtoOf = Object.getPrototypeOf;
8
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
- var __esm = (fn, res) => function __init() {
10
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
9
+ var __esm = (fn, res, err) => function __init() {
10
+ if (err) throw err[0];
11
+ try {
12
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
13
+ } catch (e) {
14
+ throw err = [e], e;
15
+ }
11
16
  };
12
17
  var __export = (target, all) => {
13
18
  for (var name in all)
@@ -3837,7 +3842,7 @@ var init_types = __esm({
3837
3842
  ...processCreateParams(params)
3838
3843
  });
3839
3844
  };
3840
- BRAND = Symbol("zod_brand");
3845
+ BRAND = /* @__PURE__ */ Symbol("zod_brand");
3841
3846
  ZodBranded = class extends ZodType {
3842
3847
  _parse(input2) {
3843
3848
  const { ctx } = this._processInputParams(input2);
@@ -4011,14 +4016,14 @@ var init_types = __esm({
4011
4016
  onumber = () => numberType().optional();
4012
4017
  oboolean = () => booleanType().optional();
4013
4018
  coerce = {
4014
- string: (arg) => ZodString.create({ ...arg, coerce: true }),
4015
- number: (arg) => ZodNumber.create({ ...arg, coerce: true }),
4016
- boolean: (arg) => ZodBoolean.create({
4019
+ string: ((arg) => ZodString.create({ ...arg, coerce: true })),
4020
+ number: ((arg) => ZodNumber.create({ ...arg, coerce: true })),
4021
+ boolean: ((arg) => ZodBoolean.create({
4017
4022
  ...arg,
4018
4023
  coerce: true
4019
- }),
4020
- bigint: (arg) => ZodBigInt.create({ ...arg, coerce: true }),
4021
- date: (arg) => ZodDate.create({ ...arg, coerce: true })
4024
+ })),
4025
+ bigint: ((arg) => ZodBigInt.create({ ...arg, coerce: true })),
4026
+ date: ((arg) => ZodDate.create({ ...arg, coerce: true }))
4022
4027
  };
4023
4028
  NEVER = INVALID;
4024
4029
  }
@@ -4372,23 +4377,57 @@ var init_detect_tools = __esm({
4372
4377
  }
4373
4378
  });
4374
4379
 
4375
- // ../../packages/adapter-claude/dist/index.js
4376
- var import_promises2, import_node_path3, ClaudeSkillAdapter, claudeSkillAdapter;
4380
+ // ../../packages/adapter-sdk/dist/index.js
4381
+ async function writeSkillDirectory(skillDir, input2) {
4382
+ const entryPath = (0, import_node_path3.join)(skillDir, "SKILL.md");
4383
+ const seen = /* @__PURE__ */ new Set(["skill.md"]);
4384
+ const files = (input2.supportingFiles ?? []).map((file) => {
4385
+ const normalized = (0, import_node_path3.normalize)(file.path);
4386
+ const target = (0, import_node_path3.resolve)(skillDir, normalized);
4387
+ const fromSkillDir = (0, import_node_path3.relative)(skillDir, target);
4388
+ return { file, normalized, target, fromSkillDir, key: fromSkillDir.toLowerCase() };
4389
+ });
4390
+ for (const { file, normalized, fromSkillDir, key } of files) {
4391
+ if (!normalized || normalized === "." || (0, import_node_path3.isAbsolute)(normalized) || fromSkillDir === ".." || fromSkillDir.startsWith(`..${import_node_path3.sep}`) || (0, import_node_path3.isAbsolute)(fromSkillDir) || seen.has(key)) {
4392
+ throw new Error(`Unsafe or duplicate skill supporting file path: ${file.path}`);
4393
+ }
4394
+ seen.add(key);
4395
+ }
4396
+ await (0, import_promises2.mkdir)(skillDir, { recursive: true });
4397
+ await (0, import_promises2.writeFile)(entryPath, input2.skillMarkdown, "utf8");
4398
+ const writtenPaths = [entryPath];
4399
+ for (const { file, target } of files) {
4400
+ await (0, import_promises2.mkdir)((0, import_node_path3.dirname)(target), { recursive: true });
4401
+ await (0, import_promises2.writeFile)(target, file.content);
4402
+ if (file.mode !== void 0)
4403
+ await (0, import_promises2.chmod)(target, file.mode & 511);
4404
+ writtenPaths.push(target);
4405
+ }
4406
+ return { writtenPaths };
4407
+ }
4408
+ var import_promises2, import_node_path3;
4377
4409
  var init_dist2 = __esm({
4378
- "../../packages/adapter-claude/dist/index.js"() {
4410
+ "../../packages/adapter-sdk/dist/index.js"() {
4379
4411
  "use strict";
4380
4412
  import_promises2 = require("node:fs/promises");
4381
4413
  import_node_path3 = require("node:path");
4414
+ }
4415
+ });
4416
+
4417
+ // ../../packages/adapter-claude/dist/index.js
4418
+ var import_node_path4, ClaudeSkillAdapter, claudeSkillAdapter;
4419
+ var init_dist3 = __esm({
4420
+ "../../packages/adapter-claude/dist/index.js"() {
4421
+ "use strict";
4422
+ import_node_path4 = require("node:path");
4423
+ init_dist2();
4382
4424
  init_dist();
4383
4425
  ClaudeSkillAdapter = class {
4384
4426
  tool = "claude";
4385
4427
  async installSkill(input2) {
4386
4428
  const short = shortNameFromScopeName(input2.packageName);
4387
- const skillDir = (0, import_node_path3.join)(input2.projectRoot, ".claude", "aipm", "skills", short);
4388
- await (0, import_promises2.mkdir)(skillDir, { recursive: true });
4389
- const filePath = (0, import_node_path3.join)(skillDir, "SKILL.md");
4390
- await (0, import_promises2.writeFile)(filePath, input2.skillMarkdown, "utf8");
4391
- return { writtenPaths: [filePath] };
4429
+ const skillDir = (0, import_node_path4.join)(input2.projectRoot, ".claude", "skills", short);
4430
+ return writeSkillDirectory(skillDir, input2);
4392
4431
  }
4393
4432
  };
4394
4433
  claudeSkillAdapter = new ClaudeSkillAdapter();
@@ -4396,20 +4435,20 @@ var init_dist2 = __esm({
4396
4435
  });
4397
4436
 
4398
4437
  // ../../packages/adapter-cursor/dist/index.js
4399
- var import_promises3, import_node_path4, CursorSkillAdapter, cursorSkillAdapter;
4400
- var init_dist3 = __esm({
4438
+ var import_promises3, import_node_path5, CursorSkillAdapter, cursorSkillAdapter;
4439
+ var init_dist4 = __esm({
4401
4440
  "../../packages/adapter-cursor/dist/index.js"() {
4402
4441
  "use strict";
4403
4442
  import_promises3 = require("node:fs/promises");
4404
- import_node_path4 = require("node:path");
4443
+ import_node_path5 = require("node:path");
4405
4444
  init_dist();
4406
4445
  CursorSkillAdapter = class {
4407
4446
  tool = "cursor";
4408
4447
  async installSkill(input2) {
4409
4448
  const short = shortNameFromScopeName(input2.packageName);
4410
- const skillsDir = (0, import_node_path4.join)(input2.projectRoot, ".cursor", "aipm", "skills");
4449
+ const skillsDir = (0, import_node_path5.join)(input2.projectRoot, ".cursor", "aipm", "skills");
4411
4450
  await (0, import_promises3.mkdir)(skillsDir, { recursive: true });
4412
- const filePath = (0, import_node_path4.join)(skillsDir, `${short}.md`);
4451
+ const filePath = (0, import_node_path5.join)(skillsDir, `${short}.md`);
4413
4452
  await (0, import_promises3.writeFile)(filePath, input2.skillMarkdown, "utf8");
4414
4453
  return { writtenPaths: [filePath] };
4415
4454
  }
@@ -4419,22 +4458,19 @@ var init_dist3 = __esm({
4419
4458
  });
4420
4459
 
4421
4460
  // ../../packages/adapter-codex/dist/index.js
4422
- var import_promises4, import_node_path5, CodexSkillAdapter, codexSkillAdapter;
4423
- var init_dist4 = __esm({
4461
+ var import_node_path6, CodexSkillAdapter, codexSkillAdapter;
4462
+ var init_dist5 = __esm({
4424
4463
  "../../packages/adapter-codex/dist/index.js"() {
4425
4464
  "use strict";
4426
- import_promises4 = require("node:fs/promises");
4427
- import_node_path5 = require("node:path");
4465
+ import_node_path6 = require("node:path");
4466
+ init_dist2();
4428
4467
  init_dist();
4429
4468
  CodexSkillAdapter = class {
4430
4469
  tool = "codex";
4431
4470
  async installSkill(input2) {
4432
4471
  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] };
4472
+ const skillDir = (0, import_node_path6.join)(input2.projectRoot, ".agents", "skills", short);
4473
+ return writeSkillDirectory(skillDir, input2);
4438
4474
  }
4439
4475
  };
4440
4476
  codexSkillAdapter = new CodexSkillAdapter();
@@ -4459,6 +4495,7 @@ async function installSkillPackage(options) {
4459
4495
  packageName: options.manifest.name,
4460
4496
  version: options.manifest.version,
4461
4497
  skillMarkdown: options.skillMarkdown,
4498
+ supportingFiles: options.supportingFiles,
4462
4499
  projectRoot: options.projectRoot
4463
4500
  });
4464
4501
  installed[tool] = result.writtenPaths;
@@ -4469,9 +4506,9 @@ var adapters;
4469
4506
  var init_install_skill = __esm({
4470
4507
  "../../packages/engine/dist/install-skill.js"() {
4471
4508
  "use strict";
4472
- init_dist2();
4473
4509
  init_dist3();
4474
4510
  init_dist4();
4511
+ init_dist5();
4475
4512
  init_detect_tools();
4476
4513
  adapters = {
4477
4514
  cursor: cursorSkillAdapter,
@@ -4488,7 +4525,7 @@ __export(dist_exports, {
4488
4525
  installSkillPackage: () => installSkillPackage,
4489
4526
  resolveInstallTools: () => resolveInstallTools
4490
4527
  });
4491
- var init_dist5 = __esm({
4528
+ var init_dist6 = __esm({
4492
4529
  "../../packages/engine/dist/index.js"() {
4493
4530
  "use strict";
4494
4531
  init_detect_tools();
@@ -7869,24 +7906,33 @@ var import_node_crypto3 = require("node:crypto");
7869
7906
  var import_node_child_process4 = require("node:child_process");
7870
7907
  var import_node_http = require("node:http");
7871
7908
  var import_promises12 = require("node:fs/promises");
7872
- var import_node_path15 = require("node:path");
7909
+ var import_node_path17 = require("node:path");
7873
7910
  var import_node_process5 = require("node:process");
7874
7911
  init_dist();
7875
- init_dist5();
7912
+ init_dist6();
7876
7913
 
7877
7914
  // src/pack.ts
7878
7915
  var import_node_child_process2 = require("node:child_process");
7879
7916
  var import_node_util3 = require("node:util");
7880
- var import_promises6 = require("node:fs/promises");
7881
- var import_node_path7 = require("node:path");
7917
+ var import_promises5 = require("node:fs/promises");
7918
+ var import_node_path8 = require("node:path");
7882
7919
  var import_node_os = require("node:os");
7883
7920
 
7884
7921
  // src/publish-state.ts
7885
7922
  var import_node_crypto = require("node:crypto");
7886
- var import_promises5 = require("node:fs/promises");
7887
- var import_node_path6 = require("node:path");
7923
+ var import_promises4 = require("node:fs/promises");
7924
+ var import_node_path7 = require("node:path");
7888
7925
  init_dist();
7889
- var STATE_PATH = (0, import_node_path6.join)(".aipm", "publish-state.json");
7926
+
7927
+ // src/recommend-cmd.ts
7928
+ function recommendCmd(command, stream = process.stderr) {
7929
+ const quoted = `"${command}"`;
7930
+ if (!stream.isTTY) return quoted;
7931
+ return `\x1B[36m${quoted}\x1B[39m`;
7932
+ }
7933
+
7934
+ // src/publish-state.ts
7935
+ var STATE_PATH = (0, import_node_path7.join)(".aipm", "publish-state.json");
7890
7936
  var IGNORE_FILE = ".aipmignore";
7891
7937
  var MAX_PACKAGE_BYTES = 50 * 1024 * 1024;
7892
7938
  var EXCLUDED_SEGMENTS = /* @__PURE__ */ new Set([".aipm", ".git", "node_modules", "dist", ".next"]);
@@ -7901,11 +7947,11 @@ var SECRET_PATTERNS = [
7901
7947
  /<publishData/i
7902
7948
  ];
7903
7949
  function publishStatePath(root) {
7904
- return (0, import_node_path6.join)(root, STATE_PATH);
7950
+ return (0, import_node_path7.join)(root, STATE_PATH);
7905
7951
  }
7906
7952
  function normalizePublishPath(root, filePath) {
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("/");
7953
+ const abs = (0, import_node_path7.resolve)(root, filePath);
7954
+ const rel = (0, import_node_path7.normalize)((0, import_node_path7.relative)(root, abs)).split(import_node_path7.sep).join("/");
7909
7955
  if (!rel || rel === ".") return ".";
7910
7956
  if (rel.startsWith("../") || rel === ".." || rel.startsWith("/")) {
7911
7957
  throw new Error(`Path is outside the skill folder: ${filePath}`);
@@ -7922,7 +7968,7 @@ function isExcludedPublishPath(rel) {
7922
7968
  }
7923
7969
  async function readAipmIgnore(root) {
7924
7970
  try {
7925
- const raw = await (0, import_promises5.readFile)((0, import_node_path6.join)(root, IGNORE_FILE), "utf8");
7971
+ const raw = await (0, import_promises4.readFile)((0, import_node_path7.join)(root, IGNORE_FILE), "utf8");
7926
7972
  return raw.split(/\r?\n/).map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
7927
7973
  } catch {
7928
7974
  return [];
@@ -7943,7 +7989,7 @@ async function isIgnoredByAipmIgnore(root, rel) {
7943
7989
  return rules.some((rule) => matchesIgnoreRule(rel, rule));
7944
7990
  }
7945
7991
  async function assertNoObviousSecrets(path2, rel) {
7946
- const data = await (0, import_promises5.readFile)(path2);
7992
+ const data = await (0, import_promises4.readFile)(path2);
7947
7993
  if (data.includes(0)) return;
7948
7994
  const text = data.toString("utf8");
7949
7995
  if (SECRET_PATTERNS.some((pattern) => pattern.test(text))) {
@@ -7951,7 +7997,7 @@ async function assertNoObviousSecrets(path2, rel) {
7951
7997
  }
7952
7998
  }
7953
7999
  async function fileHash(path2) {
7954
- const data = await (0, import_promises5.readFile)(path2);
8000
+ const data = await (0, import_promises4.readFile)(path2);
7955
8001
  return {
7956
8002
  hash: (0, import_node_crypto.createHash)("sha256").update(data).digest("hex"),
7957
8003
  size: data.length
@@ -7959,7 +8005,7 @@ async function fileHash(path2) {
7959
8005
  }
7960
8006
  async function readPublishState(root) {
7961
8007
  try {
7962
- const raw = await (0, import_promises5.readFile)(publishStatePath(root), "utf8");
8008
+ const raw = await (0, import_promises4.readFile)(publishStatePath(root), "utf8");
7963
8009
  const parsed = JSON.parse(raw);
7964
8010
  return {
7965
8011
  schemaVersion: "0.1",
@@ -7971,18 +8017,18 @@ async function readPublishState(root) {
7971
8017
  }
7972
8018
  async function writePublishState(root, state) {
7973
8019
  const path2 = publishStatePath(root);
7974
- await (0, import_promises5.mkdir)((0, import_node_path6.dirname)(path2), { recursive: true });
8020
+ await (0, import_promises4.mkdir)((0, import_node_path7.dirname)(path2), { recursive: true });
7975
8021
  const files = [...state.files].sort((a, b) => a.path.localeCompare(b.path));
7976
- await (0, import_promises5.writeFile)(path2, JSON.stringify({ schemaVersion: "0.1", files }, null, 2) + "\n", "utf8");
8022
+ await (0, import_promises4.writeFile)(path2, JSON.stringify({ schemaVersion: "0.1", files }, null, 2) + "\n", "utf8");
7977
8023
  }
7978
8024
  async function expandPublishPath(root, rel) {
7979
8025
  if (isExcludedPublishPath(rel)) return [];
7980
8026
  if (await isIgnoredByAipmIgnore(root, rel)) return [];
7981
- const abs = (0, import_node_path6.join)(root, rel);
7982
- const info = await (0, import_promises5.stat)(abs);
8027
+ const abs = (0, import_node_path7.join)(root, rel);
8028
+ const info = await (0, import_promises4.stat)(abs);
7983
8029
  if (info.isFile()) return [rel];
7984
8030
  if (!info.isDirectory()) return [];
7985
- const entries = await (0, import_promises5.readdir)(abs);
8031
+ const entries = await (0, import_promises4.readdir)(abs);
7986
8032
  const nested = await Promise.all(
7987
8033
  entries.map((entry) => expandPublishPath(root, `${rel === "." ? "" : `${rel}/`}${entry}`))
7988
8034
  );
@@ -7998,7 +8044,7 @@ async function addPublishFiles(root, paths) {
7998
8044
  for (const rel of files) {
7999
8045
  if (isExcludedPublishPath(rel)) continue;
8000
8046
  if (await isIgnoredByAipmIgnore(root, rel)) continue;
8001
- const hashed = await fileHash((0, import_node_path6.join)(root, rel));
8047
+ const hashed = await fileHash((0, import_node_path7.join)(root, rel));
8002
8048
  byPath.set(rel, { path: rel, ...hashed });
8003
8049
  }
8004
8050
  }
@@ -8017,15 +8063,17 @@ async function removePublishFiles(root, paths) {
8017
8063
  return next;
8018
8064
  }
8019
8065
  async function resetPublishState(root) {
8020
- await (0, import_promises5.rm)(publishStatePath(root), { force: true });
8066
+ await (0, import_promises4.rm)(publishStatePath(root), { force: true });
8021
8067
  }
8022
8068
  async function readManifest(root) {
8023
8069
  try {
8024
- const raw = await (0, import_promises5.readFile)((0, import_node_path6.join)(root, "aipm.manifest.json"), "utf8");
8070
+ const raw = await (0, import_promises4.readFile)((0, import_node_path7.join)(root, "aipm.manifest.json"), "utf8");
8025
8071
  return PackageManifestSchema.parse(JSON.parse(raw));
8026
8072
  } catch (error) {
8027
8073
  if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
8028
- throw new Error("No aipm.manifest.json found. Run aipm publish init --name @org/skill.");
8074
+ throw new Error(
8075
+ `No aipm.manifest.json found. Run ${recommendCmd("aipm publish init --name <@org/skill>")}.`
8076
+ );
8029
8077
  }
8030
8078
  throw error;
8031
8079
  }
@@ -8041,7 +8089,9 @@ function installReferencedFiles(manifest) {
8041
8089
  async function validatePublishState(root) {
8042
8090
  const manifest = await readManifest(root);
8043
8091
  const state = await readPublishState(root);
8044
- if (state.files.length === 0) throw new Error("No files staged. Run aipm publish add <files...>.");
8092
+ if (state.files.length === 0) {
8093
+ throw new Error(`No files staged. Run ${recommendCmd("aipm publish add <files...>")}.`);
8094
+ }
8045
8095
  const staged = new Set(state.files.map((entry) => entry.path));
8046
8096
  if (!staged.has("aipm.manifest.json")) throw new Error("aipm.manifest.json must be staged.");
8047
8097
  if (!staged.has(manifest.entry)) throw new Error(`Manifest entry must be staged: ${manifest.entry}`);
@@ -8054,12 +8104,14 @@ async function validatePublishState(root) {
8054
8104
  if (await isIgnoredByAipmIgnore(root, entry.path)) {
8055
8105
  throw new Error(`Refusing to publish ignored path: ${entry.path}`);
8056
8106
  }
8057
- const abs = (0, import_node_path6.join)(root, entry.path);
8058
- const info = await (0, import_promises5.stat)(abs).catch(() => null);
8107
+ const abs = (0, import_node_path7.join)(root, entry.path);
8108
+ const info = await (0, import_promises4.stat)(abs).catch(() => null);
8059
8109
  if (!info?.isFile()) throw new Error(`Staged file is missing: ${entry.path}`);
8060
8110
  const hashed = await fileHash(abs);
8061
8111
  if (hashed.hash !== entry.hash) {
8062
- throw new Error(`Staged file changed after add: ${entry.path}. Run aipm publish add ${entry.path}`);
8112
+ throw new Error(
8113
+ `Staged file changed after add: ${entry.path}. Run ${recommendCmd(`aipm publish add ${entry.path}`)}.`
8114
+ );
8063
8115
  }
8064
8116
  await assertNoObviousSecrets(abs, entry.path);
8065
8117
  size += hashed.size;
@@ -8071,7 +8123,7 @@ async function statusPublishState(root) {
8071
8123
  const state = await readPublishState(root);
8072
8124
  const rows = [];
8073
8125
  for (const entry of state.files) {
8074
- const current = await fileHash((0, import_node_path6.join)(root, entry.path)).catch(() => null);
8126
+ const current = await fileHash((0, import_node_path7.join)(root, entry.path)).catch(() => null);
8075
8127
  rows.push({ ...entry, changed: !current || current.hash !== entry.hash });
8076
8128
  }
8077
8129
  return rows;
@@ -8079,9 +8131,9 @@ async function statusPublishState(root) {
8079
8131
  async function copyStagedFiles(root, destination) {
8080
8132
  const state = await readPublishState(root);
8081
8133
  for (const entry of state.files) {
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);
8134
+ const target = (0, import_node_path7.join)(destination, entry.path);
8135
+ await (0, import_promises4.mkdir)((0, import_node_path7.dirname)(target), { recursive: true });
8136
+ await (0, import_promises4.cp)((0, import_node_path7.join)(root, entry.path), target);
8085
8137
  }
8086
8138
  }
8087
8139
 
@@ -8095,21 +8147,21 @@ async function packDirectory(dir) {
8095
8147
  return Buffer.isBuffer(stdout) ? stdout : Buffer.from(stdout);
8096
8148
  }
8097
8149
  async function packStagedFiles(root) {
8098
- const tempDir = await (0, import_promises6.mkdtemp)((0, import_node_path7.join)((0, import_node_os.tmpdir)(), "aipm-publish-stage-"));
8150
+ const tempDir = await (0, import_promises5.mkdtemp)((0, import_node_path8.join)((0, import_node_os.tmpdir)(), "aipm-publish-stage-"));
8099
8151
  try {
8100
8152
  await copyStagedFiles(root, tempDir);
8101
8153
  return await packDirectory(tempDir);
8102
8154
  } finally {
8103
- await (0, import_promises6.rm)(tempDir, { recursive: true, force: true });
8155
+ await (0, import_promises5.rm)(tempDir, { recursive: true, force: true });
8104
8156
  }
8105
8157
  }
8106
8158
  async function unpackTarballToDirectory(tarball) {
8107
- const { mkdtemp: mkdtemp2, writeFile: writeFile9 } = await import("node:fs/promises");
8108
- const { join: join15 } = await import("node:path");
8159
+ const { mkdtemp: mkdtemp2, writeFile: writeFile8 } = await import("node:fs/promises");
8160
+ const { join: join16 } = await import("node:path");
8109
8161
  const { tmpdir: tmpdir2 } = await import("node:os");
8110
- const tempDir = await mkdtemp2(join15(tmpdir2(), "aipm-install-"));
8111
- const tgzPath = join15(tempDir, "pkg.tgz");
8112
- await writeFile9(tgzPath, tarball);
8162
+ const tempDir = await mkdtemp2(join16(tmpdir2(), "aipm-install-"));
8163
+ const tgzPath = join16(tempDir, "pkg.tgz");
8164
+ await writeFile8(tgzPath, tarball);
8113
8165
  await execFileAsync("tar", ["-xzf", tgzPath, "-C", tempDir]);
8114
8166
  return tempDir;
8115
8167
  }
@@ -8122,7 +8174,7 @@ function registryFetchError(registry, cause) {
8122
8174
  const msg = cause instanceof Error ? cause.message : String(cause);
8123
8175
  if (msg === "fetch failed" || msg.includes("ECONNREFUSED") || msg.includes("ENOTFOUND") || msg.includes("timed out") || msg.includes("aborted")) {
8124
8176
  return new Error(
8125
- `Cannot reach registry at ${registry}. Check your connection or pass --registry <url>.`
8177
+ `Cannot reach registry at ${registry}. Check your connection or pass ${recommendCmd("--registry <url>")}.`
8126
8178
  );
8127
8179
  }
8128
8180
  return new Error(`Registry request failed (${registry}): ${msg}`);
@@ -8143,13 +8195,13 @@ function privateInstallHint(name, status, token) {
8143
8195
  if (status !== 404 || !name.startsWith("@") || token) {
8144
8196
  return `Package not found: ${name} (${status})`;
8145
8197
  }
8146
- return `Package not found: ${name} (${status}). If @org/pkg is private, run aipm login, or set AIPM_TOKEN for CI.`;
8198
+ return `Package not found: ${name} (${status}). If @org/pkg is private, run ${recommendCmd("aipm login")}, or set AIPM_TOKEN for CI.`;
8147
8199
  }
8148
8200
  function publishTokenHint(name, error) {
8149
8201
  if (error === "Publish token required") {
8150
8202
  return [
8151
8203
  `Publish token required for ${name}.`,
8152
- `Generate a fresh 5-minute publish token for ${name} from the package dashboard, then retry with AIPM_TOKEN=<token> aipm publish push --yes.`
8204
+ `Generate a fresh 5-minute publish token for ${name} from the package dashboard, then retry with ${recommendCmd("AIPM_TOKEN=<token> aipm publish push --yes")}.`
8153
8205
  ].join(" ");
8154
8206
  }
8155
8207
  if (error === "Invalid publish token") {
@@ -8305,16 +8357,16 @@ async function publishPrompt(registry, input2, accessToken, sampleImage) {
8305
8357
  }
8306
8358
 
8307
8359
  // src/auth-store.ts
8308
- var import_promises7 = require("node:fs/promises");
8360
+ var import_promises6 = require("node:fs/promises");
8309
8361
  var import_node_os2 = require("node:os");
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");
8362
+ var import_node_path9 = require("node:path");
8363
+ var AUTH_FILE = (0, import_node_path9.join)((0, import_node_os2.homedir)(), ".aipm", "auth.json");
8312
8364
  function normalizeRegistry(registry) {
8313
8365
  return registry.replace(/\/$/, "");
8314
8366
  }
8315
8367
  async function readAuthStore() {
8316
8368
  try {
8317
- const raw = await (0, import_promises7.readFile)(AUTH_FILE, "utf8");
8369
+ const raw = await (0, import_promises6.readFile)(AUTH_FILE, "utf8");
8318
8370
  const parsed = JSON.parse(raw);
8319
8371
  return { registries: parsed.registries ?? {} };
8320
8372
  } catch {
@@ -8322,9 +8374,9 @@ async function readAuthStore() {
8322
8374
  }
8323
8375
  }
8324
8376
  async function writeAuthStore(store) {
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);
8377
+ await (0, import_promises6.mkdir)((0, import_node_path9.dirname)(AUTH_FILE), { recursive: true });
8378
+ await (0, import_promises6.writeFile)(AUTH_FILE, JSON.stringify(store, null, 2) + "\n", "utf8");
8379
+ await (0, import_promises6.chmod)(AUTH_FILE, 384).catch(() => void 0);
8328
8380
  }
8329
8381
  async function getStoredRegistryAuth(registry) {
8330
8382
  const store = await readAuthStore();
@@ -8353,41 +8405,41 @@ function isAccessTokenFresh(auth, skewMs = 6e4) {
8353
8405
  }
8354
8406
 
8355
8407
  // src/install-one.ts
8356
- init_dist5();
8357
- var import_promises9 = require("node:fs/promises");
8358
- var import_node_path10 = require("node:path");
8408
+ init_dist6();
8409
+ var import_promises8 = require("node:fs/promises");
8410
+ var import_node_path11 = require("node:path");
8359
8411
 
8360
8412
  // src/project-files.ts
8361
- var import_promises8 = require("node:fs/promises");
8362
- var import_node_path9 = require("node:path");
8413
+ var import_promises7 = require("node:fs/promises");
8414
+ var import_node_path10 = require("node:path");
8363
8415
  init_dist();
8364
8416
  var PACKAGE_JSON = "aipm.package.json";
8365
8417
  var LOCKFILE = "aipm-lock.json";
8366
8418
  async function readProjectPackageJson(projectRoot) {
8367
8419
  try {
8368
- const raw = await (0, import_promises8.readFile)((0, import_node_path9.join)(projectRoot, PACKAGE_JSON), "utf8");
8420
+ const raw = await (0, import_promises7.readFile)((0, import_node_path10.join)(projectRoot, PACKAGE_JSON), "utf8");
8369
8421
  return ProjectPackageJsonSchema.parse(JSON.parse(raw));
8370
8422
  } catch {
8371
8423
  return null;
8372
8424
  }
8373
8425
  }
8374
8426
  async function writeProjectPackageJson(projectRoot, data) {
8375
- await (0, import_promises8.writeFile)(
8376
- (0, import_node_path9.join)(projectRoot, PACKAGE_JSON),
8427
+ await (0, import_promises7.writeFile)(
8428
+ (0, import_node_path10.join)(projectRoot, PACKAGE_JSON),
8377
8429
  JSON.stringify(data, null, 2) + "\n",
8378
8430
  "utf8"
8379
8431
  );
8380
8432
  }
8381
8433
  async function readLockfile(projectRoot) {
8382
8434
  try {
8383
- const raw = await (0, import_promises8.readFile)((0, import_node_path9.join)(projectRoot, LOCKFILE), "utf8");
8435
+ const raw = await (0, import_promises7.readFile)((0, import_node_path10.join)(projectRoot, LOCKFILE), "utf8");
8384
8436
  return LockfileSchema.parse(JSON.parse(raw));
8385
8437
  } catch {
8386
8438
  return null;
8387
8439
  }
8388
8440
  }
8389
8441
  async function writeLockfile(projectRoot, data) {
8390
- await (0, import_promises8.writeFile)((0, import_node_path9.join)(projectRoot, LOCKFILE), JSON.stringify(data, null, 2) + "\n", "utf8");
8442
+ await (0, import_promises7.writeFile)((0, import_node_path10.join)(projectRoot, LOCKFILE), JSON.stringify(data, null, 2) + "\n", "utf8");
8391
8443
  }
8392
8444
  function upsertLockEntry(lock, name, entry) {
8393
8445
  return {
@@ -8462,7 +8514,7 @@ async function promptForConfirmation(question) {
8462
8514
 
8463
8515
  // src/install-one.ts
8464
8516
  function normalizeRelativePath(path2) {
8465
- return (0, import_node_path10.normalize)(path2).split(import_node_path10.sep).join("/");
8517
+ return (0, import_node_path11.normalize)(path2).split(import_node_path11.sep).join("/");
8466
8518
  }
8467
8519
  function assertSafeRelativePath(path2) {
8468
8520
  const rel = normalizeRelativePath(path2.trim());
@@ -8473,8 +8525,8 @@ function assertSafeRelativePath(path2) {
8473
8525
  }
8474
8526
  function safeJoin(root, relPath) {
8475
8527
  const rel = assertSafeRelativePath(relPath);
8476
- const target = (0, import_node_path10.resolve)(root, rel);
8477
- const fromRoot = normalizeRelativePath((0, import_node_path10.relative)(root, target));
8528
+ const target = (0, import_node_path11.resolve)(root, rel);
8529
+ const fromRoot = normalizeRelativePath((0, import_node_path11.relative)(root, target));
8478
8530
  if (fromRoot === ".." || fromRoot.startsWith("../") || fromRoot.startsWith("/")) {
8479
8531
  throw new Error(`Unsafe install path: ${relPath}`);
8480
8532
  }
@@ -8485,33 +8537,82 @@ function packageHelperSlug(name) {
8485
8537
  return `${scope}__${pkg}`;
8486
8538
  }
8487
8539
  function helperRootFor(configRoot, 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);
8540
+ const base = (0, import_node_path11.basename)(configRoot) === ".aipm" ? (0, import_node_path11.join)(configRoot, "helpers") : (0, import_node_path11.join)(configRoot, ".aipm", "helpers");
8541
+ return (0, import_node_path11.join)(base, packageHelperSlug(packageName), version);
8490
8542
  }
8491
8543
  async function assertSourceFile(packageRoot, relPath) {
8492
8544
  const source = safeJoin(packageRoot, relPath);
8493
- const info = await (0, import_promises9.stat)(source).catch(() => null);
8545
+ const info = await (0, import_promises8.stat)(source).catch(() => null);
8494
8546
  if (!info?.isFile()) throw new Error(`Package install file not found: ${relPath}`);
8495
8547
  return source;
8496
8548
  }
8549
+ function installReferencedFiles2(install) {
8550
+ return [
8551
+ ...(install?.mainFiles ?? []).map((file) => file.from),
8552
+ ...(install?.helperFiles ?? []).map((file) => file.from)
8553
+ ];
8554
+ }
8555
+ function isRootLicenseOrNotice(path2) {
8556
+ return !path2.includes("/") && /^(?:licen[cs]e|notice|copying)(?:$|[._-])/i.test(path2);
8557
+ }
8558
+ async function listRegularPackageFiles(root, current = root) {
8559
+ const files = [];
8560
+ for (const entry of await (0, import_promises8.readdir)(current, { withFileTypes: true })) {
8561
+ const absolute = (0, import_node_path11.join)(current, entry.name);
8562
+ if (entry.isDirectory()) {
8563
+ files.push(...await listRegularPackageFiles(root, absolute));
8564
+ } else if (entry.isFile()) {
8565
+ files.push(normalizeRelativePath((0, import_node_path11.relative)(root, absolute)));
8566
+ }
8567
+ }
8568
+ return files;
8569
+ }
8570
+ async function collectSkillSupportingFiles(packageRoot, manifest) {
8571
+ const entry = assertSafeRelativePath(manifest.entry);
8572
+ const entryDirectory = (0, import_node_path11.dirname)(entry);
8573
+ const excluded = /* @__PURE__ */ new Set([
8574
+ entry,
8575
+ "aipm.manifest.json",
8576
+ "pkg.tgz",
8577
+ ...installReferencedFiles2(manifest.install).map(assertSafeRelativePath)
8578
+ ]);
8579
+ const supportingFiles = [];
8580
+ for (const sourcePath of await listRegularPackageFiles(packageRoot)) {
8581
+ if (excluded.has(sourcePath)) continue;
8582
+ const fromEntryDirectory = normalizeRelativePath((0, import_node_path11.relative)(entryDirectory, sourcePath));
8583
+ const isEntrySibling = fromEntryDirectory !== ".." && !fromEntryDirectory.startsWith("../") && !fromEntryDirectory.startsWith("/");
8584
+ if (!isEntrySibling && !isRootLicenseOrNotice(sourcePath)) continue;
8585
+ const destinationPath = isEntrySibling ? fromEntryDirectory : (0, import_node_path11.basename)(sourcePath);
8586
+ if (destinationPath.toLowerCase() === "skill.md") {
8587
+ throw new Error(`Package supporting file conflicts with installed SKILL.md: ${sourcePath}`);
8588
+ }
8589
+ const source = safeJoin(packageRoot, sourcePath);
8590
+ supportingFiles.push({
8591
+ path: destinationPath,
8592
+ content: await (0, import_promises8.readFile)(source),
8593
+ mode: (await (0, import_promises8.stat)(source)).mode
8594
+ });
8595
+ }
8596
+ return supportingFiles.sort((a, b) => a.path.localeCompare(b.path));
8597
+ }
8497
8598
  async function copyMainFile(input2) {
8498
8599
  const source = await assertSourceFile(input2.packageRoot, input2.file.from);
8499
8600
  const target = safeJoin(input2.installRoot, input2.file.to);
8500
8601
  const overwrite = input2.file.overwrite ?? "fail";
8501
- const exists = await (0, import_promises9.stat)(target).catch(() => null);
8602
+ const exists = await (0, import_promises8.stat)(target).catch(() => null);
8502
8603
  if (exists) {
8503
8604
  if (overwrite === "skip") return null;
8504
8605
  if (overwrite === "fail") throw new Error(`Install target already exists: ${target}`);
8505
8606
  }
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" });
8607
+ await (0, import_promises8.mkdir)((0, import_node_path11.dirname)(target), { recursive: true });
8608
+ await (0, import_promises8.cp)(source, target, { force: overwrite === "replace" });
8508
8609
  return target;
8509
8610
  }
8510
8611
  async function copyHelperFile(input2) {
8511
8612
  const source = await assertSourceFile(input2.packageRoot, input2.file.from);
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 });
8613
+ const target = safeJoin(input2.helperRoot, input2.file.to ?? (0, import_node_path11.basename)(input2.file.from));
8614
+ await (0, import_promises8.mkdir)((0, import_node_path11.dirname)(target), { recursive: true });
8615
+ await (0, import_promises8.cp)(source, target, { force: true });
8515
8616
  return target;
8516
8617
  }
8517
8618
  async function installPackageAssets(options) {
@@ -8528,7 +8629,7 @@ async function installPackageAssets(options) {
8528
8629
  }
8529
8630
  const helperRoot = helperRootFor(options.configRoot, options.packageName, options.version);
8530
8631
  if ((install.helperFiles ?? []).length > 0) {
8531
- await (0, import_promises9.rm)(helperRoot, { recursive: true, force: true });
8632
+ await (0, import_promises8.rm)(helperRoot, { recursive: true, force: true });
8532
8633
  }
8533
8634
  for (const file of install.helperFiles ?? []) {
8534
8635
  assets.helper.push(await copyHelperFile({ packageRoot: options.packageRoot, helperRoot, file }));
@@ -8573,7 +8674,7 @@ async function installOnePackage(options) {
8573
8674
  }
8574
8675
  let explicitTarget = options.explicitTarget;
8575
8676
  let preferredTools = options.project.preferredTools;
8576
- const { resolveInstallTools: resolveInstallTools2 } = await Promise.resolve().then(() => (init_dist5(), dist_exports));
8677
+ const { resolveInstallTools: resolveInstallTools2 } = await Promise.resolve().then(() => (init_dist6(), dist_exports));
8577
8678
  let tools = await resolveInstallTools2({
8578
8679
  projectRoot: installRoot,
8579
8680
  manifest,
@@ -8604,11 +8705,13 @@ async function installOnePackage(options) {
8604
8705
  );
8605
8706
  const packageRoot = await unpackTarballToDirectory(tarball);
8606
8707
  try {
8607
- const skillMarkdown = await (0, import_promises9.readFile)(safeJoin(packageRoot, manifest.entry), "utf8");
8708
+ const skillMarkdown = await (0, import_promises8.readFile)(safeJoin(packageRoot, manifest.entry), "utf8");
8709
+ const supportingFiles = await collectSkillSupportingFiles(packageRoot, manifest);
8608
8710
  const result = await installSkillPackage({
8609
8711
  projectRoot: installRoot,
8610
8712
  manifest,
8611
8713
  skillMarkdown,
8714
+ supportingFiles,
8612
8715
  preferredTools,
8613
8716
  explicitTarget
8614
8717
  });
@@ -8645,23 +8748,23 @@ async function installOnePackage(options) {
8645
8748
  console.log(`Installed ${options.name}@${options.version} \u2192 ${result.resolvedTools.join(", ")}`);
8646
8749
  printPostInstallNotice(options.name, postInstall);
8647
8750
  } finally {
8648
- await (0, import_promises9.rm)(packageRoot, { recursive: true, force: true });
8751
+ await (0, import_promises8.rm)(packageRoot, { recursive: true, force: true });
8649
8752
  }
8650
8753
  }
8651
8754
 
8652
8755
  // src/doctor.ts
8653
8756
  var import_node_child_process3 = require("node:child_process");
8654
- var import_promises10 = require("node:fs/promises");
8655
- var import_node_path12 = require("node:path");
8757
+ var import_promises9 = require("node:fs/promises");
8758
+ var import_node_path13 = require("node:path");
8656
8759
  var import_node_util4 = require("node:util");
8657
8760
 
8658
8761
  // src/project-root.ts
8659
8762
  var import_node_os3 = require("node:os");
8660
- var import_node_path11 = require("node:path");
8763
+ var import_node_path12 = require("node:path");
8661
8764
  var import_node_process3 = require("node:process");
8662
8765
  function globalConfigDir(env2 = process.env) {
8663
8766
  const fromEnv = env2.AIPM_HOME?.trim();
8664
- return fromEnv ? fromEnv.replace(/\/$/, "") : (0, import_node_path11.join)((0, import_node_os3.homedir)(), ".aipm");
8767
+ return fromEnv ? fromEnv.replace(/\/$/, "") : (0, import_node_path12.join)((0, import_node_os3.homedir)(), ".aipm");
8665
8768
  }
8666
8769
  function resolveConfigRoot(options = {}) {
8667
8770
  return options.global ? globalConfigDir() : (0, import_node_process3.cwd)();
@@ -8673,7 +8776,7 @@ function scopeLabel(options) {
8673
8776
  return options.global ? "global" : "project";
8674
8777
  }
8675
8778
  function initRequiredMessage(options) {
8676
- return options.global ? "Run aipm init -g first" : "Run aipm init first";
8779
+ return options.global ? `Run ${recommendCmd("aipm init -g")} first` : `Run ${recommendCmd("aipm init")} first`;
8677
8780
  }
8678
8781
 
8679
8782
  // src/doctor.ts
@@ -8692,11 +8795,11 @@ async function commandOutput(command, args) {
8692
8795
  }
8693
8796
  function npmGlobalBin(prefix) {
8694
8797
  if (!prefix) return null;
8695
- return process.platform === "win32" ? prefix : `${prefix}${import_node_path12.sep}bin`;
8798
+ return process.platform === "win32" ? prefix : `${prefix}${import_node_path13.sep}bin`;
8696
8799
  }
8697
8800
  function pathIncludes(pathDir) {
8698
8801
  if (!pathDir) return false;
8699
- return (process.env.PATH ?? "").split(import_node_path12.delimiter).includes(pathDir);
8802
+ return (process.env.PATH ?? "").split(import_node_path13.delimiter).includes(pathDir);
8700
8803
  }
8701
8804
  function shellPathHint(binDir) {
8702
8805
  if (!binDir) return "Run npm prefix -g and add its bin folder to PATH.";
@@ -8749,11 +8852,11 @@ async function runDoctor(options) {
8749
8852
  if (!options.publish) {
8750
8853
  const configLabel = options.global ? "Global config" : "Project config";
8751
8854
  try {
8752
- await (0, import_promises10.access)((0, import_node_path12.join)(configRoot, "aipm.package.json"));
8855
+ await (0, import_promises9.access)((0, import_node_path13.join)(configRoot, "aipm.package.json"));
8753
8856
  checks.push({
8754
8857
  name: configLabel,
8755
8858
  ok: true,
8756
- detail: `${(0, import_node_path12.join)(configRoot, "aipm.package.json")} exists (${scopeLabel(scope)}).`
8859
+ detail: `${(0, import_node_path13.join)(configRoot, "aipm.package.json")} exists (${scopeLabel(scope)}).`
8757
8860
  });
8758
8861
  } catch {
8759
8862
  checks.push({
@@ -8783,7 +8886,7 @@ async function runDoctor(options) {
8783
8886
  checks.push({
8784
8887
  name: "Publish stage",
8785
8888
  ok: rows.length > 0 && changed.length === 0,
8786
- detail: rows.length === 0 ? "No files staged. Run aipm publish add ." : changed.length > 0 ? `${changed.length} staged file(s) changed after add. Run aipm publish add . again.` : `${rows.length} file(s) staged.`
8889
+ detail: rows.length === 0 ? `No files staged. Run ${recommendCmd("aipm publish add .")}` : changed.length > 0 ? `${changed.length} staged file(s) changed after add. Run ${recommendCmd("aipm publish add .")} again.` : `${rows.length} file(s) staged.`
8787
8890
  });
8788
8891
  } catch (error) {
8789
8892
  checks.push({
@@ -8823,8 +8926,8 @@ async function runDoctor(options) {
8823
8926
 
8824
8927
  // src/prompt-install.ts
8825
8928
  var import_node_crypto2 = require("node:crypto");
8826
- var import_promises11 = require("node:fs/promises");
8827
- var import_node_path13 = require("node:path");
8929
+ var import_promises10 = require("node:fs/promises");
8930
+ var import_node_path14 = require("node:path");
8828
8931
  var PROMPT_SITE_ORIGIN = "https://www.aipm-registry.com";
8829
8932
  function parsePromptUrl(value) {
8830
8933
  let url;
@@ -8849,10 +8952,10 @@ function parsePromptUrl(value) {
8849
8952
  };
8850
8953
  }
8851
8954
  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");
8955
+ return (0, import_node_path14.basename)(configRoot) === ".aipm" ? (0, import_node_path14.join)(configRoot, "prompts") : (0, import_node_path14.join)(configRoot, ".aipm", "prompts");
8853
8956
  }
8854
8957
  function promptSnapshotPath(configRoot, reference) {
8855
- return (0, import_node_path13.join)(promptDirectory(configRoot), `${reference.publisher}--${reference.slug}.md`);
8958
+ return (0, import_node_path14.join)(promptDirectory(configRoot), `${reference.publisher}--${reference.slug}.md`);
8856
8959
  }
8857
8960
  function canonicalPromptContent(prompt) {
8858
8961
  return JSON.stringify({
@@ -8911,7 +9014,7 @@ function emptyLock() {
8911
9014
  return { schemaVersion: "0.1", packages: {}, prompts: {} };
8912
9015
  }
8913
9016
  function installedRelativePath(configRoot, absolutePath) {
8914
- return (0, import_node_path13.relative)(configRoot, absolutePath).split("\\").join("/");
9017
+ return (0, import_node_path14.relative)(configRoot, absolutePath).split("\\").join("/");
8915
9018
  }
8916
9019
  async function installTrackedPrompt(input2) {
8917
9020
  const prompt = await fetchPromptDetail(
@@ -8929,11 +9032,11 @@ async function installTrackedPrompt(input2) {
8929
9032
  const contentHash = promptContentHash(prompt);
8930
9033
  const lock = await readLockfile(input2.configRoot) ?? emptyLock();
8931
9034
  const existing = lock.prompts[input2.reference.alias];
8932
- const fileExists = Boolean(await (0, import_promises11.stat)(outputPath).catch(() => null));
9035
+ const fileExists = Boolean(await (0, import_promises10.stat)(outputPath).catch(() => null));
8933
9036
  const changed = !existing || existing.contentHash !== contentHash || !fileExists;
8934
9037
  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");
9038
+ await (0, import_promises10.mkdir)((0, import_node_path14.dirname)(outputPath), { recursive: true });
9039
+ await (0, import_promises10.writeFile)(outputPath, renderPromptMarkdown(prompt, canonicalReference.url), "utf8");
8937
9040
  }
8938
9041
  if (input2.track) {
8939
9042
  const currentUrl = input2.project.prompts[input2.reference.alias];
@@ -8975,12 +9078,12 @@ function resolveTrackedPrompt(project, value) {
8975
9078
  return parsed ? { ...parsed, alias: value } : null;
8976
9079
  }
8977
9080
  async function readInstalledPrompt(configRoot, entry) {
8978
- return (0, import_promises11.readFile)((0, import_node_path13.join)(configRoot, entry.installedPath), "utf8");
9081
+ return (0, import_promises10.readFile)((0, import_node_path14.join)(configRoot, entry.installedPath), "utf8");
8979
9082
  }
8980
9083
  async function removeTrackedPrompt(input2) {
8981
9084
  const lock = await readLockfile(input2.configRoot);
8982
9085
  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 });
9086
+ if (entry) await (0, import_promises10.rm)((0, import_node_path14.join)(input2.configRoot, entry.installedPath), { force: true });
8984
9087
  const prompts = { ...input2.project.prompts };
8985
9088
  delete prompts[input2.reference.alias];
8986
9089
  await writeProjectPackageJson(input2.configRoot, { ...input2.project, prompts });
@@ -8991,11 +9094,65 @@ async function removeTrackedPrompt(input2) {
8991
9094
  }
8992
9095
  }
8993
9096
 
9097
+ // src/remove-package.ts
9098
+ var import_promises11 = require("node:fs/promises");
9099
+ var import_node_path15 = require("node:path");
9100
+ function within(root, path2) {
9101
+ const rel = (0, import_node_path15.relative)((0, import_node_path15.resolve)(root), (0, import_node_path15.resolve)(path2));
9102
+ return rel === "" || !rel.startsWith("..") && !(0, import_node_path15.isAbsolute)(rel);
9103
+ }
9104
+ function trackedInstalledPackagePaths(entry) {
9105
+ return [
9106
+ ...Object.values(entry.installed).flat(),
9107
+ ...entry.installedAssets?.main ?? [],
9108
+ ...entry.installedAssets?.helper ?? []
9109
+ ];
9110
+ }
9111
+ async function pruneEmptyParents(path2, roots) {
9112
+ let current = (0, import_node_path15.dirname)(path2);
9113
+ while (roots.some((root) => within(root, current)) && !roots.some((root) => (0, import_node_path15.resolve)(root) === current)) {
9114
+ try {
9115
+ await (0, import_promises11.rmdir)(current);
9116
+ } catch {
9117
+ return;
9118
+ }
9119
+ current = (0, import_node_path15.dirname)(current);
9120
+ }
9121
+ }
9122
+ async function removeInstalledPackageFiles(input2) {
9123
+ const roots = [.../* @__PURE__ */ new Set([(0, import_node_path15.resolve)(input2.configRoot), (0, import_node_path15.resolve)(input2.installRoot)])];
9124
+ const realRoots = await Promise.all(roots.map((root) => (0, import_promises11.realpath)(root).catch(() => root)));
9125
+ const requestedPaths = [...new Set(trackedInstalledPackagePaths(input2.entry).map((path2) => (0, import_node_path15.resolve)(path2)))];
9126
+ const protectedPaths = new Set(
9127
+ (input2.otherEntries ?? []).flatMap(trackedInstalledPackagePaths).map((path2) => (0, import_node_path15.resolve)(path2))
9128
+ );
9129
+ const paths = requestedPaths.filter((path2) => !protectedPaths.has(path2));
9130
+ const unsafe = paths.find((path2) => !roots.some((root) => within(root, path2)));
9131
+ if (unsafe) throw new Error(`Refusing to remove an untrusted path from the lockfile: ${unsafe}`);
9132
+ for (const path2 of paths) {
9133
+ const stat5 = await (0, import_promises11.lstat)(path2).catch(() => null);
9134
+ if (!stat5 || stat5.isSymbolicLink()) continue;
9135
+ const canonicalPath = await (0, import_promises11.realpath)(path2);
9136
+ if (!realRoots.some((root) => within(root, canonicalPath))) {
9137
+ throw new Error(`Refusing to remove a path that resolves outside the install roots: ${path2}`);
9138
+ }
9139
+ }
9140
+ let removed = 0;
9141
+ for (const path2 of paths) {
9142
+ if (await (0, import_promises11.lstat)(path2).catch(() => null)) removed += 1;
9143
+ await (0, import_promises11.rm)(path2, { force: true });
9144
+ }
9145
+ for (const path2 of paths.sort((a, b) => b.length - a.length)) {
9146
+ await pruneEmptyParents(path2, roots);
9147
+ }
9148
+ return { removed, retainedShared: requestedPaths.length - paths.length };
9149
+ }
9150
+
8994
9151
  // src/version.ts
8995
9152
  var import_node_fs2 = require("node:fs");
8996
- var import_node_path14 = require("node:path");
9153
+ var import_node_path16 = require("node:path");
8997
9154
  function getCliVersion() {
8998
- const pkgPath = (0, import_node_path14.join)(__dirname, "..", "package.json");
9155
+ const pkgPath = (0, import_node_path16.join)(__dirname, "..", "package.json");
8999
9156
  const pkg = JSON.parse((0, import_node_fs2.readFileSync)(pkgPath, "utf8"));
9000
9157
  return pkg.version;
9001
9158
  }
@@ -9141,7 +9298,7 @@ async function writeStarterFile(path2, content) {
9141
9298
  }
9142
9299
  async function inferEntryFromSource(source, fallback) {
9143
9300
  const sourceInfo = await (0, import_promises12.stat)(source);
9144
- if (sourceInfo.isFile()) return (0, import_node_path15.basename)(source);
9301
+ if (sourceInfo.isFile()) return (0, import_node_path17.basename)(source);
9145
9302
  if (!sourceInfo.isDirectory()) return fallback;
9146
9303
  const entries = await (0, import_promises12.readdir)(source);
9147
9304
  if (entries.includes(fallback)) return fallback;
@@ -9153,7 +9310,7 @@ async function copySourceIntoSkillRoot(source, root, sourceLabel) {
9153
9310
  if (sourceInfo.isDirectory()) {
9154
9311
  const entries = await (0, import_promises12.readdir)(source);
9155
9312
  for (const entry of entries) {
9156
- await (0, import_promises12.cp)((0, import_node_path15.join)(source, entry), (0, import_node_path15.join)(root, entry), {
9313
+ await (0, import_promises12.cp)((0, import_node_path17.join)(source, entry), (0, import_node_path17.join)(root, entry), {
9157
9314
  recursive: true,
9158
9315
  force: false,
9159
9316
  errorOnExist: true
@@ -9162,7 +9319,7 @@ async function copySourceIntoSkillRoot(source, root, sourceLabel) {
9162
9319
  return;
9163
9320
  }
9164
9321
  if (sourceInfo.isFile()) {
9165
- await (0, import_promises12.cp)(source, (0, import_node_path15.join)(root, (0, import_node_path15.basename)(source)), { force: false, errorOnExist: true });
9322
+ await (0, import_promises12.cp)(source, (0, import_node_path17.join)(root, (0, import_node_path17.basename)(source)), { force: false, errorOnExist: true });
9166
9323
  return;
9167
9324
  }
9168
9325
  throw new Error(`Unsupported source path: ${sourceLabel}`);
@@ -9196,11 +9353,11 @@ function packageDashboardUrl(name) {
9196
9353
  return `${DASHBOARD_URL}/packages/${name.replace(/^@/, "")}`;
9197
9354
  }
9198
9355
  function helperRootFromTrackedPath(path2) {
9199
- const parts = path2.split(import_node_path15.sep).filter(Boolean);
9356
+ const parts = path2.split(import_node_path17.sep).filter(Boolean);
9200
9357
  const helpersIndex = parts.lastIndexOf("helpers");
9201
9358
  if (helpersIndex === -1 || parts.length <= helpersIndex + 2) return null;
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));
9359
+ const prefix = path2.startsWith(import_node_path17.sep) ? import_node_path17.sep : "";
9360
+ return (0, import_node_path17.join)(prefix, ...parts.slice(0, helpersIndex + 3));
9204
9361
  }
9205
9362
  async function showInstalledPrompt(configRoot, packageArg) {
9206
9363
  const { name } = parsePackageArg(packageArg);
@@ -9293,7 +9450,7 @@ async function authTokenForRegistry(registry, options = {}) {
9293
9450
  return refreshed.accessToken;
9294
9451
  } catch {
9295
9452
  await clearStoredRegistryAuth(registry);
9296
- const message = `CLI login for ${registry} expired or was revoked. Run aipm login to access private packages.`;
9453
+ const message = `CLI login for ${registry} expired or was revoked. Run ${recommendCmd("aipm login")} to access private packages.`;
9297
9454
  if (options.throwOnFailure) throw new Error(message);
9298
9455
  if (!options.quiet) console.warn(`Warning: ${message}`);
9299
9456
  return void 0;
@@ -9542,9 +9699,9 @@ function buildStarterQualityMetadata(options) {
9542
9699
  }
9543
9700
  async function initSkill(opts) {
9544
9701
  if (!isValidScopeName(opts.name)) throw new Error("Invalid @scope/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));
9702
+ const root = opts.here ? (0, import_node_process5.cwd)() : (0, import_node_path17.resolve)((0, import_node_process5.cwd)(), opts.dir ?? skillFolderName(opts.name));
9546
9703
  const cdTarget = opts.here ? null : opts.dir ?? skillFolderName(opts.name);
9547
- const source = opts.from ? (0, import_node_path15.resolve)(opts.from) : null;
9704
+ const source = opts.from ? (0, import_node_path17.resolve)(opts.from) : null;
9548
9705
  const entry = source ? await inferEntryFromSource(source, opts.entry) : opts.entry;
9549
9706
  const template = parseSkillTemplate(opts.template ?? "blank");
9550
9707
  const manifest = PackageManifestSchema.parse({
@@ -9567,15 +9724,15 @@ async function initSkill(opts) {
9567
9724
  } else {
9568
9725
  await (0, import_promises12.mkdir)(root, { recursive: true });
9569
9726
  }
9570
- await writeStarterFile((0, import_node_path15.join)(root, "aipm.manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
9727
+ await writeStarterFile((0, import_node_path17.join)(root, "aipm.manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
9571
9728
  if (!opts.from) {
9572
9729
  await writeStarterFile(
9573
- (0, import_node_path15.join)(root, entry),
9730
+ (0, import_node_path17.join)(root, entry),
9574
9731
  SKILL_TEMPLATES[template](opts.name)
9575
9732
  );
9576
9733
  }
9577
9734
  await writeStarterFile(
9578
- (0, import_node_path15.join)(root, ".aipmignore"),
9735
+ (0, import_node_path17.join)(root, ".aipmignore"),
9579
9736
  [
9580
9737
  "# Files that should never be published with this skill",
9581
9738
  ".env*",
@@ -9593,7 +9750,7 @@ async function initSkill(opts) {
9593
9750
  ""
9594
9751
  ].join("\n")
9595
9752
  );
9596
- await (0, import_promises12.mkdir)((0, import_node_path15.join)(root, ".aipm"), { recursive: true });
9753
+ await (0, import_promises12.mkdir)((0, import_node_path17.join)(root, ".aipm"), { recursive: true });
9597
9754
  console.log(`Created ${opts.name} skill folder: ${root}`);
9598
9755
  printPublishGuide(
9599
9756
  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.`,
@@ -9734,7 +9891,7 @@ program2.command("add <package-or-prompt>").description("Add a skill package or
9734
9891
  const { name, version: requestedVersion } = parsePackageArg(pkgArg);
9735
9892
  const token = await tokenForRead(registry, opts.token);
9736
9893
  const version = requestedVersion ?? project.packages[name] ?? await latestVersionForPackage(registry, name, token);
9737
- if (!version) throw new Error("Specify version: aipm add @scope/pkg@1.0.0");
9894
+ if (!version) throw new Error(`Specify version: ${recommendCmd("aipm add <@scope/pkg>@<version>")}`);
9738
9895
  project = {
9739
9896
  ...project,
9740
9897
  packages: { ...project.packages, [name]: version.replace(/^\^/, "") }
@@ -9826,7 +9983,7 @@ program2.command("show <package-or-prompt>").description("Show an installed prom
9826
9983
  if (reference && project.prompts[reference.alias] === reference.url) {
9827
9984
  const lock2 = await readLockfile(configRoot);
9828
9985
  const entry2 = lock2?.prompts[reference.alias];
9829
- if (!entry2) throw new Error(`Prompt is not installed: ${reference.url}. Run aipm install.`);
9986
+ if (!entry2) throw new Error(`Prompt is not installed: ${reference.url}. Run ${recommendCmd("aipm install")}.`);
9830
9987
  console.log(await readInstalledPrompt(configRoot, entry2));
9831
9988
  return;
9832
9989
  }
@@ -9840,7 +9997,7 @@ program2.command("show-prompt <package>").description("Show the manual setup pro
9840
9997
  await showInstalledPrompt(resolveConfigRoot({ global: opts.global }), pkgArg);
9841
9998
  });
9842
9999
  function promptImageContentType(path2) {
9843
- const extension = (0, import_node_path15.extname)(path2).toLowerCase();
10000
+ const extension = (0, import_node_path17.extname)(path2).toLowerCase();
9844
10001
  if (extension === ".png") return "image/png";
9845
10002
  if (extension === ".jpg" || extension === ".jpeg") return "image/jpeg";
9846
10003
  if (extension === ".webp") return "image/webp";
@@ -9850,8 +10007,8 @@ var promptCommand = program2.command("prompt").description("Publish prompt listi
9850
10007
  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
10008
  const registry = registryFromEnvOrDefault(opts.registry);
9852
10009
  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);
10010
+ if (!accessToken) throw new Error(`Run ${recommendCmd("aipm login")} before publishing prompts.`);
10011
+ const absoluteFile = (0, import_node_path17.resolve)(file);
9855
10012
  const parsed = JSON.parse(await (0, import_promises12.readFile)(absoluteFile, "utf8"));
9856
10013
  const rows = Array.isArray(parsed) ? parsed : "prompts" in parsed ? parsed.prompts : [parsed];
9857
10014
  if (rows.length === 0) throw new Error("Prompt publish file contains no prompts.");
@@ -9863,10 +10020,10 @@ promptCommand.command("publish <file>").description("Publish one prompt or a bat
9863
10020
  const { sampleImagePath, ...input2 } = row;
9864
10021
  let sampleImage;
9865
10022
  if (sampleImagePath) {
9866
- const imagePath = (0, import_node_path15.resolve)((0, import_node_path15.dirname)(absoluteFile), sampleImagePath);
10023
+ const imagePath = (0, import_node_path17.resolve)((0, import_node_path17.dirname)(absoluteFile), sampleImagePath);
9867
10024
  sampleImage = {
9868
10025
  data: await (0, import_promises12.readFile)(imagePath),
9869
- filename: (0, import_node_path15.basename)(imagePath),
10026
+ filename: (0, import_node_path17.basename)(imagePath),
9870
10027
  contentType: promptImageContentType(imagePath)
9871
10028
  };
9872
10029
  }
@@ -9889,8 +10046,8 @@ var publish = program2.command("publish [dir]").description("Stage, validate, an
9889
10046
  publish.help();
9890
10047
  return;
9891
10048
  }
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");
10049
+ const abs = (0, import_node_path17.resolve)(dir);
10050
+ const manifestRaw = await (0, import_promises12.readFile)((0, import_node_path17.join)(abs, "aipm.manifest.json"), "utf8");
9894
10051
  const manifest = PackageManifestSchema.parse(JSON.parse(manifestRaw));
9895
10052
  const tarball = await packDirectory(abs);
9896
10053
  const registry = registryFromEnvOrDefault(opts.registry);
@@ -10055,6 +10212,7 @@ program2.command("list").description("List installed skills and prompts from aip
10055
10212
  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) => {
10056
10213
  const scope = { global: opts.global };
10057
10214
  const configRoot = resolveConfigRoot(scope);
10215
+ const installRoot = resolveInstallRoot(scope);
10058
10216
  const project = await readProjectPackageJson(configRoot);
10059
10217
  if (!project) throw new Error(initRequiredMessage(scope));
10060
10218
  const promptReference = resolveTrackedPrompt(project, pkgArg);
@@ -10064,17 +10222,30 @@ program2.command("remove <package-or-prompt>").alias("rm").description("Remove a
10064
10222
  return;
10065
10223
  }
10066
10224
  const { name } = parsePackageArg(pkgArg);
10225
+ const lock = await readLockfile(configRoot);
10226
+ const lockEntry = lock?.packages[name];
10227
+ if (lockEntry) {
10228
+ const otherEntries = Object.entries(lock.packages).filter(([packageName]) => packageName !== name).map(([, entry]) => entry);
10229
+ const removal = await removeInstalledPackageFiles({
10230
+ configRoot,
10231
+ installRoot,
10232
+ entry: lockEntry,
10233
+ otherEntries
10234
+ });
10235
+ console.log(`Deleted ${removal.removed} tracked installed ${removal.removed === 1 ? "file" : "files"}.`);
10236
+ if (removal.retainedShared > 0) {
10237
+ console.log(`Retained ${removal.retainedShared} ${removal.retainedShared === 1 ? "file" : "files"} shared with another installed package.`);
10238
+ }
10239
+ }
10067
10240
  const packages = { ...project.packages };
10068
10241
  delete packages[name];
10069
10242
  await writeProjectPackageJson(configRoot, { ...project, packages });
10070
- const lock = await readLockfile(configRoot);
10071
10243
  if (lock) {
10072
10244
  const lockPackages = { ...lock.packages };
10073
10245
  delete lockPackages[name];
10074
10246
  await writeLockfile(configRoot, { ...lock, packages: lockPackages });
10075
10247
  }
10076
10248
  console.log(`Removed ${name} from AIPM ${scopeLabel(scope)} files.`);
10077
- console.log("Adapter-written files are not deleted yet; review your project before committing.");
10078
10249
  });
10079
10250
  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) => {
10080
10251
  const scope = { global: opts.global };