@aipm-registry/cli 0.3.4 → 0.4.0
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/LICENSE +185 -1
- package/README.md +53 -14
- package/dist/bin.cjs +419 -60
- package/dist/bin.cjs.map +4 -4
- package/package.json +4 -4
package/dist/bin.cjs
CHANGED
|
@@ -4237,7 +4237,7 @@ var init_manifest = __esm({
|
|
|
4237
4237
|
});
|
|
4238
4238
|
|
|
4239
4239
|
// ../../packages/schemas/dist/package-json.js
|
|
4240
|
-
var scopedPackageKey, ProjectPackageJsonSchema;
|
|
4240
|
+
var scopedPackageKey, promptAliasKey, ProjectPackageJsonSchema;
|
|
4241
4241
|
var init_package_json = __esm({
|
|
4242
4242
|
"../../packages/schemas/dist/package-json.js"() {
|
|
4243
4243
|
"use strict";
|
|
@@ -4245,17 +4245,19 @@ var init_package_json = __esm({
|
|
|
4245
4245
|
init_scope_name();
|
|
4246
4246
|
init_manifest();
|
|
4247
4247
|
scopedPackageKey = external_exports.string().regex(SCOPE_NAME_REGEX);
|
|
4248
|
+
promptAliasKey = external_exports.string().trim().min(1).max(120);
|
|
4248
4249
|
ProjectPackageJsonSchema = external_exports.object({
|
|
4249
4250
|
schemaVersion: external_exports.literal("0.1"),
|
|
4250
4251
|
registry: external_exports.string().url(),
|
|
4251
4252
|
preferredTools: external_exports.array(AiToolSchema).optional(),
|
|
4252
|
-
packages: external_exports.record(scopedPackageKey, external_exports.string().min(1))
|
|
4253
|
+
packages: external_exports.record(scopedPackageKey, external_exports.string().min(1)),
|
|
4254
|
+
prompts: external_exports.record(promptAliasKey, external_exports.string().url()).default({})
|
|
4253
4255
|
});
|
|
4254
4256
|
}
|
|
4255
4257
|
});
|
|
4256
4258
|
|
|
4257
4259
|
// ../../packages/schemas/dist/lockfile.js
|
|
4258
|
-
var scopedPackageKey2, InstalledPathsSchema, LockfilePackageEntrySchema, LockfileSchema;
|
|
4260
|
+
var scopedPackageKey2, InstalledPathsSchema, LockfilePromptEntrySchema, LockfilePackageEntrySchema, LockfileSchema;
|
|
4259
4261
|
var init_lockfile = __esm({
|
|
4260
4262
|
"../../packages/schemas/dist/lockfile.js"() {
|
|
4261
4263
|
"use strict";
|
|
@@ -4264,6 +4266,15 @@ var init_lockfile = __esm({
|
|
|
4264
4266
|
init_manifest();
|
|
4265
4267
|
scopedPackageKey2 = external_exports.string().regex(SCOPE_NAME_REGEX);
|
|
4266
4268
|
InstalledPathsSchema = external_exports.record(AiToolSchema, external_exports.array(external_exports.string()));
|
|
4269
|
+
LockfilePromptEntrySchema = external_exports.object({
|
|
4270
|
+
id: external_exports.string().min(1),
|
|
4271
|
+
url: external_exports.string().url(),
|
|
4272
|
+
publisher: external_exports.string().min(1),
|
|
4273
|
+
slug: external_exports.string().min(1),
|
|
4274
|
+
contentHash: external_exports.string().min(1),
|
|
4275
|
+
updatedAt: external_exports.string().min(1),
|
|
4276
|
+
installedPath: external_exports.string().min(1)
|
|
4277
|
+
});
|
|
4267
4278
|
LockfilePackageEntrySchema = external_exports.object({
|
|
4268
4279
|
version: external_exports.string(),
|
|
4269
4280
|
integrity: external_exports.string(),
|
|
@@ -4283,7 +4294,8 @@ var init_lockfile = __esm({
|
|
|
4283
4294
|
});
|
|
4284
4295
|
LockfileSchema = external_exports.object({
|
|
4285
4296
|
schemaVersion: external_exports.literal("0.1"),
|
|
4286
|
-
packages: external_exports.record(scopedPackageKey2, LockfilePackageEntrySchema)
|
|
4297
|
+
packages: external_exports.record(scopedPackageKey2, LockfilePackageEntrySchema),
|
|
4298
|
+
prompts: external_exports.record(external_exports.string().min(1), LockfilePromptEntrySchema).default({})
|
|
4287
4299
|
});
|
|
4288
4300
|
}
|
|
4289
4301
|
});
|
|
@@ -7826,11 +7838,11 @@ function useColor() {
|
|
|
7826
7838
|
var program = new Command();
|
|
7827
7839
|
|
|
7828
7840
|
// src/bin.ts
|
|
7829
|
-
var
|
|
7841
|
+
var import_node_crypto3 = require("node:crypto");
|
|
7830
7842
|
var import_node_child_process4 = require("node:child_process");
|
|
7831
7843
|
var import_node_http = require("node:http");
|
|
7832
|
-
var
|
|
7833
|
-
var
|
|
7844
|
+
var import_promises11 = require("node:fs/promises");
|
|
7845
|
+
var import_node_path14 = require("node:path");
|
|
7834
7846
|
var import_node_process5 = require("node:process");
|
|
7835
7847
|
init_dist();
|
|
7836
7848
|
init_dist4();
|
|
@@ -8065,12 +8077,12 @@ async function packStagedFiles(root) {
|
|
|
8065
8077
|
}
|
|
8066
8078
|
}
|
|
8067
8079
|
async function unpackTarballToDirectory(tarball) {
|
|
8068
|
-
const { mkdtemp: mkdtemp2, writeFile:
|
|
8069
|
-
const { join:
|
|
8080
|
+
const { mkdtemp: mkdtemp2, writeFile: writeFile8 } = await import("node:fs/promises");
|
|
8081
|
+
const { join: join14 } = await import("node:path");
|
|
8070
8082
|
const { tmpdir: tmpdir2 } = await import("node:os");
|
|
8071
|
-
const tempDir = await mkdtemp2(
|
|
8072
|
-
const tgzPath =
|
|
8073
|
-
await
|
|
8083
|
+
const tempDir = await mkdtemp2(join14(tmpdir2(), "aipm-install-"));
|
|
8084
|
+
const tgzPath = join14(tempDir, "pkg.tgz");
|
|
8085
|
+
await writeFile8(tgzPath, tarball);
|
|
8074
8086
|
await execFileAsync("tar", ["-xzf", tgzPath, "-C", tempDir]);
|
|
8075
8087
|
return tempDir;
|
|
8076
8088
|
}
|
|
@@ -8229,6 +8241,41 @@ async function recordPackageInstall(registry, name, token) {
|
|
|
8229
8241
|
}
|
|
8230
8242
|
return res.json();
|
|
8231
8243
|
}
|
|
8244
|
+
async function fetchPromptDetail(registry, publisher, slug) {
|
|
8245
|
+
const base = registry.replace(/\/$/, "");
|
|
8246
|
+
const url = `${base}/v1/prompts/${encodeURIComponent(publisher)}/${encodeURIComponent(slug)}`;
|
|
8247
|
+
const res = await registryFetch(url, base);
|
|
8248
|
+
if (!res.ok) throw new Error(`Prompt not found: @${publisher}/${slug} (${res.status})`);
|
|
8249
|
+
return res.json();
|
|
8250
|
+
}
|
|
8251
|
+
async function recordPromptCopy(registry, publisher, slug) {
|
|
8252
|
+
const base = registry.replace(/\/$/, "");
|
|
8253
|
+
const url = `${base}/v1/prompts/${encodeURIComponent(publisher)}/${encodeURIComponent(slug)}/copy`;
|
|
8254
|
+
const res = await registryFetch(url, base, { method: "POST" });
|
|
8255
|
+
if (!res.ok) throw new Error(`Prompt copy recording failed: ${res.status}`);
|
|
8256
|
+
}
|
|
8257
|
+
async function publishPrompt(registry, input2, accessToken, sampleImage) {
|
|
8258
|
+
const base = registry.replace(/\/$/, "");
|
|
8259
|
+
const form = new FormData();
|
|
8260
|
+
form.append("data", JSON.stringify(input2));
|
|
8261
|
+
if (sampleImage) {
|
|
8262
|
+
form.append(
|
|
8263
|
+
"sampleImage",
|
|
8264
|
+
new Blob([sampleImage.data], { type: sampleImage.contentType }),
|
|
8265
|
+
sampleImage.filename
|
|
8266
|
+
);
|
|
8267
|
+
}
|
|
8268
|
+
const res = await registryFetch(`${base}/v1/prompts`, base, {
|
|
8269
|
+
method: "POST",
|
|
8270
|
+
headers: registryAuthHeaders(accessToken),
|
|
8271
|
+
body: form
|
|
8272
|
+
});
|
|
8273
|
+
if (!res.ok) {
|
|
8274
|
+
const error = await res.json().catch(() => ({}));
|
|
8275
|
+
throw new Error(error.error ?? `Prompt publish failed: ${res.status}`);
|
|
8276
|
+
}
|
|
8277
|
+
return res.json();
|
|
8278
|
+
}
|
|
8232
8279
|
|
|
8233
8280
|
// src/auth-store.ts
|
|
8234
8281
|
var import_promises6 = require("node:fs/promises");
|
|
@@ -8321,6 +8368,12 @@ function upsertLockEntry(lock, name, entry) {
|
|
|
8321
8368
|
packages: { ...lock.packages, [name]: entry }
|
|
8322
8369
|
};
|
|
8323
8370
|
}
|
|
8371
|
+
function upsertPromptLockEntry(lock, name, entry) {
|
|
8372
|
+
return {
|
|
8373
|
+
...lock,
|
|
8374
|
+
prompts: { ...lock.prompts, [name]: entry }
|
|
8375
|
+
};
|
|
8376
|
+
}
|
|
8324
8377
|
function resolveRegistryUrl(project, flag, fallback) {
|
|
8325
8378
|
const fromEnv = process.env.AIPM_REGISTRY_URL ?? process.env.AIPM_REGISTRY;
|
|
8326
8379
|
if (flag) return flag.replace(/\/$/, "");
|
|
@@ -8540,7 +8593,8 @@ async function installOnePackage(options) {
|
|
|
8540
8593
|
});
|
|
8541
8594
|
const lock = await readLockfile(configRoot) ?? {
|
|
8542
8595
|
schemaVersion: "0.1",
|
|
8543
|
-
packages: {}
|
|
8596
|
+
packages: {},
|
|
8597
|
+
prompts: {}
|
|
8544
8598
|
};
|
|
8545
8599
|
const installed = {};
|
|
8546
8600
|
for (const tool of result.resolvedTools) {
|
|
@@ -8738,11 +8792,181 @@ async function runDoctor(options) {
|
|
|
8738
8792
|
}
|
|
8739
8793
|
}
|
|
8740
8794
|
|
|
8795
|
+
// src/prompt-install.ts
|
|
8796
|
+
var import_node_crypto2 = require("node:crypto");
|
|
8797
|
+
var import_promises10 = require("node:fs/promises");
|
|
8798
|
+
var import_node_path12 = require("node:path");
|
|
8799
|
+
var PROMPT_SITE_ORIGIN = "https://www.aipm-registry.com";
|
|
8800
|
+
function parsePromptUrl(value) {
|
|
8801
|
+
let url;
|
|
8802
|
+
try {
|
|
8803
|
+
url = new URL(value);
|
|
8804
|
+
} catch {
|
|
8805
|
+
return null;
|
|
8806
|
+
}
|
|
8807
|
+
if (url.origin !== PROMPT_SITE_ORIGIN) return null;
|
|
8808
|
+
const parts = url.pathname.split("/").filter(Boolean);
|
|
8809
|
+
if (parts.length !== 3 || parts[0] !== "prompts") return null;
|
|
8810
|
+
const publisher = decodeURIComponent(parts[1] ?? "").trim().toLowerCase();
|
|
8811
|
+
const slug = decodeURIComponent(parts[2] ?? "").trim().toLowerCase();
|
|
8812
|
+
if (!publisher || !slug || !/^[a-z0-9][a-z0-9-]*$/.test(publisher) || !/^[a-z0-9][a-z0-9-]*$/.test(slug)) {
|
|
8813
|
+
return null;
|
|
8814
|
+
}
|
|
8815
|
+
return {
|
|
8816
|
+
url: `${PROMPT_SITE_ORIGIN}/prompts/${encodeURIComponent(publisher)}/${encodeURIComponent(slug)}`,
|
|
8817
|
+
publisher,
|
|
8818
|
+
slug,
|
|
8819
|
+
alias: slug
|
|
8820
|
+
};
|
|
8821
|
+
}
|
|
8822
|
+
function promptDirectory(configRoot) {
|
|
8823
|
+
return (0, import_node_path12.basename)(configRoot) === ".aipm" ? (0, import_node_path12.join)(configRoot, "prompts") : (0, import_node_path12.join)(configRoot, ".aipm", "prompts");
|
|
8824
|
+
}
|
|
8825
|
+
function promptSnapshotPath(configRoot, reference) {
|
|
8826
|
+
return (0, import_node_path12.join)(promptDirectory(configRoot), `${reference.publisher}--${reference.slug}.md`);
|
|
8827
|
+
}
|
|
8828
|
+
function canonicalPromptContent(prompt) {
|
|
8829
|
+
return JSON.stringify({
|
|
8830
|
+
promptText: prompt.promptText,
|
|
8831
|
+
variables: prompt.variables,
|
|
8832
|
+
exampleInput: prompt.exampleInput,
|
|
8833
|
+
exampleOutput: prompt.exampleOutput,
|
|
8834
|
+
usageNotes: prompt.usageNotes,
|
|
8835
|
+
license: prompt.license
|
|
8836
|
+
});
|
|
8837
|
+
}
|
|
8838
|
+
function promptContentHash(prompt) {
|
|
8839
|
+
return (0, import_node_crypto2.createHash)("sha256").update(canonicalPromptContent(prompt)).digest("hex");
|
|
8840
|
+
}
|
|
8841
|
+
function section(title, value) {
|
|
8842
|
+
return value?.trim() ? `
|
|
8843
|
+
## ${title}
|
|
8844
|
+
|
|
8845
|
+
${value.trim()}
|
|
8846
|
+
` : "";
|
|
8847
|
+
}
|
|
8848
|
+
function renderPromptMarkdown(prompt, sourceUrl) {
|
|
8849
|
+
const variables = prompt.variables.length ? `
|
|
8850
|
+
## Variables
|
|
8851
|
+
|
|
8852
|
+
${prompt.variables.map(
|
|
8853
|
+
(variable) => `- \`{{${variable.name}}}\`${variable.required ? " \u2014 required" : " \u2014 optional"}: ${variable.description}${variable.example ? ` Example: ${variable.example}` : ""}`
|
|
8854
|
+
).join("\n")}
|
|
8855
|
+
` : "";
|
|
8856
|
+
const publisher = prompt.publisher.org?.name ?? prompt.publisher.user.name ?? `@${prompt.publisher.scope}`;
|
|
8857
|
+
return [
|
|
8858
|
+
`# ${prompt.title}`,
|
|
8859
|
+
"",
|
|
8860
|
+
prompt.summary,
|
|
8861
|
+
"",
|
|
8862
|
+
`- Source: ${sourceUrl}`,
|
|
8863
|
+
`- Publisher: ${publisher} (@${prompt.publisher.scope})`,
|
|
8864
|
+
`- Category: ${prompt.category}`,
|
|
8865
|
+
`- Inputs: ${prompt.inputTypes.join(", ") || "none"}`,
|
|
8866
|
+
`- Outputs: ${prompt.outputTypes.join(", ") || "none"}`,
|
|
8867
|
+
`- Effort: ${prompt.effort}`,
|
|
8868
|
+
`- Language: ${prompt.language}`,
|
|
8869
|
+
`- License: ${prompt.license}`,
|
|
8870
|
+
`- Updated: ${prompt.updatedAt}`,
|
|
8871
|
+
"",
|
|
8872
|
+
"## Prompt",
|
|
8873
|
+
"",
|
|
8874
|
+
prompt.promptText.trim(),
|
|
8875
|
+
variables,
|
|
8876
|
+
section("Example input", prompt.exampleInput),
|
|
8877
|
+
section("Example output", prompt.exampleOutput),
|
|
8878
|
+
section("Usage notes", prompt.usageNotes)
|
|
8879
|
+
].join("\n").replace(/\n{3,}/g, "\n\n").trimEnd().concat("\n");
|
|
8880
|
+
}
|
|
8881
|
+
function emptyLock() {
|
|
8882
|
+
return { schemaVersion: "0.1", packages: {}, prompts: {} };
|
|
8883
|
+
}
|
|
8884
|
+
function installedRelativePath(configRoot, absolutePath) {
|
|
8885
|
+
return (0, import_node_path12.relative)(configRoot, absolutePath).split("\\").join("/");
|
|
8886
|
+
}
|
|
8887
|
+
async function installTrackedPrompt(input2) {
|
|
8888
|
+
const prompt = await fetchPromptDetail(
|
|
8889
|
+
input2.registry,
|
|
8890
|
+
input2.reference.publisher,
|
|
8891
|
+
input2.reference.slug
|
|
8892
|
+
);
|
|
8893
|
+
const canonicalReference = {
|
|
8894
|
+
...input2.reference,
|
|
8895
|
+
publisher: prompt.publisher.scope,
|
|
8896
|
+
slug: prompt.slug,
|
|
8897
|
+
url: `${PROMPT_SITE_ORIGIN}/prompts/${encodeURIComponent(prompt.publisher.scope)}/${encodeURIComponent(prompt.slug)}`
|
|
8898
|
+
};
|
|
8899
|
+
const outputPath = promptSnapshotPath(input2.configRoot, canonicalReference);
|
|
8900
|
+
const contentHash = promptContentHash(prompt);
|
|
8901
|
+
const lock = await readLockfile(input2.configRoot) ?? emptyLock();
|
|
8902
|
+
const existing = lock.prompts[input2.reference.alias];
|
|
8903
|
+
const fileExists = Boolean(await (0, import_promises10.stat)(outputPath).catch(() => null));
|
|
8904
|
+
const changed = !existing || existing.contentHash !== contentHash || !fileExists;
|
|
8905
|
+
if (!input2.updateOnly || changed) {
|
|
8906
|
+
await (0, import_promises10.mkdir)((0, import_node_path12.dirname)(outputPath), { recursive: true });
|
|
8907
|
+
await (0, import_promises10.writeFile)(outputPath, renderPromptMarkdown(prompt, canonicalReference.url), "utf8");
|
|
8908
|
+
}
|
|
8909
|
+
if (input2.track) {
|
|
8910
|
+
const currentUrl = input2.project.prompts[input2.reference.alias];
|
|
8911
|
+
if (currentUrl && currentUrl !== canonicalReference.url) {
|
|
8912
|
+
throw new Error(
|
|
8913
|
+
`Prompt alias "${input2.reference.alias}" already tracks ${currentUrl}. Remove it first or use a unique slug.`
|
|
8914
|
+
);
|
|
8915
|
+
}
|
|
8916
|
+
await writeProjectPackageJson(input2.configRoot, {
|
|
8917
|
+
...input2.project,
|
|
8918
|
+
prompts: { ...input2.project.prompts, [input2.reference.alias]: canonicalReference.url }
|
|
8919
|
+
});
|
|
8920
|
+
}
|
|
8921
|
+
const entry = {
|
|
8922
|
+
id: prompt.id,
|
|
8923
|
+
url: canonicalReference.url,
|
|
8924
|
+
publisher: canonicalReference.publisher,
|
|
8925
|
+
slug: canonicalReference.slug,
|
|
8926
|
+
contentHash,
|
|
8927
|
+
updatedAt: prompt.updatedAt,
|
|
8928
|
+
installedPath: installedRelativePath(input2.configRoot, outputPath)
|
|
8929
|
+
};
|
|
8930
|
+
await writeLockfile(input2.configRoot, upsertPromptLockEntry(lock, input2.reference.alias, entry));
|
|
8931
|
+
if (input2.recordCopy) {
|
|
8932
|
+
await recordPromptCopy(input2.registry, canonicalReference.publisher, canonicalReference.slug).catch(
|
|
8933
|
+
() => void 0
|
|
8934
|
+
);
|
|
8935
|
+
}
|
|
8936
|
+
return { changed, path: outputPath, prompt };
|
|
8937
|
+
}
|
|
8938
|
+
function resolveTrackedPrompt(project, value) {
|
|
8939
|
+
const direct = parsePromptUrl(value);
|
|
8940
|
+
if (direct) {
|
|
8941
|
+
const match = Object.entries(project.prompts).find(([, url2]) => url2 === direct.url);
|
|
8942
|
+
return match ? { ...direct, alias: match[0] } : direct;
|
|
8943
|
+
}
|
|
8944
|
+
const url = project.prompts[value];
|
|
8945
|
+
const parsed = url ? parsePromptUrl(url) : null;
|
|
8946
|
+
return parsed ? { ...parsed, alias: value } : null;
|
|
8947
|
+
}
|
|
8948
|
+
async function readInstalledPrompt(configRoot, entry) {
|
|
8949
|
+
return (0, import_promises10.readFile)((0, import_node_path12.join)(configRoot, entry.installedPath), "utf8");
|
|
8950
|
+
}
|
|
8951
|
+
async function removeTrackedPrompt(input2) {
|
|
8952
|
+
const lock = await readLockfile(input2.configRoot);
|
|
8953
|
+
const entry = lock?.prompts[input2.reference.alias];
|
|
8954
|
+
if (entry) await (0, import_promises10.rm)((0, import_node_path12.join)(input2.configRoot, entry.installedPath), { force: true });
|
|
8955
|
+
const prompts = { ...input2.project.prompts };
|
|
8956
|
+
delete prompts[input2.reference.alias];
|
|
8957
|
+
await writeProjectPackageJson(input2.configRoot, { ...input2.project, prompts });
|
|
8958
|
+
if (lock) {
|
|
8959
|
+
const lockedPrompts = { ...lock.prompts };
|
|
8960
|
+
delete lockedPrompts[input2.reference.alias];
|
|
8961
|
+
await writeLockfile(input2.configRoot, { ...lock, prompts: lockedPrompts });
|
|
8962
|
+
}
|
|
8963
|
+
}
|
|
8964
|
+
|
|
8741
8965
|
// src/version.ts
|
|
8742
8966
|
var import_node_fs2 = require("node:fs");
|
|
8743
|
-
var
|
|
8967
|
+
var import_node_path13 = require("node:path");
|
|
8744
8968
|
function getCliVersion() {
|
|
8745
|
-
const pkgPath = (0,
|
|
8969
|
+
const pkgPath = (0, import_node_path13.join)(__dirname, "..", "package.json");
|
|
8746
8970
|
const pkg = JSON.parse((0, import_node_fs2.readFileSync)(pkgPath, "utf8"));
|
|
8747
8971
|
return pkg.version;
|
|
8748
8972
|
}
|
|
@@ -8831,11 +9055,14 @@ async function notifyCliUpdateIfNeeded(currentVersion, options) {
|
|
|
8831
9055
|
var CLI_VERSION = getCliVersion();
|
|
8832
9056
|
var program2 = new Command();
|
|
8833
9057
|
var DEFAULT_REGISTRY = "https://api.aipm-registry.com";
|
|
8834
|
-
var SITE_URL = "https://aipm-registry.com";
|
|
8835
|
-
var PUBLISH_DOC_URL =
|
|
8836
|
-
var DASHBOARD_URL =
|
|
9058
|
+
var SITE_URL = "https://www.aipm-registry.com";
|
|
9059
|
+
var PUBLISH_DOC_URL = `${SITE_URL}/publish`;
|
|
9060
|
+
var DASHBOARD_URL = `${SITE_URL}/dashboard`;
|
|
8837
9061
|
var GLOBAL_OPTION = "-g, --global";
|
|
8838
9062
|
var GLOBAL_OPTION_DESC = "Use global config (~/.aipm) and install skills under your home directory";
|
|
9063
|
+
var normalizedArgv = process.argv.map(
|
|
9064
|
+
(arg) => arg === "-prompt" ? "--prompt" : arg === "-skill" ? "--skill" : arg
|
|
9065
|
+
);
|
|
8839
9066
|
program2.name("aipm").description("AI package manager").enablePositionalOptions().version(CLI_VERSION, "-v, --version", "output the current version").option("--verbose", "Print extra diagnostic output").option("--quiet", "Reduce non-essential output").addHelpText(
|
|
8840
9067
|
"after",
|
|
8841
9068
|
`
|
|
@@ -8863,6 +9090,10 @@ function packagePageUrl(packageName, version) {
|
|
|
8863
9090
|
const [scope, name] = packageName.replace(/^@/, "").split("/");
|
|
8864
9091
|
return `${SITE_URL}/packages/${encodeURIComponent(scope ?? "")}/${encodeURIComponent(name ?? "")}/${encodeURIComponent(version)}`;
|
|
8865
9092
|
}
|
|
9093
|
+
function packageBadgeMarkdown(packageName, version) {
|
|
9094
|
+
const pageUrl = packagePageUrl(packageName, version);
|
|
9095
|
+
return `[](${pageUrl})`;
|
|
9096
|
+
}
|
|
8866
9097
|
function parsePackageArg(value) {
|
|
8867
9098
|
const match = value.match(/^(@[^@]+)(?:@(.+))?$/);
|
|
8868
9099
|
if (!match) throw new Error("Package must be @scope/name or @scope/name@version");
|
|
@@ -8877,23 +9108,23 @@ function skillFolderName(name) {
|
|
|
8877
9108
|
return folder;
|
|
8878
9109
|
}
|
|
8879
9110
|
async function writeStarterFile(path2, content) {
|
|
8880
|
-
await (0,
|
|
9111
|
+
await (0, import_promises11.writeFile)(path2, content, { encoding: "utf8", flag: "wx" });
|
|
8881
9112
|
}
|
|
8882
9113
|
async function inferEntryFromSource(source, fallback) {
|
|
8883
|
-
const sourceInfo = await (0,
|
|
8884
|
-
if (sourceInfo.isFile()) return (0,
|
|
9114
|
+
const sourceInfo = await (0, import_promises11.stat)(source);
|
|
9115
|
+
if (sourceInfo.isFile()) return (0, import_node_path14.basename)(source);
|
|
8885
9116
|
if (!sourceInfo.isDirectory()) return fallback;
|
|
8886
|
-
const entries = await (0,
|
|
9117
|
+
const entries = await (0, import_promises11.readdir)(source);
|
|
8887
9118
|
if (entries.includes(fallback)) return fallback;
|
|
8888
9119
|
return entries.find((entry) => entry.endsWith(".md")) ?? entries.find((entry) => entry.endsWith(".mdc")) ?? fallback;
|
|
8889
9120
|
}
|
|
8890
9121
|
async function copySourceIntoSkillRoot(source, root, sourceLabel) {
|
|
8891
|
-
const sourceInfo = await (0,
|
|
8892
|
-
await (0,
|
|
9122
|
+
const sourceInfo = await (0, import_promises11.stat)(source);
|
|
9123
|
+
await (0, import_promises11.mkdir)(root, { recursive: true });
|
|
8893
9124
|
if (sourceInfo.isDirectory()) {
|
|
8894
|
-
const entries = await (0,
|
|
9125
|
+
const entries = await (0, import_promises11.readdir)(source);
|
|
8895
9126
|
for (const entry of entries) {
|
|
8896
|
-
await (0,
|
|
9127
|
+
await (0, import_promises11.cp)((0, import_node_path14.join)(source, entry), (0, import_node_path14.join)(root, entry), {
|
|
8897
9128
|
recursive: true,
|
|
8898
9129
|
force: false,
|
|
8899
9130
|
errorOnExist: true
|
|
@@ -8902,7 +9133,7 @@ async function copySourceIntoSkillRoot(source, root, sourceLabel) {
|
|
|
8902
9133
|
return;
|
|
8903
9134
|
}
|
|
8904
9135
|
if (sourceInfo.isFile()) {
|
|
8905
|
-
await (0,
|
|
9136
|
+
await (0, import_promises11.cp)(source, (0, import_node_path14.join)(root, (0, import_node_path14.basename)(source)), { force: false, errorOnExist: true });
|
|
8906
9137
|
return;
|
|
8907
9138
|
}
|
|
8908
9139
|
throw new Error(`Unsupported source path: ${sourceLabel}`);
|
|
@@ -8936,11 +9167,11 @@ function packageDashboardUrl(name) {
|
|
|
8936
9167
|
return `${DASHBOARD_URL}/packages/${name.replace(/^@/, "")}`;
|
|
8937
9168
|
}
|
|
8938
9169
|
function helperRootFromTrackedPath(path2) {
|
|
8939
|
-
const parts = path2.split(
|
|
9170
|
+
const parts = path2.split(import_node_path14.sep).filter(Boolean);
|
|
8940
9171
|
const helpersIndex = parts.lastIndexOf("helpers");
|
|
8941
9172
|
if (helpersIndex === -1 || parts.length <= helpersIndex + 2) return null;
|
|
8942
|
-
const prefix = path2.startsWith(
|
|
8943
|
-
return (0,
|
|
9173
|
+
const prefix = path2.startsWith(import_node_path14.sep) ? import_node_path14.sep : "";
|
|
9174
|
+
return (0, import_node_path14.join)(prefix, ...parts.slice(0, helpersIndex + 3));
|
|
8944
9175
|
}
|
|
8945
9176
|
async function showInstalledPrompt(configRoot, packageArg) {
|
|
8946
9177
|
const { name } = parsePackageArg(packageArg);
|
|
@@ -8953,7 +9184,7 @@ async function showInstalledPrompt(configRoot, packageArg) {
|
|
|
8953
9184
|
if (entry.postInstall.status === "cleaned") {
|
|
8954
9185
|
throw new Error(`Setup helper files were cleaned for ${name}. Reinstall the package to restore them.`);
|
|
8955
9186
|
}
|
|
8956
|
-
const content = await (0,
|
|
9187
|
+
const content = await (0, import_promises11.readFile)(entry.postInstall.promptFile, "utf8").catch(() => {
|
|
8957
9188
|
throw new Error(`Setup prompt file is missing: ${entry.postInstall?.promptFile}`);
|
|
8958
9189
|
});
|
|
8959
9190
|
console.log(`Prompt file: ${entry.postInstall.promptFile}`);
|
|
@@ -8986,9 +9217,9 @@ async function cleanupInstalledHelpers(options) {
|
|
|
8986
9217
|
entry.installedAssets.helper.map((path2) => helperRootFromTrackedPath(path2)).filter((path2) => Boolean(path2))
|
|
8987
9218
|
);
|
|
8988
9219
|
if (roots.size > 0) {
|
|
8989
|
-
for (const root of roots) await (0,
|
|
9220
|
+
for (const root of roots) await (0, import_promises11.rm)(root, { recursive: true, force: true });
|
|
8990
9221
|
} else {
|
|
8991
|
-
for (const helperPath of entry.installedAssets.helper) await (0,
|
|
9222
|
+
for (const helperPath of entry.installedAssets.helper) await (0, import_promises11.rm)(helperPath, { force: true });
|
|
8992
9223
|
}
|
|
8993
9224
|
await writeLockfile(options.configRoot, {
|
|
8994
9225
|
...lock,
|
|
@@ -9011,10 +9242,10 @@ async function openUrl(url) {
|
|
|
9011
9242
|
console.log(`Open: ${url}`);
|
|
9012
9243
|
}
|
|
9013
9244
|
function randomBase64Url(bytes = 32) {
|
|
9014
|
-
return (0,
|
|
9245
|
+
return (0, import_node_crypto3.randomBytes)(bytes).toString("base64url");
|
|
9015
9246
|
}
|
|
9016
9247
|
function sha256Base64Url(value) {
|
|
9017
|
-
return (0,
|
|
9248
|
+
return (0, import_node_crypto3.createHash)("sha256").update(value).digest("base64url");
|
|
9018
9249
|
}
|
|
9019
9250
|
function deviceName() {
|
|
9020
9251
|
return `AIPM CLI on ${process.platform}`;
|
|
@@ -9282,9 +9513,9 @@ function buildStarterQualityMetadata(options) {
|
|
|
9282
9513
|
}
|
|
9283
9514
|
async function initSkill(opts) {
|
|
9284
9515
|
if (!isValidScopeName(opts.name)) throw new Error("Invalid @scope/name");
|
|
9285
|
-
const root = opts.here ? (0, import_node_process5.cwd)() : (0,
|
|
9516
|
+
const root = opts.here ? (0, import_node_process5.cwd)() : (0, import_node_path14.resolve)((0, import_node_process5.cwd)(), opts.dir ?? skillFolderName(opts.name));
|
|
9286
9517
|
const cdTarget = opts.here ? null : opts.dir ?? skillFolderName(opts.name);
|
|
9287
|
-
const source = opts.from ? (0,
|
|
9518
|
+
const source = opts.from ? (0, import_node_path14.resolve)(opts.from) : null;
|
|
9288
9519
|
const entry = source ? await inferEntryFromSource(source, opts.entry) : opts.entry;
|
|
9289
9520
|
const template = parseSkillTemplate(opts.template ?? "blank");
|
|
9290
9521
|
const manifest = PackageManifestSchema.parse({
|
|
@@ -9305,17 +9536,17 @@ async function initSkill(opts) {
|
|
|
9305
9536
|
if (source) {
|
|
9306
9537
|
await copySourceIntoSkillRoot(source, root, source);
|
|
9307
9538
|
} else {
|
|
9308
|
-
await (0,
|
|
9539
|
+
await (0, import_promises11.mkdir)(root, { recursive: true });
|
|
9309
9540
|
}
|
|
9310
|
-
await writeStarterFile((0,
|
|
9541
|
+
await writeStarterFile((0, import_node_path14.join)(root, "aipm.manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
|
|
9311
9542
|
if (!opts.from) {
|
|
9312
9543
|
await writeStarterFile(
|
|
9313
|
-
(0,
|
|
9544
|
+
(0, import_node_path14.join)(root, entry),
|
|
9314
9545
|
SKILL_TEMPLATES[template](opts.name)
|
|
9315
9546
|
);
|
|
9316
9547
|
}
|
|
9317
9548
|
await writeStarterFile(
|
|
9318
|
-
(0,
|
|
9549
|
+
(0, import_node_path14.join)(root, ".aipmignore"),
|
|
9319
9550
|
[
|
|
9320
9551
|
"# Files that should never be published with this skill",
|
|
9321
9552
|
".env*",
|
|
@@ -9333,7 +9564,7 @@ async function initSkill(opts) {
|
|
|
9333
9564
|
""
|
|
9334
9565
|
].join("\n")
|
|
9335
9566
|
);
|
|
9336
|
-
await (0,
|
|
9567
|
+
await (0, import_promises11.mkdir)((0, import_node_path14.join)(root, ".aipm"), { recursive: true });
|
|
9337
9568
|
console.log(`Created ${opts.name} skill folder: ${root}`);
|
|
9338
9569
|
printPublishGuide(
|
|
9339
9570
|
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.`,
|
|
@@ -9348,7 +9579,7 @@ program2.command("init").description("Create aipm.package.json in the current pr
|
|
|
9348
9579
|
console.log(`aipm.package.json already exists (${scopeLabel(scope)}).`);
|
|
9349
9580
|
return;
|
|
9350
9581
|
}
|
|
9351
|
-
if (scope.global) await (0,
|
|
9582
|
+
if (scope.global) await (0, import_promises11.mkdir)(configRoot, { recursive: true });
|
|
9352
9583
|
const installRoot = resolveInstallRoot(scope);
|
|
9353
9584
|
const detected = await detectToolsInProject(installRoot);
|
|
9354
9585
|
const parsedTarget = parseTargetFlag(opts.target);
|
|
@@ -9362,7 +9593,8 @@ program2.command("init").description("Create aipm.package.json in the current pr
|
|
|
9362
9593
|
schemaVersion: "0.1",
|
|
9363
9594
|
registry,
|
|
9364
9595
|
preferredTools: preferredTools.length ? preferredTools : void 0,
|
|
9365
|
-
packages: {}
|
|
9596
|
+
packages: {},
|
|
9597
|
+
prompts: {}
|
|
9366
9598
|
});
|
|
9367
9599
|
console.log(`Created aipm.package.json (${scopeLabel(scope)}, registry: ${registry})`);
|
|
9368
9600
|
});
|
|
@@ -9450,14 +9682,27 @@ program2.command("config").description("Show resolved AIPM CLI and project confi
|
|
|
9450
9682
|
console.log(`Auth file: ${data.authFile}`);
|
|
9451
9683
|
console.log(`Publish guide: ${data.publishDoc}`);
|
|
9452
9684
|
});
|
|
9453
|
-
program2.command("add <package>").description("Add
|
|
9454
|
-
const { name, version: requestedVersion } = parsePackageArg(pkgArg);
|
|
9685
|
+
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, or *").option("--token <token>", "Install token for private packages").option("--ci", "Non-interactive; fail if prompt needed").action(async (pkgArg, opts) => {
|
|
9455
9686
|
const scope = { global: opts.global };
|
|
9456
9687
|
const configRoot = resolveConfigRoot(scope);
|
|
9457
9688
|
const installRoot = resolveInstallRoot(scope);
|
|
9458
9689
|
let project = await readProjectPackageJson(configRoot);
|
|
9459
9690
|
if (!project) throw new Error(initRequiredMessage(scope));
|
|
9460
9691
|
const registry = resolveRegistryUrl(project, opts.registry, DEFAULT_REGISTRY);
|
|
9692
|
+
const promptReference = parsePromptUrl(pkgArg);
|
|
9693
|
+
if (promptReference) {
|
|
9694
|
+
const result = await installTrackedPrompt({
|
|
9695
|
+
configRoot,
|
|
9696
|
+
registry,
|
|
9697
|
+
project,
|
|
9698
|
+
reference: promptReference,
|
|
9699
|
+
track: true,
|
|
9700
|
+
recordCopy: true
|
|
9701
|
+
});
|
|
9702
|
+
console.log(`Installed prompt ${result.prompt.title} \u2192 ${result.path}`);
|
|
9703
|
+
return;
|
|
9704
|
+
}
|
|
9705
|
+
const { name, version: requestedVersion } = parsePackageArg(pkgArg);
|
|
9461
9706
|
const token = await tokenForRead(registry, opts.token);
|
|
9462
9707
|
const version = requestedVersion ?? project.packages[name] ?? await latestVersionForPackage(registry, name, token);
|
|
9463
9708
|
if (!version) throw new Error("Specify version: aipm add @scope/pkg@1.0.0");
|
|
@@ -9531,10 +9776,75 @@ program2.command("install").description("Install all packages from aipm.package.
|
|
|
9531
9776
|
token
|
|
9532
9777
|
});
|
|
9533
9778
|
}
|
|
9779
|
+
for (const [alias, url] of Object.entries(project.prompts)) {
|
|
9780
|
+
const reference = parsePromptUrl(url);
|
|
9781
|
+
if (!reference) throw new Error(`Invalid tracked prompt URL for ${alias}: ${url}`);
|
|
9782
|
+
const result = await installTrackedPrompt({
|
|
9783
|
+
configRoot,
|
|
9784
|
+
registry,
|
|
9785
|
+
project,
|
|
9786
|
+
reference: { ...reference, alias },
|
|
9787
|
+
track: false
|
|
9788
|
+
});
|
|
9789
|
+
console.log(`Restored prompt ${result.prompt.title} \u2192 ${result.path}`);
|
|
9790
|
+
}
|
|
9791
|
+
});
|
|
9792
|
+
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) => {
|
|
9793
|
+
const configRoot = resolveConfigRoot({ global: opts.global });
|
|
9794
|
+
const project = await readProjectPackageJson(configRoot);
|
|
9795
|
+
if (!project) throw new Error(initRequiredMessage({ global: opts.global }));
|
|
9796
|
+
const reference = resolveTrackedPrompt(project, value);
|
|
9797
|
+
if (reference && project.prompts[reference.alias] === reference.url) {
|
|
9798
|
+
const lock2 = await readLockfile(configRoot);
|
|
9799
|
+
const entry2 = lock2?.prompts[reference.alias];
|
|
9800
|
+
if (!entry2) throw new Error(`Prompt is not installed: ${reference.url}. Run aipm install.`);
|
|
9801
|
+
console.log(await readInstalledPrompt(configRoot, entry2));
|
|
9802
|
+
return;
|
|
9803
|
+
}
|
|
9804
|
+
const { name } = parsePackageArg(value);
|
|
9805
|
+
const lock = await readLockfile(configRoot);
|
|
9806
|
+
const entry = lock?.packages[name];
|
|
9807
|
+
if (!entry) throw new Error(`Package is not installed: ${name}`);
|
|
9808
|
+
console.log(`${name}@${entry.version} [${entry.resolvedTools.join(", ")}]`);
|
|
9534
9809
|
});
|
|
9535
9810
|
program2.command("show-prompt <package>").description("Show the manual setup prompt installed with a package").option(GLOBAL_OPTION, GLOBAL_OPTION_DESC).action(async (pkgArg, opts) => {
|
|
9536
9811
|
await showInstalledPrompt(resolveConfigRoot({ global: opts.global }), pkgArg);
|
|
9537
9812
|
});
|
|
9813
|
+
function promptImageContentType(path2) {
|
|
9814
|
+
const extension = (0, import_node_path14.extname)(path2).toLowerCase();
|
|
9815
|
+
if (extension === ".png") return "image/png";
|
|
9816
|
+
if (extension === ".jpg" || extension === ".jpeg") return "image/jpeg";
|
|
9817
|
+
if (extension === ".webp") return "image/webp";
|
|
9818
|
+
throw new Error(`Unsupported prompt sample image: ${path2}`);
|
|
9819
|
+
}
|
|
9820
|
+
var promptCommand = program2.command("prompt").description("Publish prompt listings from structured JSON");
|
|
9821
|
+
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) => {
|
|
9822
|
+
const registry = registryFromEnvOrDefault(opts.registry);
|
|
9823
|
+
const accessToken = await authTokenForRegistry(registry, { throwOnFailure: true });
|
|
9824
|
+
if (!accessToken) throw new Error("Run aipm login before publishing prompts.");
|
|
9825
|
+
const absoluteFile = (0, import_node_path14.resolve)(file);
|
|
9826
|
+
const parsed = JSON.parse(await (0, import_promises11.readFile)(absoluteFile, "utf8"));
|
|
9827
|
+
const rows = Array.isArray(parsed) ? parsed : "prompts" in parsed ? parsed.prompts : [parsed];
|
|
9828
|
+
if (rows.length === 0) throw new Error("Prompt publish file contains no prompts.");
|
|
9829
|
+
if (!opts.yes) {
|
|
9830
|
+
console.log(`Reminder: this will publish ${rows.length} public prompt${rows.length === 1 ? "" : "s"}.`);
|
|
9831
|
+
console.log("Use --yes to skip this reminder in automation.");
|
|
9832
|
+
}
|
|
9833
|
+
for (const row of rows) {
|
|
9834
|
+
const { sampleImagePath, ...input2 } = row;
|
|
9835
|
+
let sampleImage;
|
|
9836
|
+
if (sampleImagePath) {
|
|
9837
|
+
const imagePath = (0, import_node_path14.resolve)((0, import_node_path14.dirname)(absoluteFile), sampleImagePath);
|
|
9838
|
+
sampleImage = {
|
|
9839
|
+
data: await (0, import_promises11.readFile)(imagePath),
|
|
9840
|
+
filename: (0, import_node_path14.basename)(imagePath),
|
|
9841
|
+
contentType: promptImageContentType(imagePath)
|
|
9842
|
+
};
|
|
9843
|
+
}
|
|
9844
|
+
const saved = await publishPrompt(registry, input2, accessToken, sampleImage);
|
|
9845
|
+
console.log(`Published ${saved.title} \u2192 ${SITE_URL}${saved.path}`);
|
|
9846
|
+
}
|
|
9847
|
+
});
|
|
9538
9848
|
program2.command("cleanup <package>").description("Delete temporary helper files installed with a package").option(GLOBAL_OPTION, GLOBAL_OPTION_DESC).option("--yes", "Delete helper files without asking for confirmation").action(async (pkgArg, opts) => {
|
|
9539
9849
|
await cleanupInstalledHelpers({
|
|
9540
9850
|
configRoot: resolveConfigRoot({ global: opts.global }),
|
|
@@ -9550,8 +9860,8 @@ var publish = program2.command("publish [dir]").description("Stage, validate, an
|
|
|
9550
9860
|
publish.help();
|
|
9551
9861
|
return;
|
|
9552
9862
|
}
|
|
9553
|
-
const abs = (0,
|
|
9554
|
-
const manifestRaw = await (0,
|
|
9863
|
+
const abs = (0, import_node_path14.resolve)(dir);
|
|
9864
|
+
const manifestRaw = await (0, import_promises11.readFile)((0, import_node_path14.join)(abs, "aipm.manifest.json"), "utf8");
|
|
9555
9865
|
const manifest = PackageManifestSchema.parse(JSON.parse(manifestRaw));
|
|
9556
9866
|
const tarball = await packDirectory(abs);
|
|
9557
9867
|
const registry = registryFromEnvOrDefault(opts.registry);
|
|
@@ -9559,6 +9869,7 @@ var publish = program2.command("publish [dir]").description("Stage, validate, an
|
|
|
9559
9869
|
console.log(`Published ${manifest.name}@${result.version}`);
|
|
9560
9870
|
console.log(`View: ${packagePageUrl(manifest.name, result.version)}`);
|
|
9561
9871
|
console.log(`Install: aipm add ${manifest.name}@${result.version} --target ${manifest.targets[0]} --ci`);
|
|
9872
|
+
console.log(`README badge: ${packageBadgeMarkdown(manifest.name, result.version)}`);
|
|
9562
9873
|
printPublishGuide(
|
|
9563
9874
|
`Published ${manifest.name}@${result.version} from ${abs}.`,
|
|
9564
9875
|
"Share the install command or open the package page to confirm the listing."
|
|
@@ -9684,28 +9995,46 @@ publish.command("push").description("Publish staged files").option("--registry <
|
|
|
9684
9995
|
console.log(`Published ${manifest.name}@${result.version}`);
|
|
9685
9996
|
console.log(`View: ${packagePageUrl(manifest.name, result.version)}`);
|
|
9686
9997
|
console.log(`Install: aipm add ${manifest.name}@${result.version} --target ${manifest.targets[0]} --ci`);
|
|
9998
|
+
console.log(`README badge: ${packageBadgeMarkdown(manifest.name, result.version)}`);
|
|
9687
9999
|
printPublishGuide(
|
|
9688
10000
|
`Published ${manifest.name}@${result.version} to ${registry}.`,
|
|
9689
10001
|
"Open the registry page, test the install command in a clean project, and share the package link."
|
|
9690
10002
|
);
|
|
9691
10003
|
});
|
|
9692
|
-
program2.command("list").description("List
|
|
10004
|
+
program2.command("list").description("List installed skills and prompts from aipm-lock.json").option(GLOBAL_OPTION, GLOBAL_OPTION_DESC).option("-p, --prompt", "List prompts only").option("-s, --skill", "List skills only").action(async (opts) => {
|
|
9693
10005
|
const configRoot = resolveConfigRoot({ global: opts.global });
|
|
9694
10006
|
const lock = await readLockfile(configRoot);
|
|
9695
|
-
|
|
9696
|
-
|
|
10007
|
+
const showSkills = !opts.prompt || Boolean(opts.skill);
|
|
10008
|
+
const showPrompts = !opts.skill || Boolean(opts.prompt);
|
|
10009
|
+
const skillRows = lock ? Object.entries(lock.packages) : [];
|
|
10010
|
+
const promptRows = lock ? Object.entries(lock.prompts) : [];
|
|
10011
|
+
if ((!showSkills || skillRows.length === 0) && (!showPrompts || promptRows.length === 0)) {
|
|
10012
|
+
console.log(opts.prompt && !opts.skill ? "No prompts installed." : opts.skill && !opts.prompt ? "No skills installed." : "No skills or prompts installed.");
|
|
9697
10013
|
return;
|
|
9698
10014
|
}
|
|
9699
|
-
|
|
9700
|
-
|
|
10015
|
+
if (showSkills) {
|
|
10016
|
+
for (const [name, entry] of skillRows) {
|
|
10017
|
+
console.log(`skill ${name}@${entry.version} [${entry.resolvedTools.join(", ")}]`);
|
|
10018
|
+
}
|
|
10019
|
+
}
|
|
10020
|
+
if (showPrompts) {
|
|
10021
|
+
for (const [name, entry] of promptRows) {
|
|
10022
|
+
console.log(`prompt ${name} ${entry.url} ${entry.installedPath}`);
|
|
10023
|
+
}
|
|
9701
10024
|
}
|
|
9702
10025
|
});
|
|
9703
|
-
program2.command("remove <package>").alias("rm").description("Remove a
|
|
9704
|
-
const { name } = parsePackageArg(pkgArg);
|
|
10026
|
+
program2.command("remove <package-or-prompt>").alias("rm").description("Remove a tracked skill or prompt and its installed files").option(GLOBAL_OPTION, GLOBAL_OPTION_DESC).action(async (pkgArg, opts) => {
|
|
9705
10027
|
const scope = { global: opts.global };
|
|
9706
10028
|
const configRoot = resolveConfigRoot(scope);
|
|
9707
10029
|
const project = await readProjectPackageJson(configRoot);
|
|
9708
10030
|
if (!project) throw new Error(initRequiredMessage(scope));
|
|
10031
|
+
const promptReference = resolveTrackedPrompt(project, pkgArg);
|
|
10032
|
+
if (promptReference && project.prompts[promptReference.alias] === promptReference.url) {
|
|
10033
|
+
await removeTrackedPrompt({ configRoot, project, reference: promptReference });
|
|
10034
|
+
console.log(`Removed prompt ${promptReference.url} and its local snapshot.`);
|
|
10035
|
+
return;
|
|
10036
|
+
}
|
|
10037
|
+
const { name } = parsePackageArg(pkgArg);
|
|
9709
10038
|
const packages = { ...project.packages };
|
|
9710
10039
|
delete packages[name];
|
|
9711
10040
|
await writeProjectPackageJson(configRoot, { ...project, packages });
|
|
@@ -9725,12 +10054,24 @@ program2.command("update [package]").description("Update one installed package,
|
|
|
9725
10054
|
let project = await readProjectPackageJson(configRoot);
|
|
9726
10055
|
if (!project) throw new Error(initRequiredMessage(scope));
|
|
9727
10056
|
const registry = resolveRegistryUrl(project, opts.registry, DEFAULT_REGISTRY);
|
|
9728
|
-
const
|
|
9729
|
-
|
|
9730
|
-
|
|
9731
|
-
|
|
10057
|
+
const requestedPrompt = pkgArg ? resolveTrackedPrompt(project, pkgArg) : null;
|
|
10058
|
+
if (requestedPrompt) {
|
|
10059
|
+
if (project.prompts[requestedPrompt.alias] !== requestedPrompt.url) {
|
|
10060
|
+
throw new Error(`Prompt is not tracked: ${requestedPrompt.url}`);
|
|
10061
|
+
}
|
|
10062
|
+
const result = await installTrackedPrompt({
|
|
10063
|
+
configRoot,
|
|
10064
|
+
registry,
|
|
10065
|
+
project,
|
|
10066
|
+
reference: requestedPrompt,
|
|
10067
|
+
track: false,
|
|
10068
|
+
updateOnly: true
|
|
10069
|
+
});
|
|
10070
|
+
console.log(result.changed ? `Updated prompt ${result.prompt.title} \u2192 ${result.path}` : `Prompt unchanged: ${result.prompt.title}`);
|
|
9732
10071
|
return;
|
|
9733
10072
|
}
|
|
10073
|
+
const token = await tokenForRead(registry, opts.token);
|
|
10074
|
+
const names = pkgArg ? [parsePackageArg(pkgArg).name] : Object.keys(project.packages);
|
|
9734
10075
|
for (const name of names) {
|
|
9735
10076
|
const version = await latestVersionForPackage(registry, name, token);
|
|
9736
10077
|
project = {
|
|
@@ -9750,9 +10091,27 @@ program2.command("update [package]").description("Update one installed package,
|
|
|
9750
10091
|
token
|
|
9751
10092
|
});
|
|
9752
10093
|
}
|
|
10094
|
+
if (!pkgArg) {
|
|
10095
|
+
for (const [alias, url] of Object.entries(project.prompts)) {
|
|
10096
|
+
const reference = parsePromptUrl(url);
|
|
10097
|
+
if (!reference) throw new Error(`Invalid tracked prompt URL for ${alias}: ${url}`);
|
|
10098
|
+
const result = await installTrackedPrompt({
|
|
10099
|
+
configRoot,
|
|
10100
|
+
registry,
|
|
10101
|
+
project,
|
|
10102
|
+
reference: { ...reference, alias },
|
|
10103
|
+
track: false,
|
|
10104
|
+
updateOnly: true
|
|
10105
|
+
});
|
|
10106
|
+
console.log(result.changed ? `Updated prompt ${result.prompt.title} \u2192 ${result.path}` : `Prompt unchanged: ${result.prompt.title}`);
|
|
10107
|
+
}
|
|
10108
|
+
}
|
|
10109
|
+
if (names.length === 0 && Object.keys(project.prompts).length === 0) {
|
|
10110
|
+
console.log("No skills or prompts configured.");
|
|
10111
|
+
}
|
|
9753
10112
|
});
|
|
9754
10113
|
program2.command("doctor").description("Check PATH, Node, registry, project config, and publish readiness").option(GLOBAL_OPTION, GLOBAL_OPTION_DESC).option("--registry <url>", "Registry base URL").option("--json", "Print machine-readable JSON").option("--publish", "Only show publish-readiness checks").action((opts) => runDoctor(opts));
|
|
9755
|
-
program2.parseAsync(
|
|
10114
|
+
program2.parseAsync(normalizedArgv).catch((err) => {
|
|
9756
10115
|
console.error(err.message);
|
|
9757
10116
|
const message = err.message.toLowerCase();
|
|
9758
10117
|
if (message.includes("token") || message.includes("unauthorized") || message.includes("forbidden")) {
|