@aipm-registry/cli 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.cjs CHANGED
@@ -4160,7 +4160,19 @@ function expandTargets(targets) {
4160
4160
  return [...ALL_TOOLS];
4161
4161
  return targets.filter((t) => t !== "*");
4162
4162
  }
4163
- var AiToolSchema, ALL_TOOLS, PackageExampleSchema, PackageManifestSchema;
4163
+ function isSafeRelativePath(value) {
4164
+ const trimmed = value.trim();
4165
+ if (!trimmed || trimmed.startsWith("/") || trimmed.startsWith("\\") || /^[A-Za-z]:/.test(trimmed)) {
4166
+ return false;
4167
+ }
4168
+ const normalized = trimmed.replace(/\\/g, "/");
4169
+ return normalized.split("/").filter(Boolean).every((segment) => segment !== "." && segment !== "..");
4170
+ }
4171
+ function basename(value) {
4172
+ const parts = value.replace(/\\/g, "/").split("/").filter(Boolean);
4173
+ return parts[parts.length - 1] ?? value;
4174
+ }
4175
+ var AiToolSchema, ALL_TOOLS, PackageExampleSchema, unsafePathMessage, InstallPathSchema, InstallOverwriteSchema, PackageInstallSchema, PackageManifestSchema;
4164
4176
  var init_manifest = __esm({
4165
4177
  "../../packages/schemas/dist/manifest.js"() {
4166
4178
  "use strict";
@@ -4173,6 +4185,36 @@ var init_manifest = __esm({
4173
4185
  description: external_exports.string().trim().min(1).max(240).optional(),
4174
4186
  prompt: external_exports.string().trim().min(1).max(1e3)
4175
4187
  });
4188
+ unsafePathMessage = "path must be relative and must not contain .. segments";
4189
+ InstallPathSchema = external_exports.string().trim().min(1).refine(isSafeRelativePath, unsafePathMessage);
4190
+ InstallOverwriteSchema = external_exports.enum(["fail", "skip", "replace"]);
4191
+ PackageInstallSchema = external_exports.object({
4192
+ mainFiles: external_exports.array(external_exports.object({
4193
+ from: InstallPathSchema,
4194
+ to: InstallPathSchema,
4195
+ overwrite: InstallOverwriteSchema.optional()
4196
+ })).optional(),
4197
+ helperFiles: external_exports.array(external_exports.object({
4198
+ from: InstallPathSchema,
4199
+ to: InstallPathSchema.optional()
4200
+ })).optional(),
4201
+ postInstall: external_exports.object({
4202
+ mode: external_exports.literal("manual_prompt"),
4203
+ promptFile: InstallPathSchema,
4204
+ cleanup: external_exports.enum(["manual", "after_user_confirmation"]).optional()
4205
+ }).optional()
4206
+ }).superRefine((install, ctx) => {
4207
+ if (!install.postInstall)
4208
+ return;
4209
+ const helperTargets = new Set((install.helperFiles ?? []).map((file) => file.to ?? basename(file.from)));
4210
+ if (!helperTargets.has(install.postInstall.promptFile)) {
4211
+ ctx.addIssue({
4212
+ code: external_exports.ZodIssueCode.custom,
4213
+ path: ["postInstall", "promptFile"],
4214
+ message: "postInstall.promptFile must point to an installed helper file"
4215
+ });
4216
+ }
4217
+ });
4176
4218
  PackageManifestSchema = external_exports.object({
4177
4219
  schemaVersion: external_exports.literal("0.1"),
4178
4220
  name: external_exports.string().regex(SCOPE_NAME_REGEX, "name must be @scope/name"),
@@ -4183,11 +4225,13 @@ var init_manifest = __esm({
4183
4225
  targets: external_exports.array(AiToolSchema).min(1),
4184
4226
  license: external_exports.string().optional(),
4185
4227
  usage: external_exports.string().trim().min(1).max(2e3).optional(),
4228
+ agentDescription: external_exports.string().trim().min(1).max(12e3).optional(),
4186
4229
  tags: external_exports.array(external_exports.string().trim().min(1).max(40)).max(12).optional(),
4187
4230
  categories: external_exports.array(external_exports.string().trim().min(1).max(40)).max(4).optional(),
4188
4231
  sourceUrl: external_exports.string().trim().url().max(500).optional(),
4189
4232
  examples: external_exports.array(PackageExampleSchema).max(5).optional(),
4190
- releaseNotes: external_exports.string().trim().min(1).max(2e3).optional()
4233
+ releaseNotes: external_exports.string().trim().min(1).max(2e3).optional(),
4234
+ install: PackageInstallSchema.optional()
4191
4235
  });
4192
4236
  }
4193
4237
  });
@@ -4225,7 +4269,17 @@ var init_lockfile = __esm({
4225
4269
  integrity: external_exports.string(),
4226
4270
  registry: external_exports.string().url(),
4227
4271
  resolvedTools: external_exports.array(AiToolSchema),
4228
- installed: InstalledPathsSchema
4272
+ installed: InstalledPathsSchema,
4273
+ installedAssets: external_exports.object({
4274
+ main: external_exports.array(external_exports.string()),
4275
+ helper: external_exports.array(external_exports.string())
4276
+ }).optional(),
4277
+ postInstall: external_exports.object({
4278
+ mode: external_exports.literal("manual_prompt"),
4279
+ status: external_exports.enum(["pending", "cleaned"]),
4280
+ promptFile: external_exports.string(),
4281
+ cleanup: external_exports.enum(["manual", "after_user_confirmation"])
4282
+ }).optional()
4229
4283
  });
4230
4284
  LockfileSchema = external_exports.object({
4231
4285
  schemaVersion: external_exports.literal("0.1"),
@@ -7776,16 +7830,15 @@ var import_node_crypto2 = require("node:crypto");
7776
7830
  var import_node_child_process4 = require("node:child_process");
7777
7831
  var import_node_http = require("node:http");
7778
7832
  var import_promises10 = require("node:fs/promises");
7779
- var import_node_path12 = require("node:path");
7833
+ var import_node_path13 = require("node:path");
7780
7834
  var import_node_process5 = require("node:process");
7781
7835
  init_dist();
7782
7836
  init_dist4();
7783
7837
 
7784
7838
  // src/pack.ts
7785
7839
  var import_node_child_process2 = require("node:child_process");
7786
- var import_promises5 = require("node:fs/promises");
7787
7840
  var import_node_util3 = require("node:util");
7788
- var import_promises6 = require("node:fs/promises");
7841
+ var import_promises5 = require("node:fs/promises");
7789
7842
  var import_node_path6 = require("node:path");
7790
7843
  var import_node_os = require("node:os");
7791
7844
 
@@ -7938,6 +7991,14 @@ async function readManifest(root) {
7938
7991
  throw error;
7939
7992
  }
7940
7993
  }
7994
+ function installReferencedFiles(manifest) {
7995
+ const install = manifest.install;
7996
+ if (!install) return [];
7997
+ return [
7998
+ ...(install.mainFiles ?? []).map((file) => file.from),
7999
+ ...(install.helperFiles ?? []).map((file) => file.from)
8000
+ ];
8001
+ }
7941
8002
  async function validatePublishState(root) {
7942
8003
  const manifest = await readManifest(root);
7943
8004
  const state = await readPublishState(root);
@@ -7945,6 +8006,9 @@ async function validatePublishState(root) {
7945
8006
  const staged = new Set(state.files.map((entry) => entry.path));
7946
8007
  if (!staged.has("aipm.manifest.json")) throw new Error("aipm.manifest.json must be staged.");
7947
8008
  if (!staged.has(manifest.entry)) throw new Error(`Manifest entry must be staged: ${manifest.entry}`);
8009
+ for (const file of installReferencedFiles(manifest)) {
8010
+ if (!staged.has(file)) throw new Error(`Manifest install file must be staged: ${file}`);
8011
+ }
7948
8012
  let size = 0;
7949
8013
  for (const entry of state.files) {
7950
8014
  if (isExcludedPublishPath(entry.path)) throw new Error(`Refusing to publish excluded path: ${entry.path}`);
@@ -7992,27 +8056,23 @@ async function packDirectory(dir) {
7992
8056
  return Buffer.isBuffer(stdout) ? stdout : Buffer.from(stdout);
7993
8057
  }
7994
8058
  async function packStagedFiles(root) {
7995
- const tempDir = await (0, import_promises6.mkdtemp)((0, import_node_path6.join)((0, import_node_os.tmpdir)(), "aipm-publish-stage-"));
8059
+ const tempDir = await (0, import_promises5.mkdtemp)((0, import_node_path6.join)((0, import_node_os.tmpdir)(), "aipm-publish-stage-"));
7996
8060
  try {
7997
8061
  await copyStagedFiles(root, tempDir);
7998
8062
  return await packDirectory(tempDir);
7999
8063
  } finally {
8000
- await (0, import_promises6.rm)(tempDir, { recursive: true, force: true });
8064
+ await (0, import_promises5.rm)(tempDir, { recursive: true, force: true });
8001
8065
  }
8002
8066
  }
8003
- async function unpackTarballToBuffer(tarball, entry) {
8004
- const { mkdtemp: mkdtemp2, rm: rm3, writeFile: writeFile7 } = await import("node:fs/promises");
8005
- const { join: join12 } = await import("node:path");
8067
+ async function unpackTarballToDirectory(tarball) {
8068
+ const { mkdtemp: mkdtemp2, writeFile: writeFile7 } = await import("node:fs/promises");
8069
+ const { join: join13 } = await import("node:path");
8006
8070
  const { tmpdir: tmpdir2 } = await import("node:os");
8007
- const tempDir = await mkdtemp2(join12(tmpdir2(), "aipm-install-"));
8008
- const tgzPath = join12(tempDir, "pkg.tgz");
8009
- try {
8010
- await writeFile7(tgzPath, tarball);
8011
- await execFileAsync("tar", ["-xzf", tgzPath, "-C", tempDir]);
8012
- return (0, import_promises5.readFile)(join12(tempDir, entry), "utf8");
8013
- } finally {
8014
- await rm3(tempDir, { recursive: true, force: true });
8015
- }
8071
+ const tempDir = await mkdtemp2(join13(tmpdir2(), "aipm-install-"));
8072
+ const tgzPath = join13(tempDir, "pkg.tgz");
8073
+ await writeFile7(tgzPath, tarball);
8074
+ await execFileAsync("tar", ["-xzf", tgzPath, "-C", tempDir]);
8075
+ return tempDir;
8016
8076
  }
8017
8077
 
8018
8078
  // src/registry-client.ts
@@ -8155,7 +8215,7 @@ async function recordPackageInstall(registry, name, token) {
8155
8215
  }
8156
8216
 
8157
8217
  // src/auth-store.ts
8158
- var import_promises7 = require("node:fs/promises");
8218
+ var import_promises6 = require("node:fs/promises");
8159
8219
  var import_node_os2 = require("node:os");
8160
8220
  var import_node_path7 = require("node:path");
8161
8221
  var AUTH_FILE = (0, import_node_path7.join)((0, import_node_os2.homedir)(), ".aipm", "auth.json");
@@ -8164,7 +8224,7 @@ function normalizeRegistry(registry) {
8164
8224
  }
8165
8225
  async function readAuthStore() {
8166
8226
  try {
8167
- const raw = await (0, import_promises7.readFile)(AUTH_FILE, "utf8");
8227
+ const raw = await (0, import_promises6.readFile)(AUTH_FILE, "utf8");
8168
8228
  const parsed = JSON.parse(raw);
8169
8229
  return { registries: parsed.registries ?? {} };
8170
8230
  } catch {
@@ -8172,9 +8232,9 @@ async function readAuthStore() {
8172
8232
  }
8173
8233
  }
8174
8234
  async function writeAuthStore(store) {
8175
- await (0, import_promises7.mkdir)((0, import_node_path7.dirname)(AUTH_FILE), { recursive: true });
8176
- await (0, import_promises7.writeFile)(AUTH_FILE, JSON.stringify(store, null, 2) + "\n", "utf8");
8177
- await (0, import_promises7.chmod)(AUTH_FILE, 384).catch(() => void 0);
8235
+ await (0, import_promises6.mkdir)((0, import_node_path7.dirname)(AUTH_FILE), { recursive: true });
8236
+ await (0, import_promises6.writeFile)(AUTH_FILE, JSON.stringify(store, null, 2) + "\n", "utf8");
8237
+ await (0, import_promises6.chmod)(AUTH_FILE, 384).catch(() => void 0);
8178
8238
  }
8179
8239
  async function getStoredRegistryAuth(registry) {
8180
8240
  const store = await readAuthStore();
@@ -8204,23 +8264,25 @@ function isAccessTokenFresh(auth, skewMs = 6e4) {
8204
8264
 
8205
8265
  // src/install-one.ts
8206
8266
  init_dist4();
8267
+ var import_promises8 = require("node:fs/promises");
8268
+ var import_node_path9 = require("node:path");
8207
8269
 
8208
8270
  // src/project-files.ts
8209
- var import_promises8 = require("node:fs/promises");
8271
+ var import_promises7 = require("node:fs/promises");
8210
8272
  var import_node_path8 = require("node:path");
8211
8273
  init_dist();
8212
8274
  var PACKAGE_JSON = "aipm.package.json";
8213
8275
  var LOCKFILE = "aipm-lock.json";
8214
8276
  async function readProjectPackageJson(projectRoot) {
8215
8277
  try {
8216
- const raw = await (0, import_promises8.readFile)((0, import_node_path8.join)(projectRoot, PACKAGE_JSON), "utf8");
8278
+ const raw = await (0, import_promises7.readFile)((0, import_node_path8.join)(projectRoot, PACKAGE_JSON), "utf8");
8217
8279
  return ProjectPackageJsonSchema.parse(JSON.parse(raw));
8218
8280
  } catch {
8219
8281
  return null;
8220
8282
  }
8221
8283
  }
8222
8284
  async function writeProjectPackageJson(projectRoot, data) {
8223
- await (0, import_promises8.writeFile)(
8285
+ await (0, import_promises7.writeFile)(
8224
8286
  (0, import_node_path8.join)(projectRoot, PACKAGE_JSON),
8225
8287
  JSON.stringify(data, null, 2) + "\n",
8226
8288
  "utf8"
@@ -8228,14 +8290,14 @@ async function writeProjectPackageJson(projectRoot, data) {
8228
8290
  }
8229
8291
  async function readLockfile(projectRoot) {
8230
8292
  try {
8231
- const raw = await (0, import_promises8.readFile)((0, import_node_path8.join)(projectRoot, LOCKFILE), "utf8");
8293
+ const raw = await (0, import_promises7.readFile)((0, import_node_path8.join)(projectRoot, LOCKFILE), "utf8");
8232
8294
  return LockfileSchema.parse(JSON.parse(raw));
8233
8295
  } catch {
8234
8296
  return null;
8235
8297
  }
8236
8298
  }
8237
8299
  async function writeLockfile(projectRoot, data) {
8238
- await (0, import_promises8.writeFile)((0, import_node_path8.join)(projectRoot, LOCKFILE), JSON.stringify(data, null, 2) + "\n", "utf8");
8300
+ await (0, import_promises7.writeFile)((0, import_node_path8.join)(projectRoot, LOCKFILE), JSON.stringify(data, null, 2) + "\n", "utf8");
8239
8301
  }
8240
8302
  function upsertLockEntry(lock, name, entry) {
8241
8303
  return {
@@ -8289,8 +8351,113 @@ async function promptForTool() {
8289
8351
  rl.close();
8290
8352
  }
8291
8353
  }
8354
+ async function promptForConfirmation(question) {
8355
+ const rl = readline.createInterface({ input: import_node_process2.stdin, output: import_node_process2.stdout });
8356
+ try {
8357
+ const answer = await rl.question(`${question} (y/N): `);
8358
+ const normalized = answer.trim().toLowerCase();
8359
+ return normalized === "y" || normalized === "yes";
8360
+ } finally {
8361
+ rl.close();
8362
+ }
8363
+ }
8292
8364
 
8293
8365
  // src/install-one.ts
8366
+ function normalizeRelativePath(path2) {
8367
+ return (0, import_node_path9.normalize)(path2).split(import_node_path9.sep).join("/");
8368
+ }
8369
+ function assertSafeRelativePath(path2) {
8370
+ const rel = normalizeRelativePath(path2.trim());
8371
+ if (!rel || rel === "." || rel.startsWith("/") || rel === ".." || rel.startsWith("../") || rel.includes("/../")) {
8372
+ throw new Error(`Unsafe install path: ${path2}`);
8373
+ }
8374
+ return rel;
8375
+ }
8376
+ function safeJoin(root, relPath) {
8377
+ const rel = assertSafeRelativePath(relPath);
8378
+ const target = (0, import_node_path9.resolve)(root, rel);
8379
+ const fromRoot = normalizeRelativePath((0, import_node_path9.relative)(root, target));
8380
+ if (fromRoot === ".." || fromRoot.startsWith("../") || fromRoot.startsWith("/")) {
8381
+ throw new Error(`Unsafe install path: ${relPath}`);
8382
+ }
8383
+ return target;
8384
+ }
8385
+ function packageHelperSlug(name) {
8386
+ const [scope = "", pkg = ""] = name.replace(/^@/, "").split("/");
8387
+ return `${scope}__${pkg}`;
8388
+ }
8389
+ function helperRootFor(configRoot, packageName, version) {
8390
+ const base = (0, import_node_path9.basename)(configRoot) === ".aipm" ? (0, import_node_path9.join)(configRoot, "helpers") : (0, import_node_path9.join)(configRoot, ".aipm", "helpers");
8391
+ return (0, import_node_path9.join)(base, packageHelperSlug(packageName), version);
8392
+ }
8393
+ async function assertSourceFile(packageRoot, relPath) {
8394
+ const source = safeJoin(packageRoot, relPath);
8395
+ const info = await (0, import_promises8.stat)(source).catch(() => null);
8396
+ if (!info?.isFile()) throw new Error(`Package install file not found: ${relPath}`);
8397
+ return source;
8398
+ }
8399
+ async function copyMainFile(input2) {
8400
+ const source = await assertSourceFile(input2.packageRoot, input2.file.from);
8401
+ const target = safeJoin(input2.installRoot, input2.file.to);
8402
+ const overwrite = input2.file.overwrite ?? "fail";
8403
+ const exists = await (0, import_promises8.stat)(target).catch(() => null);
8404
+ if (exists) {
8405
+ if (overwrite === "skip") return null;
8406
+ if (overwrite === "fail") throw new Error(`Install target already exists: ${target}`);
8407
+ }
8408
+ await (0, import_promises8.mkdir)((0, import_node_path9.dirname)(target), { recursive: true });
8409
+ await (0, import_promises8.cp)(source, target, { force: overwrite === "replace" });
8410
+ return target;
8411
+ }
8412
+ async function copyHelperFile(input2) {
8413
+ const source = await assertSourceFile(input2.packageRoot, input2.file.from);
8414
+ const target = safeJoin(input2.helperRoot, input2.file.to ?? (0, import_node_path9.basename)(input2.file.from));
8415
+ await (0, import_promises8.mkdir)((0, import_node_path9.dirname)(target), { recursive: true });
8416
+ await (0, import_promises8.cp)(source, target, { force: true });
8417
+ return target;
8418
+ }
8419
+ async function installPackageAssets(options) {
8420
+ const assets = { main: [], helper: [] };
8421
+ const install = options.install;
8422
+ if (!install) return { assets };
8423
+ for (const file of install.mainFiles ?? []) {
8424
+ const copied = await copyMainFile({
8425
+ packageRoot: options.packageRoot,
8426
+ installRoot: options.installRoot,
8427
+ file
8428
+ });
8429
+ if (copied) assets.main.push(copied);
8430
+ }
8431
+ const helperRoot = helperRootFor(options.configRoot, options.packageName, options.version);
8432
+ if ((install.helperFiles ?? []).length > 0) {
8433
+ await (0, import_promises8.rm)(helperRoot, { recursive: true, force: true });
8434
+ }
8435
+ for (const file of install.helperFiles ?? []) {
8436
+ assets.helper.push(await copyHelperFile({ packageRoot: options.packageRoot, helperRoot, file }));
8437
+ }
8438
+ if (!install.postInstall) return { assets };
8439
+ const promptFile = safeJoin(helperRoot, install.postInstall.promptFile);
8440
+ if (!assets.helper.includes(promptFile)) {
8441
+ throw new Error(`Post-install prompt was not installed: ${install.postInstall.promptFile}`);
8442
+ }
8443
+ return {
8444
+ assets,
8445
+ postInstall: {
8446
+ mode: "manual_prompt",
8447
+ status: "pending",
8448
+ promptFile,
8449
+ cleanup: install.postInstall.cleanup ?? "manual"
8450
+ }
8451
+ };
8452
+ }
8453
+ function printPostInstallNotice(name, postInstall) {
8454
+ if (!postInstall || postInstall.mode !== "manual_prompt") return;
8455
+ console.log("");
8456
+ console.log("This package requires AI-assisted setup.");
8457
+ console.log(`Prompt saved to: ${postInstall.promptFile}`);
8458
+ console.log(`Run: aipm show-prompt ${name}`);
8459
+ console.log(`After setup, run: aipm cleanup ${name}`);
8460
+ }
8294
8461
  async function installOnePackage(options) {
8295
8462
  const configRoot = options.configRoot;
8296
8463
  const installRoot = options.installRoot ?? configRoot;
@@ -8337,49 +8504,65 @@ async function installOnePackage(options) {
8337
8504
  options.version,
8338
8505
  options.token
8339
8506
  );
8340
- const skillMarkdown = await unpackTarballToBuffer(tarball, manifest.entry);
8341
- const result = await installSkillPackage({
8342
- projectRoot: installRoot,
8343
- manifest,
8344
- skillMarkdown,
8345
- preferredTools,
8346
- explicitTarget
8347
- });
8348
- const lock = await readLockfile(configRoot) ?? {
8349
- schemaVersion: "0.1",
8350
- packages: {}
8351
- };
8352
- const installed = {};
8353
- for (const tool of result.resolvedTools) {
8354
- const paths = result.installed[tool];
8355
- if (paths) installed[tool] = paths;
8356
- }
8357
- await writeLockfile(
8358
- configRoot,
8359
- upsertLockEntry(lock, options.name, {
8507
+ const packageRoot = await unpackTarballToDirectory(tarball);
8508
+ try {
8509
+ const skillMarkdown = await (0, import_promises8.readFile)(safeJoin(packageRoot, manifest.entry), "utf8");
8510
+ const result = await installSkillPackage({
8511
+ projectRoot: installRoot,
8512
+ manifest,
8513
+ skillMarkdown,
8514
+ preferredTools,
8515
+ explicitTarget
8516
+ });
8517
+ const { assets, postInstall } = await installPackageAssets({
8518
+ configRoot,
8519
+ installRoot,
8520
+ packageRoot,
8521
+ packageName: options.name,
8360
8522
  version: options.version,
8361
- integrity: remoteIntegrity,
8362
- registry: options.registry,
8363
- resolvedTools: result.resolvedTools,
8364
- installed
8365
- })
8366
- );
8367
- console.log(`Installed ${options.name}@${options.version} \u2192 ${result.resolvedTools.join(", ")}`);
8523
+ install: manifest.install
8524
+ });
8525
+ const lock = await readLockfile(configRoot) ?? {
8526
+ schemaVersion: "0.1",
8527
+ packages: {}
8528
+ };
8529
+ const installed = {};
8530
+ for (const tool of result.resolvedTools) {
8531
+ const paths = result.installed[tool];
8532
+ if (paths) installed[tool] = paths;
8533
+ }
8534
+ await writeLockfile(
8535
+ configRoot,
8536
+ upsertLockEntry(lock, options.name, {
8537
+ version: options.version,
8538
+ integrity: remoteIntegrity,
8539
+ registry: options.registry,
8540
+ resolvedTools: result.resolvedTools,
8541
+ installed,
8542
+ ...assets.main.length || assets.helper.length ? { installedAssets: assets } : {},
8543
+ ...postInstall ? { postInstall } : {}
8544
+ })
8545
+ );
8546
+ console.log(`Installed ${options.name}@${options.version} \u2192 ${result.resolvedTools.join(", ")}`);
8547
+ printPostInstallNotice(options.name, postInstall);
8548
+ } finally {
8549
+ await (0, import_promises8.rm)(packageRoot, { recursive: true, force: true });
8550
+ }
8368
8551
  }
8369
8552
 
8370
8553
  // src/doctor.ts
8371
8554
  var import_node_child_process3 = require("node:child_process");
8372
8555
  var import_promises9 = require("node:fs/promises");
8373
- var import_node_path10 = require("node:path");
8556
+ var import_node_path11 = require("node:path");
8374
8557
  var import_node_util4 = require("node:util");
8375
8558
 
8376
8559
  // src/project-root.ts
8377
8560
  var import_node_os3 = require("node:os");
8378
- var import_node_path9 = require("node:path");
8561
+ var import_node_path10 = require("node:path");
8379
8562
  var import_node_process3 = require("node:process");
8380
8563
  function globalConfigDir(env2 = process.env) {
8381
8564
  const fromEnv = env2.AIPM_HOME?.trim();
8382
- return fromEnv ? fromEnv.replace(/\/$/, "") : (0, import_node_path9.join)((0, import_node_os3.homedir)(), ".aipm");
8565
+ return fromEnv ? fromEnv.replace(/\/$/, "") : (0, import_node_path10.join)((0, import_node_os3.homedir)(), ".aipm");
8383
8566
  }
8384
8567
  function resolveConfigRoot(options = {}) {
8385
8568
  return options.global ? globalConfigDir() : (0, import_node_process3.cwd)();
@@ -8410,11 +8593,11 @@ async function commandOutput(command, args) {
8410
8593
  }
8411
8594
  function npmGlobalBin(prefix) {
8412
8595
  if (!prefix) return null;
8413
- return process.platform === "win32" ? prefix : `${prefix}${import_node_path10.sep}bin`;
8596
+ return process.platform === "win32" ? prefix : `${prefix}${import_node_path11.sep}bin`;
8414
8597
  }
8415
8598
  function pathIncludes(pathDir) {
8416
8599
  if (!pathDir) return false;
8417
- return (process.env.PATH ?? "").split(import_node_path10.delimiter).includes(pathDir);
8600
+ return (process.env.PATH ?? "").split(import_node_path11.delimiter).includes(pathDir);
8418
8601
  }
8419
8602
  function shellPathHint(binDir) {
8420
8603
  if (!binDir) return "Run npm prefix -g and add its bin folder to PATH.";
@@ -8467,11 +8650,11 @@ async function runDoctor(options) {
8467
8650
  if (!options.publish) {
8468
8651
  const configLabel = options.global ? "Global config" : "Project config";
8469
8652
  try {
8470
- await (0, import_promises9.access)((0, import_node_path10.join)(configRoot, "aipm.package.json"));
8653
+ await (0, import_promises9.access)((0, import_node_path11.join)(configRoot, "aipm.package.json"));
8471
8654
  checks.push({
8472
8655
  name: configLabel,
8473
8656
  ok: true,
8474
- detail: `${(0, import_node_path10.join)(configRoot, "aipm.package.json")} exists (${scopeLabel(scope)}).`
8657
+ detail: `${(0, import_node_path11.join)(configRoot, "aipm.package.json")} exists (${scopeLabel(scope)}).`
8475
8658
  });
8476
8659
  } catch {
8477
8660
  checks.push({
@@ -8541,9 +8724,9 @@ async function runDoctor(options) {
8541
8724
 
8542
8725
  // src/version.ts
8543
8726
  var import_node_fs2 = require("node:fs");
8544
- var import_node_path11 = require("node:path");
8727
+ var import_node_path12 = require("node:path");
8545
8728
  function getCliVersion() {
8546
- const pkgPath = (0, import_node_path11.join)(__dirname, "..", "package.json");
8729
+ const pkgPath = (0, import_node_path12.join)(__dirname, "..", "package.json");
8547
8730
  const pkg = JSON.parse((0, import_node_fs2.readFileSync)(pkgPath, "utf8"));
8548
8731
  return pkg.version;
8549
8732
  }
@@ -8682,7 +8865,7 @@ async function writeStarterFile(path2, content) {
8682
8865
  }
8683
8866
  async function inferEntryFromSource(source, fallback) {
8684
8867
  const sourceInfo = await (0, import_promises10.stat)(source);
8685
- if (sourceInfo.isFile()) return (0, import_node_path12.basename)(source);
8868
+ if (sourceInfo.isFile()) return (0, import_node_path13.basename)(source);
8686
8869
  if (!sourceInfo.isDirectory()) return fallback;
8687
8870
  const entries = await (0, import_promises10.readdir)(source);
8688
8871
  if (entries.includes(fallback)) return fallback;
@@ -8694,7 +8877,7 @@ async function copySourceIntoSkillRoot(source, root, sourceLabel) {
8694
8877
  if (sourceInfo.isDirectory()) {
8695
8878
  const entries = await (0, import_promises10.readdir)(source);
8696
8879
  for (const entry of entries) {
8697
- await (0, import_promises10.cp)((0, import_node_path12.join)(source, entry), (0, import_node_path12.join)(root, entry), {
8880
+ await (0, import_promises10.cp)((0, import_node_path13.join)(source, entry), (0, import_node_path13.join)(root, entry), {
8698
8881
  recursive: true,
8699
8882
  force: false,
8700
8883
  errorOnExist: true
@@ -8703,7 +8886,7 @@ async function copySourceIntoSkillRoot(source, root, sourceLabel) {
8703
8886
  return;
8704
8887
  }
8705
8888
  if (sourceInfo.isFile()) {
8706
- await (0, import_promises10.cp)(source, (0, import_node_path12.join)(root, (0, import_node_path12.basename)(source)), { force: false, errorOnExist: true });
8889
+ await (0, import_promises10.cp)(source, (0, import_node_path13.join)(root, (0, import_node_path13.basename)(source)), { force: false, errorOnExist: true });
8707
8890
  return;
8708
8891
  }
8709
8892
  throw new Error(`Unsupported source path: ${sourceLabel}`);
@@ -8736,6 +8919,73 @@ function packageDashboardUrl(name) {
8736
8919
  if (!name) return DASHBOARD_URL;
8737
8920
  return `${DASHBOARD_URL}/packages/${name.replace(/^@/, "")}`;
8738
8921
  }
8922
+ function helperRootFromTrackedPath(path2) {
8923
+ const parts = path2.split(import_node_path13.sep).filter(Boolean);
8924
+ const helpersIndex = parts.lastIndexOf("helpers");
8925
+ if (helpersIndex === -1 || parts.length <= helpersIndex + 2) return null;
8926
+ const prefix = path2.startsWith(import_node_path13.sep) ? import_node_path13.sep : "";
8927
+ return (0, import_node_path13.join)(prefix, ...parts.slice(0, helpersIndex + 3));
8928
+ }
8929
+ async function showInstalledPrompt(configRoot, packageArg) {
8930
+ const { name } = parsePackageArg(packageArg);
8931
+ const lock = await readLockfile(configRoot);
8932
+ const entry = lock?.packages[name];
8933
+ if (!entry) throw new Error(`Package is not installed: ${name}`);
8934
+ if (!entry.postInstall || entry.postInstall.mode !== "manual_prompt") {
8935
+ throw new Error(`Package has no setup prompt: ${name}`);
8936
+ }
8937
+ if (entry.postInstall.status === "cleaned") {
8938
+ throw new Error(`Setup helper files were cleaned for ${name}. Reinstall the package to restore them.`);
8939
+ }
8940
+ const content = await (0, import_promises10.readFile)(entry.postInstall.promptFile, "utf8").catch(() => {
8941
+ throw new Error(`Setup prompt file is missing: ${entry.postInstall?.promptFile}`);
8942
+ });
8943
+ console.log(`Prompt file: ${entry.postInstall.promptFile}`);
8944
+ console.log("");
8945
+ console.log(content.trimEnd());
8946
+ }
8947
+ async function cleanupInstalledHelpers(options) {
8948
+ const { name } = parsePackageArg(options.packageArg);
8949
+ const lock = await readLockfile(options.configRoot);
8950
+ const entry = lock?.packages[name];
8951
+ if (!lock || !entry) throw new Error(`Package is not installed: ${name}`);
8952
+ if (!entry.installedAssets?.helper?.length) {
8953
+ throw new Error(`Package has no helper files to clean: ${name}`);
8954
+ }
8955
+ if (!entry.postInstall) {
8956
+ throw new Error(`Package has no post-install helper state: ${name}`);
8957
+ }
8958
+ if (entry.postInstall.status === "cleaned") {
8959
+ console.log(`Helper files already cleaned for ${name}.`);
8960
+ return;
8961
+ }
8962
+ if (!options.yes) {
8963
+ const confirmed = await promptForConfirmation(`Delete tracked helper files for ${name}?`);
8964
+ if (!confirmed) {
8965
+ console.log("Cleanup cancelled.");
8966
+ return;
8967
+ }
8968
+ }
8969
+ const roots = new Set(
8970
+ entry.installedAssets.helper.map((path2) => helperRootFromTrackedPath(path2)).filter((path2) => Boolean(path2))
8971
+ );
8972
+ if (roots.size > 0) {
8973
+ for (const root of roots) await (0, import_promises10.rm)(root, { recursive: true, force: true });
8974
+ } else {
8975
+ for (const helperPath of entry.installedAssets.helper) await (0, import_promises10.rm)(helperPath, { force: true });
8976
+ }
8977
+ await writeLockfile(options.configRoot, {
8978
+ ...lock,
8979
+ packages: {
8980
+ ...lock.packages,
8981
+ [name]: {
8982
+ ...entry,
8983
+ postInstall: { ...entry.postInstall, status: "cleaned" }
8984
+ }
8985
+ }
8986
+ });
8987
+ console.log(`Cleaned helper files for ${name}.`);
8988
+ }
8739
8989
  async function openUrl(url) {
8740
8990
  const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
8741
8991
  const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
@@ -8874,6 +9124,11 @@ async function printPublishPreview(root, json) {
8874
9124
  description: manifest.description,
8875
9125
  targets: manifest.targets,
8876
9126
  entry: manifest.entry,
9127
+ install: manifest.install ? {
9128
+ mainFiles: manifest.install.mainFiles ?? [],
9129
+ helperFiles: manifest.install.helperFiles ?? [],
9130
+ postInstall: manifest.install.postInstall ?? null
9131
+ } : null,
8877
9132
  files: rows.map((row) => ({ path: row.path, size: row.size, changed: row.changed })),
8878
9133
  totalBytes: size,
8879
9134
  warnings: [
@@ -8891,6 +9146,13 @@ async function printPublishPreview(root, json) {
8891
9146
  console.log(`Description: ${preview.description}`);
8892
9147
  console.log(`Targets: ${preview.targets.join(", ")}`);
8893
9148
  console.log(`Entry: ${preview.entry}`);
9149
+ if (preview.install) {
9150
+ console.log(`Main files: ${preview.install.mainFiles.length}`);
9151
+ console.log(`Helper files: ${preview.install.helperFiles.length}`);
9152
+ if (preview.install.postInstall) {
9153
+ console.log(`Post-install: ${preview.install.postInstall.mode} (${preview.install.postInstall.promptFile})`);
9154
+ }
9155
+ }
8894
9156
  console.log(`Total: ${preview.files.length} file${preview.files.length === 1 ? "" : "s"}, ${preview.totalBytes} bytes`);
8895
9157
  for (const file of preview.files) {
8896
9158
  console.log(`${file.changed ? "modified" : "staged"} ${file.path} (${file.size} bytes)`);
@@ -9004,9 +9266,9 @@ function buildStarterQualityMetadata(options) {
9004
9266
  }
9005
9267
  async function initSkill(opts) {
9006
9268
  if (!isValidScopeName(opts.name)) throw new Error("Invalid @scope/name");
9007
- const root = opts.here ? (0, import_node_process5.cwd)() : (0, import_node_path12.resolve)((0, import_node_process5.cwd)(), opts.dir ?? skillFolderName(opts.name));
9269
+ const root = opts.here ? (0, import_node_process5.cwd)() : (0, import_node_path13.resolve)((0, import_node_process5.cwd)(), opts.dir ?? skillFolderName(opts.name));
9008
9270
  const cdTarget = opts.here ? null : opts.dir ?? skillFolderName(opts.name);
9009
- const source = opts.from ? (0, import_node_path12.resolve)(opts.from) : null;
9271
+ const source = opts.from ? (0, import_node_path13.resolve)(opts.from) : null;
9010
9272
  const entry = source ? await inferEntryFromSource(source, opts.entry) : opts.entry;
9011
9273
  const template = parseSkillTemplate(opts.template ?? "blank");
9012
9274
  const manifest = PackageManifestSchema.parse({
@@ -9029,15 +9291,15 @@ async function initSkill(opts) {
9029
9291
  } else {
9030
9292
  await (0, import_promises10.mkdir)(root, { recursive: true });
9031
9293
  }
9032
- await writeStarterFile((0, import_node_path12.join)(root, "aipm.manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
9294
+ await writeStarterFile((0, import_node_path13.join)(root, "aipm.manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
9033
9295
  if (!opts.from) {
9034
9296
  await writeStarterFile(
9035
- (0, import_node_path12.join)(root, entry),
9297
+ (0, import_node_path13.join)(root, entry),
9036
9298
  SKILL_TEMPLATES[template](opts.name)
9037
9299
  );
9038
9300
  }
9039
9301
  await writeStarterFile(
9040
- (0, import_node_path12.join)(root, ".aipmignore"),
9302
+ (0, import_node_path13.join)(root, ".aipmignore"),
9041
9303
  [
9042
9304
  "# Files that should never be published with this skill",
9043
9305
  ".env*",
@@ -9055,7 +9317,7 @@ async function initSkill(opts) {
9055
9317
  ""
9056
9318
  ].join("\n")
9057
9319
  );
9058
- await (0, import_promises10.mkdir)((0, import_node_path12.join)(root, ".aipm"), { recursive: true });
9320
+ await (0, import_promises10.mkdir)((0, import_node_path13.join)(root, ".aipm"), { recursive: true });
9059
9321
  console.log(`Created ${opts.name} skill folder: ${root}`);
9060
9322
  printPublishGuide(
9061
9323
  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.`,
@@ -9254,6 +9516,16 @@ program2.command("install").description("Install all packages from aipm.package.
9254
9516
  });
9255
9517
  }
9256
9518
  });
9519
+ 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) => {
9520
+ await showInstalledPrompt(resolveConfigRoot({ global: opts.global }), pkgArg);
9521
+ });
9522
+ 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) => {
9523
+ await cleanupInstalledHelpers({
9524
+ configRoot: resolveConfigRoot({ global: opts.global }),
9525
+ packageArg: pkgArg,
9526
+ yes: opts.yes
9527
+ });
9528
+ });
9257
9529
  program2.command("skill").description("Create and manage local skill packages").command("init").description("Create aipm.manifest.json and SKILL.md").requiredOption("--name <name>", "Package name, e.g. @org/skill").option("--version <version>", "Initial version", "1.0.0").option("--description <description>", "Skill description", "AIPM skill").option("--targets <targets>", "Comma-separated targets", "cursor").option("--entry <entry>", "Entry file", "SKILL.md").option("--template <template>", "Starter SKILL.md template: blank, code-review, issue-summary, release-notes", "blank").option("--dir <dir>", "Create files in this folder").option("--here", "Create files in the current folder instead of a skill-named folder").option("--from <path>", "Copy an existing AI-tool skill file or folder into the package folder").action(
9258
9530
  async (opts) => initSkill(opts)
9259
9531
  );
@@ -9262,8 +9534,8 @@ var publish = program2.command("publish [dir]").description("Stage, validate, an
9262
9534
  publish.help();
9263
9535
  return;
9264
9536
  }
9265
- const abs = (0, import_node_path12.resolve)(dir);
9266
- const manifestRaw = await (0, import_promises10.readFile)((0, import_node_path12.join)(abs, "aipm.manifest.json"), "utf8");
9537
+ const abs = (0, import_node_path13.resolve)(dir);
9538
+ const manifestRaw = await (0, import_promises10.readFile)((0, import_node_path13.join)(abs, "aipm.manifest.json"), "utf8");
9267
9539
  const manifest = PackageManifestSchema.parse(JSON.parse(manifestRaw));
9268
9540
  const tarball = await packDirectory(abs);
9269
9541
  const registry = registryFromEnvOrDefault(opts.registry);