@aipm-registry/cli 0.4.3 → 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 +1227 -296
- package/dist/bin.cjs.map +4 -4
- package/package.json +9 -9
package/dist/bin.cjs
CHANGED
|
@@ -4319,7 +4319,7 @@ var init_dist = __esm({
|
|
|
4319
4319
|
// ../../packages/engine/dist/detect-tools.js
|
|
4320
4320
|
async function pathExists(path2) {
|
|
4321
4321
|
try {
|
|
4322
|
-
await (0,
|
|
4322
|
+
await (0, import_promises4.access)(path2);
|
|
4323
4323
|
return true;
|
|
4324
4324
|
} catch {
|
|
4325
4325
|
return false;
|
|
@@ -4327,11 +4327,11 @@ async function pathExists(path2) {
|
|
|
4327
4327
|
}
|
|
4328
4328
|
async function detectToolsInProject(projectRoot) {
|
|
4329
4329
|
const detected = [];
|
|
4330
|
-
if (await pathExists((0,
|
|
4330
|
+
if (await pathExists((0, import_node_path5.join)(projectRoot, ".cursor")))
|
|
4331
4331
|
detected.push("cursor");
|
|
4332
|
-
if (await pathExists((0,
|
|
4332
|
+
if (await pathExists((0, import_node_path5.join)(projectRoot, ".claude")))
|
|
4333
4333
|
detected.push("claude");
|
|
4334
|
-
if (await pathExists((0,
|
|
4334
|
+
if (await pathExists((0, import_node_path5.join)(projectRoot, ".codex")))
|
|
4335
4335
|
detected.push("codex");
|
|
4336
4336
|
return detected;
|
|
4337
4337
|
}
|
|
@@ -4342,7 +4342,6 @@ function manifestAllowsTool(manifest, tool) {
|
|
|
4342
4342
|
}
|
|
4343
4343
|
async function resolveInstallTools(options) {
|
|
4344
4344
|
const { manifest } = options;
|
|
4345
|
-
const expandedAllowed = expandTargets(manifest.targets);
|
|
4346
4345
|
if (options.explicitTarget) {
|
|
4347
4346
|
if (options.explicitTarget === "*") {
|
|
4348
4347
|
return [...ALL_TOOLS];
|
|
@@ -4363,70 +4362,68 @@ async function resolveInstallTools(options) {
|
|
|
4363
4362
|
const preferred = expandTargets(options.preferredTools ?? []).filter((t) => manifestAllowsTool(manifest, t));
|
|
4364
4363
|
if (preferred.length > 0)
|
|
4365
4364
|
return preferred;
|
|
4366
|
-
if (manifest.targets.includes("*"))
|
|
4367
|
-
return expandedAllowed;
|
|
4368
4365
|
return [];
|
|
4369
4366
|
}
|
|
4370
|
-
var
|
|
4367
|
+
var import_promises4, import_node_path5;
|
|
4371
4368
|
var init_detect_tools = __esm({
|
|
4372
4369
|
"../../packages/engine/dist/detect-tools.js"() {
|
|
4373
4370
|
"use strict";
|
|
4374
|
-
|
|
4375
|
-
|
|
4371
|
+
import_promises4 = require("node:fs/promises");
|
|
4372
|
+
import_node_path5 = require("node:path");
|
|
4376
4373
|
init_dist();
|
|
4377
4374
|
}
|
|
4378
4375
|
});
|
|
4379
4376
|
|
|
4380
4377
|
// ../../packages/adapter-sdk/dist/index.js
|
|
4381
4378
|
async function writeSkillDirectory(skillDir, input2) {
|
|
4382
|
-
const entryPath = (0,
|
|
4379
|
+
const entryPath = (0, import_node_path6.join)(skillDir, "SKILL.md");
|
|
4383
4380
|
const seen = /* @__PURE__ */ new Set(["skill.md"]);
|
|
4384
4381
|
const files = (input2.supportingFiles ?? []).map((file) => {
|
|
4385
|
-
const normalized = (0,
|
|
4386
|
-
const target = (0,
|
|
4387
|
-
const fromSkillDir = (0,
|
|
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);
|
|
4388
4385
|
return { file, normalized, target, fromSkillDir, key: fromSkillDir.toLowerCase() };
|
|
4389
4386
|
});
|
|
4390
4387
|
for (const { file, normalized, fromSkillDir, key } of files) {
|
|
4391
|
-
if (!normalized || normalized === "." || (0,
|
|
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)) {
|
|
4392
4389
|
throw new Error(`Unsafe or duplicate skill supporting file path: ${file.path}`);
|
|
4393
4390
|
}
|
|
4394
4391
|
seen.add(key);
|
|
4395
4392
|
}
|
|
4396
|
-
await (0,
|
|
4397
|
-
await (0,
|
|
4393
|
+
await (0, import_promises5.mkdir)(skillDir, { recursive: true });
|
|
4394
|
+
await (0, import_promises5.writeFile)(entryPath, input2.skillMarkdown, "utf8");
|
|
4398
4395
|
const writtenPaths = [entryPath];
|
|
4399
4396
|
for (const { file, target } of files) {
|
|
4400
|
-
await (0,
|
|
4401
|
-
await (0,
|
|
4397
|
+
await (0, import_promises5.mkdir)((0, import_node_path6.dirname)(target), { recursive: true });
|
|
4398
|
+
await (0, import_promises5.writeFile)(target, file.content);
|
|
4402
4399
|
if (file.mode !== void 0)
|
|
4403
|
-
await (0,
|
|
4400
|
+
await (0, import_promises5.chmod)(target, file.mode & 511);
|
|
4404
4401
|
writtenPaths.push(target);
|
|
4405
4402
|
}
|
|
4406
4403
|
return { writtenPaths };
|
|
4407
4404
|
}
|
|
4408
|
-
var
|
|
4405
|
+
var import_promises5, import_node_path6;
|
|
4409
4406
|
var init_dist2 = __esm({
|
|
4410
4407
|
"../../packages/adapter-sdk/dist/index.js"() {
|
|
4411
4408
|
"use strict";
|
|
4412
|
-
|
|
4413
|
-
|
|
4409
|
+
import_promises5 = require("node:fs/promises");
|
|
4410
|
+
import_node_path6 = require("node:path");
|
|
4414
4411
|
}
|
|
4415
4412
|
});
|
|
4416
4413
|
|
|
4417
4414
|
// ../../packages/adapter-claude/dist/index.js
|
|
4418
|
-
var
|
|
4415
|
+
var import_node_path7, ClaudeSkillAdapter, claudeSkillAdapter;
|
|
4419
4416
|
var init_dist3 = __esm({
|
|
4420
4417
|
"../../packages/adapter-claude/dist/index.js"() {
|
|
4421
4418
|
"use strict";
|
|
4422
|
-
|
|
4419
|
+
import_node_path7 = require("node:path");
|
|
4423
4420
|
init_dist2();
|
|
4424
4421
|
init_dist();
|
|
4425
4422
|
ClaudeSkillAdapter = class {
|
|
4426
4423
|
tool = "claude";
|
|
4427
4424
|
async installSkill(input2) {
|
|
4428
4425
|
const short = shortNameFromScopeName(input2.packageName);
|
|
4429
|
-
const skillDir = (0,
|
|
4426
|
+
const skillDir = (0, import_node_path7.join)(input2.projectRoot, ".claude", "skills", short);
|
|
4430
4427
|
return writeSkillDirectory(skillDir, input2);
|
|
4431
4428
|
}
|
|
4432
4429
|
};
|
|
@@ -4435,21 +4432,21 @@ var init_dist3 = __esm({
|
|
|
4435
4432
|
});
|
|
4436
4433
|
|
|
4437
4434
|
// ../../packages/adapter-cursor/dist/index.js
|
|
4438
|
-
var
|
|
4435
|
+
var import_promises6, import_node_path8, CursorSkillAdapter, cursorSkillAdapter;
|
|
4439
4436
|
var init_dist4 = __esm({
|
|
4440
4437
|
"../../packages/adapter-cursor/dist/index.js"() {
|
|
4441
4438
|
"use strict";
|
|
4442
|
-
|
|
4443
|
-
|
|
4439
|
+
import_promises6 = require("node:fs/promises");
|
|
4440
|
+
import_node_path8 = require("node:path");
|
|
4444
4441
|
init_dist();
|
|
4445
4442
|
CursorSkillAdapter = class {
|
|
4446
4443
|
tool = "cursor";
|
|
4447
4444
|
async installSkill(input2) {
|
|
4448
4445
|
const short = shortNameFromScopeName(input2.packageName);
|
|
4449
|
-
const skillsDir = (0,
|
|
4450
|
-
await (0,
|
|
4451
|
-
const filePath = (0,
|
|
4452
|
-
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");
|
|
4453
4450
|
return { writtenPaths: [filePath] };
|
|
4454
4451
|
}
|
|
4455
4452
|
};
|
|
@@ -4458,18 +4455,18 @@ var init_dist4 = __esm({
|
|
|
4458
4455
|
});
|
|
4459
4456
|
|
|
4460
4457
|
// ../../packages/adapter-codex/dist/index.js
|
|
4461
|
-
var
|
|
4458
|
+
var import_node_path9, CodexSkillAdapter, codexSkillAdapter;
|
|
4462
4459
|
var init_dist5 = __esm({
|
|
4463
4460
|
"../../packages/adapter-codex/dist/index.js"() {
|
|
4464
4461
|
"use strict";
|
|
4465
|
-
|
|
4462
|
+
import_node_path9 = require("node:path");
|
|
4466
4463
|
init_dist2();
|
|
4467
4464
|
init_dist();
|
|
4468
4465
|
CodexSkillAdapter = class {
|
|
4469
4466
|
tool = "codex";
|
|
4470
4467
|
async installSkill(input2) {
|
|
4471
4468
|
const short = shortNameFromScopeName(input2.packageName);
|
|
4472
|
-
const skillDir = (0,
|
|
4469
|
+
const skillDir = (0, import_node_path9.join)(input2.projectRoot, ".agents", "skills", short);
|
|
4473
4470
|
return writeSkillDirectory(skillDir, input2);
|
|
4474
4471
|
}
|
|
4475
4472
|
};
|
|
@@ -7905,34 +7902,107 @@ var program = new Command();
|
|
|
7905
7902
|
var import_node_crypto3 = require("node:crypto");
|
|
7906
7903
|
var import_node_child_process4 = require("node:child_process");
|
|
7907
7904
|
var import_node_http = require("node:http");
|
|
7908
|
-
var
|
|
7909
|
-
var
|
|
7910
|
-
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");
|
|
7911
7908
|
init_dist();
|
|
7912
|
-
init_dist6();
|
|
7913
7909
|
|
|
7914
7910
|
// src/pack.ts
|
|
7915
7911
|
var import_node_child_process2 = require("node:child_process");
|
|
7916
7912
|
var import_node_util3 = require("node:util");
|
|
7917
|
-
var
|
|
7918
|
-
var
|
|
7913
|
+
var import_promises2 = require("node:fs/promises");
|
|
7914
|
+
var import_node_path3 = require("node:path");
|
|
7919
7915
|
var import_node_os = require("node:os");
|
|
7920
7916
|
|
|
7921
7917
|
// src/publish-state.ts
|
|
7922
7918
|
var import_node_crypto = require("node:crypto");
|
|
7923
|
-
var
|
|
7924
|
-
var
|
|
7919
|
+
var import_promises = require("node:fs/promises");
|
|
7920
|
+
var import_node_path2 = require("node:path");
|
|
7925
7921
|
init_dist();
|
|
7926
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
|
+
|
|
7927
7996
|
// src/recommend-cmd.ts
|
|
7928
|
-
function recommendCmd(command, stream = process.stderr) {
|
|
7997
|
+
function recommendCmd(command, stream = process.stderr, env2) {
|
|
7929
7998
|
const quoted = `"${command}"`;
|
|
7930
|
-
|
|
7931
|
-
return
|
|
7999
|
+
const theme = createTheme(stream, env2);
|
|
8000
|
+
if (!theme.color) return quoted;
|
|
8001
|
+
return `${theme.accent}${quoted}${theme.reset}`;
|
|
7932
8002
|
}
|
|
7933
8003
|
|
|
7934
8004
|
// src/publish-state.ts
|
|
7935
|
-
var STATE_PATH = (0,
|
|
8005
|
+
var STATE_PATH = (0, import_node_path2.join)(".aipm", "publish-state.json");
|
|
7936
8006
|
var IGNORE_FILE = ".aipmignore";
|
|
7937
8007
|
var MAX_PACKAGE_BYTES = 50 * 1024 * 1024;
|
|
7938
8008
|
var EXCLUDED_SEGMENTS = /* @__PURE__ */ new Set([".aipm", ".git", "node_modules", "dist", ".next"]);
|
|
@@ -7947,11 +8017,11 @@ var SECRET_PATTERNS = [
|
|
|
7947
8017
|
/<publishData/i
|
|
7948
8018
|
];
|
|
7949
8019
|
function publishStatePath(root) {
|
|
7950
|
-
return (0,
|
|
8020
|
+
return (0, import_node_path2.join)(root, STATE_PATH);
|
|
7951
8021
|
}
|
|
7952
8022
|
function normalizePublishPath(root, filePath) {
|
|
7953
|
-
const abs = (0,
|
|
7954
|
-
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("/");
|
|
7955
8025
|
if (!rel || rel === ".") return ".";
|
|
7956
8026
|
if (rel.startsWith("../") || rel === ".." || rel.startsWith("/")) {
|
|
7957
8027
|
throw new Error(`Path is outside the skill folder: ${filePath}`);
|
|
@@ -7968,7 +8038,7 @@ function isExcludedPublishPath(rel) {
|
|
|
7968
8038
|
}
|
|
7969
8039
|
async function readAipmIgnore(root) {
|
|
7970
8040
|
try {
|
|
7971
|
-
const raw = await (0,
|
|
8041
|
+
const raw = await (0, import_promises.readFile)((0, import_node_path2.join)(root, IGNORE_FILE), "utf8");
|
|
7972
8042
|
return raw.split(/\r?\n/).map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
|
|
7973
8043
|
} catch {
|
|
7974
8044
|
return [];
|
|
@@ -7989,7 +8059,7 @@ async function isIgnoredByAipmIgnore(root, rel) {
|
|
|
7989
8059
|
return rules.some((rule) => matchesIgnoreRule(rel, rule));
|
|
7990
8060
|
}
|
|
7991
8061
|
async function assertNoObviousSecrets(path2, rel) {
|
|
7992
|
-
const data = await (0,
|
|
8062
|
+
const data = await (0, import_promises.readFile)(path2);
|
|
7993
8063
|
if (data.includes(0)) return;
|
|
7994
8064
|
const text = data.toString("utf8");
|
|
7995
8065
|
if (SECRET_PATTERNS.some((pattern) => pattern.test(text))) {
|
|
@@ -7997,7 +8067,7 @@ async function assertNoObviousSecrets(path2, rel) {
|
|
|
7997
8067
|
}
|
|
7998
8068
|
}
|
|
7999
8069
|
async function fileHash(path2) {
|
|
8000
|
-
const data = await (0,
|
|
8070
|
+
const data = await (0, import_promises.readFile)(path2);
|
|
8001
8071
|
return {
|
|
8002
8072
|
hash: (0, import_node_crypto.createHash)("sha256").update(data).digest("hex"),
|
|
8003
8073
|
size: data.length
|
|
@@ -8005,7 +8075,7 @@ async function fileHash(path2) {
|
|
|
8005
8075
|
}
|
|
8006
8076
|
async function readPublishState(root) {
|
|
8007
8077
|
try {
|
|
8008
|
-
const raw = await (0,
|
|
8078
|
+
const raw = await (0, import_promises.readFile)(publishStatePath(root), "utf8");
|
|
8009
8079
|
const parsed = JSON.parse(raw);
|
|
8010
8080
|
return {
|
|
8011
8081
|
schemaVersion: "0.1",
|
|
@@ -8017,18 +8087,18 @@ async function readPublishState(root) {
|
|
|
8017
8087
|
}
|
|
8018
8088
|
async function writePublishState(root, state) {
|
|
8019
8089
|
const path2 = publishStatePath(root);
|
|
8020
|
-
await (0,
|
|
8090
|
+
await (0, import_promises.mkdir)((0, import_node_path2.dirname)(path2), { recursive: true });
|
|
8021
8091
|
const files = [...state.files].sort((a, b) => a.path.localeCompare(b.path));
|
|
8022
|
-
await (0,
|
|
8092
|
+
await (0, import_promises.writeFile)(path2, JSON.stringify({ schemaVersion: "0.1", files }, null, 2) + "\n", "utf8");
|
|
8023
8093
|
}
|
|
8024
8094
|
async function expandPublishPath(root, rel) {
|
|
8025
8095
|
if (isExcludedPublishPath(rel)) return [];
|
|
8026
8096
|
if (await isIgnoredByAipmIgnore(root, rel)) return [];
|
|
8027
|
-
const abs = (0,
|
|
8028
|
-
const info = await (0,
|
|
8097
|
+
const abs = (0, import_node_path2.join)(root, rel);
|
|
8098
|
+
const info = await (0, import_promises.stat)(abs);
|
|
8029
8099
|
if (info.isFile()) return [rel];
|
|
8030
8100
|
if (!info.isDirectory()) return [];
|
|
8031
|
-
const entries = await (0,
|
|
8101
|
+
const entries = await (0, import_promises.readdir)(abs);
|
|
8032
8102
|
const nested = await Promise.all(
|
|
8033
8103
|
entries.map((entry) => expandPublishPath(root, `${rel === "." ? "" : `${rel}/`}${entry}`))
|
|
8034
8104
|
);
|
|
@@ -8044,7 +8114,7 @@ async function addPublishFiles(root, paths) {
|
|
|
8044
8114
|
for (const rel of files) {
|
|
8045
8115
|
if (isExcludedPublishPath(rel)) continue;
|
|
8046
8116
|
if (await isIgnoredByAipmIgnore(root, rel)) continue;
|
|
8047
|
-
const hashed = await fileHash((0,
|
|
8117
|
+
const hashed = await fileHash((0, import_node_path2.join)(root, rel));
|
|
8048
8118
|
byPath.set(rel, { path: rel, ...hashed });
|
|
8049
8119
|
}
|
|
8050
8120
|
}
|
|
@@ -8063,11 +8133,11 @@ async function removePublishFiles(root, paths) {
|
|
|
8063
8133
|
return next;
|
|
8064
8134
|
}
|
|
8065
8135
|
async function resetPublishState(root) {
|
|
8066
|
-
await (0,
|
|
8136
|
+
await (0, import_promises.rm)(publishStatePath(root), { force: true });
|
|
8067
8137
|
}
|
|
8068
8138
|
async function readManifest(root) {
|
|
8069
8139
|
try {
|
|
8070
|
-
const raw = await (0,
|
|
8140
|
+
const raw = await (0, import_promises.readFile)((0, import_node_path2.join)(root, "aipm.manifest.json"), "utf8");
|
|
8071
8141
|
return PackageManifestSchema.parse(JSON.parse(raw));
|
|
8072
8142
|
} catch (error) {
|
|
8073
8143
|
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
@@ -8104,8 +8174,8 @@ async function validatePublishState(root) {
|
|
|
8104
8174
|
if (await isIgnoredByAipmIgnore(root, entry.path)) {
|
|
8105
8175
|
throw new Error(`Refusing to publish ignored path: ${entry.path}`);
|
|
8106
8176
|
}
|
|
8107
|
-
const abs = (0,
|
|
8108
|
-
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);
|
|
8109
8179
|
if (!info?.isFile()) throw new Error(`Staged file is missing: ${entry.path}`);
|
|
8110
8180
|
const hashed = await fileHash(abs);
|
|
8111
8181
|
if (hashed.hash !== entry.hash) {
|
|
@@ -8123,7 +8193,7 @@ async function statusPublishState(root) {
|
|
|
8123
8193
|
const state = await readPublishState(root);
|
|
8124
8194
|
const rows = [];
|
|
8125
8195
|
for (const entry of state.files) {
|
|
8126
|
-
const current = await fileHash((0,
|
|
8196
|
+
const current = await fileHash((0, import_node_path2.join)(root, entry.path)).catch(() => null);
|
|
8127
8197
|
rows.push({ ...entry, changed: !current || current.hash !== entry.hash });
|
|
8128
8198
|
}
|
|
8129
8199
|
return rows;
|
|
@@ -8131,9 +8201,9 @@ async function statusPublishState(root) {
|
|
|
8131
8201
|
async function copyStagedFiles(root, destination) {
|
|
8132
8202
|
const state = await readPublishState(root);
|
|
8133
8203
|
for (const entry of state.files) {
|
|
8134
|
-
const target = (0,
|
|
8135
|
-
await (0,
|
|
8136
|
-
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);
|
|
8137
8207
|
}
|
|
8138
8208
|
}
|
|
8139
8209
|
|
|
@@ -8147,20 +8217,20 @@ async function packDirectory(dir) {
|
|
|
8147
8217
|
return Buffer.isBuffer(stdout) ? stdout : Buffer.from(stdout);
|
|
8148
8218
|
}
|
|
8149
8219
|
async function packStagedFiles(root) {
|
|
8150
|
-
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-"));
|
|
8151
8221
|
try {
|
|
8152
8222
|
await copyStagedFiles(root, tempDir);
|
|
8153
8223
|
return await packDirectory(tempDir);
|
|
8154
8224
|
} finally {
|
|
8155
|
-
await (0,
|
|
8225
|
+
await (0, import_promises2.rm)(tempDir, { recursive: true, force: true });
|
|
8156
8226
|
}
|
|
8157
8227
|
}
|
|
8158
8228
|
async function unpackTarballToDirectory(tarball) {
|
|
8159
8229
|
const { mkdtemp: mkdtemp2, writeFile: writeFile8 } = await import("node:fs/promises");
|
|
8160
|
-
const { join:
|
|
8230
|
+
const { join: join17 } = await import("node:path");
|
|
8161
8231
|
const { tmpdir: tmpdir2 } = await import("node:os");
|
|
8162
|
-
const tempDir = await mkdtemp2(
|
|
8163
|
-
const tgzPath =
|
|
8232
|
+
const tempDir = await mkdtemp2(join17(tmpdir2(), "aipm-install-"));
|
|
8233
|
+
const tgzPath = join17(tempDir, "pkg.tgz");
|
|
8164
8234
|
await writeFile8(tgzPath, tarball);
|
|
8165
8235
|
await execFileAsync("tar", ["-xzf", tgzPath, "-C", tempDir]);
|
|
8166
8236
|
return tempDir;
|
|
@@ -8222,7 +8292,7 @@ async function assertRegistryReachable(registry) {
|
|
|
8222
8292
|
}
|
|
8223
8293
|
async function publishPackage(registry, name, tarball, token) {
|
|
8224
8294
|
const base = registry.replace(/\/$/, "");
|
|
8225
|
-
const url = `${base}/v1/
|
|
8295
|
+
const url = `${base}/v1/skills/${encodePackageName(name)}/versions`;
|
|
8226
8296
|
const form = new FormData();
|
|
8227
8297
|
form.append("tarball", new Blob([tarball]), "package.tgz");
|
|
8228
8298
|
const headers = token ? { Authorization: `Bearer ${token}` } : void 0;
|
|
@@ -8239,12 +8309,12 @@ async function searchPackages(registry, query, limit = 20, token) {
|
|
|
8239
8309
|
if (query) params.set("q", query);
|
|
8240
8310
|
params.set("limit", String(limit));
|
|
8241
8311
|
if (token) params.set("includePrivate", "true");
|
|
8242
|
-
const res = await registryFetch(`${base}/v1/
|
|
8312
|
+
const res = await registryFetch(`${base}/v1/skills?${params}`, base, {
|
|
8243
8313
|
headers: registryAuthHeaders(token)
|
|
8244
8314
|
});
|
|
8245
8315
|
if (!res.ok) throw new Error(`Search failed: ${res.status}`);
|
|
8246
8316
|
const data = await res.json();
|
|
8247
|
-
return data.packages ?? [];
|
|
8317
|
+
return data.skills ?? data.packages ?? [];
|
|
8248
8318
|
}
|
|
8249
8319
|
async function exchangeCliAuthCode(registry, input2) {
|
|
8250
8320
|
const base = registry.replace(/\/$/, "");
|
|
@@ -8293,7 +8363,7 @@ async function logoutCliAuth(registry, refreshToken) {
|
|
|
8293
8363
|
}
|
|
8294
8364
|
async function fetchPackageMetadata(registry, name, version, token) {
|
|
8295
8365
|
const base = registry.replace(/\/$/, "");
|
|
8296
|
-
const url = `${base}/v1/
|
|
8366
|
+
const url = `${base}/v1/skills/${encodePackageName(name)}/versions/${version}`;
|
|
8297
8367
|
const res = await registryFetch(url, base, { headers: registryAuthHeaders(token) });
|
|
8298
8368
|
if (!res.ok) throw new Error(privateInstallHint(`${name}@${version}`, res.status, token));
|
|
8299
8369
|
const data = await res.json();
|
|
@@ -8301,7 +8371,7 @@ async function fetchPackageMetadata(registry, name, version, token) {
|
|
|
8301
8371
|
}
|
|
8302
8372
|
async function fetchPackageTarball(registry, name, version, token) {
|
|
8303
8373
|
const base = registry.replace(/\/$/, "");
|
|
8304
|
-
const url = `${base}/v1/
|
|
8374
|
+
const url = `${base}/v1/skills/${encodePackageName(name)}/versions/${version}/tarball`;
|
|
8305
8375
|
const res = await registryFetch(url, base, { headers: registryAuthHeaders(token) });
|
|
8306
8376
|
if (!res.ok) throw new Error(privateInstallHint(`${name}@${version}`, res.status, token));
|
|
8307
8377
|
const ab = await res.arrayBuffer();
|
|
@@ -8309,7 +8379,7 @@ async function fetchPackageTarball(registry, name, version, token) {
|
|
|
8309
8379
|
}
|
|
8310
8380
|
async function recordPackageInstall(registry, name, token) {
|
|
8311
8381
|
const base = registry.replace(/\/$/, "");
|
|
8312
|
-
const url = `${base}/v1/
|
|
8382
|
+
const url = `${base}/v1/skills/${encodePackageName(name)}/installs`;
|
|
8313
8383
|
const res = await registryFetch(url, base, {
|
|
8314
8384
|
method: "POST",
|
|
8315
8385
|
headers: registryAuthHeaders(token)
|
|
@@ -8357,16 +8427,16 @@ async function publishPrompt(registry, input2, accessToken, sampleImage) {
|
|
|
8357
8427
|
}
|
|
8358
8428
|
|
|
8359
8429
|
// src/auth-store.ts
|
|
8360
|
-
var
|
|
8430
|
+
var import_promises3 = require("node:fs/promises");
|
|
8361
8431
|
var import_node_os2 = require("node:os");
|
|
8362
|
-
var
|
|
8363
|
-
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");
|
|
8364
8434
|
function normalizeRegistry(registry) {
|
|
8365
8435
|
return registry.replace(/\/$/, "");
|
|
8366
8436
|
}
|
|
8367
8437
|
async function readAuthStore() {
|
|
8368
8438
|
try {
|
|
8369
|
-
const raw = await (0,
|
|
8439
|
+
const raw = await (0, import_promises3.readFile)(AUTH_FILE, "utf8");
|
|
8370
8440
|
const parsed = JSON.parse(raw);
|
|
8371
8441
|
return { registries: parsed.registries ?? {} };
|
|
8372
8442
|
} catch {
|
|
@@ -8374,9 +8444,9 @@ async function readAuthStore() {
|
|
|
8374
8444
|
}
|
|
8375
8445
|
}
|
|
8376
8446
|
async function writeAuthStore(store) {
|
|
8377
|
-
await (0,
|
|
8378
|
-
await (0,
|
|
8379
|
-
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);
|
|
8380
8450
|
}
|
|
8381
8451
|
async function getStoredRegistryAuth(registry) {
|
|
8382
8452
|
const store = await readAuthStore();
|
|
@@ -8481,37 +8551,389 @@ function parseTargetsFlag(value) {
|
|
|
8481
8551
|
return unique;
|
|
8482
8552
|
}
|
|
8483
8553
|
|
|
8484
|
-
// src/
|
|
8485
|
-
var readline = __toESM(require("node:readline/promises"), 1);
|
|
8554
|
+
// src/ui/select.ts
|
|
8486
8555
|
var import_node_process2 = require("node:process");
|
|
8487
|
-
|
|
8488
|
-
|
|
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();
|
|
8489
8694
|
try {
|
|
8490
|
-
|
|
8491
|
-
const
|
|
8492
|
-
|
|
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
|
+
`
|
|
8493
8840
|
);
|
|
8494
|
-
|
|
8495
|
-
|
|
8496
|
-
|
|
8841
|
+
},
|
|
8842
|
+
fail(finalText = text) {
|
|
8843
|
+
stop();
|
|
8844
|
+
if (disabled) {
|
|
8845
|
+
stream.write(`${finalText}
|
|
8846
|
+
`);
|
|
8847
|
+
return;
|
|
8497
8848
|
}
|
|
8498
|
-
|
|
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;
|
|
8499
8864
|
}
|
|
8500
|
-
} finally {
|
|
8501
|
-
rl.close();
|
|
8502
8865
|
}
|
|
8503
8866
|
}
|
|
8504
|
-
|
|
8505
|
-
|
|
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
|
+
}
|
|
8506
8891
|
try {
|
|
8507
|
-
|
|
8508
|
-
|
|
8509
|
-
|
|
8510
|
-
|
|
8511
|
-
|
|
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;
|
|
8512
8901
|
}
|
|
8513
8902
|
}
|
|
8514
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
|
+
|
|
8515
8937
|
// src/install-one.ts
|
|
8516
8938
|
function normalizeRelativePath(path2) {
|
|
8517
8939
|
return (0, import_node_path11.normalize)(path2).split(import_node_path11.sep).join("/");
|
|
@@ -8598,11 +9020,27 @@ async function collectSkillSupportingFiles(packageRoot, manifest) {
|
|
|
8598
9020
|
async function copyMainFile(input2) {
|
|
8599
9021
|
const source = await assertSourceFile(input2.packageRoot, input2.file.from);
|
|
8600
9022
|
const target = safeJoin(input2.installRoot, input2.file.to);
|
|
8601
|
-
|
|
9023
|
+
let overwrite = input2.file.overwrite ?? "fail";
|
|
8602
9024
|
const exists = await (0, import_promises8.stat)(target).catch(() => null);
|
|
8603
9025
|
if (exists) {
|
|
8604
9026
|
if (overwrite === "skip") return null;
|
|
8605
|
-
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
|
+
}
|
|
8606
9044
|
}
|
|
8607
9045
|
await (0, import_promises8.mkdir)((0, import_node_path11.dirname)(target), { recursive: true });
|
|
8608
9046
|
await (0, import_promises8.cp)(source, target, { force: overwrite === "replace" });
|
|
@@ -8623,7 +9061,8 @@ async function installPackageAssets(options) {
|
|
|
8623
9061
|
const copied = await copyMainFile({
|
|
8624
9062
|
packageRoot: options.packageRoot,
|
|
8625
9063
|
installRoot: options.installRoot,
|
|
8626
|
-
file
|
|
9064
|
+
file,
|
|
9065
|
+
ci: options.ci
|
|
8627
9066
|
});
|
|
8628
9067
|
if (copied) assets.main.push(copied);
|
|
8629
9068
|
}
|
|
@@ -8660,95 +9099,135 @@ function printPostInstallNotice(name, postInstall) {
|
|
|
8660
9099
|
async function installOnePackage(options) {
|
|
8661
9100
|
const configRoot = options.configRoot;
|
|
8662
9101
|
const installRoot = options.installRoot ?? configRoot;
|
|
8663
|
-
|
|
8664
|
-
|
|
8665
|
-
options.
|
|
8666
|
-
options.name,
|
|
8667
|
-
options.version,
|
|
8668
|
-
options.token
|
|
8669
|
-
);
|
|
8670
|
-
if (deprecated) {
|
|
8671
|
-
console.warn(
|
|
8672
|
-
`Warning: ${options.name} is deprecated${deprecated.message ? `: ${deprecated.message}` : ""}.`
|
|
8673
|
-
);
|
|
8674
|
-
}
|
|
8675
|
-
let explicitTarget = options.explicitTarget;
|
|
8676
|
-
let preferredTools = options.project.preferredTools;
|
|
8677
|
-
const { resolveInstallTools: resolveInstallTools2 } = await Promise.resolve().then(() => (init_dist6(), dist_exports));
|
|
8678
|
-
let tools = await resolveInstallTools2({
|
|
8679
|
-
projectRoot: installRoot,
|
|
8680
|
-
manifest,
|
|
8681
|
-
preferredTools,
|
|
8682
|
-
explicitTarget
|
|
9102
|
+
const spinner = createSpinner({
|
|
9103
|
+
text: `Fetching ${options.name}@${options.version}`,
|
|
9104
|
+
disabled: options.ci
|
|
8683
9105
|
});
|
|
8684
|
-
|
|
8685
|
-
if (options.ci) {
|
|
8686
|
-
throw new Error(
|
|
8687
|
-
"No tool detected. Use --target cursor|claude|codex|* in CI mode."
|
|
8688
|
-
);
|
|
8689
|
-
}
|
|
8690
|
-
const choice = await promptForTool();
|
|
8691
|
-
explicitTarget = choice;
|
|
8692
|
-
preferredTools = [choice];
|
|
8693
|
-
tools = await resolveInstallTools2({
|
|
8694
|
-
projectRoot: installRoot,
|
|
8695
|
-
manifest,
|
|
8696
|
-
preferredTools,
|
|
8697
|
-
explicitTarget
|
|
8698
|
-
});
|
|
8699
|
-
}
|
|
8700
|
-
const tarball = await fetchPackageTarball(
|
|
8701
|
-
options.registry,
|
|
8702
|
-
options.name,
|
|
8703
|
-
options.version,
|
|
8704
|
-
options.token
|
|
8705
|
-
);
|
|
8706
|
-
const packageRoot = await unpackTarballToDirectory(tarball);
|
|
9106
|
+
spinner.start();
|
|
8707
9107
|
try {
|
|
8708
|
-
|
|
8709
|
-
const
|
|
8710
|
-
|
|
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({
|
|
8711
9128
|
projectRoot: installRoot,
|
|
8712
9129
|
manifest,
|
|
8713
|
-
skillMarkdown,
|
|
8714
|
-
supportingFiles,
|
|
8715
9130
|
preferredTools,
|
|
8716
9131
|
explicitTarget
|
|
8717
9132
|
});
|
|
8718
|
-
|
|
8719
|
-
|
|
8720
|
-
|
|
8721
|
-
|
|
8722
|
-
|
|
8723
|
-
|
|
8724
|
-
|
|
8725
|
-
|
|
8726
|
-
|
|
8727
|
-
|
|
8728
|
-
|
|
8729
|
-
|
|
8730
|
-
|
|
8731
|
-
|
|
8732
|
-
|
|
8733
|
-
|
|
8734
|
-
|
|
8735
|
-
|
|
8736
|
-
|
|
8737
|
-
|
|
8738
|
-
|
|
8739
|
-
|
|
8740
|
-
|
|
8741
|
-
|
|
8742
|
-
|
|
8743
|
-
|
|
8744
|
-
|
|
8745
|
-
|
|
8746
|
-
})
|
|
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
|
|
8747
9174
|
);
|
|
8748
|
-
|
|
8749
|
-
|
|
8750
|
-
|
|
8751
|
-
|
|
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;
|
|
8752
9231
|
}
|
|
8753
9232
|
}
|
|
8754
9233
|
|
|
@@ -8761,16 +9240,16 @@ var import_node_util4 = require("node:util");
|
|
|
8761
9240
|
// src/project-root.ts
|
|
8762
9241
|
var import_node_os3 = require("node:os");
|
|
8763
9242
|
var import_node_path12 = require("node:path");
|
|
8764
|
-
var
|
|
9243
|
+
var import_node_process4 = require("node:process");
|
|
8765
9244
|
function globalConfigDir(env2 = process.env) {
|
|
8766
9245
|
const fromEnv = env2.AIPM_HOME?.trim();
|
|
8767
9246
|
return fromEnv ? fromEnv.replace(/\/$/, "") : (0, import_node_path12.join)((0, import_node_os3.homedir)(), ".aipm");
|
|
8768
9247
|
}
|
|
8769
9248
|
function resolveConfigRoot(options = {}) {
|
|
8770
|
-
return options.global ? globalConfigDir() : (0,
|
|
9249
|
+
return options.global ? globalConfigDir() : (0, import_node_process4.cwd)();
|
|
8771
9250
|
}
|
|
8772
9251
|
function resolveInstallRoot(options = {}) {
|
|
8773
|
-
return options.global ? (0, import_node_os3.homedir)() : (0,
|
|
9252
|
+
return options.global ? (0, import_node_os3.homedir)() : (0, import_node_process4.cwd)();
|
|
8774
9253
|
}
|
|
8775
9254
|
function scopeLabel(options) {
|
|
8776
9255
|
return options.global ? "global" : "project";
|
|
@@ -8928,6 +9407,86 @@ async function runDoctor(options) {
|
|
|
8928
9407
|
var import_node_crypto2 = require("node:crypto");
|
|
8929
9408
|
var import_promises10 = require("node:fs/promises");
|
|
8930
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
|
|
8931
9490
|
var PROMPT_SITE_ORIGIN = "https://www.aipm-registry.com";
|
|
8932
9491
|
function parsePromptUrl(value) {
|
|
8933
9492
|
let url;
|
|
@@ -9041,9 +9600,12 @@ async function installTrackedPrompt(input2) {
|
|
|
9041
9600
|
if (input2.track) {
|
|
9042
9601
|
const currentUrl = input2.project.prompts[input2.reference.alias];
|
|
9043
9602
|
if (currentUrl && currentUrl !== canonicalReference.url) {
|
|
9044
|
-
|
|
9045
|
-
|
|
9046
|
-
|
|
9603
|
+
await resolvePromptAliasConflict({
|
|
9604
|
+
ci: input2.ci,
|
|
9605
|
+
alias: input2.reference.alias,
|
|
9606
|
+
currentUrl,
|
|
9607
|
+
nextUrl: canonicalReference.url
|
|
9608
|
+
});
|
|
9047
9609
|
}
|
|
9048
9610
|
await writeProjectPackageJson(input2.configRoot, {
|
|
9049
9611
|
...input2.project,
|
|
@@ -9130,8 +9692,8 @@ async function removeInstalledPackageFiles(input2) {
|
|
|
9130
9692
|
const unsafe = paths.find((path2) => !roots.some((root) => within(root, path2)));
|
|
9131
9693
|
if (unsafe) throw new Error(`Refusing to remove an untrusted path from the lockfile: ${unsafe}`);
|
|
9132
9694
|
for (const path2 of paths) {
|
|
9133
|
-
const
|
|
9134
|
-
if (!
|
|
9695
|
+
const stat6 = await (0, import_promises11.lstat)(path2).catch(() => null);
|
|
9696
|
+
if (!stat6 || stat6.isSymbolicLink()) continue;
|
|
9135
9697
|
const canonicalPath = await (0, import_promises11.realpath)(path2);
|
|
9136
9698
|
if (!realRoots.some((root) => within(root, canonicalPath))) {
|
|
9137
9699
|
throw new Error(`Refusing to remove a path that resolves outside the install roots: ${path2}`);
|
|
@@ -9148,17 +9710,138 @@ async function removeInstalledPackageFiles(input2) {
|
|
|
9148
9710
|
return { removed, retainedShared: requestedPaths.length - paths.length };
|
|
9149
9711
|
}
|
|
9150
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
|
+
|
|
9151
9834
|
// src/version.ts
|
|
9152
9835
|
var import_node_fs2 = require("node:fs");
|
|
9153
|
-
var
|
|
9836
|
+
var import_node_path17 = require("node:path");
|
|
9154
9837
|
function getCliVersion() {
|
|
9155
|
-
const pkgPath = (0,
|
|
9838
|
+
const pkgPath = (0, import_node_path17.join)(__dirname, "..", "package.json");
|
|
9156
9839
|
const pkg = JSON.parse((0, import_node_fs2.readFileSync)(pkgPath, "utf8"));
|
|
9157
9840
|
return pkg.version;
|
|
9158
9841
|
}
|
|
9159
9842
|
|
|
9160
9843
|
// src/cli-update-check.ts
|
|
9161
|
-
var
|
|
9844
|
+
var import_node_process5 = require("node:process");
|
|
9162
9845
|
var CLI_PACKAGE_NAME = "@aipm-registry/cli";
|
|
9163
9846
|
var CLI_UPDATE_COMMAND = `npm install -g ${CLI_PACKAGE_NAME}`;
|
|
9164
9847
|
var NPM_REGISTRY = "https://registry.npmjs.org";
|
|
@@ -9209,7 +9892,7 @@ function formatCliUpdateNotice(options) {
|
|
|
9209
9892
|
return `A new AIPM CLI version is available (${currentVersion} \u2192 ${latestVersion}). Run: ${cmd}`;
|
|
9210
9893
|
}
|
|
9211
9894
|
function printCliUpdateNotice(options) {
|
|
9212
|
-
const useColor2 = options.useColor ??
|
|
9895
|
+
const useColor2 = options.useColor ?? import_node_process5.stderr.isTTY;
|
|
9213
9896
|
const writeInfo = options.writeInfo ?? ((message2) => console.log(message2));
|
|
9214
9897
|
const writeWarn = options.writeWarn ?? ((message2) => console.error(message2));
|
|
9215
9898
|
const message = formatCliUpdateNotice(options);
|
|
@@ -9274,7 +9957,7 @@ function registryFromEnvOrDefault(flag) {
|
|
|
9274
9957
|
}
|
|
9275
9958
|
function packagePageUrl(packageName, version) {
|
|
9276
9959
|
const [scope, name] = packageName.replace(/^@/, "").split("/");
|
|
9277
|
-
return `${SITE_URL}/
|
|
9960
|
+
return `${SITE_URL}/skills/${encodeURIComponent(scope ?? "")}/${encodeURIComponent(name ?? "")}/${encodeURIComponent(version)}`;
|
|
9278
9961
|
}
|
|
9279
9962
|
function packageBadgeMarkdown(packageName, version) {
|
|
9280
9963
|
const pageUrl = packagePageUrl(packageName, version);
|
|
@@ -9294,23 +9977,23 @@ function skillFolderName(name) {
|
|
|
9294
9977
|
return folder;
|
|
9295
9978
|
}
|
|
9296
9979
|
async function writeStarterFile(path2, content) {
|
|
9297
|
-
await (0,
|
|
9980
|
+
await (0, import_promises14.writeFile)(path2, content, { encoding: "utf8", flag: "wx" });
|
|
9298
9981
|
}
|
|
9299
9982
|
async function inferEntryFromSource(source, fallback) {
|
|
9300
|
-
const sourceInfo = await (0,
|
|
9301
|
-
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);
|
|
9302
9985
|
if (!sourceInfo.isDirectory()) return fallback;
|
|
9303
|
-
const entries = await (0,
|
|
9986
|
+
const entries = await (0, import_promises14.readdir)(source);
|
|
9304
9987
|
if (entries.includes(fallback)) return fallback;
|
|
9305
9988
|
return entries.find((entry) => entry.endsWith(".md")) ?? entries.find((entry) => entry.endsWith(".mdc")) ?? fallback;
|
|
9306
9989
|
}
|
|
9307
9990
|
async function copySourceIntoSkillRoot(source, root, sourceLabel) {
|
|
9308
|
-
const sourceInfo = await (0,
|
|
9309
|
-
await (0,
|
|
9991
|
+
const sourceInfo = await (0, import_promises14.stat)(source);
|
|
9992
|
+
await (0, import_promises14.mkdir)(root, { recursive: true });
|
|
9310
9993
|
if (sourceInfo.isDirectory()) {
|
|
9311
|
-
const entries = await (0,
|
|
9994
|
+
const entries = await (0, import_promises14.readdir)(source);
|
|
9312
9995
|
for (const entry of entries) {
|
|
9313
|
-
await (0,
|
|
9996
|
+
await (0, import_promises14.cp)((0, import_node_path18.join)(source, entry), (0, import_node_path18.join)(root, entry), {
|
|
9314
9997
|
recursive: true,
|
|
9315
9998
|
force: false,
|
|
9316
9999
|
errorOnExist: true
|
|
@@ -9319,7 +10002,7 @@ async function copySourceIntoSkillRoot(source, root, sourceLabel) {
|
|
|
9319
10002
|
return;
|
|
9320
10003
|
}
|
|
9321
10004
|
if (sourceInfo.isFile()) {
|
|
9322
|
-
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 });
|
|
9323
10006
|
return;
|
|
9324
10007
|
}
|
|
9325
10008
|
throw new Error(`Unsupported source path: ${sourceLabel}`);
|
|
@@ -9350,14 +10033,14 @@ function formatBytes(bytes) {
|
|
|
9350
10033
|
}
|
|
9351
10034
|
function packageDashboardUrl(name) {
|
|
9352
10035
|
if (!name) return DASHBOARD_URL;
|
|
9353
|
-
return `${DASHBOARD_URL}/
|
|
10036
|
+
return `${DASHBOARD_URL}/skills/${name.replace(/^@/, "")}`;
|
|
9354
10037
|
}
|
|
9355
10038
|
function helperRootFromTrackedPath(path2) {
|
|
9356
|
-
const parts = path2.split(
|
|
10039
|
+
const parts = path2.split(import_node_path18.sep).filter(Boolean);
|
|
9357
10040
|
const helpersIndex = parts.lastIndexOf("helpers");
|
|
9358
10041
|
if (helpersIndex === -1 || parts.length <= helpersIndex + 2) return null;
|
|
9359
|
-
const prefix = path2.startsWith(
|
|
9360
|
-
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));
|
|
9361
10044
|
}
|
|
9362
10045
|
async function showInstalledPrompt(configRoot, packageArg) {
|
|
9363
10046
|
const { name } = parsePackageArg(packageArg);
|
|
@@ -9370,7 +10053,7 @@ async function showInstalledPrompt(configRoot, packageArg) {
|
|
|
9370
10053
|
if (entry.postInstall.status === "cleaned") {
|
|
9371
10054
|
throw new Error(`Setup helper files were cleaned for ${name}. Reinstall the package to restore them.`);
|
|
9372
10055
|
}
|
|
9373
|
-
const content = await (0,
|
|
10056
|
+
const content = await (0, import_promises14.readFile)(entry.postInstall.promptFile, "utf8").catch(() => {
|
|
9374
10057
|
throw new Error(`Setup prompt file is missing: ${entry.postInstall?.promptFile}`);
|
|
9375
10058
|
});
|
|
9376
10059
|
console.log(`Prompt file: ${entry.postInstall.promptFile}`);
|
|
@@ -9403,9 +10086,9 @@ async function cleanupInstalledHelpers(options) {
|
|
|
9403
10086
|
entry.installedAssets.helper.map((path2) => helperRootFromTrackedPath(path2)).filter((path2) => Boolean(path2))
|
|
9404
10087
|
);
|
|
9405
10088
|
if (roots.size > 0) {
|
|
9406
|
-
for (const root of roots) await (0,
|
|
10089
|
+
for (const root of roots) await (0, import_promises14.rm)(root, { recursive: true, force: true });
|
|
9407
10090
|
} else {
|
|
9408
|
-
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 });
|
|
9409
10092
|
}
|
|
9410
10093
|
await writeLockfile(options.configRoot, {
|
|
9411
10094
|
...lock,
|
|
@@ -9457,13 +10140,69 @@ async function authTokenForRegistry(registry, options = {}) {
|
|
|
9457
10140
|
}
|
|
9458
10141
|
}
|
|
9459
10142
|
async function tokenForRead(registry, explicitToken, options = {}) {
|
|
9460
|
-
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
|
+
}
|
|
9461
10194
|
}
|
|
9462
10195
|
async function runLoopbackLogin(options) {
|
|
9463
10196
|
const state = randomBase64Url(24);
|
|
9464
10197
|
const verifier = randomBase64Url(48);
|
|
9465
10198
|
const challenge = sha256Base64Url(verifier);
|
|
9466
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
|
+
};
|
|
9467
10206
|
const server = (0, import_node_http.createServer)((request, response) => {
|
|
9468
10207
|
const host = request.headers.host ?? "127.0.0.1";
|
|
9469
10208
|
const url = new URL(request.url ?? "/", `http://${host}`);
|
|
@@ -9475,29 +10214,33 @@ async function runLoopbackLogin(options) {
|
|
|
9475
10214
|
if (url.searchParams.get("state") !== state) {
|
|
9476
10215
|
response.writeHead(400, { "content-type": "text/html" });
|
|
9477
10216
|
response.end("<h1>AIPM login failed</h1><p>Invalid state. Return to the terminal and try again.</p>");
|
|
9478
|
-
|
|
9479
|
-
|
|
10217
|
+
forceCloseServer(server);
|
|
10218
|
+
settle(() => rejectLogin(new Error("Invalid CLI login state")));
|
|
9480
10219
|
return;
|
|
9481
10220
|
}
|
|
9482
10221
|
const code = url.searchParams.get("code");
|
|
9483
10222
|
if (!code) {
|
|
9484
10223
|
response.writeHead(400, { "content-type": "text/html" });
|
|
9485
10224
|
response.end("<h1>AIPM login failed</h1><p>Missing code. Return to the terminal and try again.</p>");
|
|
9486
|
-
|
|
9487
|
-
|
|
10225
|
+
forceCloseServer(server);
|
|
10226
|
+
settle(() => rejectLogin(new Error("Missing CLI authorization code")));
|
|
9488
10227
|
return;
|
|
9489
10228
|
}
|
|
9490
10229
|
response.writeHead(200, { "content-type": "text/html" });
|
|
9491
10230
|
response.end("<h1>AIPM CLI is signed in</h1><p>You can close this window and return to the terminal.</p>");
|
|
9492
10231
|
const address = server.address();
|
|
9493
10232
|
if (!address || typeof address === "string") {
|
|
9494
|
-
|
|
9495
|
-
|
|
9496
|
-
|
|
10233
|
+
forceCloseServer(server);
|
|
10234
|
+
settle(() => rejectLogin(new Error("Could not read CLI callback address")));
|
|
10235
|
+
return;
|
|
9497
10236
|
}
|
|
9498
|
-
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));
|
|
9499
10243
|
});
|
|
9500
|
-
server.once("error", rejectLogin);
|
|
9501
10244
|
server.listen(0, "127.0.0.1", () => {
|
|
9502
10245
|
const address = server.address();
|
|
9503
10246
|
const redirectUri = `http://127.0.0.1:${address.port}/callback`;
|
|
@@ -9510,10 +10253,12 @@ async function runLoopbackLogin(options) {
|
|
|
9510
10253
|
if (options.open) void openUrl(loginUrl.toString());
|
|
9511
10254
|
else console.log(`Open: ${loginUrl.toString()}`);
|
|
9512
10255
|
});
|
|
9513
|
-
|
|
9514
|
-
|
|
9515
|
-
|
|
9516
|
-
});
|
|
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));
|
|
9517
10262
|
});
|
|
9518
10263
|
const tokens = await exchangeCliAuthCode(options.registry, {
|
|
9519
10264
|
code: result.code,
|
|
@@ -9532,6 +10277,12 @@ async function runLoopbackLogin(options) {
|
|
|
9532
10277
|
console.log(`Logged in as ${label}.`);
|
|
9533
10278
|
console.log(`Credentials saved to ${authFilePath()}`);
|
|
9534
10279
|
}
|
|
10280
|
+
function forceCloseServer(server) {
|
|
10281
|
+
if (typeof server.closeAllConnections === "function") {
|
|
10282
|
+
server.closeAllConnections();
|
|
10283
|
+
}
|
|
10284
|
+
server.close();
|
|
10285
|
+
}
|
|
9535
10286
|
function printPublishFlow() {
|
|
9536
10287
|
console.log("AIPM publish flow");
|
|
9537
10288
|
console.log("1. Create an account and organization in the dashboard.");
|
|
@@ -9699,9 +10450,9 @@ function buildStarterQualityMetadata(options) {
|
|
|
9699
10450
|
}
|
|
9700
10451
|
async function initSkill(opts) {
|
|
9701
10452
|
if (!isValidScopeName(opts.name)) throw new Error("Invalid @scope/name");
|
|
9702
|
-
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));
|
|
9703
10454
|
const cdTarget = opts.here ? null : opts.dir ?? skillFolderName(opts.name);
|
|
9704
|
-
const source = opts.from ? (0,
|
|
10455
|
+
const source = opts.from ? (0, import_node_path18.resolve)(opts.from) : null;
|
|
9705
10456
|
const entry = source ? await inferEntryFromSource(source, opts.entry) : opts.entry;
|
|
9706
10457
|
const template = parseSkillTemplate(opts.template ?? "blank");
|
|
9707
10458
|
const manifest = PackageManifestSchema.parse({
|
|
@@ -9722,17 +10473,17 @@ async function initSkill(opts) {
|
|
|
9722
10473
|
if (source) {
|
|
9723
10474
|
await copySourceIntoSkillRoot(source, root, source);
|
|
9724
10475
|
} else {
|
|
9725
|
-
await (0,
|
|
10476
|
+
await (0, import_promises14.mkdir)(root, { recursive: true });
|
|
9726
10477
|
}
|
|
9727
|
-
await writeStarterFile((0,
|
|
10478
|
+
await writeStarterFile((0, import_node_path18.join)(root, "aipm.manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
|
|
9728
10479
|
if (!opts.from) {
|
|
9729
10480
|
await writeStarterFile(
|
|
9730
|
-
(0,
|
|
10481
|
+
(0, import_node_path18.join)(root, entry),
|
|
9731
10482
|
SKILL_TEMPLATES[template](opts.name)
|
|
9732
10483
|
);
|
|
9733
10484
|
}
|
|
9734
10485
|
await writeStarterFile(
|
|
9735
|
-
(0,
|
|
10486
|
+
(0, import_node_path18.join)(root, ".aipmignore"),
|
|
9736
10487
|
[
|
|
9737
10488
|
"# Files that should never be published with this skill",
|
|
9738
10489
|
".env*",
|
|
@@ -9750,7 +10501,7 @@ async function initSkill(opts) {
|
|
|
9750
10501
|
""
|
|
9751
10502
|
].join("\n")
|
|
9752
10503
|
);
|
|
9753
|
-
await (0,
|
|
10504
|
+
await (0, import_promises14.mkdir)((0, import_node_path18.join)(root, ".aipm"), { recursive: true });
|
|
9754
10505
|
console.log(`Created ${opts.name} skill folder: ${root}`);
|
|
9755
10506
|
printPublishGuide(
|
|
9756
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.`,
|
|
@@ -9765,24 +10516,11 @@ program2.command("init").description("Create aipm.package.json in the current pr
|
|
|
9765
10516
|
console.log(`aipm.package.json already exists (${scopeLabel(scope)}).`);
|
|
9766
10517
|
return;
|
|
9767
10518
|
}
|
|
9768
|
-
|
|
9769
|
-
|
|
9770
|
-
|
|
9771
|
-
|
|
9772
|
-
let preferredTools = parsedTarget ? [parsedTarget] : detected;
|
|
9773
|
-
if (!opts.target && detected.length === 0) {
|
|
9774
|
-
const choice = await promptForTool();
|
|
9775
|
-
preferredTools = [choice];
|
|
9776
|
-
}
|
|
9777
|
-
const registry = registryFromEnvOrDefault(opts.registry);
|
|
9778
|
-
await writeProjectPackageJson(configRoot, {
|
|
9779
|
-
schemaVersion: "0.1",
|
|
9780
|
-
registry,
|
|
9781
|
-
preferredTools: preferredTools.length ? preferredTools : void 0,
|
|
9782
|
-
packages: {},
|
|
9783
|
-
prompts: {}
|
|
10519
|
+
await createProjectPackageJson({
|
|
10520
|
+
scope,
|
|
10521
|
+
registry: registryFromEnvOrDefault(opts.registry),
|
|
10522
|
+
target: opts.target
|
|
9784
10523
|
});
|
|
9785
|
-
console.log(`Created aipm.package.json (${scopeLabel(scope)}, registry: ${registry})`);
|
|
9786
10524
|
});
|
|
9787
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) => {
|
|
9788
10526
|
const registry = registryFromEnvOrDefault(opts.registry);
|
|
@@ -9837,9 +10575,9 @@ program2.command("config").description("Show resolved AIPM CLI and project confi
|
|
|
9837
10575
|
scope: scopeLabel(scope),
|
|
9838
10576
|
configRoot,
|
|
9839
10577
|
installRoot,
|
|
9840
|
-
cwd: (0,
|
|
10578
|
+
cwd: (0, import_node_process6.cwd)(),
|
|
9841
10579
|
registry,
|
|
9842
|
-
envRegistry:
|
|
10580
|
+
envRegistry: import_node_process6.env.AIPM_REGISTRY_URL ?? import_node_process6.env.AIPM_REGISTRY ?? null,
|
|
9843
10581
|
hasProjectConfig: Boolean(project),
|
|
9844
10582
|
projectPackages: project ? Object.keys(project.packages) : [],
|
|
9845
10583
|
hasLockfile: Boolean(lock),
|
|
@@ -9868,11 +10606,58 @@ program2.command("config").description("Show resolved AIPM CLI and project confi
|
|
|
9868
10606
|
console.log(`Auth file: ${data.authFile}`);
|
|
9869
10607
|
console.log(`Publish guide: ${data.publishDoc}`);
|
|
9870
10608
|
});
|
|
9871
|
-
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) => {
|
|
9872
10610
|
const scope = { global: opts.global };
|
|
9873
10611
|
const configRoot = resolveConfigRoot(scope);
|
|
9874
10612
|
const installRoot = resolveInstallRoot(scope);
|
|
9875
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
|
+
}
|
|
9876
10661
|
if (!project) throw new Error(initRequiredMessage(scope));
|
|
9877
10662
|
const registry = resolveRegistryUrl(project, opts.registry, DEFAULT_REGISTRY);
|
|
9878
10663
|
const promptReference = parsePromptUrl(pkgArg);
|
|
@@ -9883,7 +10668,8 @@ program2.command("add <package-or-prompt>").description("Add a skill package or
|
|
|
9883
10668
|
project,
|
|
9884
10669
|
reference: promptReference,
|
|
9885
10670
|
track: true,
|
|
9886
|
-
recordCopy: true
|
|
10671
|
+
recordCopy: true,
|
|
10672
|
+
ci: opts.ci
|
|
9887
10673
|
});
|
|
9888
10674
|
console.log(`Installed prompt ${result.prompt.title} \u2192 ${result.path}`);
|
|
9889
10675
|
return;
|
|
@@ -9897,7 +10683,7 @@ program2.command("add <package-or-prompt>").description("Add a skill package or
|
|
|
9897
10683
|
packages: { ...project.packages, [name]: version.replace(/^\^/, "") }
|
|
9898
10684
|
};
|
|
9899
10685
|
await writeProjectPackageJson(configRoot, project);
|
|
9900
|
-
await
|
|
10686
|
+
await installOnePackageWithRecovery({
|
|
9901
10687
|
configRoot,
|
|
9902
10688
|
installRoot,
|
|
9903
10689
|
registry,
|
|
@@ -9925,7 +10711,7 @@ program2.command("search [query]").description("Search public registry packages"
|
|
|
9925
10711
|
const token = await tokenForRead(registry, opts.token, { quiet: opts.json });
|
|
9926
10712
|
const packages = await searchPackages(registry, query, Number(opts.limit), token);
|
|
9927
10713
|
if (opts.json) {
|
|
9928
|
-
console.log(JSON.stringify({ packages }, null, 2));
|
|
10714
|
+
console.log(JSON.stringify({ skills: packages }, null, 2));
|
|
9929
10715
|
return;
|
|
9930
10716
|
}
|
|
9931
10717
|
if (packages.length === 0) {
|
|
@@ -9944,13 +10730,31 @@ program2.command("install").description("Install all packages from aipm.package.
|
|
|
9944
10730
|
const scope = { global: opts.global };
|
|
9945
10731
|
const configRoot = resolveConfigRoot(scope);
|
|
9946
10732
|
const installRoot = resolveInstallRoot(scope);
|
|
9947
|
-
|
|
9948
|
-
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
|
+
}
|
|
9949
10753
|
const registry = resolveRegistryUrl(project, opts.registry, DEFAULT_REGISTRY);
|
|
9950
10754
|
const target = parseTargetFlag(opts.target);
|
|
9951
10755
|
const token = await tokenForRead(registry, opts.token);
|
|
9952
10756
|
for (const [name, version] of Object.entries(project.packages)) {
|
|
9953
|
-
await
|
|
10757
|
+
await installOnePackageWithRecovery({
|
|
9954
10758
|
configRoot,
|
|
9955
10759
|
installRoot,
|
|
9956
10760
|
registry,
|
|
@@ -9970,15 +10774,28 @@ program2.command("install").description("Install all packages from aipm.package.
|
|
|
9970
10774
|
registry,
|
|
9971
10775
|
project,
|
|
9972
10776
|
reference: { ...reference, alias },
|
|
9973
|
-
track: false
|
|
10777
|
+
track: false,
|
|
10778
|
+
ci: opts.ci
|
|
9974
10779
|
});
|
|
9975
10780
|
console.log(`Restored prompt ${result.prompt.title} \u2192 ${result.path}`);
|
|
9976
10781
|
}
|
|
9977
10782
|
});
|
|
9978
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) => {
|
|
9979
|
-
const
|
|
9980
|
-
const
|
|
9981
|
-
|
|
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
|
+
}
|
|
9982
10799
|
const reference = resolveTrackedPrompt(project, value);
|
|
9983
10800
|
if (reference && project.prompts[reference.alias] === reference.url) {
|
|
9984
10801
|
const lock2 = await readLockfile(configRoot);
|
|
@@ -9997,7 +10814,7 @@ program2.command("show-prompt <package>").description("Show the manual setup pro
|
|
|
9997
10814
|
await showInstalledPrompt(resolveConfigRoot({ global: opts.global }), pkgArg);
|
|
9998
10815
|
});
|
|
9999
10816
|
function promptImageContentType(path2) {
|
|
10000
|
-
const extension = (0,
|
|
10817
|
+
const extension = (0, import_node_path18.extname)(path2).toLowerCase();
|
|
10001
10818
|
if (extension === ".png") return "image/png";
|
|
10002
10819
|
if (extension === ".jpg" || extension === ".jpeg") return "image/jpeg";
|
|
10003
10820
|
if (extension === ".webp") return "image/webp";
|
|
@@ -10006,10 +10823,18 @@ function promptImageContentType(path2) {
|
|
|
10006
10823
|
var promptCommand = program2.command("prompt").description("Publish prompt listings from structured JSON");
|
|
10007
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) => {
|
|
10008
10825
|
const registry = registryFromEnvOrDefault(opts.registry);
|
|
10009
|
-
|
|
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
|
+
}
|
|
10010
10835
|
if (!accessToken) throw new Error(`Run ${recommendCmd("aipm login")} before publishing prompts.`);
|
|
10011
|
-
const absoluteFile = (0,
|
|
10012
|
-
const parsed = JSON.parse(await (0,
|
|
10836
|
+
const absoluteFile = (0, import_node_path18.resolve)(file);
|
|
10837
|
+
const parsed = JSON.parse(await (0, import_promises14.readFile)(absoluteFile, "utf8"));
|
|
10013
10838
|
const rows = Array.isArray(parsed) ? parsed : "prompts" in parsed ? parsed.prompts : [parsed];
|
|
10014
10839
|
if (rows.length === 0) throw new Error("Prompt publish file contains no prompts.");
|
|
10015
10840
|
if (!opts.yes) {
|
|
@@ -10020,10 +10845,10 @@ promptCommand.command("publish <file>").description("Publish one prompt or a bat
|
|
|
10020
10845
|
const { sampleImagePath, ...input2 } = row;
|
|
10021
10846
|
let sampleImage;
|
|
10022
10847
|
if (sampleImagePath) {
|
|
10023
|
-
const imagePath = (0,
|
|
10848
|
+
const imagePath = (0, import_node_path18.resolve)((0, import_node_path18.dirname)(absoluteFile), sampleImagePath);
|
|
10024
10849
|
sampleImage = {
|
|
10025
|
-
data: await (0,
|
|
10026
|
-
filename: (0,
|
|
10850
|
+
data: await (0, import_promises14.readFile)(imagePath),
|
|
10851
|
+
filename: (0, import_node_path18.basename)(imagePath),
|
|
10027
10852
|
contentType: promptImageContentType(imagePath)
|
|
10028
10853
|
};
|
|
10029
10854
|
}
|
|
@@ -10046,12 +10871,12 @@ var publish = program2.command("publish [dir]").description("Stage, validate, an
|
|
|
10046
10871
|
publish.help();
|
|
10047
10872
|
return;
|
|
10048
10873
|
}
|
|
10049
|
-
const abs = (0,
|
|
10050
|
-
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");
|
|
10051
10876
|
const manifest = PackageManifestSchema.parse(JSON.parse(manifestRaw));
|
|
10052
10877
|
const tarball = await packDirectory(abs);
|
|
10053
10878
|
const registry = registryFromEnvOrDefault(opts.registry);
|
|
10054
|
-
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);
|
|
10055
10880
|
console.log(`Published ${manifest.name}@${result.version}`);
|
|
10056
10881
|
console.log(`View: ${packagePageUrl(manifest.name, result.version)}`);
|
|
10057
10882
|
console.log(`Install: aipm add ${manifest.name}@${result.version} --target ${manifest.targets[0]} --ci`);
|
|
@@ -10066,7 +10891,7 @@ publish.command("import <source>").description("Create a package folder from an
|
|
|
10066
10891
|
async (source, opts) => initSkill({ ...opts, from: source })
|
|
10067
10892
|
);
|
|
10068
10893
|
publish.command("add [files...]").description("Stage files for publishing").action(async (files) => {
|
|
10069
|
-
const state = await addPublishFiles((0,
|
|
10894
|
+
const state = await addPublishFiles((0, import_node_process6.cwd)(), files);
|
|
10070
10895
|
console.log(`Staged ${state.files.length} file${state.files.length === 1 ? "" : "s"}.`);
|
|
10071
10896
|
printPublishGuide(
|
|
10072
10897
|
`Added ${files.length > 0 ? files.join(", ") : "."} to the publish stage, respecting .aipmignore and secret-file exclusions.`,
|
|
@@ -10097,7 +10922,7 @@ publish.command("token").description("Open the dashboard page used to generate a
|
|
|
10097
10922
|
);
|
|
10098
10923
|
});
|
|
10099
10924
|
publish.command("remove <files...>").alias("rm").description("Remove files from the publish stage").action(async (files) => {
|
|
10100
|
-
const state = await removePublishFiles((0,
|
|
10925
|
+
const state = await removePublishFiles((0, import_node_process6.cwd)(), files);
|
|
10101
10926
|
console.log(`Staged ${state.files.length} file${state.files.length === 1 ? "" : "s"}.`);
|
|
10102
10927
|
printPublishGuide(
|
|
10103
10928
|
`Removed ${files.join(", ")} from the publish stage.`,
|
|
@@ -10105,7 +10930,7 @@ publish.command("remove <files...>").alias("rm").description("Remove files from
|
|
|
10105
10930
|
);
|
|
10106
10931
|
});
|
|
10107
10932
|
publish.command("reset").description("Clear staged publish files").action(async () => {
|
|
10108
|
-
await resetPublishState((0,
|
|
10933
|
+
await resetPublishState((0, import_node_process6.cwd)());
|
|
10109
10934
|
console.log("Cleared publish stage.");
|
|
10110
10935
|
printPublishGuide(
|
|
10111
10936
|
"Cleared all staged publish files.",
|
|
@@ -10113,7 +10938,7 @@ publish.command("reset").description("Clear staged publish files").action(async
|
|
|
10113
10938
|
);
|
|
10114
10939
|
});
|
|
10115
10940
|
publish.command("status").description("Show staged publish files").option("--json", "Print machine-readable JSON").action(async (opts) => {
|
|
10116
|
-
const rows = await statusPublishState((0,
|
|
10941
|
+
const rows = await statusPublishState((0, import_node_process6.cwd)());
|
|
10117
10942
|
if (opts.json) {
|
|
10118
10943
|
console.log(JSON.stringify({ files: rows }, null, 2));
|
|
10119
10944
|
return;
|
|
@@ -10136,7 +10961,7 @@ publish.command("status").description("Show staged publish files").option("--jso
|
|
|
10136
10961
|
);
|
|
10137
10962
|
});
|
|
10138
10963
|
publish.command("diff").description("Show staged files that changed since add").action(async () => {
|
|
10139
|
-
const rows = await statusPublishState((0,
|
|
10964
|
+
const rows = await statusPublishState((0, import_node_process6.cwd)());
|
|
10140
10965
|
const changed = rows.filter((row) => row.changed);
|
|
10141
10966
|
if (changed.length === 0) {
|
|
10142
10967
|
console.log("No staged files changed.");
|
|
@@ -10153,16 +10978,16 @@ publish.command("diff").description("Show staged files that changed since add").
|
|
|
10153
10978
|
);
|
|
10154
10979
|
});
|
|
10155
10980
|
publish.command("validate").description("Validate staged files before publish").action(async () => {
|
|
10156
|
-
const result = await validatePublishState((0,
|
|
10981
|
+
const result = await validatePublishState((0, import_node_process6.cwd)());
|
|
10157
10982
|
console.log(`Valid ${result.manifest.name}@${result.manifest.version} (${result.size} bytes staged).`);
|
|
10158
10983
|
printPublishGuide(
|
|
10159
10984
|
`Validated ${result.manifest.name}@${result.manifest.version}; manifest, entry file, staged hashes, ignored paths, package size, and obvious secrets passed.`,
|
|
10160
10985
|
"Generate a 5-minute token from the dashboard, then run AIPM_TOKEN=<token> aipm publish push."
|
|
10161
10986
|
);
|
|
10162
10987
|
});
|
|
10163
|
-
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));
|
|
10164
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) => {
|
|
10165
|
-
const root = (0,
|
|
10990
|
+
const root = (0, import_node_process6.cwd)();
|
|
10166
10991
|
const { manifest } = await validatePublishState(root);
|
|
10167
10992
|
const registry = registryFromEnvOrDefault(opts.registry);
|
|
10168
10993
|
const tarball = await packStagedFiles(root);
|
|
@@ -10177,7 +11002,7 @@ publish.command("push").description("Publish staged files").option("--registry <
|
|
|
10177
11002
|
console.log(`Verbose: package ${manifest.name}@${manifest.version}; tarball ${tarball.length} bytes.`);
|
|
10178
11003
|
}
|
|
10179
11004
|
for (const entry of staged) console.log(` ${entry.path}`);
|
|
10180
|
-
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);
|
|
10181
11006
|
console.log(`Published ${manifest.name}@${result.version}`);
|
|
10182
11007
|
console.log(`View: ${packagePageUrl(manifest.name, result.version)}`);
|
|
10183
11008
|
console.log(`Install: aipm add ${manifest.name}@${result.version} --target ${manifest.targets[0]} --ci`);
|
|
@@ -10209,11 +11034,51 @@ program2.command("list").description("List installed skills and prompts from aip
|
|
|
10209
11034
|
}
|
|
10210
11035
|
}
|
|
10211
11036
|
});
|
|
10212
|
-
program2.command("remove <package-or-prompt>").alias("rm").description("Remove a tracked skill or prompt and its installed files").option(GLOBAL_OPTION, GLOBAL_OPTION_DESC).action(async (pkgArg, opts) => {
|
|
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) => {
|
|
10213
11038
|
const scope = { global: opts.global };
|
|
10214
11039
|
const configRoot = resolveConfigRoot(scope);
|
|
10215
11040
|
const installRoot = resolveInstallRoot(scope);
|
|
10216
|
-
|
|
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
|
+
}
|
|
10217
11082
|
if (!project) throw new Error(initRequiredMessage(scope));
|
|
10218
11083
|
const promptReference = resolveTrackedPrompt(project, pkgArg);
|
|
10219
11084
|
if (promptReference && project.prompts[promptReference.alias] === promptReference.url) {
|
|
@@ -10247,11 +11112,71 @@ program2.command("remove <package-or-prompt>").alias("rm").description("Remove a
|
|
|
10247
11112
|
}
|
|
10248
11113
|
console.log(`Removed ${name} from AIPM ${scopeLabel(scope)} files.`);
|
|
10249
11114
|
});
|
|
10250
|
-
program2.command("update [package]").description("Update one installed package, or all installed packages, to the latest registry version").option(GLOBAL_OPTION, GLOBAL_OPTION_DESC).option("--registry <url>", "Registry base URL").option("--target <tool>", "cursor, claude, codex, or *").option("--token <token>", "Install token for private packages").option("--ci", "Non-interactive").action(async (pkgArg, opts) => {
|
|
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) => {
|
|
10251
11116
|
const scope = { global: opts.global };
|
|
10252
11117
|
const configRoot = resolveConfigRoot(scope);
|
|
10253
11118
|
const installRoot = resolveInstallRoot(scope);
|
|
10254
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
|
+
}
|
|
10255
11180
|
if (!project) throw new Error(initRequiredMessage(scope));
|
|
10256
11181
|
const registry = resolveRegistryUrl(project, opts.registry, DEFAULT_REGISTRY);
|
|
10257
11182
|
const requestedPrompt = pkgArg ? resolveTrackedPrompt(project, pkgArg) : null;
|
|
@@ -10265,7 +11190,8 @@ program2.command("update [package]").description("Update one installed package,
|
|
|
10265
11190
|
project,
|
|
10266
11191
|
reference: requestedPrompt,
|
|
10267
11192
|
track: false,
|
|
10268
|
-
updateOnly: true
|
|
11193
|
+
updateOnly: true,
|
|
11194
|
+
ci: opts.ci
|
|
10269
11195
|
});
|
|
10270
11196
|
console.log(result.changed ? `Updated prompt ${result.prompt.title} \u2192 ${result.path}` : `Prompt unchanged: ${result.prompt.title}`);
|
|
10271
11197
|
return;
|
|
@@ -10279,7 +11205,7 @@ program2.command("update [package]").description("Update one installed package,
|
|
|
10279
11205
|
packages: { ...project.packages, [name]: version }
|
|
10280
11206
|
};
|
|
10281
11207
|
await writeProjectPackageJson(configRoot, project);
|
|
10282
|
-
await
|
|
11208
|
+
await installOnePackageWithRecovery({
|
|
10283
11209
|
configRoot,
|
|
10284
11210
|
installRoot,
|
|
10285
11211
|
registry,
|
|
@@ -10301,7 +11227,8 @@ program2.command("update [package]").description("Update one installed package,
|
|
|
10301
11227
|
project,
|
|
10302
11228
|
reference: { ...reference, alias },
|
|
10303
11229
|
track: false,
|
|
10304
|
-
updateOnly: true
|
|
11230
|
+
updateOnly: true,
|
|
11231
|
+
ci: opts.ci
|
|
10305
11232
|
});
|
|
10306
11233
|
console.log(result.changed ? `Updated prompt ${result.prompt.title} \u2192 ${result.path}` : `Prompt unchanged: ${result.prompt.title}`);
|
|
10307
11234
|
}
|
|
@@ -10312,6 +11239,10 @@ program2.command("update [package]").description("Update one installed package,
|
|
|
10312
11239
|
});
|
|
10313
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));
|
|
10314
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
|
+
}
|
|
10315
11246
|
console.error(err.message);
|
|
10316
11247
|
const message = err.message.toLowerCase();
|
|
10317
11248
|
if (message.includes("token") || message.includes("unauthorized") || message.includes("forbidden")) {
|