@aipm-registry/cli 0.4.1 → 0.4.8
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/README.md +22 -0
- package/dist/bin.cjs +1459 -357
- package/dist/bin.cjs.map +4 -4
- package/package.json +9 -9
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
|
-
|
|
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
|
}
|
|
@@ -4314,7 +4319,7 @@ var init_dist = __esm({
|
|
|
4314
4319
|
// ../../packages/engine/dist/detect-tools.js
|
|
4315
4320
|
async function pathExists(path2) {
|
|
4316
4321
|
try {
|
|
4317
|
-
await (0,
|
|
4322
|
+
await (0, import_promises4.access)(path2);
|
|
4318
4323
|
return true;
|
|
4319
4324
|
} catch {
|
|
4320
4325
|
return false;
|
|
@@ -4322,11 +4327,11 @@ async function pathExists(path2) {
|
|
|
4322
4327
|
}
|
|
4323
4328
|
async function detectToolsInProject(projectRoot) {
|
|
4324
4329
|
const detected = [];
|
|
4325
|
-
if (await pathExists((0,
|
|
4330
|
+
if (await pathExists((0, import_node_path5.join)(projectRoot, ".cursor")))
|
|
4326
4331
|
detected.push("cursor");
|
|
4327
|
-
if (await pathExists((0,
|
|
4332
|
+
if (await pathExists((0, import_node_path5.join)(projectRoot, ".claude")))
|
|
4328
4333
|
detected.push("claude");
|
|
4329
|
-
if (await pathExists((0,
|
|
4334
|
+
if (await pathExists((0, import_node_path5.join)(projectRoot, ".codex")))
|
|
4330
4335
|
detected.push("codex");
|
|
4331
4336
|
return detected;
|
|
4332
4337
|
}
|
|
@@ -4337,7 +4342,6 @@ function manifestAllowsTool(manifest, tool) {
|
|
|
4337
4342
|
}
|
|
4338
4343
|
async function resolveInstallTools(options) {
|
|
4339
4344
|
const { manifest } = options;
|
|
4340
|
-
const expandedAllowed = expandTargets(manifest.targets);
|
|
4341
4345
|
if (options.explicitTarget) {
|
|
4342
4346
|
if (options.explicitTarget === "*") {
|
|
4343
4347
|
return [...ALL_TOOLS];
|
|
@@ -4358,37 +4362,69 @@ async function resolveInstallTools(options) {
|
|
|
4358
4362
|
const preferred = expandTargets(options.preferredTools ?? []).filter((t) => manifestAllowsTool(manifest, t));
|
|
4359
4363
|
if (preferred.length > 0)
|
|
4360
4364
|
return preferred;
|
|
4361
|
-
if (manifest.targets.includes("*"))
|
|
4362
|
-
return expandedAllowed;
|
|
4363
4365
|
return [];
|
|
4364
4366
|
}
|
|
4365
|
-
var
|
|
4367
|
+
var import_promises4, import_node_path5;
|
|
4366
4368
|
var init_detect_tools = __esm({
|
|
4367
4369
|
"../../packages/engine/dist/detect-tools.js"() {
|
|
4368
4370
|
"use strict";
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
+
import_promises4 = require("node:fs/promises");
|
|
4372
|
+
import_node_path5 = require("node:path");
|
|
4371
4373
|
init_dist();
|
|
4372
4374
|
}
|
|
4373
4375
|
});
|
|
4374
4376
|
|
|
4375
|
-
// ../../packages/adapter-
|
|
4376
|
-
|
|
4377
|
+
// ../../packages/adapter-sdk/dist/index.js
|
|
4378
|
+
async function writeSkillDirectory(skillDir, input2) {
|
|
4379
|
+
const entryPath = (0, import_node_path6.join)(skillDir, "SKILL.md");
|
|
4380
|
+
const seen = /* @__PURE__ */ new Set(["skill.md"]);
|
|
4381
|
+
const files = (input2.supportingFiles ?? []).map((file) => {
|
|
4382
|
+
const normalized = (0, import_node_path6.normalize)(file.path);
|
|
4383
|
+
const target = (0, import_node_path6.resolve)(skillDir, normalized);
|
|
4384
|
+
const fromSkillDir = (0, import_node_path6.relative)(skillDir, target);
|
|
4385
|
+
return { file, normalized, target, fromSkillDir, key: fromSkillDir.toLowerCase() };
|
|
4386
|
+
});
|
|
4387
|
+
for (const { file, normalized, fromSkillDir, key } of files) {
|
|
4388
|
+
if (!normalized || normalized === "." || (0, import_node_path6.isAbsolute)(normalized) || fromSkillDir === ".." || fromSkillDir.startsWith(`..${import_node_path6.sep}`) || (0, import_node_path6.isAbsolute)(fromSkillDir) || seen.has(key)) {
|
|
4389
|
+
throw new Error(`Unsafe or duplicate skill supporting file path: ${file.path}`);
|
|
4390
|
+
}
|
|
4391
|
+
seen.add(key);
|
|
4392
|
+
}
|
|
4393
|
+
await (0, import_promises5.mkdir)(skillDir, { recursive: true });
|
|
4394
|
+
await (0, import_promises5.writeFile)(entryPath, input2.skillMarkdown, "utf8");
|
|
4395
|
+
const writtenPaths = [entryPath];
|
|
4396
|
+
for (const { file, target } of files) {
|
|
4397
|
+
await (0, import_promises5.mkdir)((0, import_node_path6.dirname)(target), { recursive: true });
|
|
4398
|
+
await (0, import_promises5.writeFile)(target, file.content);
|
|
4399
|
+
if (file.mode !== void 0)
|
|
4400
|
+
await (0, import_promises5.chmod)(target, file.mode & 511);
|
|
4401
|
+
writtenPaths.push(target);
|
|
4402
|
+
}
|
|
4403
|
+
return { writtenPaths };
|
|
4404
|
+
}
|
|
4405
|
+
var import_promises5, import_node_path6;
|
|
4377
4406
|
var init_dist2 = __esm({
|
|
4407
|
+
"../../packages/adapter-sdk/dist/index.js"() {
|
|
4408
|
+
"use strict";
|
|
4409
|
+
import_promises5 = require("node:fs/promises");
|
|
4410
|
+
import_node_path6 = require("node:path");
|
|
4411
|
+
}
|
|
4412
|
+
});
|
|
4413
|
+
|
|
4414
|
+
// ../../packages/adapter-claude/dist/index.js
|
|
4415
|
+
var import_node_path7, ClaudeSkillAdapter, claudeSkillAdapter;
|
|
4416
|
+
var init_dist3 = __esm({
|
|
4378
4417
|
"../../packages/adapter-claude/dist/index.js"() {
|
|
4379
4418
|
"use strict";
|
|
4380
|
-
|
|
4381
|
-
|
|
4419
|
+
import_node_path7 = require("node:path");
|
|
4420
|
+
init_dist2();
|
|
4382
4421
|
init_dist();
|
|
4383
4422
|
ClaudeSkillAdapter = class {
|
|
4384
4423
|
tool = "claude";
|
|
4385
4424
|
async installSkill(input2) {
|
|
4386
4425
|
const short = shortNameFromScopeName(input2.packageName);
|
|
4387
|
-
const skillDir = (0,
|
|
4388
|
-
|
|
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] };
|
|
4426
|
+
const skillDir = (0, import_node_path7.join)(input2.projectRoot, ".claude", "skills", short);
|
|
4427
|
+
return writeSkillDirectory(skillDir, input2);
|
|
4392
4428
|
}
|
|
4393
4429
|
};
|
|
4394
4430
|
claudeSkillAdapter = new ClaudeSkillAdapter();
|
|
@@ -4396,21 +4432,21 @@ var init_dist2 = __esm({
|
|
|
4396
4432
|
});
|
|
4397
4433
|
|
|
4398
4434
|
// ../../packages/adapter-cursor/dist/index.js
|
|
4399
|
-
var
|
|
4400
|
-
var
|
|
4435
|
+
var import_promises6, import_node_path8, CursorSkillAdapter, cursorSkillAdapter;
|
|
4436
|
+
var init_dist4 = __esm({
|
|
4401
4437
|
"../../packages/adapter-cursor/dist/index.js"() {
|
|
4402
4438
|
"use strict";
|
|
4403
|
-
|
|
4404
|
-
|
|
4439
|
+
import_promises6 = require("node:fs/promises");
|
|
4440
|
+
import_node_path8 = require("node:path");
|
|
4405
4441
|
init_dist();
|
|
4406
4442
|
CursorSkillAdapter = class {
|
|
4407
4443
|
tool = "cursor";
|
|
4408
4444
|
async installSkill(input2) {
|
|
4409
4445
|
const short = shortNameFromScopeName(input2.packageName);
|
|
4410
|
-
const skillsDir = (0,
|
|
4411
|
-
await (0,
|
|
4412
|
-
const filePath = (0,
|
|
4413
|
-
await (0,
|
|
4446
|
+
const skillsDir = (0, import_node_path8.join)(input2.projectRoot, ".cursor", "aipm", "skills");
|
|
4447
|
+
await (0, import_promises6.mkdir)(skillsDir, { recursive: true });
|
|
4448
|
+
const filePath = (0, import_node_path8.join)(skillsDir, `${short}.md`);
|
|
4449
|
+
await (0, import_promises6.writeFile)(filePath, input2.skillMarkdown, "utf8");
|
|
4414
4450
|
return { writtenPaths: [filePath] };
|
|
4415
4451
|
}
|
|
4416
4452
|
};
|
|
@@ -4419,22 +4455,19 @@ var init_dist3 = __esm({
|
|
|
4419
4455
|
});
|
|
4420
4456
|
|
|
4421
4457
|
// ../../packages/adapter-codex/dist/index.js
|
|
4422
|
-
var
|
|
4423
|
-
var
|
|
4458
|
+
var import_node_path9, CodexSkillAdapter, codexSkillAdapter;
|
|
4459
|
+
var init_dist5 = __esm({
|
|
4424
4460
|
"../../packages/adapter-codex/dist/index.js"() {
|
|
4425
4461
|
"use strict";
|
|
4426
|
-
|
|
4427
|
-
|
|
4462
|
+
import_node_path9 = require("node:path");
|
|
4463
|
+
init_dist2();
|
|
4428
4464
|
init_dist();
|
|
4429
4465
|
CodexSkillAdapter = class {
|
|
4430
4466
|
tool = "codex";
|
|
4431
4467
|
async installSkill(input2) {
|
|
4432
4468
|
const short = shortNameFromScopeName(input2.packageName);
|
|
4433
|
-
const skillDir = (0,
|
|
4434
|
-
|
|
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] };
|
|
4469
|
+
const skillDir = (0, import_node_path9.join)(input2.projectRoot, ".agents", "skills", short);
|
|
4470
|
+
return writeSkillDirectory(skillDir, input2);
|
|
4438
4471
|
}
|
|
4439
4472
|
};
|
|
4440
4473
|
codexSkillAdapter = new CodexSkillAdapter();
|
|
@@ -4459,6 +4492,7 @@ async function installSkillPackage(options) {
|
|
|
4459
4492
|
packageName: options.manifest.name,
|
|
4460
4493
|
version: options.manifest.version,
|
|
4461
4494
|
skillMarkdown: options.skillMarkdown,
|
|
4495
|
+
supportingFiles: options.supportingFiles,
|
|
4462
4496
|
projectRoot: options.projectRoot
|
|
4463
4497
|
});
|
|
4464
4498
|
installed[tool] = result.writtenPaths;
|
|
@@ -4469,9 +4503,9 @@ var adapters;
|
|
|
4469
4503
|
var init_install_skill = __esm({
|
|
4470
4504
|
"../../packages/engine/dist/install-skill.js"() {
|
|
4471
4505
|
"use strict";
|
|
4472
|
-
init_dist2();
|
|
4473
4506
|
init_dist3();
|
|
4474
4507
|
init_dist4();
|
|
4508
|
+
init_dist5();
|
|
4475
4509
|
init_detect_tools();
|
|
4476
4510
|
adapters = {
|
|
4477
4511
|
cursor: cursorSkillAdapter,
|
|
@@ -4488,7 +4522,7 @@ __export(dist_exports, {
|
|
|
4488
4522
|
installSkillPackage: () => installSkillPackage,
|
|
4489
4523
|
resolveInstallTools: () => resolveInstallTools
|
|
4490
4524
|
});
|
|
4491
|
-
var
|
|
4525
|
+
var init_dist6 = __esm({
|
|
4492
4526
|
"../../packages/engine/dist/index.js"() {
|
|
4493
4527
|
"use strict";
|
|
4494
4528
|
init_detect_tools();
|
|
@@ -7868,25 +7902,107 @@ var program = new Command();
|
|
|
7868
7902
|
var import_node_crypto3 = require("node:crypto");
|
|
7869
7903
|
var import_node_child_process4 = require("node:child_process");
|
|
7870
7904
|
var import_node_http = require("node:http");
|
|
7871
|
-
var
|
|
7872
|
-
var
|
|
7873
|
-
var
|
|
7905
|
+
var import_promises14 = require("node:fs/promises");
|
|
7906
|
+
var import_node_path18 = require("node:path");
|
|
7907
|
+
var import_node_process6 = require("node:process");
|
|
7874
7908
|
init_dist();
|
|
7875
|
-
init_dist5();
|
|
7876
7909
|
|
|
7877
7910
|
// src/pack.ts
|
|
7878
7911
|
var import_node_child_process2 = require("node:child_process");
|
|
7879
7912
|
var import_node_util3 = require("node:util");
|
|
7880
|
-
var
|
|
7881
|
-
var
|
|
7913
|
+
var import_promises2 = require("node:fs/promises");
|
|
7914
|
+
var import_node_path3 = require("node:path");
|
|
7882
7915
|
var import_node_os = require("node:os");
|
|
7883
7916
|
|
|
7884
7917
|
// src/publish-state.ts
|
|
7885
7918
|
var import_node_crypto = require("node:crypto");
|
|
7886
|
-
var
|
|
7887
|
-
var
|
|
7919
|
+
var import_promises = require("node:fs/promises");
|
|
7920
|
+
var import_node_path2 = require("node:path");
|
|
7888
7921
|
init_dist();
|
|
7889
|
-
|
|
7922
|
+
|
|
7923
|
+
// src/ui/theme.ts
|
|
7924
|
+
var brand = {
|
|
7925
|
+
/** Primary accent — dark-mode site --accent / close to logo mint */
|
|
7926
|
+
accent: { r: 72, g: 185, b: 141 },
|
|
7927
|
+
// #48b98d
|
|
7928
|
+
/** Stronger / hover — dark --accent-strong */
|
|
7929
|
+
accentStrong: { r: 123, g: 217, b: 173 },
|
|
7930
|
+
// #7bd9ad
|
|
7931
|
+
/** Logo mint (slightly brighter) */
|
|
7932
|
+
mint: { r: 79, g: 196, b: 154 },
|
|
7933
|
+
// #4FC49A
|
|
7934
|
+
/** On-accent text (dark green-black) */
|
|
7935
|
+
onAccent: { r: 7, g: 17, b: 13 },
|
|
7936
|
+
// #07110d
|
|
7937
|
+
/** Muted secondary text */
|
|
7938
|
+
muted: { r: 157, g: 175, b: 170 },
|
|
7939
|
+
// #9dafaa
|
|
7940
|
+
/** Warning */
|
|
7941
|
+
warning: { r: 245, g: 165, b: 36 },
|
|
7942
|
+
// #f5a524
|
|
7943
|
+
/** Danger */
|
|
7944
|
+
danger: { r: 249, g: 112, b: 102 }
|
|
7945
|
+
// #f97066
|
|
7946
|
+
};
|
|
7947
|
+
function fgTruecolor({ r, g, b }) {
|
|
7948
|
+
return `\x1B[38;2;${r};${g};${b}m`;
|
|
7949
|
+
}
|
|
7950
|
+
function bgTruecolor({ r, g, b }) {
|
|
7951
|
+
return `\x1B[48;2;${r};${g};${b}m`;
|
|
7952
|
+
}
|
|
7953
|
+
function envValue(env2, key) {
|
|
7954
|
+
return env2[key];
|
|
7955
|
+
}
|
|
7956
|
+
function supportsColor(stream, env2 = process.env) {
|
|
7957
|
+
const noColor = envValue(env2, "NO_COLOR");
|
|
7958
|
+
if (noColor != null && noColor !== "") return false;
|
|
7959
|
+
const force = envValue(env2, "FORCE_COLOR");
|
|
7960
|
+
if (force === "0") return false;
|
|
7961
|
+
if (force != null && force !== "") return true;
|
|
7962
|
+
return Boolean(stream.isTTY);
|
|
7963
|
+
}
|
|
7964
|
+
function supportsTruecolor(stream, env2 = process.env) {
|
|
7965
|
+
if (!supportsColor(stream, env2)) return false;
|
|
7966
|
+
const force = envValue(env2, "FORCE_COLOR");
|
|
7967
|
+
if (force === "3") return true;
|
|
7968
|
+
const colorterm = (envValue(env2, "COLORTERM") ?? "").toLowerCase();
|
|
7969
|
+
if (colorterm.includes("truecolor") || colorterm.includes("24bit")) return true;
|
|
7970
|
+
return Boolean(stream.isTTY);
|
|
7971
|
+
}
|
|
7972
|
+
function createTheme(stream, env2 = process.env) {
|
|
7973
|
+
const color = supportsColor(stream, env2);
|
|
7974
|
+
const truecolor = supportsTruecolor(stream, env2);
|
|
7975
|
+
const accentFg = !color ? "" : truecolor ? fgTruecolor(brand.accent) : "\x1B[32m";
|
|
7976
|
+
const accentStrongFg = !color ? "" : truecolor ? fgTruecolor(brand.accentStrong) : "\x1B[32m";
|
|
7977
|
+
const mutedFg = !color ? "" : truecolor ? fgTruecolor(brand.muted) : "\x1B[2m";
|
|
7978
|
+
const onAccentFg = !color ? "" : truecolor ? fgTruecolor(brand.onAccent) : "\x1B[30m";
|
|
7979
|
+
const accentBg = !color ? "" : truecolor ? bgTruecolor(brand.accent) : "\x1B[42m";
|
|
7980
|
+
return {
|
|
7981
|
+
color,
|
|
7982
|
+
truecolor,
|
|
7983
|
+
reset: color ? "\x1B[0m" : "",
|
|
7984
|
+
bold: color ? "\x1B[1m" : "",
|
|
7985
|
+
dim: color ? "\x1B[2m" : "",
|
|
7986
|
+
accent: accentFg,
|
|
7987
|
+
accentStrong: accentStrongFg,
|
|
7988
|
+
muted: mutedFg,
|
|
7989
|
+
/** Selected row: mint background + dark text (site on-accent). */
|
|
7990
|
+
highlight: color ? `${accentBg}${onAccentFg}\x1B[1m` : "",
|
|
7991
|
+
warning: color ? truecolor ? fgTruecolor(brand.warning) : "\x1B[33m" : "",
|
|
7992
|
+
danger: color ? truecolor ? fgTruecolor(brand.danger) : "\x1B[31m" : ""
|
|
7993
|
+
};
|
|
7994
|
+
}
|
|
7995
|
+
|
|
7996
|
+
// src/recommend-cmd.ts
|
|
7997
|
+
function recommendCmd(command, stream = process.stderr, env2) {
|
|
7998
|
+
const quoted = `"${command}"`;
|
|
7999
|
+
const theme = createTheme(stream, env2);
|
|
8000
|
+
if (!theme.color) return quoted;
|
|
8001
|
+
return `${theme.accent}${quoted}${theme.reset}`;
|
|
8002
|
+
}
|
|
8003
|
+
|
|
8004
|
+
// src/publish-state.ts
|
|
8005
|
+
var STATE_PATH = (0, import_node_path2.join)(".aipm", "publish-state.json");
|
|
7890
8006
|
var IGNORE_FILE = ".aipmignore";
|
|
7891
8007
|
var MAX_PACKAGE_BYTES = 50 * 1024 * 1024;
|
|
7892
8008
|
var EXCLUDED_SEGMENTS = /* @__PURE__ */ new Set([".aipm", ".git", "node_modules", "dist", ".next"]);
|
|
@@ -7901,11 +8017,11 @@ var SECRET_PATTERNS = [
|
|
|
7901
8017
|
/<publishData/i
|
|
7902
8018
|
];
|
|
7903
8019
|
function publishStatePath(root) {
|
|
7904
|
-
return (0,
|
|
8020
|
+
return (0, import_node_path2.join)(root, STATE_PATH);
|
|
7905
8021
|
}
|
|
7906
8022
|
function normalizePublishPath(root, filePath) {
|
|
7907
|
-
const abs = (0,
|
|
7908
|
-
const rel = (0,
|
|
8023
|
+
const abs = (0, import_node_path2.resolve)(root, filePath);
|
|
8024
|
+
const rel = (0, import_node_path2.normalize)((0, import_node_path2.relative)(root, abs)).split(import_node_path2.sep).join("/");
|
|
7909
8025
|
if (!rel || rel === ".") return ".";
|
|
7910
8026
|
if (rel.startsWith("../") || rel === ".." || rel.startsWith("/")) {
|
|
7911
8027
|
throw new Error(`Path is outside the skill folder: ${filePath}`);
|
|
@@ -7922,7 +8038,7 @@ function isExcludedPublishPath(rel) {
|
|
|
7922
8038
|
}
|
|
7923
8039
|
async function readAipmIgnore(root) {
|
|
7924
8040
|
try {
|
|
7925
|
-
const raw = await (0,
|
|
8041
|
+
const raw = await (0, import_promises.readFile)((0, import_node_path2.join)(root, IGNORE_FILE), "utf8");
|
|
7926
8042
|
return raw.split(/\r?\n/).map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
|
|
7927
8043
|
} catch {
|
|
7928
8044
|
return [];
|
|
@@ -7943,7 +8059,7 @@ async function isIgnoredByAipmIgnore(root, rel) {
|
|
|
7943
8059
|
return rules.some((rule) => matchesIgnoreRule(rel, rule));
|
|
7944
8060
|
}
|
|
7945
8061
|
async function assertNoObviousSecrets(path2, rel) {
|
|
7946
|
-
const data = await (0,
|
|
8062
|
+
const data = await (0, import_promises.readFile)(path2);
|
|
7947
8063
|
if (data.includes(0)) return;
|
|
7948
8064
|
const text = data.toString("utf8");
|
|
7949
8065
|
if (SECRET_PATTERNS.some((pattern) => pattern.test(text))) {
|
|
@@ -7951,7 +8067,7 @@ async function assertNoObviousSecrets(path2, rel) {
|
|
|
7951
8067
|
}
|
|
7952
8068
|
}
|
|
7953
8069
|
async function fileHash(path2) {
|
|
7954
|
-
const data = await (0,
|
|
8070
|
+
const data = await (0, import_promises.readFile)(path2);
|
|
7955
8071
|
return {
|
|
7956
8072
|
hash: (0, import_node_crypto.createHash)("sha256").update(data).digest("hex"),
|
|
7957
8073
|
size: data.length
|
|
@@ -7959,7 +8075,7 @@ async function fileHash(path2) {
|
|
|
7959
8075
|
}
|
|
7960
8076
|
async function readPublishState(root) {
|
|
7961
8077
|
try {
|
|
7962
|
-
const raw = await (0,
|
|
8078
|
+
const raw = await (0, import_promises.readFile)(publishStatePath(root), "utf8");
|
|
7963
8079
|
const parsed = JSON.parse(raw);
|
|
7964
8080
|
return {
|
|
7965
8081
|
schemaVersion: "0.1",
|
|
@@ -7971,18 +8087,18 @@ async function readPublishState(root) {
|
|
|
7971
8087
|
}
|
|
7972
8088
|
async function writePublishState(root, state) {
|
|
7973
8089
|
const path2 = publishStatePath(root);
|
|
7974
|
-
await (0,
|
|
8090
|
+
await (0, import_promises.mkdir)((0, import_node_path2.dirname)(path2), { recursive: true });
|
|
7975
8091
|
const files = [...state.files].sort((a, b) => a.path.localeCompare(b.path));
|
|
7976
|
-
await (0,
|
|
8092
|
+
await (0, import_promises.writeFile)(path2, JSON.stringify({ schemaVersion: "0.1", files }, null, 2) + "\n", "utf8");
|
|
7977
8093
|
}
|
|
7978
8094
|
async function expandPublishPath(root, rel) {
|
|
7979
8095
|
if (isExcludedPublishPath(rel)) return [];
|
|
7980
8096
|
if (await isIgnoredByAipmIgnore(root, rel)) return [];
|
|
7981
|
-
const abs = (0,
|
|
7982
|
-
const info = await (0,
|
|
8097
|
+
const abs = (0, import_node_path2.join)(root, rel);
|
|
8098
|
+
const info = await (0, import_promises.stat)(abs);
|
|
7983
8099
|
if (info.isFile()) return [rel];
|
|
7984
8100
|
if (!info.isDirectory()) return [];
|
|
7985
|
-
const entries = await (0,
|
|
8101
|
+
const entries = await (0, import_promises.readdir)(abs);
|
|
7986
8102
|
const nested = await Promise.all(
|
|
7987
8103
|
entries.map((entry) => expandPublishPath(root, `${rel === "." ? "" : `${rel}/`}${entry}`))
|
|
7988
8104
|
);
|
|
@@ -7998,7 +8114,7 @@ async function addPublishFiles(root, paths) {
|
|
|
7998
8114
|
for (const rel of files) {
|
|
7999
8115
|
if (isExcludedPublishPath(rel)) continue;
|
|
8000
8116
|
if (await isIgnoredByAipmIgnore(root, rel)) continue;
|
|
8001
|
-
const hashed = await fileHash((0,
|
|
8117
|
+
const hashed = await fileHash((0, import_node_path2.join)(root, rel));
|
|
8002
8118
|
byPath.set(rel, { path: rel, ...hashed });
|
|
8003
8119
|
}
|
|
8004
8120
|
}
|
|
@@ -8017,15 +8133,17 @@ async function removePublishFiles(root, paths) {
|
|
|
8017
8133
|
return next;
|
|
8018
8134
|
}
|
|
8019
8135
|
async function resetPublishState(root) {
|
|
8020
|
-
await (0,
|
|
8136
|
+
await (0, import_promises.rm)(publishStatePath(root), { force: true });
|
|
8021
8137
|
}
|
|
8022
8138
|
async function readManifest(root) {
|
|
8023
8139
|
try {
|
|
8024
|
-
const raw = await (0,
|
|
8140
|
+
const raw = await (0, import_promises.readFile)((0, import_node_path2.join)(root, "aipm.manifest.json"), "utf8");
|
|
8025
8141
|
return PackageManifestSchema.parse(JSON.parse(raw));
|
|
8026
8142
|
} catch (error) {
|
|
8027
8143
|
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
8028
|
-
throw new Error(
|
|
8144
|
+
throw new Error(
|
|
8145
|
+
`No aipm.manifest.json found. Run ${recommendCmd("aipm publish init --name <@org/skill>")}.`
|
|
8146
|
+
);
|
|
8029
8147
|
}
|
|
8030
8148
|
throw error;
|
|
8031
8149
|
}
|
|
@@ -8041,7 +8159,9 @@ function installReferencedFiles(manifest) {
|
|
|
8041
8159
|
async function validatePublishState(root) {
|
|
8042
8160
|
const manifest = await readManifest(root);
|
|
8043
8161
|
const state = await readPublishState(root);
|
|
8044
|
-
if (state.files.length === 0)
|
|
8162
|
+
if (state.files.length === 0) {
|
|
8163
|
+
throw new Error(`No files staged. Run ${recommendCmd("aipm publish add <files...>")}.`);
|
|
8164
|
+
}
|
|
8045
8165
|
const staged = new Set(state.files.map((entry) => entry.path));
|
|
8046
8166
|
if (!staged.has("aipm.manifest.json")) throw new Error("aipm.manifest.json must be staged.");
|
|
8047
8167
|
if (!staged.has(manifest.entry)) throw new Error(`Manifest entry must be staged: ${manifest.entry}`);
|
|
@@ -8054,12 +8174,14 @@ async function validatePublishState(root) {
|
|
|
8054
8174
|
if (await isIgnoredByAipmIgnore(root, entry.path)) {
|
|
8055
8175
|
throw new Error(`Refusing to publish ignored path: ${entry.path}`);
|
|
8056
8176
|
}
|
|
8057
|
-
const abs = (0,
|
|
8058
|
-
const info = await (0,
|
|
8177
|
+
const abs = (0, import_node_path2.join)(root, entry.path);
|
|
8178
|
+
const info = await (0, import_promises.stat)(abs).catch(() => null);
|
|
8059
8179
|
if (!info?.isFile()) throw new Error(`Staged file is missing: ${entry.path}`);
|
|
8060
8180
|
const hashed = await fileHash(abs);
|
|
8061
8181
|
if (hashed.hash !== entry.hash) {
|
|
8062
|
-
throw new Error(
|
|
8182
|
+
throw new Error(
|
|
8183
|
+
`Staged file changed after add: ${entry.path}. Run ${recommendCmd(`aipm publish add ${entry.path}`)}.`
|
|
8184
|
+
);
|
|
8063
8185
|
}
|
|
8064
8186
|
await assertNoObviousSecrets(abs, entry.path);
|
|
8065
8187
|
size += hashed.size;
|
|
@@ -8071,7 +8193,7 @@ async function statusPublishState(root) {
|
|
|
8071
8193
|
const state = await readPublishState(root);
|
|
8072
8194
|
const rows = [];
|
|
8073
8195
|
for (const entry of state.files) {
|
|
8074
|
-
const current = await fileHash((0,
|
|
8196
|
+
const current = await fileHash((0, import_node_path2.join)(root, entry.path)).catch(() => null);
|
|
8075
8197
|
rows.push({ ...entry, changed: !current || current.hash !== entry.hash });
|
|
8076
8198
|
}
|
|
8077
8199
|
return rows;
|
|
@@ -8079,9 +8201,9 @@ async function statusPublishState(root) {
|
|
|
8079
8201
|
async function copyStagedFiles(root, destination) {
|
|
8080
8202
|
const state = await readPublishState(root);
|
|
8081
8203
|
for (const entry of state.files) {
|
|
8082
|
-
const target = (0,
|
|
8083
|
-
await (0,
|
|
8084
|
-
await (0,
|
|
8204
|
+
const target = (0, import_node_path2.join)(destination, entry.path);
|
|
8205
|
+
await (0, import_promises.mkdir)((0, import_node_path2.dirname)(target), { recursive: true });
|
|
8206
|
+
await (0, import_promises.cp)((0, import_node_path2.join)(root, entry.path), target);
|
|
8085
8207
|
}
|
|
8086
8208
|
}
|
|
8087
8209
|
|
|
@@ -8095,21 +8217,21 @@ async function packDirectory(dir) {
|
|
|
8095
8217
|
return Buffer.isBuffer(stdout) ? stdout : Buffer.from(stdout);
|
|
8096
8218
|
}
|
|
8097
8219
|
async function packStagedFiles(root) {
|
|
8098
|
-
const tempDir = await (0,
|
|
8220
|
+
const tempDir = await (0, import_promises2.mkdtemp)((0, import_node_path3.join)((0, import_node_os.tmpdir)(), "aipm-publish-stage-"));
|
|
8099
8221
|
try {
|
|
8100
8222
|
await copyStagedFiles(root, tempDir);
|
|
8101
8223
|
return await packDirectory(tempDir);
|
|
8102
8224
|
} finally {
|
|
8103
|
-
await (0,
|
|
8225
|
+
await (0, import_promises2.rm)(tempDir, { recursive: true, force: true });
|
|
8104
8226
|
}
|
|
8105
8227
|
}
|
|
8106
8228
|
async function unpackTarballToDirectory(tarball) {
|
|
8107
|
-
const { mkdtemp: mkdtemp2, writeFile:
|
|
8108
|
-
const { join:
|
|
8229
|
+
const { mkdtemp: mkdtemp2, writeFile: writeFile8 } = await import("node:fs/promises");
|
|
8230
|
+
const { join: join17 } = await import("node:path");
|
|
8109
8231
|
const { tmpdir: tmpdir2 } = await import("node:os");
|
|
8110
|
-
const tempDir = await mkdtemp2(
|
|
8111
|
-
const tgzPath =
|
|
8112
|
-
await
|
|
8232
|
+
const tempDir = await mkdtemp2(join17(tmpdir2(), "aipm-install-"));
|
|
8233
|
+
const tgzPath = join17(tempDir, "pkg.tgz");
|
|
8234
|
+
await writeFile8(tgzPath, tarball);
|
|
8113
8235
|
await execFileAsync("tar", ["-xzf", tgzPath, "-C", tempDir]);
|
|
8114
8236
|
return tempDir;
|
|
8115
8237
|
}
|
|
@@ -8122,7 +8244,7 @@ function registryFetchError(registry, cause) {
|
|
|
8122
8244
|
const msg = cause instanceof Error ? cause.message : String(cause);
|
|
8123
8245
|
if (msg === "fetch failed" || msg.includes("ECONNREFUSED") || msg.includes("ENOTFOUND") || msg.includes("timed out") || msg.includes("aborted")) {
|
|
8124
8246
|
return new Error(
|
|
8125
|
-
`Cannot reach registry at ${registry}. Check your connection or pass --registry <url
|
|
8247
|
+
`Cannot reach registry at ${registry}. Check your connection or pass ${recommendCmd("--registry <url>")}.`
|
|
8126
8248
|
);
|
|
8127
8249
|
}
|
|
8128
8250
|
return new Error(`Registry request failed (${registry}): ${msg}`);
|
|
@@ -8143,13 +8265,13 @@ function privateInstallHint(name, status, token) {
|
|
|
8143
8265
|
if (status !== 404 || !name.startsWith("@") || token) {
|
|
8144
8266
|
return `Package not found: ${name} (${status})`;
|
|
8145
8267
|
}
|
|
8146
|
-
return `Package not found: ${name} (${status}). If @org/pkg is private, run aipm login, or set AIPM_TOKEN for CI.`;
|
|
8268
|
+
return `Package not found: ${name} (${status}). If @org/pkg is private, run ${recommendCmd("aipm login")}, or set AIPM_TOKEN for CI.`;
|
|
8147
8269
|
}
|
|
8148
8270
|
function publishTokenHint(name, error) {
|
|
8149
8271
|
if (error === "Publish token required") {
|
|
8150
8272
|
return [
|
|
8151
8273
|
`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.`
|
|
8274
|
+
`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
8275
|
].join(" ");
|
|
8154
8276
|
}
|
|
8155
8277
|
if (error === "Invalid publish token") {
|
|
@@ -8170,7 +8292,7 @@ async function assertRegistryReachable(registry) {
|
|
|
8170
8292
|
}
|
|
8171
8293
|
async function publishPackage(registry, name, tarball, token) {
|
|
8172
8294
|
const base = registry.replace(/\/$/, "");
|
|
8173
|
-
const url = `${base}/v1/
|
|
8295
|
+
const url = `${base}/v1/skills/${encodePackageName(name)}/versions`;
|
|
8174
8296
|
const form = new FormData();
|
|
8175
8297
|
form.append("tarball", new Blob([tarball]), "package.tgz");
|
|
8176
8298
|
const headers = token ? { Authorization: `Bearer ${token}` } : void 0;
|
|
@@ -8187,12 +8309,12 @@ async function searchPackages(registry, query, limit = 20, token) {
|
|
|
8187
8309
|
if (query) params.set("q", query);
|
|
8188
8310
|
params.set("limit", String(limit));
|
|
8189
8311
|
if (token) params.set("includePrivate", "true");
|
|
8190
|
-
const res = await registryFetch(`${base}/v1/
|
|
8312
|
+
const res = await registryFetch(`${base}/v1/skills?${params}`, base, {
|
|
8191
8313
|
headers: registryAuthHeaders(token)
|
|
8192
8314
|
});
|
|
8193
8315
|
if (!res.ok) throw new Error(`Search failed: ${res.status}`);
|
|
8194
8316
|
const data = await res.json();
|
|
8195
|
-
return data.packages ?? [];
|
|
8317
|
+
return data.skills ?? data.packages ?? [];
|
|
8196
8318
|
}
|
|
8197
8319
|
async function exchangeCliAuthCode(registry, input2) {
|
|
8198
8320
|
const base = registry.replace(/\/$/, "");
|
|
@@ -8241,7 +8363,7 @@ async function logoutCliAuth(registry, refreshToken) {
|
|
|
8241
8363
|
}
|
|
8242
8364
|
async function fetchPackageMetadata(registry, name, version, token) {
|
|
8243
8365
|
const base = registry.replace(/\/$/, "");
|
|
8244
|
-
const url = `${base}/v1/
|
|
8366
|
+
const url = `${base}/v1/skills/${encodePackageName(name)}/versions/${version}`;
|
|
8245
8367
|
const res = await registryFetch(url, base, { headers: registryAuthHeaders(token) });
|
|
8246
8368
|
if (!res.ok) throw new Error(privateInstallHint(`${name}@${version}`, res.status, token));
|
|
8247
8369
|
const data = await res.json();
|
|
@@ -8249,7 +8371,7 @@ async function fetchPackageMetadata(registry, name, version, token) {
|
|
|
8249
8371
|
}
|
|
8250
8372
|
async function fetchPackageTarball(registry, name, version, token) {
|
|
8251
8373
|
const base = registry.replace(/\/$/, "");
|
|
8252
|
-
const url = `${base}/v1/
|
|
8374
|
+
const url = `${base}/v1/skills/${encodePackageName(name)}/versions/${version}/tarball`;
|
|
8253
8375
|
const res = await registryFetch(url, base, { headers: registryAuthHeaders(token) });
|
|
8254
8376
|
if (!res.ok) throw new Error(privateInstallHint(`${name}@${version}`, res.status, token));
|
|
8255
8377
|
const ab = await res.arrayBuffer();
|
|
@@ -8257,7 +8379,7 @@ async function fetchPackageTarball(registry, name, version, token) {
|
|
|
8257
8379
|
}
|
|
8258
8380
|
async function recordPackageInstall(registry, name, token) {
|
|
8259
8381
|
const base = registry.replace(/\/$/, "");
|
|
8260
|
-
const url = `${base}/v1/
|
|
8382
|
+
const url = `${base}/v1/skills/${encodePackageName(name)}/installs`;
|
|
8261
8383
|
const res = await registryFetch(url, base, {
|
|
8262
8384
|
method: "POST",
|
|
8263
8385
|
headers: registryAuthHeaders(token)
|
|
@@ -8305,16 +8427,16 @@ async function publishPrompt(registry, input2, accessToken, sampleImage) {
|
|
|
8305
8427
|
}
|
|
8306
8428
|
|
|
8307
8429
|
// src/auth-store.ts
|
|
8308
|
-
var
|
|
8430
|
+
var import_promises3 = require("node:fs/promises");
|
|
8309
8431
|
var import_node_os2 = require("node:os");
|
|
8310
|
-
var
|
|
8311
|
-
var AUTH_FILE = (0,
|
|
8432
|
+
var import_node_path4 = require("node:path");
|
|
8433
|
+
var AUTH_FILE = (0, import_node_path4.join)((0, import_node_os2.homedir)(), ".aipm", "auth.json");
|
|
8312
8434
|
function normalizeRegistry(registry) {
|
|
8313
8435
|
return registry.replace(/\/$/, "");
|
|
8314
8436
|
}
|
|
8315
8437
|
async function readAuthStore() {
|
|
8316
8438
|
try {
|
|
8317
|
-
const raw = await (0,
|
|
8439
|
+
const raw = await (0, import_promises3.readFile)(AUTH_FILE, "utf8");
|
|
8318
8440
|
const parsed = JSON.parse(raw);
|
|
8319
8441
|
return { registries: parsed.registries ?? {} };
|
|
8320
8442
|
} catch {
|
|
@@ -8322,9 +8444,9 @@ async function readAuthStore() {
|
|
|
8322
8444
|
}
|
|
8323
8445
|
}
|
|
8324
8446
|
async function writeAuthStore(store) {
|
|
8325
|
-
await (0,
|
|
8326
|
-
await (0,
|
|
8327
|
-
await (0,
|
|
8447
|
+
await (0, import_promises3.mkdir)((0, import_node_path4.dirname)(AUTH_FILE), { recursive: true });
|
|
8448
|
+
await (0, import_promises3.writeFile)(AUTH_FILE, JSON.stringify(store, null, 2) + "\n", "utf8");
|
|
8449
|
+
await (0, import_promises3.chmod)(AUTH_FILE, 384).catch(() => void 0);
|
|
8328
8450
|
}
|
|
8329
8451
|
async function getStoredRegistryAuth(registry) {
|
|
8330
8452
|
const store = await readAuthStore();
|
|
@@ -8353,41 +8475,41 @@ function isAccessTokenFresh(auth, skewMs = 6e4) {
|
|
|
8353
8475
|
}
|
|
8354
8476
|
|
|
8355
8477
|
// src/install-one.ts
|
|
8356
|
-
|
|
8357
|
-
var
|
|
8358
|
-
var
|
|
8478
|
+
init_dist6();
|
|
8479
|
+
var import_promises8 = require("node:fs/promises");
|
|
8480
|
+
var import_node_path11 = require("node:path");
|
|
8359
8481
|
|
|
8360
8482
|
// src/project-files.ts
|
|
8361
|
-
var
|
|
8362
|
-
var
|
|
8483
|
+
var import_promises7 = require("node:fs/promises");
|
|
8484
|
+
var import_node_path10 = require("node:path");
|
|
8363
8485
|
init_dist();
|
|
8364
8486
|
var PACKAGE_JSON = "aipm.package.json";
|
|
8365
8487
|
var LOCKFILE = "aipm-lock.json";
|
|
8366
8488
|
async function readProjectPackageJson(projectRoot) {
|
|
8367
8489
|
try {
|
|
8368
|
-
const raw = await (0,
|
|
8490
|
+
const raw = await (0, import_promises7.readFile)((0, import_node_path10.join)(projectRoot, PACKAGE_JSON), "utf8");
|
|
8369
8491
|
return ProjectPackageJsonSchema.parse(JSON.parse(raw));
|
|
8370
8492
|
} catch {
|
|
8371
8493
|
return null;
|
|
8372
8494
|
}
|
|
8373
8495
|
}
|
|
8374
8496
|
async function writeProjectPackageJson(projectRoot, data) {
|
|
8375
|
-
await (0,
|
|
8376
|
-
(0,
|
|
8497
|
+
await (0, import_promises7.writeFile)(
|
|
8498
|
+
(0, import_node_path10.join)(projectRoot, PACKAGE_JSON),
|
|
8377
8499
|
JSON.stringify(data, null, 2) + "\n",
|
|
8378
8500
|
"utf8"
|
|
8379
8501
|
);
|
|
8380
8502
|
}
|
|
8381
8503
|
async function readLockfile(projectRoot) {
|
|
8382
8504
|
try {
|
|
8383
|
-
const raw = await (0,
|
|
8505
|
+
const raw = await (0, import_promises7.readFile)((0, import_node_path10.join)(projectRoot, LOCKFILE), "utf8");
|
|
8384
8506
|
return LockfileSchema.parse(JSON.parse(raw));
|
|
8385
8507
|
} catch {
|
|
8386
8508
|
return null;
|
|
8387
8509
|
}
|
|
8388
8510
|
}
|
|
8389
8511
|
async function writeLockfile(projectRoot, data) {
|
|
8390
|
-
await (0,
|
|
8512
|
+
await (0, import_promises7.writeFile)((0, import_node_path10.join)(projectRoot, LOCKFILE), JSON.stringify(data, null, 2) + "\n", "utf8");
|
|
8391
8513
|
}
|
|
8392
8514
|
function upsertLockEntry(lock, name, entry) {
|
|
8393
8515
|
return {
|
|
@@ -8429,40 +8551,392 @@ function parseTargetsFlag(value) {
|
|
|
8429
8551
|
return unique;
|
|
8430
8552
|
}
|
|
8431
8553
|
|
|
8432
|
-
// src/
|
|
8433
|
-
var readline = __toESM(require("node:readline/promises"), 1);
|
|
8554
|
+
// src/ui/select.ts
|
|
8434
8555
|
var import_node_process2 = require("node:process");
|
|
8435
|
-
|
|
8436
|
-
|
|
8556
|
+
|
|
8557
|
+
// src/ui/ansi.ts
|
|
8558
|
+
var ansi = {
|
|
8559
|
+
reset: "\x1B[0m",
|
|
8560
|
+
bold: "\x1B[1m",
|
|
8561
|
+
dim: "\x1B[2m",
|
|
8562
|
+
hideCursor: "\x1B[?25l",
|
|
8563
|
+
showCursor: "\x1B[?25h",
|
|
8564
|
+
clearLine: "\x1B[2K",
|
|
8565
|
+
cursorTo(col) {
|
|
8566
|
+
return `\x1B[${Math.max(1, col)}G`;
|
|
8567
|
+
},
|
|
8568
|
+
/** Move cursor up `n` lines (0 = no-op). */
|
|
8569
|
+
cursorUp(n) {
|
|
8570
|
+
return n > 0 ? `\x1B[${n}A` : "";
|
|
8571
|
+
}
|
|
8572
|
+
};
|
|
8573
|
+
|
|
8574
|
+
// src/ui/select.ts
|
|
8575
|
+
var SelectCancelledError = class extends Error {
|
|
8576
|
+
constructor(message = "Selection cancelled") {
|
|
8577
|
+
super(message);
|
|
8578
|
+
this.name = "SelectCancelledError";
|
|
8579
|
+
}
|
|
8580
|
+
};
|
|
8581
|
+
function assertSelectable(options) {
|
|
8582
|
+
if (options.length === 0) {
|
|
8583
|
+
throw new Error("selectPrompt requires at least one option");
|
|
8584
|
+
}
|
|
8585
|
+
}
|
|
8586
|
+
function wrap(open, text, reset) {
|
|
8587
|
+
return open ? `${open}${text}${reset}` : text;
|
|
8588
|
+
}
|
|
8589
|
+
function renderFrame(output2, opts) {
|
|
8590
|
+
const { message, options, index, theme } = opts;
|
|
8591
|
+
const lines = [];
|
|
8592
|
+
lines.push("");
|
|
8593
|
+
lines.push(
|
|
8594
|
+
`${wrap(theme.accent + theme.bold, "?", theme.reset)} ${wrap(theme.bold, message, theme.reset)}`
|
|
8595
|
+
);
|
|
8596
|
+
lines.push("");
|
|
8597
|
+
for (let i = 0; i < options.length; i++) {
|
|
8598
|
+
const option = options[i];
|
|
8599
|
+
const selected = i === index;
|
|
8600
|
+
const pointer = selected ? "\u276F" : " ";
|
|
8601
|
+
if (selected && theme.color) {
|
|
8602
|
+
lines.push(`${theme.highlight} ${pointer} ${option.label} ${theme.reset}`);
|
|
8603
|
+
if (option.hint) {
|
|
8604
|
+
lines.push(`${theme.highlight} ${option.hint} ${theme.reset}`);
|
|
8605
|
+
}
|
|
8606
|
+
} else if (selected) {
|
|
8607
|
+
lines.push(` ${pointer} ${option.label}`);
|
|
8608
|
+
if (option.hint) lines.push(` ${option.hint}`);
|
|
8609
|
+
} else {
|
|
8610
|
+
lines.push(` ${pointer} ${wrap(theme.muted, option.label, theme.reset)}`);
|
|
8611
|
+
if (option.hint) {
|
|
8612
|
+
lines.push(` ${wrap(theme.muted, option.hint, theme.reset)}`);
|
|
8613
|
+
}
|
|
8614
|
+
}
|
|
8615
|
+
lines.push("");
|
|
8616
|
+
}
|
|
8617
|
+
lines.push(wrap(theme.muted, " \u2191\u2193 navigate \xB7 enter select \xB7 esc cancel", theme.reset));
|
|
8618
|
+
lines.push("");
|
|
8619
|
+
if (opts.previous && opts.previous.lines > 0) {
|
|
8620
|
+
output2.write(ansi.cursorUp(opts.previous.lines));
|
|
8621
|
+
}
|
|
8622
|
+
for (const line of lines) {
|
|
8623
|
+
output2.write(`${ansi.clearLine}${ansi.cursorTo(1)}${line}
|
|
8624
|
+
`);
|
|
8625
|
+
}
|
|
8626
|
+
const prev = opts.previous?.lines ?? 0;
|
|
8627
|
+
for (let i = lines.length; i < prev; i++) {
|
|
8628
|
+
output2.write(`${ansi.clearLine}${ansi.cursorTo(1)}
|
|
8629
|
+
`);
|
|
8630
|
+
}
|
|
8631
|
+
if (prev > lines.length) {
|
|
8632
|
+
output2.write(ansi.cursorUp(prev - lines.length));
|
|
8633
|
+
}
|
|
8634
|
+
return { lines: Math.max(lines.length, prev) };
|
|
8635
|
+
}
|
|
8636
|
+
function clearFrame(output2, state) {
|
|
8637
|
+
if (state.lines <= 0) return;
|
|
8638
|
+
output2.write(ansi.cursorUp(state.lines));
|
|
8639
|
+
for (let i = 0; i < state.lines; i++) {
|
|
8640
|
+
output2.write(`${ansi.clearLine}${ansi.cursorTo(1)}
|
|
8641
|
+
`);
|
|
8642
|
+
}
|
|
8643
|
+
output2.write(ansi.cursorUp(state.lines));
|
|
8644
|
+
}
|
|
8645
|
+
function writeDone(output2, message, option, theme) {
|
|
8646
|
+
const prefix = wrap(theme.accent + theme.bold, "\u2714", theme.reset);
|
|
8647
|
+
const msg = wrap(theme.bold, message, theme.reset);
|
|
8648
|
+
const value = wrap(theme.accentStrong, option.label, theme.reset);
|
|
8649
|
+
output2.write("\n");
|
|
8650
|
+
output2.write(
|
|
8651
|
+
`${prefix} ${msg} ${wrap(theme.muted, "\xB7", theme.reset)} ${value}
|
|
8652
|
+
`
|
|
8653
|
+
);
|
|
8654
|
+
output2.write("\n");
|
|
8655
|
+
}
|
|
8656
|
+
function readKey(chunk) {
|
|
8657
|
+
if (chunk === "" || chunk === "\x1B") return "cancel";
|
|
8658
|
+
if (chunk === "\r" || chunk === "\n") return "enter";
|
|
8659
|
+
if (chunk === "\x1B[A" || chunk === "\x1BOA" || chunk === "k") return "up";
|
|
8660
|
+
if (chunk === "\x1B[B" || chunk === "\x1BOB" || chunk === "j") return "down";
|
|
8661
|
+
if (chunk.startsWith("\x1B")) return "ignore";
|
|
8662
|
+
return "ignore";
|
|
8663
|
+
}
|
|
8664
|
+
async function selectPrompt(options) {
|
|
8665
|
+
assertSelectable(options.options);
|
|
8666
|
+
const input2 = options.input ?? import_node_process2.stdin;
|
|
8667
|
+
const output2 = options.output ?? import_node_process2.stdout;
|
|
8668
|
+
const theme = createTheme(output2);
|
|
8669
|
+
if (!input2.isTTY || !output2.isTTY || typeof input2.setRawMode !== "function") {
|
|
8670
|
+
throw new Error(
|
|
8671
|
+
"Interactive selection requires a TTY. Pass an explicit flag (e.g. --target / --ci) or run in a terminal."
|
|
8672
|
+
);
|
|
8673
|
+
}
|
|
8674
|
+
const initial = Math.min(
|
|
8675
|
+
Math.max(options.initialIndex ?? 0, 0),
|
|
8676
|
+
options.options.length - 1
|
|
8677
|
+
);
|
|
8678
|
+
let index = initial;
|
|
8679
|
+
let frame = { lines: 0 };
|
|
8680
|
+
const wasRaw = input2.isRaw;
|
|
8681
|
+
input2.setRawMode(true);
|
|
8682
|
+
input2.resume();
|
|
8683
|
+
output2.write(ansi.hideCursor);
|
|
8684
|
+
const redraw = () => {
|
|
8685
|
+
frame = renderFrame(output2, {
|
|
8686
|
+
message: options.message,
|
|
8687
|
+
options: options.options,
|
|
8688
|
+
index,
|
|
8689
|
+
theme,
|
|
8690
|
+
previous: frame
|
|
8691
|
+
});
|
|
8692
|
+
};
|
|
8693
|
+
redraw();
|
|
8437
8694
|
try {
|
|
8438
|
-
|
|
8439
|
-
const
|
|
8440
|
-
|
|
8695
|
+
return await new Promise((resolve6, reject) => {
|
|
8696
|
+
const onData = (chunk) => {
|
|
8697
|
+
const key = readKey(typeof chunk === "string" ? chunk : chunk.toString("utf8"));
|
|
8698
|
+
if (key === "ignore") return;
|
|
8699
|
+
if (key === "up") {
|
|
8700
|
+
index = (index - 1 + options.options.length) % options.options.length;
|
|
8701
|
+
redraw();
|
|
8702
|
+
return;
|
|
8703
|
+
}
|
|
8704
|
+
if (key === "down") {
|
|
8705
|
+
index = (index + 1) % options.options.length;
|
|
8706
|
+
redraw();
|
|
8707
|
+
return;
|
|
8708
|
+
}
|
|
8709
|
+
if (key === "cancel") {
|
|
8710
|
+
cleanup();
|
|
8711
|
+
clearFrame(output2, frame);
|
|
8712
|
+
reject(new SelectCancelledError());
|
|
8713
|
+
return;
|
|
8714
|
+
}
|
|
8715
|
+
const selected = options.options[index];
|
|
8716
|
+
cleanup();
|
|
8717
|
+
clearFrame(output2, frame);
|
|
8718
|
+
writeDone(output2, options.message, selected, theme);
|
|
8719
|
+
resolve6(selected.value);
|
|
8720
|
+
};
|
|
8721
|
+
const cleanup = () => {
|
|
8722
|
+
input2.off("data", onData);
|
|
8723
|
+
};
|
|
8724
|
+
input2.on("data", onData);
|
|
8725
|
+
});
|
|
8726
|
+
} finally {
|
|
8727
|
+
output2.write(ansi.showCursor);
|
|
8728
|
+
if (typeof input2.setRawMode === "function") {
|
|
8729
|
+
input2.setRawMode(wasRaw);
|
|
8730
|
+
}
|
|
8731
|
+
if (typeof input2.pause === "function") {
|
|
8732
|
+
input2.pause();
|
|
8733
|
+
}
|
|
8734
|
+
}
|
|
8735
|
+
}
|
|
8736
|
+
|
|
8737
|
+
// src/ui/confirm.ts
|
|
8738
|
+
async function confirmPrompt(options) {
|
|
8739
|
+
return selectPrompt({
|
|
8740
|
+
message: options.message,
|
|
8741
|
+
initialIndex: options.initialConfirm ? 0 : 1,
|
|
8742
|
+
input: options.input,
|
|
8743
|
+
output: options.output,
|
|
8744
|
+
options: [
|
|
8745
|
+
{ value: true, label: options.activeLabel ?? "Yes" },
|
|
8746
|
+
{ value: false, label: options.inactiveLabel ?? "No" }
|
|
8747
|
+
]
|
|
8748
|
+
});
|
|
8749
|
+
}
|
|
8750
|
+
|
|
8751
|
+
// src/ui/note.ts
|
|
8752
|
+
function symbolFor(type) {
|
|
8753
|
+
switch (type) {
|
|
8754
|
+
case "success":
|
|
8755
|
+
return "\u2714";
|
|
8756
|
+
case "warn":
|
|
8757
|
+
return "\u26A0";
|
|
8758
|
+
case "error":
|
|
8759
|
+
return "\u2716";
|
|
8760
|
+
default:
|
|
8761
|
+
return "\u2139";
|
|
8762
|
+
}
|
|
8763
|
+
}
|
|
8764
|
+
function colorFor(type, theme) {
|
|
8765
|
+
switch (type) {
|
|
8766
|
+
case "success":
|
|
8767
|
+
return theme.accent;
|
|
8768
|
+
case "warn":
|
|
8769
|
+
return theme.warning;
|
|
8770
|
+
case "error":
|
|
8771
|
+
return theme.danger;
|
|
8772
|
+
default:
|
|
8773
|
+
return theme.accentStrong;
|
|
8774
|
+
}
|
|
8775
|
+
}
|
|
8776
|
+
function printNote(options) {
|
|
8777
|
+
const stream = options.stream ?? process.stderr;
|
|
8778
|
+
const theme = createTheme(stream);
|
|
8779
|
+
const type = options.type ?? "info";
|
|
8780
|
+
const symbol = symbolFor(type);
|
|
8781
|
+
const color = colorFor(type, theme);
|
|
8782
|
+
stream.write("\n");
|
|
8783
|
+
if (options.title) {
|
|
8784
|
+
stream.write(
|
|
8785
|
+
`${color}${theme.bold}${symbol} ${options.title}${theme.reset}
|
|
8786
|
+
`
|
|
8787
|
+
);
|
|
8788
|
+
stream.write(` ${theme.muted}${options.message}${theme.reset}
|
|
8789
|
+
`);
|
|
8790
|
+
} else {
|
|
8791
|
+
stream.write(
|
|
8792
|
+
`${color}${theme.bold}${symbol}${theme.reset} ${options.message}
|
|
8793
|
+
`
|
|
8794
|
+
);
|
|
8795
|
+
}
|
|
8796
|
+
stream.write("\n");
|
|
8797
|
+
}
|
|
8798
|
+
|
|
8799
|
+
// src/ui/spinner.ts
|
|
8800
|
+
var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
8801
|
+
function createSpinner(options) {
|
|
8802
|
+
const stream = options.stream ?? process.stderr;
|
|
8803
|
+
const theme = createTheme(stream);
|
|
8804
|
+
const disabled = options.disabled || !stream.isTTY || !theme.color;
|
|
8805
|
+
let text = options.text;
|
|
8806
|
+
let frame = 0;
|
|
8807
|
+
let timer;
|
|
8808
|
+
let active = false;
|
|
8809
|
+
const clearLine = () => {
|
|
8810
|
+
if (!stream.isTTY) return;
|
|
8811
|
+
stream.write("\r\x1B[2K");
|
|
8812
|
+
};
|
|
8813
|
+
const render = () => {
|
|
8814
|
+
if (disabled || !active) return;
|
|
8815
|
+
const spin = FRAMES[frame % FRAMES.length];
|
|
8816
|
+
stream.write(`\r\x1B[2K${theme.accent}${spin}${theme.reset} ${text}`);
|
|
8817
|
+
frame += 1;
|
|
8818
|
+
};
|
|
8819
|
+
return {
|
|
8820
|
+
start(nextText) {
|
|
8821
|
+
if (nextText) text = nextText;
|
|
8822
|
+
if (disabled) return;
|
|
8823
|
+
if (active) return;
|
|
8824
|
+
active = true;
|
|
8825
|
+
render();
|
|
8826
|
+
timer = setInterval(render, 80);
|
|
8827
|
+
timer.unref?.();
|
|
8828
|
+
},
|
|
8829
|
+
update(nextText) {
|
|
8830
|
+
text = nextText;
|
|
8831
|
+
if (disabled || !active) return;
|
|
8832
|
+
render();
|
|
8833
|
+
},
|
|
8834
|
+
succeed(finalText = text) {
|
|
8835
|
+
stop();
|
|
8836
|
+
if (disabled) return;
|
|
8837
|
+
stream.write(
|
|
8838
|
+
`${theme.accent}${theme.bold}\u2714${theme.reset} ${finalText}
|
|
8839
|
+
`
|
|
8441
8840
|
);
|
|
8442
|
-
|
|
8443
|
-
|
|
8444
|
-
|
|
8841
|
+
},
|
|
8842
|
+
fail(finalText = text) {
|
|
8843
|
+
stop();
|
|
8844
|
+
if (disabled) {
|
|
8845
|
+
stream.write(`${finalText}
|
|
8846
|
+
`);
|
|
8847
|
+
return;
|
|
8445
8848
|
}
|
|
8446
|
-
|
|
8849
|
+
stream.write(`${theme.danger}${theme.bold}\u2716${theme.reset} ${finalText}
|
|
8850
|
+
`);
|
|
8851
|
+
},
|
|
8852
|
+
stop() {
|
|
8853
|
+
stop();
|
|
8854
|
+
}
|
|
8855
|
+
};
|
|
8856
|
+
function stop() {
|
|
8857
|
+
if (timer) {
|
|
8858
|
+
clearInterval(timer);
|
|
8859
|
+
timer = void 0;
|
|
8860
|
+
}
|
|
8861
|
+
if (active) {
|
|
8862
|
+
clearLine();
|
|
8863
|
+
active = false;
|
|
8447
8864
|
}
|
|
8448
|
-
} finally {
|
|
8449
|
-
rl.close();
|
|
8450
8865
|
}
|
|
8451
8866
|
}
|
|
8452
|
-
|
|
8453
|
-
|
|
8867
|
+
|
|
8868
|
+
// src/ui/recover.ts
|
|
8869
|
+
var import_node_process3 = require("node:process");
|
|
8870
|
+
function canPromptInteractively(ci) {
|
|
8871
|
+
if (ci) return false;
|
|
8872
|
+
return Boolean(import_node_process3.stdin.isTTY && import_node_process3.stdout.isTTY);
|
|
8873
|
+
}
|
|
8874
|
+
var RecoveryCancelledError = class extends Error {
|
|
8875
|
+
constructor(message = "Cancelled.") {
|
|
8876
|
+
super(message);
|
|
8877
|
+
this.name = "RecoveryCancelledError";
|
|
8878
|
+
}
|
|
8879
|
+
};
|
|
8880
|
+
async function offerChoices(options) {
|
|
8881
|
+
if (!canPromptInteractively(options.ci)) {
|
|
8882
|
+
throw new Error(options.fallbackError);
|
|
8883
|
+
}
|
|
8884
|
+
if (options.note) {
|
|
8885
|
+
printNote({
|
|
8886
|
+
type: options.note.type ?? "warn",
|
|
8887
|
+
title: options.note.title,
|
|
8888
|
+
message: options.note.message
|
|
8889
|
+
});
|
|
8890
|
+
}
|
|
8454
8891
|
try {
|
|
8455
|
-
|
|
8456
|
-
|
|
8457
|
-
|
|
8458
|
-
|
|
8459
|
-
|
|
8892
|
+
return await selectPrompt({
|
|
8893
|
+
message: options.message,
|
|
8894
|
+
options: options.choices
|
|
8895
|
+
});
|
|
8896
|
+
} catch (error) {
|
|
8897
|
+
if (error instanceof SelectCancelledError) {
|
|
8898
|
+
throw new RecoveryCancelledError();
|
|
8899
|
+
}
|
|
8900
|
+
throw error;
|
|
8460
8901
|
}
|
|
8461
8902
|
}
|
|
8462
8903
|
|
|
8904
|
+
// src/prompt.ts
|
|
8905
|
+
async function promptForTool(tools = ["cursor", "claude", "codex"]) {
|
|
8906
|
+
const options = tools.map((tool) => ({
|
|
8907
|
+
value: tool,
|
|
8908
|
+
label: tool,
|
|
8909
|
+
hint: tool === "cursor" ? ".cursor/" : tool === "claude" ? ".claude/" : ".agents/ (Codex)"
|
|
8910
|
+
}));
|
|
8911
|
+
return selectPrompt({
|
|
8912
|
+
message: "Which AI tool should this skill be installed for?",
|
|
8913
|
+
options
|
|
8914
|
+
});
|
|
8915
|
+
}
|
|
8916
|
+
async function promptForConfirmation(question) {
|
|
8917
|
+
return confirmPrompt({ message: question, initialConfirm: false });
|
|
8918
|
+
}
|
|
8919
|
+
async function promptForNoInitConflict() {
|
|
8920
|
+
return selectPrompt({
|
|
8921
|
+
message: "Project already has aipm.package.json. How do you want to continue?",
|
|
8922
|
+
options: [
|
|
8923
|
+
{
|
|
8924
|
+
value: "no-init",
|
|
8925
|
+
label: "Continue without project tracking",
|
|
8926
|
+
hint: "keep --no-init"
|
|
8927
|
+
},
|
|
8928
|
+
{
|
|
8929
|
+
value: "tracked",
|
|
8930
|
+
label: "Use normal project mode",
|
|
8931
|
+
hint: "drop --no-init for this run"
|
|
8932
|
+
}
|
|
8933
|
+
]
|
|
8934
|
+
});
|
|
8935
|
+
}
|
|
8936
|
+
|
|
8463
8937
|
// src/install-one.ts
|
|
8464
8938
|
function normalizeRelativePath(path2) {
|
|
8465
|
-
return (0,
|
|
8939
|
+
return (0, import_node_path11.normalize)(path2).split(import_node_path11.sep).join("/");
|
|
8466
8940
|
}
|
|
8467
8941
|
function assertSafeRelativePath(path2) {
|
|
8468
8942
|
const rel = normalizeRelativePath(path2.trim());
|
|
@@ -8473,8 +8947,8 @@ function assertSafeRelativePath(path2) {
|
|
|
8473
8947
|
}
|
|
8474
8948
|
function safeJoin(root, relPath) {
|
|
8475
8949
|
const rel = assertSafeRelativePath(relPath);
|
|
8476
|
-
const target = (0,
|
|
8477
|
-
const fromRoot = normalizeRelativePath((0,
|
|
8950
|
+
const target = (0, import_node_path11.resolve)(root, rel);
|
|
8951
|
+
const fromRoot = normalizeRelativePath((0, import_node_path11.relative)(root, target));
|
|
8478
8952
|
if (fromRoot === ".." || fromRoot.startsWith("../") || fromRoot.startsWith("/")) {
|
|
8479
8953
|
throw new Error(`Unsafe install path: ${relPath}`);
|
|
8480
8954
|
}
|
|
@@ -8485,33 +8959,98 @@ function packageHelperSlug(name) {
|
|
|
8485
8959
|
return `${scope}__${pkg}`;
|
|
8486
8960
|
}
|
|
8487
8961
|
function helperRootFor(configRoot, packageName, version) {
|
|
8488
|
-
const base = (0,
|
|
8489
|
-
return (0,
|
|
8962
|
+
const base = (0, import_node_path11.basename)(configRoot) === ".aipm" ? (0, import_node_path11.join)(configRoot, "helpers") : (0, import_node_path11.join)(configRoot, ".aipm", "helpers");
|
|
8963
|
+
return (0, import_node_path11.join)(base, packageHelperSlug(packageName), version);
|
|
8490
8964
|
}
|
|
8491
8965
|
async function assertSourceFile(packageRoot, relPath) {
|
|
8492
8966
|
const source = safeJoin(packageRoot, relPath);
|
|
8493
|
-
const info = await (0,
|
|
8967
|
+
const info = await (0, import_promises8.stat)(source).catch(() => null);
|
|
8494
8968
|
if (!info?.isFile()) throw new Error(`Package install file not found: ${relPath}`);
|
|
8495
8969
|
return source;
|
|
8496
8970
|
}
|
|
8971
|
+
function installReferencedFiles2(install) {
|
|
8972
|
+
return [
|
|
8973
|
+
...(install?.mainFiles ?? []).map((file) => file.from),
|
|
8974
|
+
...(install?.helperFiles ?? []).map((file) => file.from)
|
|
8975
|
+
];
|
|
8976
|
+
}
|
|
8977
|
+
function isRootLicenseOrNotice(path2) {
|
|
8978
|
+
return !path2.includes("/") && /^(?:licen[cs]e|notice|copying)(?:$|[._-])/i.test(path2);
|
|
8979
|
+
}
|
|
8980
|
+
async function listRegularPackageFiles(root, current = root) {
|
|
8981
|
+
const files = [];
|
|
8982
|
+
for (const entry of await (0, import_promises8.readdir)(current, { withFileTypes: true })) {
|
|
8983
|
+
const absolute = (0, import_node_path11.join)(current, entry.name);
|
|
8984
|
+
if (entry.isDirectory()) {
|
|
8985
|
+
files.push(...await listRegularPackageFiles(root, absolute));
|
|
8986
|
+
} else if (entry.isFile()) {
|
|
8987
|
+
files.push(normalizeRelativePath((0, import_node_path11.relative)(root, absolute)));
|
|
8988
|
+
}
|
|
8989
|
+
}
|
|
8990
|
+
return files;
|
|
8991
|
+
}
|
|
8992
|
+
async function collectSkillSupportingFiles(packageRoot, manifest) {
|
|
8993
|
+
const entry = assertSafeRelativePath(manifest.entry);
|
|
8994
|
+
const entryDirectory = (0, import_node_path11.dirname)(entry);
|
|
8995
|
+
const excluded = /* @__PURE__ */ new Set([
|
|
8996
|
+
entry,
|
|
8997
|
+
"aipm.manifest.json",
|
|
8998
|
+
"pkg.tgz",
|
|
8999
|
+
...installReferencedFiles2(manifest.install).map(assertSafeRelativePath)
|
|
9000
|
+
]);
|
|
9001
|
+
const supportingFiles = [];
|
|
9002
|
+
for (const sourcePath of await listRegularPackageFiles(packageRoot)) {
|
|
9003
|
+
if (excluded.has(sourcePath)) continue;
|
|
9004
|
+
const fromEntryDirectory = normalizeRelativePath((0, import_node_path11.relative)(entryDirectory, sourcePath));
|
|
9005
|
+
const isEntrySibling = fromEntryDirectory !== ".." && !fromEntryDirectory.startsWith("../") && !fromEntryDirectory.startsWith("/");
|
|
9006
|
+
if (!isEntrySibling && !isRootLicenseOrNotice(sourcePath)) continue;
|
|
9007
|
+
const destinationPath = isEntrySibling ? fromEntryDirectory : (0, import_node_path11.basename)(sourcePath);
|
|
9008
|
+
if (destinationPath.toLowerCase() === "skill.md") {
|
|
9009
|
+
throw new Error(`Package supporting file conflicts with installed SKILL.md: ${sourcePath}`);
|
|
9010
|
+
}
|
|
9011
|
+
const source = safeJoin(packageRoot, sourcePath);
|
|
9012
|
+
supportingFiles.push({
|
|
9013
|
+
path: destinationPath,
|
|
9014
|
+
content: await (0, import_promises8.readFile)(source),
|
|
9015
|
+
mode: (await (0, import_promises8.stat)(source)).mode
|
|
9016
|
+
});
|
|
9017
|
+
}
|
|
9018
|
+
return supportingFiles.sort((a, b) => a.path.localeCompare(b.path));
|
|
9019
|
+
}
|
|
8497
9020
|
async function copyMainFile(input2) {
|
|
8498
9021
|
const source = await assertSourceFile(input2.packageRoot, input2.file.from);
|
|
8499
9022
|
const target = safeJoin(input2.installRoot, input2.file.to);
|
|
8500
|
-
|
|
8501
|
-
const exists = await (0,
|
|
9023
|
+
let overwrite = input2.file.overwrite ?? "fail";
|
|
9024
|
+
const exists = await (0, import_promises8.stat)(target).catch(() => null);
|
|
8502
9025
|
if (exists) {
|
|
8503
9026
|
if (overwrite === "skip") return null;
|
|
8504
|
-
if (overwrite === "fail")
|
|
9027
|
+
if (overwrite === "fail") {
|
|
9028
|
+
overwrite = await offerChoices({
|
|
9029
|
+
ci: input2.ci,
|
|
9030
|
+
note: {
|
|
9031
|
+
type: "warn",
|
|
9032
|
+
title: "File already exists",
|
|
9033
|
+
message: target
|
|
9034
|
+
},
|
|
9035
|
+
message: "How do you want to continue?",
|
|
9036
|
+
choices: [
|
|
9037
|
+
{ value: "replace", label: "Overwrite the existing file" },
|
|
9038
|
+
{ value: "skip", label: "Skip this file" }
|
|
9039
|
+
],
|
|
9040
|
+
fallbackError: `Install target already exists: ${target}`
|
|
9041
|
+
});
|
|
9042
|
+
if (overwrite === "skip") return null;
|
|
9043
|
+
}
|
|
8505
9044
|
}
|
|
8506
|
-
await (0,
|
|
8507
|
-
await (0,
|
|
9045
|
+
await (0, import_promises8.mkdir)((0, import_node_path11.dirname)(target), { recursive: true });
|
|
9046
|
+
await (0, import_promises8.cp)(source, target, { force: overwrite === "replace" });
|
|
8508
9047
|
return target;
|
|
8509
9048
|
}
|
|
8510
9049
|
async function copyHelperFile(input2) {
|
|
8511
9050
|
const source = await assertSourceFile(input2.packageRoot, input2.file.from);
|
|
8512
|
-
const target = safeJoin(input2.helperRoot, input2.file.to ?? (0,
|
|
8513
|
-
await (0,
|
|
8514
|
-
await (0,
|
|
9051
|
+
const target = safeJoin(input2.helperRoot, input2.file.to ?? (0, import_node_path11.basename)(input2.file.from));
|
|
9052
|
+
await (0, import_promises8.mkdir)((0, import_node_path11.dirname)(target), { recursive: true });
|
|
9053
|
+
await (0, import_promises8.cp)(source, target, { force: true });
|
|
8515
9054
|
return target;
|
|
8516
9055
|
}
|
|
8517
9056
|
async function installPackageAssets(options) {
|
|
@@ -8522,13 +9061,14 @@ async function installPackageAssets(options) {
|
|
|
8522
9061
|
const copied = await copyMainFile({
|
|
8523
9062
|
packageRoot: options.packageRoot,
|
|
8524
9063
|
installRoot: options.installRoot,
|
|
8525
|
-
file
|
|
9064
|
+
file,
|
|
9065
|
+
ci: options.ci
|
|
8526
9066
|
});
|
|
8527
9067
|
if (copied) assets.main.push(copied);
|
|
8528
9068
|
}
|
|
8529
9069
|
const helperRoot = helperRootFor(options.configRoot, options.packageName, options.version);
|
|
8530
9070
|
if ((install.helperFiles ?? []).length > 0) {
|
|
8531
|
-
await (0,
|
|
9071
|
+
await (0, import_promises8.rm)(helperRoot, { recursive: true, force: true });
|
|
8532
9072
|
}
|
|
8533
9073
|
for (const file of install.helperFiles ?? []) {
|
|
8534
9074
|
assets.helper.push(await copyHelperFile({ packageRoot: options.packageRoot, helperRoot, file }));
|
|
@@ -8559,121 +9099,163 @@ function printPostInstallNotice(name, postInstall) {
|
|
|
8559
9099
|
async function installOnePackage(options) {
|
|
8560
9100
|
const configRoot = options.configRoot;
|
|
8561
9101
|
const installRoot = options.installRoot ?? configRoot;
|
|
8562
|
-
|
|
8563
|
-
|
|
8564
|
-
options.
|
|
8565
|
-
options.name,
|
|
8566
|
-
options.version,
|
|
8567
|
-
options.token
|
|
8568
|
-
);
|
|
8569
|
-
if (deprecated) {
|
|
8570
|
-
console.warn(
|
|
8571
|
-
`Warning: ${options.name} is deprecated${deprecated.message ? `: ${deprecated.message}` : ""}.`
|
|
8572
|
-
);
|
|
8573
|
-
}
|
|
8574
|
-
let explicitTarget = options.explicitTarget;
|
|
8575
|
-
let preferredTools = options.project.preferredTools;
|
|
8576
|
-
const { resolveInstallTools: resolveInstallTools2 } = await Promise.resolve().then(() => (init_dist5(), dist_exports));
|
|
8577
|
-
let tools = await resolveInstallTools2({
|
|
8578
|
-
projectRoot: installRoot,
|
|
8579
|
-
manifest,
|
|
8580
|
-
preferredTools,
|
|
8581
|
-
explicitTarget
|
|
9102
|
+
const spinner = createSpinner({
|
|
9103
|
+
text: `Fetching ${options.name}@${options.version}`,
|
|
9104
|
+
disabled: options.ci
|
|
8582
9105
|
});
|
|
8583
|
-
|
|
8584
|
-
if (options.ci) {
|
|
8585
|
-
throw new Error(
|
|
8586
|
-
"No tool detected. Use --target cursor|claude|codex|* in CI mode."
|
|
8587
|
-
);
|
|
8588
|
-
}
|
|
8589
|
-
const choice = await promptForTool();
|
|
8590
|
-
explicitTarget = choice;
|
|
8591
|
-
preferredTools = [choice];
|
|
8592
|
-
tools = await resolveInstallTools2({
|
|
8593
|
-
projectRoot: installRoot,
|
|
8594
|
-
manifest,
|
|
8595
|
-
preferredTools,
|
|
8596
|
-
explicitTarget
|
|
8597
|
-
});
|
|
8598
|
-
}
|
|
8599
|
-
const tarball = await fetchPackageTarball(
|
|
8600
|
-
options.registry,
|
|
8601
|
-
options.name,
|
|
8602
|
-
options.version,
|
|
8603
|
-
options.token
|
|
8604
|
-
);
|
|
8605
|
-
const packageRoot = await unpackTarballToDirectory(tarball);
|
|
9106
|
+
spinner.start();
|
|
8606
9107
|
try {
|
|
8607
|
-
|
|
8608
|
-
const
|
|
9108
|
+
await assertRegistryReachable(options.registry);
|
|
9109
|
+
const { manifest, integrity: remoteIntegrity, deprecated } = await fetchPackageMetadata(
|
|
9110
|
+
options.registry,
|
|
9111
|
+
options.name,
|
|
9112
|
+
options.version,
|
|
9113
|
+
options.token
|
|
9114
|
+
);
|
|
9115
|
+
if (deprecated) {
|
|
9116
|
+
spinner.stop();
|
|
9117
|
+
printNote({
|
|
9118
|
+
type: "warn",
|
|
9119
|
+
title: "Deprecated package",
|
|
9120
|
+
message: `${options.name}${deprecated.message ? `: ${deprecated.message}` : ""}`
|
|
9121
|
+
});
|
|
9122
|
+
spinner.start(`Installing ${options.name}@${options.version}`);
|
|
9123
|
+
}
|
|
9124
|
+
let explicitTarget = options.explicitTarget;
|
|
9125
|
+
let preferredTools = options.project.preferredTools;
|
|
9126
|
+
const { resolveInstallTools: resolveInstallTools2 } = await Promise.resolve().then(() => (init_dist6(), dist_exports));
|
|
9127
|
+
let tools = await resolveInstallTools2({
|
|
8609
9128
|
projectRoot: installRoot,
|
|
8610
9129
|
manifest,
|
|
8611
|
-
skillMarkdown,
|
|
8612
9130
|
preferredTools,
|
|
8613
9131
|
explicitTarget
|
|
8614
9132
|
});
|
|
8615
|
-
|
|
8616
|
-
|
|
8617
|
-
|
|
8618
|
-
|
|
8619
|
-
|
|
8620
|
-
|
|
8621
|
-
|
|
8622
|
-
|
|
8623
|
-
|
|
8624
|
-
|
|
8625
|
-
|
|
8626
|
-
|
|
8627
|
-
|
|
8628
|
-
|
|
8629
|
-
|
|
8630
|
-
|
|
8631
|
-
|
|
8632
|
-
|
|
8633
|
-
|
|
8634
|
-
|
|
8635
|
-
|
|
8636
|
-
|
|
8637
|
-
|
|
8638
|
-
|
|
8639
|
-
|
|
8640
|
-
|
|
8641
|
-
|
|
8642
|
-
|
|
8643
|
-
})
|
|
9133
|
+
if (tools.length === 0) {
|
|
9134
|
+
spinner.stop();
|
|
9135
|
+
if (options.ci) {
|
|
9136
|
+
throw new Error(
|
|
9137
|
+
"No tool detected. Use --target cursor|claude|codex|* in CI mode."
|
|
9138
|
+
);
|
|
9139
|
+
}
|
|
9140
|
+
printNote({
|
|
9141
|
+
type: "info",
|
|
9142
|
+
title: "No AI tool folder found",
|
|
9143
|
+
message: "Could not find .cursor, .claude, or .codex in this project."
|
|
9144
|
+
});
|
|
9145
|
+
const choice = await promptForTool();
|
|
9146
|
+
explicitTarget = choice;
|
|
9147
|
+
preferredTools = [choice];
|
|
9148
|
+
tools = await resolveInstallTools2({
|
|
9149
|
+
projectRoot: installRoot,
|
|
9150
|
+
manifest,
|
|
9151
|
+
preferredTools,
|
|
9152
|
+
explicitTarget
|
|
9153
|
+
});
|
|
9154
|
+
spinner.start(`Installing ${options.name}@${options.version}`);
|
|
9155
|
+
} else if (tools.length > 1 && !explicitTarget && !options.ci) {
|
|
9156
|
+
spinner.stop();
|
|
9157
|
+
printNote({
|
|
9158
|
+
type: "info",
|
|
9159
|
+
title: "Multiple AI tools found",
|
|
9160
|
+
message: `Detected: ${tools.join(", ")}`
|
|
9161
|
+
});
|
|
9162
|
+
const choice = await promptForTool(tools);
|
|
9163
|
+
explicitTarget = choice;
|
|
9164
|
+
preferredTools = [choice];
|
|
9165
|
+
tools = [choice];
|
|
9166
|
+
spinner.start(`Installing ${options.name}@${options.version}`);
|
|
9167
|
+
}
|
|
9168
|
+
spinner.update(`Downloading ${options.name}@${options.version}`);
|
|
9169
|
+
const tarball = await fetchPackageTarball(
|
|
9170
|
+
options.registry,
|
|
9171
|
+
options.name,
|
|
9172
|
+
options.version,
|
|
9173
|
+
options.token
|
|
8644
9174
|
);
|
|
8645
|
-
|
|
8646
|
-
|
|
8647
|
-
|
|
8648
|
-
|
|
9175
|
+
const packageRoot = await unpackTarballToDirectory(tarball);
|
|
9176
|
+
try {
|
|
9177
|
+
spinner.update(`Installing ${options.name}@${options.version}`);
|
|
9178
|
+
const skillMarkdown = await (0, import_promises8.readFile)(safeJoin(packageRoot, manifest.entry), "utf8");
|
|
9179
|
+
const supportingFiles = await collectSkillSupportingFiles(packageRoot, manifest);
|
|
9180
|
+
const result = await installSkillPackage({
|
|
9181
|
+
projectRoot: installRoot,
|
|
9182
|
+
manifest,
|
|
9183
|
+
skillMarkdown,
|
|
9184
|
+
supportingFiles,
|
|
9185
|
+
preferredTools,
|
|
9186
|
+
explicitTarget
|
|
9187
|
+
});
|
|
9188
|
+
const { assets, postInstall } = await installPackageAssets({
|
|
9189
|
+
configRoot,
|
|
9190
|
+
installRoot,
|
|
9191
|
+
packageRoot,
|
|
9192
|
+
packageName: options.name,
|
|
9193
|
+
version: options.version,
|
|
9194
|
+
install: manifest.install,
|
|
9195
|
+
ci: options.ci
|
|
9196
|
+
});
|
|
9197
|
+
const track = options.track !== false;
|
|
9198
|
+
if (track) {
|
|
9199
|
+
const lock = await readLockfile(configRoot) ?? {
|
|
9200
|
+
schemaVersion: "0.1",
|
|
9201
|
+
packages: {},
|
|
9202
|
+
prompts: {}
|
|
9203
|
+
};
|
|
9204
|
+
const installed = {};
|
|
9205
|
+
for (const tool of result.resolvedTools) {
|
|
9206
|
+
const paths = result.installed[tool];
|
|
9207
|
+
if (paths) installed[tool] = paths;
|
|
9208
|
+
}
|
|
9209
|
+
await writeLockfile(
|
|
9210
|
+
configRoot,
|
|
9211
|
+
upsertLockEntry(lock, options.name, {
|
|
9212
|
+
version: options.version,
|
|
9213
|
+
integrity: remoteIntegrity,
|
|
9214
|
+
registry: options.registry,
|
|
9215
|
+
resolvedTools: result.resolvedTools,
|
|
9216
|
+
installed,
|
|
9217
|
+
...assets.main.length || assets.helper.length ? { installedAssets: assets } : {},
|
|
9218
|
+
...postInstall ? { postInstall } : {}
|
|
9219
|
+
})
|
|
9220
|
+
);
|
|
9221
|
+
}
|
|
9222
|
+
spinner.stop();
|
|
9223
|
+
console.log(`Installed ${options.name}@${options.version} \u2192 ${result.resolvedTools.join(", ")}`);
|
|
9224
|
+
printPostInstallNotice(options.name, postInstall);
|
|
9225
|
+
} finally {
|
|
9226
|
+
await (0, import_promises8.rm)(packageRoot, { recursive: true, force: true });
|
|
9227
|
+
}
|
|
9228
|
+
} catch (error) {
|
|
9229
|
+
spinner.fail(`Failed to install ${options.name}@${options.version}`);
|
|
9230
|
+
throw error;
|
|
8649
9231
|
}
|
|
8650
9232
|
}
|
|
8651
9233
|
|
|
8652
9234
|
// src/doctor.ts
|
|
8653
9235
|
var import_node_child_process3 = require("node:child_process");
|
|
8654
|
-
var
|
|
8655
|
-
var
|
|
9236
|
+
var import_promises9 = require("node:fs/promises");
|
|
9237
|
+
var import_node_path13 = require("node:path");
|
|
8656
9238
|
var import_node_util4 = require("node:util");
|
|
8657
9239
|
|
|
8658
9240
|
// src/project-root.ts
|
|
8659
9241
|
var import_node_os3 = require("node:os");
|
|
8660
|
-
var
|
|
8661
|
-
var
|
|
9242
|
+
var import_node_path12 = require("node:path");
|
|
9243
|
+
var import_node_process4 = require("node:process");
|
|
8662
9244
|
function globalConfigDir(env2 = process.env) {
|
|
8663
9245
|
const fromEnv = env2.AIPM_HOME?.trim();
|
|
8664
|
-
return fromEnv ? fromEnv.replace(/\/$/, "") : (0,
|
|
9246
|
+
return fromEnv ? fromEnv.replace(/\/$/, "") : (0, import_node_path12.join)((0, import_node_os3.homedir)(), ".aipm");
|
|
8665
9247
|
}
|
|
8666
9248
|
function resolveConfigRoot(options = {}) {
|
|
8667
|
-
return options.global ? globalConfigDir() : (0,
|
|
9249
|
+
return options.global ? globalConfigDir() : (0, import_node_process4.cwd)();
|
|
8668
9250
|
}
|
|
8669
9251
|
function resolveInstallRoot(options = {}) {
|
|
8670
|
-
return options.global ? (0, import_node_os3.homedir)() : (0,
|
|
9252
|
+
return options.global ? (0, import_node_os3.homedir)() : (0, import_node_process4.cwd)();
|
|
8671
9253
|
}
|
|
8672
9254
|
function scopeLabel(options) {
|
|
8673
9255
|
return options.global ? "global" : "project";
|
|
8674
9256
|
}
|
|
8675
9257
|
function initRequiredMessage(options) {
|
|
8676
|
-
return options.global ?
|
|
9258
|
+
return options.global ? `Run ${recommendCmd("aipm init -g")} first` : `Run ${recommendCmd("aipm init")} first`;
|
|
8677
9259
|
}
|
|
8678
9260
|
|
|
8679
9261
|
// src/doctor.ts
|
|
@@ -8692,11 +9274,11 @@ async function commandOutput(command, args) {
|
|
|
8692
9274
|
}
|
|
8693
9275
|
function npmGlobalBin(prefix) {
|
|
8694
9276
|
if (!prefix) return null;
|
|
8695
|
-
return process.platform === "win32" ? prefix : `${prefix}${
|
|
9277
|
+
return process.platform === "win32" ? prefix : `${prefix}${import_node_path13.sep}bin`;
|
|
8696
9278
|
}
|
|
8697
9279
|
function pathIncludes(pathDir) {
|
|
8698
9280
|
if (!pathDir) return false;
|
|
8699
|
-
return (process.env.PATH ?? "").split(
|
|
9281
|
+
return (process.env.PATH ?? "").split(import_node_path13.delimiter).includes(pathDir);
|
|
8700
9282
|
}
|
|
8701
9283
|
function shellPathHint(binDir) {
|
|
8702
9284
|
if (!binDir) return "Run npm prefix -g and add its bin folder to PATH.";
|
|
@@ -8749,11 +9331,11 @@ async function runDoctor(options) {
|
|
|
8749
9331
|
if (!options.publish) {
|
|
8750
9332
|
const configLabel = options.global ? "Global config" : "Project config";
|
|
8751
9333
|
try {
|
|
8752
|
-
await (0,
|
|
9334
|
+
await (0, import_promises9.access)((0, import_node_path13.join)(configRoot, "aipm.package.json"));
|
|
8753
9335
|
checks.push({
|
|
8754
9336
|
name: configLabel,
|
|
8755
9337
|
ok: true,
|
|
8756
|
-
detail: `${(0,
|
|
9338
|
+
detail: `${(0, import_node_path13.join)(configRoot, "aipm.package.json")} exists (${scopeLabel(scope)}).`
|
|
8757
9339
|
});
|
|
8758
9340
|
} catch {
|
|
8759
9341
|
checks.push({
|
|
@@ -8783,7 +9365,7 @@ async function runDoctor(options) {
|
|
|
8783
9365
|
checks.push({
|
|
8784
9366
|
name: "Publish stage",
|
|
8785
9367
|
ok: rows.length > 0 && changed.length === 0,
|
|
8786
|
-
detail: rows.length === 0 ?
|
|
9368
|
+
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
9369
|
});
|
|
8788
9370
|
} catch (error) {
|
|
8789
9371
|
checks.push({
|
|
@@ -8823,8 +9405,88 @@ async function runDoctor(options) {
|
|
|
8823
9405
|
|
|
8824
9406
|
// src/prompt-install.ts
|
|
8825
9407
|
var import_node_crypto2 = require("node:crypto");
|
|
8826
|
-
var
|
|
8827
|
-
var
|
|
9408
|
+
var import_promises10 = require("node:fs/promises");
|
|
9409
|
+
var import_node_path14 = require("node:path");
|
|
9410
|
+
|
|
9411
|
+
// src/cli-recover.ts
|
|
9412
|
+
async function resolvePromptRequiresTracking(options) {
|
|
9413
|
+
const choice = await offerChoices({
|
|
9414
|
+
ci: options.ci,
|
|
9415
|
+
note: {
|
|
9416
|
+
type: "warn",
|
|
9417
|
+
title: "Prompt URLs need project tracking",
|
|
9418
|
+
message: "--no-init cannot manage prompts without aipm.package.json."
|
|
9419
|
+
},
|
|
9420
|
+
message: "How do you want to continue?",
|
|
9421
|
+
choices: [
|
|
9422
|
+
{
|
|
9423
|
+
value: "tracked",
|
|
9424
|
+
label: "Continue with project tracking",
|
|
9425
|
+
hint: "drop --no-init for this run"
|
|
9426
|
+
}
|
|
9427
|
+
],
|
|
9428
|
+
fallbackError: "Prompt URLs require project tracking. Drop --no-init, or run aipm init and continue without --no-init."
|
|
9429
|
+
});
|
|
9430
|
+
return choice;
|
|
9431
|
+
}
|
|
9432
|
+
async function resolvePrivateInstallFailure(options) {
|
|
9433
|
+
const looksPrivate = /private|login|unauthorized|forbidden|401|403|aipm login/i.test(options.errorMessage);
|
|
9434
|
+
if (!looksPrivate) {
|
|
9435
|
+
throw new Error(options.errorMessage);
|
|
9436
|
+
}
|
|
9437
|
+
const choice = await offerChoices({
|
|
9438
|
+
ci: options.ci,
|
|
9439
|
+
note: {
|
|
9440
|
+
type: "error",
|
|
9441
|
+
title: "Package install needs authentication",
|
|
9442
|
+
message: options.errorMessage
|
|
9443
|
+
},
|
|
9444
|
+
message: "What do you want to do?",
|
|
9445
|
+
choices: [
|
|
9446
|
+
{
|
|
9447
|
+
value: "login-retry",
|
|
9448
|
+
label: "Log in and retry",
|
|
9449
|
+
hint: "aipm login"
|
|
9450
|
+
}
|
|
9451
|
+
],
|
|
9452
|
+
fallbackError: options.errorMessage
|
|
9453
|
+
});
|
|
9454
|
+
return choice;
|
|
9455
|
+
}
|
|
9456
|
+
async function resolveLoginRequired(options) {
|
|
9457
|
+
return offerChoices({
|
|
9458
|
+
ci: options.ci,
|
|
9459
|
+
note: {
|
|
9460
|
+
type: "warn",
|
|
9461
|
+
title: "Login required",
|
|
9462
|
+
message: options.purpose
|
|
9463
|
+
},
|
|
9464
|
+
message: "What do you want to do?",
|
|
9465
|
+
choices: [{ value: "login", label: "Log in now", hint: "opens browser" }],
|
|
9466
|
+
fallbackError: `Run ${recommendCmd("aipm login")} first.`
|
|
9467
|
+
});
|
|
9468
|
+
}
|
|
9469
|
+
async function resolvePromptAliasConflict(options) {
|
|
9470
|
+
return offerChoices({
|
|
9471
|
+
ci: options.ci,
|
|
9472
|
+
note: {
|
|
9473
|
+
type: "warn",
|
|
9474
|
+
title: "Prompt alias already in use",
|
|
9475
|
+
message: `"${options.alias}" currently tracks ${options.currentUrl}`
|
|
9476
|
+
},
|
|
9477
|
+
message: "Replace it with the new prompt URL?",
|
|
9478
|
+
choices: [
|
|
9479
|
+
{
|
|
9480
|
+
value: "replace",
|
|
9481
|
+
label: "Replace and continue",
|
|
9482
|
+
hint: options.nextUrl
|
|
9483
|
+
}
|
|
9484
|
+
],
|
|
9485
|
+
fallbackError: `Prompt alias "${options.alias}" already tracks ${options.currentUrl}. Remove it first or use a unique slug.`
|
|
9486
|
+
});
|
|
9487
|
+
}
|
|
9488
|
+
|
|
9489
|
+
// src/prompt-install.ts
|
|
8828
9490
|
var PROMPT_SITE_ORIGIN = "https://www.aipm-registry.com";
|
|
8829
9491
|
function parsePromptUrl(value) {
|
|
8830
9492
|
let url;
|
|
@@ -8849,10 +9511,10 @@ function parsePromptUrl(value) {
|
|
|
8849
9511
|
};
|
|
8850
9512
|
}
|
|
8851
9513
|
function promptDirectory(configRoot) {
|
|
8852
|
-
return (0,
|
|
9514
|
+
return (0, import_node_path14.basename)(configRoot) === ".aipm" ? (0, import_node_path14.join)(configRoot, "prompts") : (0, import_node_path14.join)(configRoot, ".aipm", "prompts");
|
|
8853
9515
|
}
|
|
8854
9516
|
function promptSnapshotPath(configRoot, reference) {
|
|
8855
|
-
return (0,
|
|
9517
|
+
return (0, import_node_path14.join)(promptDirectory(configRoot), `${reference.publisher}--${reference.slug}.md`);
|
|
8856
9518
|
}
|
|
8857
9519
|
function canonicalPromptContent(prompt) {
|
|
8858
9520
|
return JSON.stringify({
|
|
@@ -8911,7 +9573,7 @@ function emptyLock() {
|
|
|
8911
9573
|
return { schemaVersion: "0.1", packages: {}, prompts: {} };
|
|
8912
9574
|
}
|
|
8913
9575
|
function installedRelativePath(configRoot, absolutePath) {
|
|
8914
|
-
return (0,
|
|
9576
|
+
return (0, import_node_path14.relative)(configRoot, absolutePath).split("\\").join("/");
|
|
8915
9577
|
}
|
|
8916
9578
|
async function installTrackedPrompt(input2) {
|
|
8917
9579
|
const prompt = await fetchPromptDetail(
|
|
@@ -8929,18 +9591,21 @@ async function installTrackedPrompt(input2) {
|
|
|
8929
9591
|
const contentHash = promptContentHash(prompt);
|
|
8930
9592
|
const lock = await readLockfile(input2.configRoot) ?? emptyLock();
|
|
8931
9593
|
const existing = lock.prompts[input2.reference.alias];
|
|
8932
|
-
const fileExists = Boolean(await (0,
|
|
9594
|
+
const fileExists = Boolean(await (0, import_promises10.stat)(outputPath).catch(() => null));
|
|
8933
9595
|
const changed = !existing || existing.contentHash !== contentHash || !fileExists;
|
|
8934
9596
|
if (!input2.updateOnly || changed) {
|
|
8935
|
-
await (0,
|
|
8936
|
-
await (0,
|
|
9597
|
+
await (0, import_promises10.mkdir)((0, import_node_path14.dirname)(outputPath), { recursive: true });
|
|
9598
|
+
await (0, import_promises10.writeFile)(outputPath, renderPromptMarkdown(prompt, canonicalReference.url), "utf8");
|
|
8937
9599
|
}
|
|
8938
9600
|
if (input2.track) {
|
|
8939
9601
|
const currentUrl = input2.project.prompts[input2.reference.alias];
|
|
8940
9602
|
if (currentUrl && currentUrl !== canonicalReference.url) {
|
|
8941
|
-
|
|
8942
|
-
|
|
8943
|
-
|
|
9603
|
+
await resolvePromptAliasConflict({
|
|
9604
|
+
ci: input2.ci,
|
|
9605
|
+
alias: input2.reference.alias,
|
|
9606
|
+
currentUrl,
|
|
9607
|
+
nextUrl: canonicalReference.url
|
|
9608
|
+
});
|
|
8944
9609
|
}
|
|
8945
9610
|
await writeProjectPackageJson(input2.configRoot, {
|
|
8946
9611
|
...input2.project,
|
|
@@ -8975,12 +9640,12 @@ function resolveTrackedPrompt(project, value) {
|
|
|
8975
9640
|
return parsed ? { ...parsed, alias: value } : null;
|
|
8976
9641
|
}
|
|
8977
9642
|
async function readInstalledPrompt(configRoot, entry) {
|
|
8978
|
-
return (0,
|
|
9643
|
+
return (0, import_promises10.readFile)((0, import_node_path14.join)(configRoot, entry.installedPath), "utf8");
|
|
8979
9644
|
}
|
|
8980
9645
|
async function removeTrackedPrompt(input2) {
|
|
8981
9646
|
const lock = await readLockfile(input2.configRoot);
|
|
8982
9647
|
const entry = lock?.prompts[input2.reference.alias];
|
|
8983
|
-
if (entry) await (0,
|
|
9648
|
+
if (entry) await (0, import_promises10.rm)((0, import_node_path14.join)(input2.configRoot, entry.installedPath), { force: true });
|
|
8984
9649
|
const prompts = { ...input2.project.prompts };
|
|
8985
9650
|
delete prompts[input2.reference.alias];
|
|
8986
9651
|
await writeProjectPackageJson(input2.configRoot, { ...input2.project, prompts });
|
|
@@ -8991,17 +9656,192 @@ async function removeTrackedPrompt(input2) {
|
|
|
8991
9656
|
}
|
|
8992
9657
|
}
|
|
8993
9658
|
|
|
9659
|
+
// src/remove-package.ts
|
|
9660
|
+
var import_promises11 = require("node:fs/promises");
|
|
9661
|
+
var import_node_path15 = require("node:path");
|
|
9662
|
+
function within(root, path2) {
|
|
9663
|
+
const rel = (0, import_node_path15.relative)((0, import_node_path15.resolve)(root), (0, import_node_path15.resolve)(path2));
|
|
9664
|
+
return rel === "" || !rel.startsWith("..") && !(0, import_node_path15.isAbsolute)(rel);
|
|
9665
|
+
}
|
|
9666
|
+
function trackedInstalledPackagePaths(entry) {
|
|
9667
|
+
return [
|
|
9668
|
+
...Object.values(entry.installed).flat(),
|
|
9669
|
+
...entry.installedAssets?.main ?? [],
|
|
9670
|
+
...entry.installedAssets?.helper ?? []
|
|
9671
|
+
];
|
|
9672
|
+
}
|
|
9673
|
+
async function pruneEmptyParents(path2, roots) {
|
|
9674
|
+
let current = (0, import_node_path15.dirname)(path2);
|
|
9675
|
+
while (roots.some((root) => within(root, current)) && !roots.some((root) => (0, import_node_path15.resolve)(root) === current)) {
|
|
9676
|
+
try {
|
|
9677
|
+
await (0, import_promises11.rmdir)(current);
|
|
9678
|
+
} catch {
|
|
9679
|
+
return;
|
|
9680
|
+
}
|
|
9681
|
+
current = (0, import_node_path15.dirname)(current);
|
|
9682
|
+
}
|
|
9683
|
+
}
|
|
9684
|
+
async function removeInstalledPackageFiles(input2) {
|
|
9685
|
+
const roots = [.../* @__PURE__ */ new Set([(0, import_node_path15.resolve)(input2.configRoot), (0, import_node_path15.resolve)(input2.installRoot)])];
|
|
9686
|
+
const realRoots = await Promise.all(roots.map((root) => (0, import_promises11.realpath)(root).catch(() => root)));
|
|
9687
|
+
const requestedPaths = [...new Set(trackedInstalledPackagePaths(input2.entry).map((path2) => (0, import_node_path15.resolve)(path2)))];
|
|
9688
|
+
const protectedPaths = new Set(
|
|
9689
|
+
(input2.otherEntries ?? []).flatMap(trackedInstalledPackagePaths).map((path2) => (0, import_node_path15.resolve)(path2))
|
|
9690
|
+
);
|
|
9691
|
+
const paths = requestedPaths.filter((path2) => !protectedPaths.has(path2));
|
|
9692
|
+
const unsafe = paths.find((path2) => !roots.some((root) => within(root, path2)));
|
|
9693
|
+
if (unsafe) throw new Error(`Refusing to remove an untrusted path from the lockfile: ${unsafe}`);
|
|
9694
|
+
for (const path2 of paths) {
|
|
9695
|
+
const stat6 = await (0, import_promises11.lstat)(path2).catch(() => null);
|
|
9696
|
+
if (!stat6 || stat6.isSymbolicLink()) continue;
|
|
9697
|
+
const canonicalPath = await (0, import_promises11.realpath)(path2);
|
|
9698
|
+
if (!realRoots.some((root) => within(root, canonicalPath))) {
|
|
9699
|
+
throw new Error(`Refusing to remove a path that resolves outside the install roots: ${path2}`);
|
|
9700
|
+
}
|
|
9701
|
+
}
|
|
9702
|
+
let removed = 0;
|
|
9703
|
+
for (const path2 of paths) {
|
|
9704
|
+
if (await (0, import_promises11.lstat)(path2).catch(() => null)) removed += 1;
|
|
9705
|
+
await (0, import_promises11.rm)(path2, { force: true });
|
|
9706
|
+
}
|
|
9707
|
+
for (const path2 of paths.sort((a, b) => b.length - a.length)) {
|
|
9708
|
+
await pruneEmptyParents(path2, roots);
|
|
9709
|
+
}
|
|
9710
|
+
return { removed, retainedShared: requestedPaths.length - paths.length };
|
|
9711
|
+
}
|
|
9712
|
+
|
|
9713
|
+
// src/no-init.ts
|
|
9714
|
+
var import_promises12 = require("node:fs/promises");
|
|
9715
|
+
var import_node_path16 = require("node:path");
|
|
9716
|
+
init_dist();
|
|
9717
|
+
function packageHelperSlug2(name) {
|
|
9718
|
+
const [scope = "", pkg = ""] = name.replace(/^@/, "").split("/");
|
|
9719
|
+
return `${scope}__${pkg}`;
|
|
9720
|
+
}
|
|
9721
|
+
function helpersBase(configRoot) {
|
|
9722
|
+
return (0, import_node_path16.basename)(configRoot) === ".aipm" ? (0, import_node_path16.join)(configRoot, "helpers") : (0, import_node_path16.join)(configRoot, ".aipm", "helpers");
|
|
9723
|
+
}
|
|
9724
|
+
async function resolveNoInitMode(options) {
|
|
9725
|
+
if (!options.requested) return false;
|
|
9726
|
+
if (!options.projectExists) return true;
|
|
9727
|
+
if (options.ci) {
|
|
9728
|
+
throw new Error(
|
|
9729
|
+
"Project already initialized (aipm.package.json exists). Drop --no-init, or run interactively to choose whether to continue without tracking."
|
|
9730
|
+
);
|
|
9731
|
+
}
|
|
9732
|
+
console.warn("Warning: aipm.package.json already exists in this project.");
|
|
9733
|
+
console.warn("You requested --no-init (install/manage without project tracking).");
|
|
9734
|
+
const choice = await promptForNoInitConflict();
|
|
9735
|
+
return choice === "no-init";
|
|
9736
|
+
}
|
|
9737
|
+
function discoverUntrackedPackagePaths(input2) {
|
|
9738
|
+
const short = shortNameFromScopeName(input2.packageName);
|
|
9739
|
+
const slug = packageHelperSlug2(input2.packageName);
|
|
9740
|
+
return [
|
|
9741
|
+
(0, import_node_path16.join)(input2.installRoot, ".cursor", "aipm", "skills", `${short}.md`),
|
|
9742
|
+
(0, import_node_path16.join)(input2.installRoot, ".claude", "skills", short),
|
|
9743
|
+
(0, import_node_path16.join)(input2.installRoot, ".agents", "skills", short),
|
|
9744
|
+
(0, import_node_path16.join)(helpersBase(input2.configRoot), slug)
|
|
9745
|
+
];
|
|
9746
|
+
}
|
|
9747
|
+
async function removeUntrackedPackage(input2) {
|
|
9748
|
+
const candidates = discoverUntrackedPackagePaths(input2);
|
|
9749
|
+
const removed = [];
|
|
9750
|
+
for (const path2 of candidates) {
|
|
9751
|
+
const info = await (0, import_promises12.stat)(path2).catch(() => null);
|
|
9752
|
+
if (!info) continue;
|
|
9753
|
+
await (0, import_promises12.rm)(path2, { recursive: true, force: true });
|
|
9754
|
+
removed.push(path2);
|
|
9755
|
+
}
|
|
9756
|
+
if (removed.length === 0) {
|
|
9757
|
+
throw new Error(
|
|
9758
|
+
`No untracked install found for ${input2.packageName}. Nothing to remove under known tool skill paths or .aipm/helpers.`
|
|
9759
|
+
);
|
|
9760
|
+
}
|
|
9761
|
+
return { removed };
|
|
9762
|
+
}
|
|
9763
|
+
|
|
9764
|
+
// src/ensure-project.ts
|
|
9765
|
+
var import_promises13 = require("node:fs/promises");
|
|
9766
|
+
init_dist6();
|
|
9767
|
+
async function ensureProjectOrOffer(options) {
|
|
9768
|
+
if (options.project) {
|
|
9769
|
+
return { action: "ready", project: options.project };
|
|
9770
|
+
}
|
|
9771
|
+
const fallback = initRequiredMessage(options.scope);
|
|
9772
|
+
const choices = options.allowNoInit ? [
|
|
9773
|
+
{
|
|
9774
|
+
value: "init",
|
|
9775
|
+
label: "Initialize AiPM",
|
|
9776
|
+
hint: `create aipm.package.json (${scopeLabel(options.scope)})`
|
|
9777
|
+
},
|
|
9778
|
+
{
|
|
9779
|
+
value: "no-init",
|
|
9780
|
+
label: "Continue without initializing",
|
|
9781
|
+
hint: "one-time / no project tracking"
|
|
9782
|
+
}
|
|
9783
|
+
] : [
|
|
9784
|
+
{
|
|
9785
|
+
value: "init",
|
|
9786
|
+
label: "Initialize AiPM",
|
|
9787
|
+
hint: `create aipm.package.json (${scopeLabel(options.scope)})`
|
|
9788
|
+
}
|
|
9789
|
+
];
|
|
9790
|
+
const choice = await offerChoices({
|
|
9791
|
+
ci: options.ci,
|
|
9792
|
+
note: {
|
|
9793
|
+
type: "warn",
|
|
9794
|
+
title: "AiPM is not initialized",
|
|
9795
|
+
message: `No aipm.package.json found for this ${scopeLabel(options.scope)}.`
|
|
9796
|
+
},
|
|
9797
|
+
message: `How do you want to continue with aipm ${options.command}?`,
|
|
9798
|
+
choices: [...choices],
|
|
9799
|
+
fallbackError: fallback
|
|
9800
|
+
});
|
|
9801
|
+
if (choice === "no-init") {
|
|
9802
|
+
return { action: "no-init" };
|
|
9803
|
+
}
|
|
9804
|
+
const project = await createProjectPackageJson({
|
|
9805
|
+
scope: options.scope,
|
|
9806
|
+
registry: options.registry,
|
|
9807
|
+
target: options.target
|
|
9808
|
+
});
|
|
9809
|
+
return { action: "ready", project };
|
|
9810
|
+
}
|
|
9811
|
+
async function createProjectPackageJson(options) {
|
|
9812
|
+
const configRoot = resolveConfigRoot(options.scope);
|
|
9813
|
+
const installRoot = resolveInstallRoot(options.scope);
|
|
9814
|
+
if (options.scope.global) await (0, import_promises13.mkdir)(configRoot, { recursive: true });
|
|
9815
|
+
const detected = await detectToolsInProject(installRoot);
|
|
9816
|
+
const parsedTarget = parseTargetFlag(options.target);
|
|
9817
|
+
let preferredTools = parsedTarget ? [parsedTarget] : detected;
|
|
9818
|
+
if (!options.target && detected.length === 0) {
|
|
9819
|
+
const choice = await promptForTool();
|
|
9820
|
+
preferredTools = [choice];
|
|
9821
|
+
}
|
|
9822
|
+
const project = {
|
|
9823
|
+
schemaVersion: "0.1",
|
|
9824
|
+
registry: options.registry,
|
|
9825
|
+
preferredTools: preferredTools.length ? preferredTools : void 0,
|
|
9826
|
+
packages: {},
|
|
9827
|
+
prompts: {}
|
|
9828
|
+
};
|
|
9829
|
+
await writeProjectPackageJson(configRoot, project);
|
|
9830
|
+
console.log(`Created aipm.package.json (${scopeLabel(options.scope)}, registry: ${options.registry})`);
|
|
9831
|
+
return project;
|
|
9832
|
+
}
|
|
9833
|
+
|
|
8994
9834
|
// src/version.ts
|
|
8995
9835
|
var import_node_fs2 = require("node:fs");
|
|
8996
|
-
var
|
|
9836
|
+
var import_node_path17 = require("node:path");
|
|
8997
9837
|
function getCliVersion() {
|
|
8998
|
-
const pkgPath = (0,
|
|
9838
|
+
const pkgPath = (0, import_node_path17.join)(__dirname, "..", "package.json");
|
|
8999
9839
|
const pkg = JSON.parse((0, import_node_fs2.readFileSync)(pkgPath, "utf8"));
|
|
9000
9840
|
return pkg.version;
|
|
9001
9841
|
}
|
|
9002
9842
|
|
|
9003
9843
|
// src/cli-update-check.ts
|
|
9004
|
-
var
|
|
9844
|
+
var import_node_process5 = require("node:process");
|
|
9005
9845
|
var CLI_PACKAGE_NAME = "@aipm-registry/cli";
|
|
9006
9846
|
var CLI_UPDATE_COMMAND = `npm install -g ${CLI_PACKAGE_NAME}`;
|
|
9007
9847
|
var NPM_REGISTRY = "https://registry.npmjs.org";
|
|
@@ -9052,7 +9892,7 @@ function formatCliUpdateNotice(options) {
|
|
|
9052
9892
|
return `A new AIPM CLI version is available (${currentVersion} \u2192 ${latestVersion}). Run: ${cmd}`;
|
|
9053
9893
|
}
|
|
9054
9894
|
function printCliUpdateNotice(options) {
|
|
9055
|
-
const useColor2 = options.useColor ??
|
|
9895
|
+
const useColor2 = options.useColor ?? import_node_process5.stderr.isTTY;
|
|
9056
9896
|
const writeInfo = options.writeInfo ?? ((message2) => console.log(message2));
|
|
9057
9897
|
const writeWarn = options.writeWarn ?? ((message2) => console.error(message2));
|
|
9058
9898
|
const message = formatCliUpdateNotice(options);
|
|
@@ -9117,7 +9957,7 @@ function registryFromEnvOrDefault(flag) {
|
|
|
9117
9957
|
}
|
|
9118
9958
|
function packagePageUrl(packageName, version) {
|
|
9119
9959
|
const [scope, name] = packageName.replace(/^@/, "").split("/");
|
|
9120
|
-
return `${SITE_URL}/
|
|
9960
|
+
return `${SITE_URL}/skills/${encodeURIComponent(scope ?? "")}/${encodeURIComponent(name ?? "")}/${encodeURIComponent(version)}`;
|
|
9121
9961
|
}
|
|
9122
9962
|
function packageBadgeMarkdown(packageName, version) {
|
|
9123
9963
|
const pageUrl = packagePageUrl(packageName, version);
|
|
@@ -9137,23 +9977,23 @@ function skillFolderName(name) {
|
|
|
9137
9977
|
return folder;
|
|
9138
9978
|
}
|
|
9139
9979
|
async function writeStarterFile(path2, content) {
|
|
9140
|
-
await (0,
|
|
9980
|
+
await (0, import_promises14.writeFile)(path2, content, { encoding: "utf8", flag: "wx" });
|
|
9141
9981
|
}
|
|
9142
9982
|
async function inferEntryFromSource(source, fallback) {
|
|
9143
|
-
const sourceInfo = await (0,
|
|
9144
|
-
if (sourceInfo.isFile()) return (0,
|
|
9983
|
+
const sourceInfo = await (0, import_promises14.stat)(source);
|
|
9984
|
+
if (sourceInfo.isFile()) return (0, import_node_path18.basename)(source);
|
|
9145
9985
|
if (!sourceInfo.isDirectory()) return fallback;
|
|
9146
|
-
const entries = await (0,
|
|
9986
|
+
const entries = await (0, import_promises14.readdir)(source);
|
|
9147
9987
|
if (entries.includes(fallback)) return fallback;
|
|
9148
9988
|
return entries.find((entry) => entry.endsWith(".md")) ?? entries.find((entry) => entry.endsWith(".mdc")) ?? fallback;
|
|
9149
9989
|
}
|
|
9150
9990
|
async function copySourceIntoSkillRoot(source, root, sourceLabel) {
|
|
9151
|
-
const sourceInfo = await (0,
|
|
9152
|
-
await (0,
|
|
9991
|
+
const sourceInfo = await (0, import_promises14.stat)(source);
|
|
9992
|
+
await (0, import_promises14.mkdir)(root, { recursive: true });
|
|
9153
9993
|
if (sourceInfo.isDirectory()) {
|
|
9154
|
-
const entries = await (0,
|
|
9994
|
+
const entries = await (0, import_promises14.readdir)(source);
|
|
9155
9995
|
for (const entry of entries) {
|
|
9156
|
-
await (0,
|
|
9996
|
+
await (0, import_promises14.cp)((0, import_node_path18.join)(source, entry), (0, import_node_path18.join)(root, entry), {
|
|
9157
9997
|
recursive: true,
|
|
9158
9998
|
force: false,
|
|
9159
9999
|
errorOnExist: true
|
|
@@ -9162,7 +10002,7 @@ async function copySourceIntoSkillRoot(source, root, sourceLabel) {
|
|
|
9162
10002
|
return;
|
|
9163
10003
|
}
|
|
9164
10004
|
if (sourceInfo.isFile()) {
|
|
9165
|
-
await (0,
|
|
10005
|
+
await (0, import_promises14.cp)(source, (0, import_node_path18.join)(root, (0, import_node_path18.basename)(source)), { force: false, errorOnExist: true });
|
|
9166
10006
|
return;
|
|
9167
10007
|
}
|
|
9168
10008
|
throw new Error(`Unsupported source path: ${sourceLabel}`);
|
|
@@ -9193,14 +10033,14 @@ function formatBytes(bytes) {
|
|
|
9193
10033
|
}
|
|
9194
10034
|
function packageDashboardUrl(name) {
|
|
9195
10035
|
if (!name) return DASHBOARD_URL;
|
|
9196
|
-
return `${DASHBOARD_URL}/
|
|
10036
|
+
return `${DASHBOARD_URL}/skills/${name.replace(/^@/, "")}`;
|
|
9197
10037
|
}
|
|
9198
10038
|
function helperRootFromTrackedPath(path2) {
|
|
9199
|
-
const parts = path2.split(
|
|
10039
|
+
const parts = path2.split(import_node_path18.sep).filter(Boolean);
|
|
9200
10040
|
const helpersIndex = parts.lastIndexOf("helpers");
|
|
9201
10041
|
if (helpersIndex === -1 || parts.length <= helpersIndex + 2) return null;
|
|
9202
|
-
const prefix = path2.startsWith(
|
|
9203
|
-
return (0,
|
|
10042
|
+
const prefix = path2.startsWith(import_node_path18.sep) ? import_node_path18.sep : "";
|
|
10043
|
+
return (0, import_node_path18.join)(prefix, ...parts.slice(0, helpersIndex + 3));
|
|
9204
10044
|
}
|
|
9205
10045
|
async function showInstalledPrompt(configRoot, packageArg) {
|
|
9206
10046
|
const { name } = parsePackageArg(packageArg);
|
|
@@ -9213,7 +10053,7 @@ async function showInstalledPrompt(configRoot, packageArg) {
|
|
|
9213
10053
|
if (entry.postInstall.status === "cleaned") {
|
|
9214
10054
|
throw new Error(`Setup helper files were cleaned for ${name}. Reinstall the package to restore them.`);
|
|
9215
10055
|
}
|
|
9216
|
-
const content = await (0,
|
|
10056
|
+
const content = await (0, import_promises14.readFile)(entry.postInstall.promptFile, "utf8").catch(() => {
|
|
9217
10057
|
throw new Error(`Setup prompt file is missing: ${entry.postInstall?.promptFile}`);
|
|
9218
10058
|
});
|
|
9219
10059
|
console.log(`Prompt file: ${entry.postInstall.promptFile}`);
|
|
@@ -9246,9 +10086,9 @@ async function cleanupInstalledHelpers(options) {
|
|
|
9246
10086
|
entry.installedAssets.helper.map((path2) => helperRootFromTrackedPath(path2)).filter((path2) => Boolean(path2))
|
|
9247
10087
|
);
|
|
9248
10088
|
if (roots.size > 0) {
|
|
9249
|
-
for (const root of roots) await (0,
|
|
10089
|
+
for (const root of roots) await (0, import_promises14.rm)(root, { recursive: true, force: true });
|
|
9250
10090
|
} else {
|
|
9251
|
-
for (const helperPath of entry.installedAssets.helper) await (0,
|
|
10091
|
+
for (const helperPath of entry.installedAssets.helper) await (0, import_promises14.rm)(helperPath, { force: true });
|
|
9252
10092
|
}
|
|
9253
10093
|
await writeLockfile(options.configRoot, {
|
|
9254
10094
|
...lock,
|
|
@@ -9293,20 +10133,76 @@ async function authTokenForRegistry(registry, options = {}) {
|
|
|
9293
10133
|
return refreshed.accessToken;
|
|
9294
10134
|
} catch {
|
|
9295
10135
|
await clearStoredRegistryAuth(registry);
|
|
9296
|
-
const message = `CLI login for ${registry} expired or was revoked. Run aipm login to access private packages.`;
|
|
10136
|
+
const message = `CLI login for ${registry} expired or was revoked. Run ${recommendCmd("aipm login")} to access private packages.`;
|
|
9297
10137
|
if (options.throwOnFailure) throw new Error(message);
|
|
9298
10138
|
if (!options.quiet) console.warn(`Warning: ${message}`);
|
|
9299
10139
|
return void 0;
|
|
9300
10140
|
}
|
|
9301
10141
|
}
|
|
9302
10142
|
async function tokenForRead(registry, explicitToken, options = {}) {
|
|
9303
|
-
return explicitToken ??
|
|
10143
|
+
return explicitToken ?? import_node_process6.env.AIPM_TOKEN ?? await authTokenForRegistry(registry, options);
|
|
10144
|
+
}
|
|
10145
|
+
async function installOnePackageWithRecovery(options) {
|
|
10146
|
+
try {
|
|
10147
|
+
await installOnePackage(options);
|
|
10148
|
+
} catch (error) {
|
|
10149
|
+
if (error instanceof RecoveryCancelledError || error instanceof Error && error.name === "SelectCancelledError") {
|
|
10150
|
+
throw error;
|
|
10151
|
+
}
|
|
10152
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
10153
|
+
await resolvePrivateInstallFailure({ ci: options.ci, errorMessage: message });
|
|
10154
|
+
await runLoopbackLogin({
|
|
10155
|
+
registry: options.registry,
|
|
10156
|
+
siteUrl: SITE_URL,
|
|
10157
|
+
open: true
|
|
10158
|
+
});
|
|
10159
|
+
const token = await authTokenForRegistry(options.registry, { throwOnFailure: true });
|
|
10160
|
+
await installOnePackage({ ...options, token: token ?? options.token });
|
|
10161
|
+
}
|
|
10162
|
+
}
|
|
10163
|
+
async function addUntrackedPackage(options) {
|
|
10164
|
+
const { name, version: requestedVersion } = parsePackageArg(options.pkgArg);
|
|
10165
|
+
const token = await tokenForRead(options.registry, options.token);
|
|
10166
|
+
const version = requestedVersion ?? await latestVersionForPackage(options.registry, name, token);
|
|
10167
|
+
if (!version) throw new Error(`Specify version: ${recommendCmd("aipm add <@scope/pkg>@<version>")}`);
|
|
10168
|
+
const syntheticProject = {
|
|
10169
|
+
schemaVersion: "0.1",
|
|
10170
|
+
registry: options.registry,
|
|
10171
|
+
packages: {},
|
|
10172
|
+
prompts: {}
|
|
10173
|
+
};
|
|
10174
|
+
await installOnePackageWithRecovery({
|
|
10175
|
+
configRoot: options.configRoot,
|
|
10176
|
+
installRoot: options.installRoot,
|
|
10177
|
+
registry: options.registry,
|
|
10178
|
+
name,
|
|
10179
|
+
version: version.replace(/^\^/, ""),
|
|
10180
|
+
project: syntheticProject,
|
|
10181
|
+
explicitTarget: parseTargetFlag(options.target),
|
|
10182
|
+
ci: options.ci,
|
|
10183
|
+
token,
|
|
10184
|
+
track: false
|
|
10185
|
+
});
|
|
10186
|
+
try {
|
|
10187
|
+
await recordPackageInstall(options.registry, name, token);
|
|
10188
|
+
} catch (error) {
|
|
10189
|
+
if (!options.ci && !program2.opts().quiet) {
|
|
10190
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
10191
|
+
console.warn(`Warning: could not record install count: ${message}`);
|
|
10192
|
+
}
|
|
10193
|
+
}
|
|
9304
10194
|
}
|
|
9305
10195
|
async function runLoopbackLogin(options) {
|
|
9306
10196
|
const state = randomBase64Url(24);
|
|
9307
10197
|
const verifier = randomBase64Url(48);
|
|
9308
10198
|
const challenge = sha256Base64Url(verifier);
|
|
9309
10199
|
const result = await new Promise((resolveLogin, rejectLogin) => {
|
|
10200
|
+
let settled = false;
|
|
10201
|
+
const settle = (fn) => {
|
|
10202
|
+
if (settled) return;
|
|
10203
|
+
settled = true;
|
|
10204
|
+
fn();
|
|
10205
|
+
};
|
|
9310
10206
|
const server = (0, import_node_http.createServer)((request, response) => {
|
|
9311
10207
|
const host = request.headers.host ?? "127.0.0.1";
|
|
9312
10208
|
const url = new URL(request.url ?? "/", `http://${host}`);
|
|
@@ -9318,29 +10214,33 @@ async function runLoopbackLogin(options) {
|
|
|
9318
10214
|
if (url.searchParams.get("state") !== state) {
|
|
9319
10215
|
response.writeHead(400, { "content-type": "text/html" });
|
|
9320
10216
|
response.end("<h1>AIPM login failed</h1><p>Invalid state. Return to the terminal and try again.</p>");
|
|
9321
|
-
|
|
9322
|
-
|
|
10217
|
+
forceCloseServer(server);
|
|
10218
|
+
settle(() => rejectLogin(new Error("Invalid CLI login state")));
|
|
9323
10219
|
return;
|
|
9324
10220
|
}
|
|
9325
10221
|
const code = url.searchParams.get("code");
|
|
9326
10222
|
if (!code) {
|
|
9327
10223
|
response.writeHead(400, { "content-type": "text/html" });
|
|
9328
10224
|
response.end("<h1>AIPM login failed</h1><p>Missing code. Return to the terminal and try again.</p>");
|
|
9329
|
-
|
|
9330
|
-
|
|
10225
|
+
forceCloseServer(server);
|
|
10226
|
+
settle(() => rejectLogin(new Error("Missing CLI authorization code")));
|
|
9331
10227
|
return;
|
|
9332
10228
|
}
|
|
9333
10229
|
response.writeHead(200, { "content-type": "text/html" });
|
|
9334
10230
|
response.end("<h1>AIPM CLI is signed in</h1><p>You can close this window and return to the terminal.</p>");
|
|
9335
10231
|
const address = server.address();
|
|
9336
10232
|
if (!address || typeof address === "string") {
|
|
9337
|
-
|
|
9338
|
-
|
|
9339
|
-
|
|
10233
|
+
forceCloseServer(server);
|
|
10234
|
+
settle(() => rejectLogin(new Error("Could not read CLI callback address")));
|
|
10235
|
+
return;
|
|
9340
10236
|
}
|
|
9341
|
-
server
|
|
10237
|
+
forceCloseServer(server);
|
|
10238
|
+
settle(() => resolveLogin({ code, redirectUri: `http://127.0.0.1:${address.port}/callback` }));
|
|
10239
|
+
});
|
|
10240
|
+
server.once("error", (error) => {
|
|
10241
|
+
forceCloseServer(server);
|
|
10242
|
+
settle(() => rejectLogin(error));
|
|
9342
10243
|
});
|
|
9343
|
-
server.once("error", rejectLogin);
|
|
9344
10244
|
server.listen(0, "127.0.0.1", () => {
|
|
9345
10245
|
const address = server.address();
|
|
9346
10246
|
const redirectUri = `http://127.0.0.1:${address.port}/callback`;
|
|
@@ -9353,10 +10253,12 @@ async function runLoopbackLogin(options) {
|
|
|
9353
10253
|
if (options.open) void openUrl(loginUrl.toString());
|
|
9354
10254
|
else console.log(`Open: ${loginUrl.toString()}`);
|
|
9355
10255
|
});
|
|
9356
|
-
|
|
9357
|
-
|
|
9358
|
-
|
|
9359
|
-
});
|
|
10256
|
+
const timeout = setTimeout(() => {
|
|
10257
|
+
forceCloseServer(server);
|
|
10258
|
+
settle(() => rejectLogin(new Error("CLI login timed out")));
|
|
10259
|
+
}, 5 * 60 * 1e3);
|
|
10260
|
+
timeout.unref?.();
|
|
10261
|
+
server.once("close", () => clearTimeout(timeout));
|
|
9360
10262
|
});
|
|
9361
10263
|
const tokens = await exchangeCliAuthCode(options.registry, {
|
|
9362
10264
|
code: result.code,
|
|
@@ -9375,6 +10277,12 @@ async function runLoopbackLogin(options) {
|
|
|
9375
10277
|
console.log(`Logged in as ${label}.`);
|
|
9376
10278
|
console.log(`Credentials saved to ${authFilePath()}`);
|
|
9377
10279
|
}
|
|
10280
|
+
function forceCloseServer(server) {
|
|
10281
|
+
if (typeof server.closeAllConnections === "function") {
|
|
10282
|
+
server.closeAllConnections();
|
|
10283
|
+
}
|
|
10284
|
+
server.close();
|
|
10285
|
+
}
|
|
9378
10286
|
function printPublishFlow() {
|
|
9379
10287
|
console.log("AIPM publish flow");
|
|
9380
10288
|
console.log("1. Create an account and organization in the dashboard.");
|
|
@@ -9542,9 +10450,9 @@ function buildStarterQualityMetadata(options) {
|
|
|
9542
10450
|
}
|
|
9543
10451
|
async function initSkill(opts) {
|
|
9544
10452
|
if (!isValidScopeName(opts.name)) throw new Error("Invalid @scope/name");
|
|
9545
|
-
const root = opts.here ? (0,
|
|
10453
|
+
const root = opts.here ? (0, import_node_process6.cwd)() : (0, import_node_path18.resolve)((0, import_node_process6.cwd)(), opts.dir ?? skillFolderName(opts.name));
|
|
9546
10454
|
const cdTarget = opts.here ? null : opts.dir ?? skillFolderName(opts.name);
|
|
9547
|
-
const source = opts.from ? (0,
|
|
10455
|
+
const source = opts.from ? (0, import_node_path18.resolve)(opts.from) : null;
|
|
9548
10456
|
const entry = source ? await inferEntryFromSource(source, opts.entry) : opts.entry;
|
|
9549
10457
|
const template = parseSkillTemplate(opts.template ?? "blank");
|
|
9550
10458
|
const manifest = PackageManifestSchema.parse({
|
|
@@ -9565,17 +10473,17 @@ async function initSkill(opts) {
|
|
|
9565
10473
|
if (source) {
|
|
9566
10474
|
await copySourceIntoSkillRoot(source, root, source);
|
|
9567
10475
|
} else {
|
|
9568
|
-
await (0,
|
|
10476
|
+
await (0, import_promises14.mkdir)(root, { recursive: true });
|
|
9569
10477
|
}
|
|
9570
|
-
await writeStarterFile((0,
|
|
10478
|
+
await writeStarterFile((0, import_node_path18.join)(root, "aipm.manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
|
|
9571
10479
|
if (!opts.from) {
|
|
9572
10480
|
await writeStarterFile(
|
|
9573
|
-
(0,
|
|
10481
|
+
(0, import_node_path18.join)(root, entry),
|
|
9574
10482
|
SKILL_TEMPLATES[template](opts.name)
|
|
9575
10483
|
);
|
|
9576
10484
|
}
|
|
9577
10485
|
await writeStarterFile(
|
|
9578
|
-
(0,
|
|
10486
|
+
(0, import_node_path18.join)(root, ".aipmignore"),
|
|
9579
10487
|
[
|
|
9580
10488
|
"# Files that should never be published with this skill",
|
|
9581
10489
|
".env*",
|
|
@@ -9593,7 +10501,7 @@ async function initSkill(opts) {
|
|
|
9593
10501
|
""
|
|
9594
10502
|
].join("\n")
|
|
9595
10503
|
);
|
|
9596
|
-
await (0,
|
|
10504
|
+
await (0, import_promises14.mkdir)((0, import_node_path18.join)(root, ".aipm"), { recursive: true });
|
|
9597
10505
|
console.log(`Created ${opts.name} skill folder: ${root}`);
|
|
9598
10506
|
printPublishGuide(
|
|
9599
10507
|
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.`,
|
|
@@ -9608,24 +10516,11 @@ program2.command("init").description("Create aipm.package.json in the current pr
|
|
|
9608
10516
|
console.log(`aipm.package.json already exists (${scopeLabel(scope)}).`);
|
|
9609
10517
|
return;
|
|
9610
10518
|
}
|
|
9611
|
-
|
|
9612
|
-
|
|
9613
|
-
|
|
9614
|
-
|
|
9615
|
-
let preferredTools = parsedTarget ? [parsedTarget] : detected;
|
|
9616
|
-
if (!opts.target && detected.length === 0) {
|
|
9617
|
-
const choice = await promptForTool();
|
|
9618
|
-
preferredTools = [choice];
|
|
9619
|
-
}
|
|
9620
|
-
const registry = registryFromEnvOrDefault(opts.registry);
|
|
9621
|
-
await writeProjectPackageJson(configRoot, {
|
|
9622
|
-
schemaVersion: "0.1",
|
|
9623
|
-
registry,
|
|
9624
|
-
preferredTools: preferredTools.length ? preferredTools : void 0,
|
|
9625
|
-
packages: {},
|
|
9626
|
-
prompts: {}
|
|
10519
|
+
await createProjectPackageJson({
|
|
10520
|
+
scope,
|
|
10521
|
+
registry: registryFromEnvOrDefault(opts.registry),
|
|
10522
|
+
target: opts.target
|
|
9627
10523
|
});
|
|
9628
|
-
console.log(`Created aipm.package.json (${scopeLabel(scope)}, registry: ${registry})`);
|
|
9629
10524
|
});
|
|
9630
10525
|
program2.command("login").description("Sign in and store a local CLI session for private installs").option("--registry <url>", "Registry API base URL").option("--site <url>", "Website base URL", SITE_URL).option("--no-open", "Print the URL without opening a browser").action(async (opts) => {
|
|
9631
10526
|
const registry = registryFromEnvOrDefault(opts.registry);
|
|
@@ -9680,9 +10575,9 @@ program2.command("config").description("Show resolved AIPM CLI and project confi
|
|
|
9680
10575
|
scope: scopeLabel(scope),
|
|
9681
10576
|
configRoot,
|
|
9682
10577
|
installRoot,
|
|
9683
|
-
cwd: (0,
|
|
10578
|
+
cwd: (0, import_node_process6.cwd)(),
|
|
9684
10579
|
registry,
|
|
9685
|
-
envRegistry:
|
|
10580
|
+
envRegistry: import_node_process6.env.AIPM_REGISTRY_URL ?? import_node_process6.env.AIPM_REGISTRY ?? null,
|
|
9686
10581
|
hasProjectConfig: Boolean(project),
|
|
9687
10582
|
projectPackages: project ? Object.keys(project.packages) : [],
|
|
9688
10583
|
hasLockfile: Boolean(lock),
|
|
@@ -9711,11 +10606,58 @@ program2.command("config").description("Show resolved AIPM CLI and project confi
|
|
|
9711
10606
|
console.log(`Auth file: ${data.authFile}`);
|
|
9712
10607
|
console.log(`Publish guide: ${data.publishDoc}`);
|
|
9713
10608
|
});
|
|
9714
|
-
program2.command("add <package-or-prompt>").description("Add a skill package or an AIPM prompt URL").option(GLOBAL_OPTION, GLOBAL_OPTION_DESC).option("--registry <url>", "Registry base URL").option("--target <tool>", "cursor, claude, codex, or *").option("--token <token>", "Install token for private packages").option("--ci", "Non-interactive; fail if prompt needed").action(async (pkgArg, opts) => {
|
|
10609
|
+
program2.command("add <package-or-prompt>").description("Add a skill package or an AIPM prompt URL").option(GLOBAL_OPTION, GLOBAL_OPTION_DESC).option("--registry <url>", "Registry base URL").option("--target <tool>", "cursor, claude, codex, or *").option("--token <token>", "Install token for private packages").option("--ci", "Non-interactive; fail if prompt needed").option("--no-init", "Install without creating or updating aipm.package.json / aipm-lock.json").action(async (pkgArg, opts) => {
|
|
9715
10610
|
const scope = { global: opts.global };
|
|
9716
10611
|
const configRoot = resolveConfigRoot(scope);
|
|
9717
10612
|
const installRoot = resolveInstallRoot(scope);
|
|
9718
10613
|
let project = await readProjectPackageJson(configRoot);
|
|
10614
|
+
let useNoInit = await resolveNoInitMode({
|
|
10615
|
+
requested: opts.init === false,
|
|
10616
|
+
projectExists: Boolean(project),
|
|
10617
|
+
ci: opts.ci
|
|
10618
|
+
});
|
|
10619
|
+
if (!useNoInit && !project) {
|
|
10620
|
+
const ensured = await ensureProjectOrOffer({
|
|
10621
|
+
scope,
|
|
10622
|
+
project,
|
|
10623
|
+
ci: opts.ci,
|
|
10624
|
+
registry: resolveRegistryUrl(null, opts.registry, DEFAULT_REGISTRY),
|
|
10625
|
+
target: opts.target,
|
|
10626
|
+
allowNoInit: true,
|
|
10627
|
+
command: "add"
|
|
10628
|
+
});
|
|
10629
|
+
if (ensured.action === "no-init") useNoInit = true;
|
|
10630
|
+
else project = ensured.project;
|
|
10631
|
+
}
|
|
10632
|
+
if (useNoInit) {
|
|
10633
|
+
const promptReference2 = parsePromptUrl(pkgArg);
|
|
10634
|
+
if (promptReference2) {
|
|
10635
|
+
await resolvePromptRequiresTracking({ ci: opts.ci, command: "add" });
|
|
10636
|
+
useNoInit = false;
|
|
10637
|
+
if (!project) {
|
|
10638
|
+
project = await createProjectPackageJson({
|
|
10639
|
+
scope,
|
|
10640
|
+
registry: resolveRegistryUrl(null, opts.registry, DEFAULT_REGISTRY),
|
|
10641
|
+
target: opts.target
|
|
10642
|
+
});
|
|
10643
|
+
}
|
|
10644
|
+
} else {
|
|
10645
|
+
const registry2 = resolveRegistryUrl(null, opts.registry, DEFAULT_REGISTRY);
|
|
10646
|
+
await addUntrackedPackage({
|
|
10647
|
+
configRoot,
|
|
10648
|
+
installRoot,
|
|
10649
|
+
registry: registry2,
|
|
10650
|
+
pkgArg,
|
|
10651
|
+
target: opts.target,
|
|
10652
|
+
token: opts.token,
|
|
10653
|
+
ci: opts.ci
|
|
10654
|
+
});
|
|
10655
|
+
if (!opts.ci && !program2.opts().quiet) {
|
|
10656
|
+
await notifyCliUpdateIfNeeded(CLI_VERSION);
|
|
10657
|
+
}
|
|
10658
|
+
return;
|
|
10659
|
+
}
|
|
10660
|
+
}
|
|
9719
10661
|
if (!project) throw new Error(initRequiredMessage(scope));
|
|
9720
10662
|
const registry = resolveRegistryUrl(project, opts.registry, DEFAULT_REGISTRY);
|
|
9721
10663
|
const promptReference = parsePromptUrl(pkgArg);
|
|
@@ -9726,7 +10668,8 @@ program2.command("add <package-or-prompt>").description("Add a skill package or
|
|
|
9726
10668
|
project,
|
|
9727
10669
|
reference: promptReference,
|
|
9728
10670
|
track: true,
|
|
9729
|
-
recordCopy: true
|
|
10671
|
+
recordCopy: true,
|
|
10672
|
+
ci: opts.ci
|
|
9730
10673
|
});
|
|
9731
10674
|
console.log(`Installed prompt ${result.prompt.title} \u2192 ${result.path}`);
|
|
9732
10675
|
return;
|
|
@@ -9734,13 +10677,13 @@ program2.command("add <package-or-prompt>").description("Add a skill package or
|
|
|
9734
10677
|
const { name, version: requestedVersion } = parsePackageArg(pkgArg);
|
|
9735
10678
|
const token = await tokenForRead(registry, opts.token);
|
|
9736
10679
|
const version = requestedVersion ?? project.packages[name] ?? await latestVersionForPackage(registry, name, token);
|
|
9737
|
-
if (!version) throw new Error(
|
|
10680
|
+
if (!version) throw new Error(`Specify version: ${recommendCmd("aipm add <@scope/pkg>@<version>")}`);
|
|
9738
10681
|
project = {
|
|
9739
10682
|
...project,
|
|
9740
10683
|
packages: { ...project.packages, [name]: version.replace(/^\^/, "") }
|
|
9741
10684
|
};
|
|
9742
10685
|
await writeProjectPackageJson(configRoot, project);
|
|
9743
|
-
await
|
|
10686
|
+
await installOnePackageWithRecovery({
|
|
9744
10687
|
configRoot,
|
|
9745
10688
|
installRoot,
|
|
9746
10689
|
registry,
|
|
@@ -9768,7 +10711,7 @@ program2.command("search [query]").description("Search public registry packages"
|
|
|
9768
10711
|
const token = await tokenForRead(registry, opts.token, { quiet: opts.json });
|
|
9769
10712
|
const packages = await searchPackages(registry, query, Number(opts.limit), token);
|
|
9770
10713
|
if (opts.json) {
|
|
9771
|
-
console.log(JSON.stringify({ packages }, null, 2));
|
|
10714
|
+
console.log(JSON.stringify({ skills: packages }, null, 2));
|
|
9772
10715
|
return;
|
|
9773
10716
|
}
|
|
9774
10717
|
if (packages.length === 0) {
|
|
@@ -9787,13 +10730,31 @@ program2.command("install").description("Install all packages from aipm.package.
|
|
|
9787
10730
|
const scope = { global: opts.global };
|
|
9788
10731
|
const configRoot = resolveConfigRoot(scope);
|
|
9789
10732
|
const installRoot = resolveInstallRoot(scope);
|
|
9790
|
-
|
|
9791
|
-
if (!project)
|
|
10733
|
+
let project = await readProjectPackageJson(configRoot);
|
|
10734
|
+
if (!project) {
|
|
10735
|
+
const ensured = await ensureProjectOrOffer({
|
|
10736
|
+
scope,
|
|
10737
|
+
project,
|
|
10738
|
+
ci: opts.ci,
|
|
10739
|
+
registry: resolveRegistryUrl(null, opts.registry, DEFAULT_REGISTRY),
|
|
10740
|
+
target: opts.target,
|
|
10741
|
+
allowNoInit: false,
|
|
10742
|
+
command: "install"
|
|
10743
|
+
});
|
|
10744
|
+
if (ensured.action !== "ready") throw new Error(initRequiredMessage(scope));
|
|
10745
|
+
project = ensured.project;
|
|
10746
|
+
printNote({
|
|
10747
|
+
type: "info",
|
|
10748
|
+
title: "Initialized",
|
|
10749
|
+
message: "aipm.package.json is empty \u2014 add packages with aipm add, then run aipm install."
|
|
10750
|
+
});
|
|
10751
|
+
return;
|
|
10752
|
+
}
|
|
9792
10753
|
const registry = resolveRegistryUrl(project, opts.registry, DEFAULT_REGISTRY);
|
|
9793
10754
|
const target = parseTargetFlag(opts.target);
|
|
9794
10755
|
const token = await tokenForRead(registry, opts.token);
|
|
9795
10756
|
for (const [name, version] of Object.entries(project.packages)) {
|
|
9796
|
-
await
|
|
10757
|
+
await installOnePackageWithRecovery({
|
|
9797
10758
|
configRoot,
|
|
9798
10759
|
installRoot,
|
|
9799
10760
|
registry,
|
|
@@ -9813,20 +10774,33 @@ program2.command("install").description("Install all packages from aipm.package.
|
|
|
9813
10774
|
registry,
|
|
9814
10775
|
project,
|
|
9815
10776
|
reference: { ...reference, alias },
|
|
9816
|
-
track: false
|
|
10777
|
+
track: false,
|
|
10778
|
+
ci: opts.ci
|
|
9817
10779
|
});
|
|
9818
10780
|
console.log(`Restored prompt ${result.prompt.title} \u2192 ${result.path}`);
|
|
9819
10781
|
}
|
|
9820
10782
|
});
|
|
9821
10783
|
program2.command("show <package-or-prompt>").description("Show an installed prompt snapshot or tracked skill details").option(GLOBAL_OPTION, GLOBAL_OPTION_DESC).action(async (value, opts) => {
|
|
9822
|
-
const
|
|
9823
|
-
const
|
|
9824
|
-
|
|
10784
|
+
const scope = { global: opts.global };
|
|
10785
|
+
const configRoot = resolveConfigRoot(scope);
|
|
10786
|
+
let project = await readProjectPackageJson(configRoot);
|
|
10787
|
+
if (!project) {
|
|
10788
|
+
const ensured = await ensureProjectOrOffer({
|
|
10789
|
+
scope,
|
|
10790
|
+
project,
|
|
10791
|
+
ci: opts.ci,
|
|
10792
|
+
registry: DEFAULT_REGISTRY,
|
|
10793
|
+
allowNoInit: false,
|
|
10794
|
+
command: "show"
|
|
10795
|
+
});
|
|
10796
|
+
if (ensured.action !== "ready") throw new Error(initRequiredMessage(scope));
|
|
10797
|
+
project = ensured.project;
|
|
10798
|
+
}
|
|
9825
10799
|
const reference = resolveTrackedPrompt(project, value);
|
|
9826
10800
|
if (reference && project.prompts[reference.alias] === reference.url) {
|
|
9827
10801
|
const lock2 = await readLockfile(configRoot);
|
|
9828
10802
|
const entry2 = lock2?.prompts[reference.alias];
|
|
9829
|
-
if (!entry2) throw new Error(`Prompt is not installed: ${reference.url}. Run aipm install.`);
|
|
10803
|
+
if (!entry2) throw new Error(`Prompt is not installed: ${reference.url}. Run ${recommendCmd("aipm install")}.`);
|
|
9830
10804
|
console.log(await readInstalledPrompt(configRoot, entry2));
|
|
9831
10805
|
return;
|
|
9832
10806
|
}
|
|
@@ -9840,7 +10814,7 @@ program2.command("show-prompt <package>").description("Show the manual setup pro
|
|
|
9840
10814
|
await showInstalledPrompt(resolveConfigRoot({ global: opts.global }), pkgArg);
|
|
9841
10815
|
});
|
|
9842
10816
|
function promptImageContentType(path2) {
|
|
9843
|
-
const extension = (0,
|
|
10817
|
+
const extension = (0, import_node_path18.extname)(path2).toLowerCase();
|
|
9844
10818
|
if (extension === ".png") return "image/png";
|
|
9845
10819
|
if (extension === ".jpg" || extension === ".jpeg") return "image/jpeg";
|
|
9846
10820
|
if (extension === ".webp") return "image/webp";
|
|
@@ -9849,10 +10823,18 @@ function promptImageContentType(path2) {
|
|
|
9849
10823
|
var promptCommand = program2.command("prompt").description("Publish prompt listings from structured JSON");
|
|
9850
10824
|
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
10825
|
const registry = registryFromEnvOrDefault(opts.registry);
|
|
9852
|
-
|
|
9853
|
-
if (!accessToken)
|
|
9854
|
-
|
|
9855
|
-
|
|
10826
|
+
let accessToken = await authTokenForRegistry(registry, { throwOnFailure: false });
|
|
10827
|
+
if (!accessToken) {
|
|
10828
|
+
await resolveLoginRequired({
|
|
10829
|
+
ci: opts.ci,
|
|
10830
|
+
purpose: "Publishing prompts requires an AIPM login."
|
|
10831
|
+
});
|
|
10832
|
+
await runLoopbackLogin({ registry, siteUrl: SITE_URL, open: true });
|
|
10833
|
+
accessToken = await authTokenForRegistry(registry, { throwOnFailure: true });
|
|
10834
|
+
}
|
|
10835
|
+
if (!accessToken) throw new Error(`Run ${recommendCmd("aipm login")} before publishing prompts.`);
|
|
10836
|
+
const absoluteFile = (0, import_node_path18.resolve)(file);
|
|
10837
|
+
const parsed = JSON.parse(await (0, import_promises14.readFile)(absoluteFile, "utf8"));
|
|
9856
10838
|
const rows = Array.isArray(parsed) ? parsed : "prompts" in parsed ? parsed.prompts : [parsed];
|
|
9857
10839
|
if (rows.length === 0) throw new Error("Prompt publish file contains no prompts.");
|
|
9858
10840
|
if (!opts.yes) {
|
|
@@ -9863,10 +10845,10 @@ promptCommand.command("publish <file>").description("Publish one prompt or a bat
|
|
|
9863
10845
|
const { sampleImagePath, ...input2 } = row;
|
|
9864
10846
|
let sampleImage;
|
|
9865
10847
|
if (sampleImagePath) {
|
|
9866
|
-
const imagePath = (0,
|
|
10848
|
+
const imagePath = (0, import_node_path18.resolve)((0, import_node_path18.dirname)(absoluteFile), sampleImagePath);
|
|
9867
10849
|
sampleImage = {
|
|
9868
|
-
data: await (0,
|
|
9869
|
-
filename: (0,
|
|
10850
|
+
data: await (0, import_promises14.readFile)(imagePath),
|
|
10851
|
+
filename: (0, import_node_path18.basename)(imagePath),
|
|
9870
10852
|
contentType: promptImageContentType(imagePath)
|
|
9871
10853
|
};
|
|
9872
10854
|
}
|
|
@@ -9889,12 +10871,12 @@ var publish = program2.command("publish [dir]").description("Stage, validate, an
|
|
|
9889
10871
|
publish.help();
|
|
9890
10872
|
return;
|
|
9891
10873
|
}
|
|
9892
|
-
const abs = (0,
|
|
9893
|
-
const manifestRaw = await (0,
|
|
10874
|
+
const abs = (0, import_node_path18.resolve)(dir);
|
|
10875
|
+
const manifestRaw = await (0, import_promises14.readFile)((0, import_node_path18.join)(abs, "aipm.manifest.json"), "utf8");
|
|
9894
10876
|
const manifest = PackageManifestSchema.parse(JSON.parse(manifestRaw));
|
|
9895
10877
|
const tarball = await packDirectory(abs);
|
|
9896
10878
|
const registry = registryFromEnvOrDefault(opts.registry);
|
|
9897
|
-
const result = await publishPackage(registry, manifest.name, tarball, opts.token ??
|
|
10879
|
+
const result = await publishPackage(registry, manifest.name, tarball, opts.token ?? import_node_process6.env.AIPM_TOKEN);
|
|
9898
10880
|
console.log(`Published ${manifest.name}@${result.version}`);
|
|
9899
10881
|
console.log(`View: ${packagePageUrl(manifest.name, result.version)}`);
|
|
9900
10882
|
console.log(`Install: aipm add ${manifest.name}@${result.version} --target ${manifest.targets[0]} --ci`);
|
|
@@ -9909,7 +10891,7 @@ publish.command("import <source>").description("Create a package folder from an
|
|
|
9909
10891
|
async (source, opts) => initSkill({ ...opts, from: source })
|
|
9910
10892
|
);
|
|
9911
10893
|
publish.command("add [files...]").description("Stage files for publishing").action(async (files) => {
|
|
9912
|
-
const state = await addPublishFiles((0,
|
|
10894
|
+
const state = await addPublishFiles((0, import_node_process6.cwd)(), files);
|
|
9913
10895
|
console.log(`Staged ${state.files.length} file${state.files.length === 1 ? "" : "s"}.`);
|
|
9914
10896
|
printPublishGuide(
|
|
9915
10897
|
`Added ${files.length > 0 ? files.join(", ") : "."} to the publish stage, respecting .aipmignore and secret-file exclusions.`,
|
|
@@ -9940,7 +10922,7 @@ publish.command("token").description("Open the dashboard page used to generate a
|
|
|
9940
10922
|
);
|
|
9941
10923
|
});
|
|
9942
10924
|
publish.command("remove <files...>").alias("rm").description("Remove files from the publish stage").action(async (files) => {
|
|
9943
|
-
const state = await removePublishFiles((0,
|
|
10925
|
+
const state = await removePublishFiles((0, import_node_process6.cwd)(), files);
|
|
9944
10926
|
console.log(`Staged ${state.files.length} file${state.files.length === 1 ? "" : "s"}.`);
|
|
9945
10927
|
printPublishGuide(
|
|
9946
10928
|
`Removed ${files.join(", ")} from the publish stage.`,
|
|
@@ -9948,7 +10930,7 @@ publish.command("remove <files...>").alias("rm").description("Remove files from
|
|
|
9948
10930
|
);
|
|
9949
10931
|
});
|
|
9950
10932
|
publish.command("reset").description("Clear staged publish files").action(async () => {
|
|
9951
|
-
await resetPublishState((0,
|
|
10933
|
+
await resetPublishState((0, import_node_process6.cwd)());
|
|
9952
10934
|
console.log("Cleared publish stage.");
|
|
9953
10935
|
printPublishGuide(
|
|
9954
10936
|
"Cleared all staged publish files.",
|
|
@@ -9956,7 +10938,7 @@ publish.command("reset").description("Clear staged publish files").action(async
|
|
|
9956
10938
|
);
|
|
9957
10939
|
});
|
|
9958
10940
|
publish.command("status").description("Show staged publish files").option("--json", "Print machine-readable JSON").action(async (opts) => {
|
|
9959
|
-
const rows = await statusPublishState((0,
|
|
10941
|
+
const rows = await statusPublishState((0, import_node_process6.cwd)());
|
|
9960
10942
|
if (opts.json) {
|
|
9961
10943
|
console.log(JSON.stringify({ files: rows }, null, 2));
|
|
9962
10944
|
return;
|
|
@@ -9979,7 +10961,7 @@ publish.command("status").description("Show staged publish files").option("--jso
|
|
|
9979
10961
|
);
|
|
9980
10962
|
});
|
|
9981
10963
|
publish.command("diff").description("Show staged files that changed since add").action(async () => {
|
|
9982
|
-
const rows = await statusPublishState((0,
|
|
10964
|
+
const rows = await statusPublishState((0, import_node_process6.cwd)());
|
|
9983
10965
|
const changed = rows.filter((row) => row.changed);
|
|
9984
10966
|
if (changed.length === 0) {
|
|
9985
10967
|
console.log("No staged files changed.");
|
|
@@ -9996,16 +10978,16 @@ publish.command("diff").description("Show staged files that changed since add").
|
|
|
9996
10978
|
);
|
|
9997
10979
|
});
|
|
9998
10980
|
publish.command("validate").description("Validate staged files before publish").action(async () => {
|
|
9999
|
-
const result = await validatePublishState((0,
|
|
10981
|
+
const result = await validatePublishState((0, import_node_process6.cwd)());
|
|
10000
10982
|
console.log(`Valid ${result.manifest.name}@${result.manifest.version} (${result.size} bytes staged).`);
|
|
10001
10983
|
printPublishGuide(
|
|
10002
10984
|
`Validated ${result.manifest.name}@${result.manifest.version}; manifest, entry file, staged hashes, ignored paths, package size, and obvious secrets passed.`,
|
|
10003
10985
|
"Generate a 5-minute token from the dashboard, then run AIPM_TOKEN=<token> aipm publish push."
|
|
10004
10986
|
);
|
|
10005
10987
|
});
|
|
10006
|
-
publish.command("preview").description("Preview exactly what will be uploaded").option("--json", "Print machine-readable JSON").action((opts) => printPublishPreview((0,
|
|
10988
|
+
publish.command("preview").description("Preview exactly what will be uploaded").option("--json", "Print machine-readable JSON").action((opts) => printPublishPreview((0, import_node_process6.cwd)(), opts.json));
|
|
10007
10989
|
publish.command("push").description("Publish staged files").option("--registry <url>", "Registry base URL").option("--token <token>", "Publish token").option("--yes", "Confirm upload without an extra reminder").option("--verbose", "Print extra diagnostic output").action(async (opts) => {
|
|
10008
|
-
const root = (0,
|
|
10990
|
+
const root = (0, import_node_process6.cwd)();
|
|
10009
10991
|
const { manifest } = await validatePublishState(root);
|
|
10010
10992
|
const registry = registryFromEnvOrDefault(opts.registry);
|
|
10011
10993
|
const tarball = await packStagedFiles(root);
|
|
@@ -10020,7 +11002,7 @@ publish.command("push").description("Publish staged files").option("--registry <
|
|
|
10020
11002
|
console.log(`Verbose: package ${manifest.name}@${manifest.version}; tarball ${tarball.length} bytes.`);
|
|
10021
11003
|
}
|
|
10022
11004
|
for (const entry of staged) console.log(` ${entry.path}`);
|
|
10023
|
-
const result = await publishPackage(registry, manifest.name, tarball, opts.token ??
|
|
11005
|
+
const result = await publishPackage(registry, manifest.name, tarball, opts.token ?? import_node_process6.env.AIPM_TOKEN);
|
|
10024
11006
|
console.log(`Published ${manifest.name}@${result.version}`);
|
|
10025
11007
|
console.log(`View: ${packagePageUrl(manifest.name, result.version)}`);
|
|
10026
11008
|
console.log(`Install: aipm add ${manifest.name}@${result.version} --target ${manifest.targets[0]} --ci`);
|
|
@@ -10052,10 +11034,51 @@ program2.command("list").description("List installed skills and prompts from aip
|
|
|
10052
11034
|
}
|
|
10053
11035
|
}
|
|
10054
11036
|
});
|
|
10055
|
-
program2.command("remove <package-or-prompt>").alias("rm").description("Remove a tracked skill or prompt and its installed files").option(GLOBAL_OPTION, GLOBAL_OPTION_DESC).action(async (pkgArg, opts) => {
|
|
11037
|
+
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).option("--no-init", "Remove by filesystem discovery without reading or writing aipm.package.json / aipm-lock.json").option("--ci", "Non-interactive; fail if --no-init conflicts with an initialized project").action(async (pkgArg, opts) => {
|
|
10056
11038
|
const scope = { global: opts.global };
|
|
10057
11039
|
const configRoot = resolveConfigRoot(scope);
|
|
10058
|
-
const
|
|
11040
|
+
const installRoot = resolveInstallRoot(scope);
|
|
11041
|
+
let project = await readProjectPackageJson(configRoot);
|
|
11042
|
+
let useNoInit = await resolveNoInitMode({
|
|
11043
|
+
requested: opts.init === false,
|
|
11044
|
+
projectExists: Boolean(project),
|
|
11045
|
+
ci: opts.ci
|
|
11046
|
+
});
|
|
11047
|
+
if (!useNoInit && !project) {
|
|
11048
|
+
const ensured = await ensureProjectOrOffer({
|
|
11049
|
+
scope,
|
|
11050
|
+
project,
|
|
11051
|
+
ci: opts.ci,
|
|
11052
|
+
registry: DEFAULT_REGISTRY,
|
|
11053
|
+
allowNoInit: true,
|
|
11054
|
+
command: "remove"
|
|
11055
|
+
});
|
|
11056
|
+
if (ensured.action === "no-init") useNoInit = true;
|
|
11057
|
+
else project = ensured.project;
|
|
11058
|
+
}
|
|
11059
|
+
if (useNoInit) {
|
|
11060
|
+
if (parsePromptUrl(pkgArg)) {
|
|
11061
|
+
await resolvePromptRequiresTracking({ ci: opts.ci, command: "remove" });
|
|
11062
|
+
useNoInit = false;
|
|
11063
|
+
if (!project) {
|
|
11064
|
+
project = await createProjectPackageJson({
|
|
11065
|
+
scope,
|
|
11066
|
+
registry: DEFAULT_REGISTRY
|
|
11067
|
+
});
|
|
11068
|
+
}
|
|
11069
|
+
} else {
|
|
11070
|
+
const { name: name2 } = parsePackageArg(pkgArg);
|
|
11071
|
+
const removal = await removeUntrackedPackage({
|
|
11072
|
+
installRoot,
|
|
11073
|
+
configRoot,
|
|
11074
|
+
packageName: name2
|
|
11075
|
+
});
|
|
11076
|
+
console.log(
|
|
11077
|
+
`Removed ${removal.removed.length} untracked ${removal.removed.length === 1 ? "path" : "paths"} for ${name2}.`
|
|
11078
|
+
);
|
|
11079
|
+
return;
|
|
11080
|
+
}
|
|
11081
|
+
}
|
|
10059
11082
|
if (!project) throw new Error(initRequiredMessage(scope));
|
|
10060
11083
|
const promptReference = resolveTrackedPrompt(project, pkgArg);
|
|
10061
11084
|
if (promptReference && project.prompts[promptReference.alias] === promptReference.url) {
|
|
@@ -10064,23 +11087,96 @@ program2.command("remove <package-or-prompt>").alias("rm").description("Remove a
|
|
|
10064
11087
|
return;
|
|
10065
11088
|
}
|
|
10066
11089
|
const { name } = parsePackageArg(pkgArg);
|
|
11090
|
+
const lock = await readLockfile(configRoot);
|
|
11091
|
+
const lockEntry = lock?.packages[name];
|
|
11092
|
+
if (lockEntry) {
|
|
11093
|
+
const otherEntries = Object.entries(lock.packages).filter(([packageName]) => packageName !== name).map(([, entry]) => entry);
|
|
11094
|
+
const removal = await removeInstalledPackageFiles({
|
|
11095
|
+
configRoot,
|
|
11096
|
+
installRoot,
|
|
11097
|
+
entry: lockEntry,
|
|
11098
|
+
otherEntries
|
|
11099
|
+
});
|
|
11100
|
+
console.log(`Deleted ${removal.removed} tracked installed ${removal.removed === 1 ? "file" : "files"}.`);
|
|
11101
|
+
if (removal.retainedShared > 0) {
|
|
11102
|
+
console.log(`Retained ${removal.retainedShared} ${removal.retainedShared === 1 ? "file" : "files"} shared with another installed package.`);
|
|
11103
|
+
}
|
|
11104
|
+
}
|
|
10067
11105
|
const packages = { ...project.packages };
|
|
10068
11106
|
delete packages[name];
|
|
10069
11107
|
await writeProjectPackageJson(configRoot, { ...project, packages });
|
|
10070
|
-
const lock = await readLockfile(configRoot);
|
|
10071
11108
|
if (lock) {
|
|
10072
11109
|
const lockPackages = { ...lock.packages };
|
|
10073
11110
|
delete lockPackages[name];
|
|
10074
11111
|
await writeLockfile(configRoot, { ...lock, packages: lockPackages });
|
|
10075
11112
|
}
|
|
10076
11113
|
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
11114
|
});
|
|
10079
|
-
program2.command("update [package]").description("Update one installed package, or all installed packages, to the latest registry version").option(GLOBAL_OPTION, GLOBAL_OPTION_DESC).option("--registry <url>", "Registry base URL").option("--target <tool>", "cursor, claude, codex, or *").option("--token <token>", "Install token for private packages").option("--ci", "Non-interactive").action(async (pkgArg, opts) => {
|
|
11115
|
+
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").option("--no-init", "Update without reading or writing aipm.package.json / aipm-lock.json (requires a package arg; always latest)").action(async (pkgArg, opts) => {
|
|
10080
11116
|
const scope = { global: opts.global };
|
|
10081
11117
|
const configRoot = resolveConfigRoot(scope);
|
|
10082
11118
|
const installRoot = resolveInstallRoot(scope);
|
|
10083
11119
|
let project = await readProjectPackageJson(configRoot);
|
|
11120
|
+
let useNoInit = await resolveNoInitMode({
|
|
11121
|
+
requested: opts.init === false,
|
|
11122
|
+
projectExists: Boolean(project),
|
|
11123
|
+
ci: opts.ci
|
|
11124
|
+
});
|
|
11125
|
+
if (!useNoInit && !project) {
|
|
11126
|
+
const ensured = await ensureProjectOrOffer({
|
|
11127
|
+
scope,
|
|
11128
|
+
project,
|
|
11129
|
+
ci: opts.ci,
|
|
11130
|
+
registry: resolveRegistryUrl(null, opts.registry, DEFAULT_REGISTRY),
|
|
11131
|
+
target: opts.target,
|
|
11132
|
+
allowNoInit: Boolean(pkgArg) && !parsePromptUrl(pkgArg ?? ""),
|
|
11133
|
+
command: "update"
|
|
11134
|
+
});
|
|
11135
|
+
if (ensured.action === "no-init") useNoInit = true;
|
|
11136
|
+
else project = ensured.project;
|
|
11137
|
+
}
|
|
11138
|
+
if (useNoInit) {
|
|
11139
|
+
if (!pkgArg) {
|
|
11140
|
+
throw new Error(
|
|
11141
|
+
`Update with --no-init requires a package: ${recommendCmd("aipm update <@scope/pkg> --no-init")}`
|
|
11142
|
+
);
|
|
11143
|
+
}
|
|
11144
|
+
if (parsePromptUrl(pkgArg)) {
|
|
11145
|
+
await resolvePromptRequiresTracking({ ci: opts.ci, command: "update" });
|
|
11146
|
+
useNoInit = false;
|
|
11147
|
+
if (!project) {
|
|
11148
|
+
project = await createProjectPackageJson({
|
|
11149
|
+
scope,
|
|
11150
|
+
registry: resolveRegistryUrl(null, opts.registry, DEFAULT_REGISTRY),
|
|
11151
|
+
target: opts.target
|
|
11152
|
+
});
|
|
11153
|
+
}
|
|
11154
|
+
} else {
|
|
11155
|
+
const registry2 = resolveRegistryUrl(null, opts.registry, DEFAULT_REGISTRY);
|
|
11156
|
+
const { name } = parsePackageArg(pkgArg);
|
|
11157
|
+
const token2 = await tokenForRead(registry2, opts.token);
|
|
11158
|
+
const version = await latestVersionForPackage(registry2, name, token2);
|
|
11159
|
+
const syntheticProject = {
|
|
11160
|
+
schemaVersion: "0.1",
|
|
11161
|
+
registry: registry2,
|
|
11162
|
+
packages: {},
|
|
11163
|
+
prompts: {}
|
|
11164
|
+
};
|
|
11165
|
+
await installOnePackageWithRecovery({
|
|
11166
|
+
configRoot,
|
|
11167
|
+
installRoot,
|
|
11168
|
+
registry: registry2,
|
|
11169
|
+
name,
|
|
11170
|
+
version,
|
|
11171
|
+
project: syntheticProject,
|
|
11172
|
+
explicitTarget: parseTargetFlag(opts.target),
|
|
11173
|
+
ci: opts.ci,
|
|
11174
|
+
token: token2,
|
|
11175
|
+
track: false
|
|
11176
|
+
});
|
|
11177
|
+
return;
|
|
11178
|
+
}
|
|
11179
|
+
}
|
|
10084
11180
|
if (!project) throw new Error(initRequiredMessage(scope));
|
|
10085
11181
|
const registry = resolveRegistryUrl(project, opts.registry, DEFAULT_REGISTRY);
|
|
10086
11182
|
const requestedPrompt = pkgArg ? resolveTrackedPrompt(project, pkgArg) : null;
|
|
@@ -10094,7 +11190,8 @@ program2.command("update [package]").description("Update one installed package,
|
|
|
10094
11190
|
project,
|
|
10095
11191
|
reference: requestedPrompt,
|
|
10096
11192
|
track: false,
|
|
10097
|
-
updateOnly: true
|
|
11193
|
+
updateOnly: true,
|
|
11194
|
+
ci: opts.ci
|
|
10098
11195
|
});
|
|
10099
11196
|
console.log(result.changed ? `Updated prompt ${result.prompt.title} \u2192 ${result.path}` : `Prompt unchanged: ${result.prompt.title}`);
|
|
10100
11197
|
return;
|
|
@@ -10108,7 +11205,7 @@ program2.command("update [package]").description("Update one installed package,
|
|
|
10108
11205
|
packages: { ...project.packages, [name]: version }
|
|
10109
11206
|
};
|
|
10110
11207
|
await writeProjectPackageJson(configRoot, project);
|
|
10111
|
-
await
|
|
11208
|
+
await installOnePackageWithRecovery({
|
|
10112
11209
|
configRoot,
|
|
10113
11210
|
installRoot,
|
|
10114
11211
|
registry,
|
|
@@ -10130,7 +11227,8 @@ program2.command("update [package]").description("Update one installed package,
|
|
|
10130
11227
|
project,
|
|
10131
11228
|
reference: { ...reference, alias },
|
|
10132
11229
|
track: false,
|
|
10133
|
-
updateOnly: true
|
|
11230
|
+
updateOnly: true,
|
|
11231
|
+
ci: opts.ci
|
|
10134
11232
|
});
|
|
10135
11233
|
console.log(result.changed ? `Updated prompt ${result.prompt.title} \u2192 ${result.path}` : `Prompt unchanged: ${result.prompt.title}`);
|
|
10136
11234
|
}
|
|
@@ -10141,6 +11239,10 @@ program2.command("update [package]").description("Update one installed package,
|
|
|
10141
11239
|
});
|
|
10142
11240
|
program2.command("doctor").description("Check PATH, Node, registry, project config, and publish readiness").option(GLOBAL_OPTION, GLOBAL_OPTION_DESC).option("--registry <url>", "Registry base URL").option("--json", "Print machine-readable JSON").option("--publish", "Only show publish-readiness checks").action((opts) => runDoctor(opts));
|
|
10143
11241
|
program2.parseAsync(normalizedArgv).catch((err) => {
|
|
11242
|
+
if (err.name === "RecoveryCancelledError" || err.name === "SelectCancelledError") {
|
|
11243
|
+
printNote({ type: "info", title: "Cancelled", message: err.message || "Cancelled." });
|
|
11244
|
+
process.exit(130);
|
|
11245
|
+
}
|
|
10144
11246
|
console.error(err.message);
|
|
10145
11247
|
const message = err.message.toLowerCase();
|
|
10146
11248
|
if (message.includes("token") || message.includes("unauthorized") || message.includes("forbidden")) {
|