@aipm-registry/cli 0.2.2 → 0.2.13

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 CHANGED
@@ -1,7 +1,25 @@
1
+ # Search the registry at [aipm-registry.com](https://aipm-registry.com) for a skill
2
+
3
+
1
4
  # AIPM CLI
2
5
 
3
- AIPM installs project-ready AI skills into supported tools such as Cursor and
4
- Claude.
6
+ **An Open source command-line tool for AI skills management and discovery [AIPM](https://aipm-registry.com)**
7
+
8
+ ## What is this?
9
+
10
+ AI coding tools like Cursor and Claude can follow **skills**: small instruction files that teach the assistant how to do a job (code review, release notes, issue summaries, and more).
11
+
12
+ AIPM makes skills easy to **find, install, and share** — like npm, but for AI skills.
13
+
14
+ This package gives you the `aipm` command. With it you can:
15
+
16
+ - **Browse and install** skills from the public registry at **[aipm-registry.com](https://aipm-registry.com)**
17
+ - **Add skills to your project** so your whole team uses the same instructions
18
+ - **Publish your own skills** so others can install them
19
+
20
+ No other AIPM packages are required — everything is bundled in one install.
21
+
22
+ **Supported tools today:** Cursor and Claude (more targets at [aipm-registry.com/targets](https://aipm-registry.com/targets)).
5
23
 
6
24
  ## Install
7
25
 
@@ -9,91 +27,109 @@ Claude.
9
27
  npm install -g @aipm-registry/cli
10
28
  aipm --version
11
29
  aipm doctor
12
- aipm config
13
30
  ```
14
31
 
15
- ## Use
32
+ `aipm doctor` checks that your setup is ready. Run it if something does not work.
16
33
 
17
- Initialize a project:
34
+ ## Install skills in your project
35
+
36
+ Start in any project folder:
18
37
 
19
38
  ```bash
20
39
  aipm init
21
40
  ```
22
41
 
23
- Install a skill from the public registry:
42
+ *Search the registry at [aipm-registry.com](https://aipm-registry.com) for a skill:*
24
43
 
25
44
  ```bash
26
- aipm add @scope/name@1.0.0 --target cursor --ci
45
+ aipm search sentry
27
46
  ```
28
47
 
29
- List installed skills:
48
+ Results show version, supported tools, description, publisher, license, and size.
49
+
50
+ Install a skill:
30
51
 
31
52
  ```bash
32
- aipm list
53
+ aipm add @scope/name@1.0.0 --target cursor
33
54
  ```
34
55
 
35
- Search the registry:
56
+ See what is installed:
36
57
 
37
58
  ```bash
38
- aipm search sentry
59
+ aipm list
39
60
  ```
40
61
 
41
- Search results include package version, supported targets, description,
42
- publisher identity when available, license, and package size.
43
-
44
62
  Update installed skills:
45
63
 
46
64
  ```bash
47
65
  aipm update
48
66
  ```
49
67
 
50
- This package is bundled. Installing `@aipm-registry/cli` gives you the `aipm`
51
- command without needing to install any other AIPM npm packages manually.
68
+ Browse more packages on the website: **[aipm-registry.com](https://aipm-registry.com)**
69
+
70
+ ## Publish your skill (public / private / organization-only)
52
71
 
53
- Publish a public skill:
72
+ Share a skill with your team or the public registry.
73
+
74
+ 1. Create a free account at **[aipm-registry.com](https://aipm-registry.com)**
75
+ 2. Create an org and reserve your package name (for example `@team/review-helper`)
76
+ 3. Scaffold a new skill package:
54
77
 
55
78
  ```bash
56
79
  aipm publish init --name @team/review-helper --template code-review
57
80
  cd review-helper
58
- aipm publish explain # (optional)
81
+ ```
82
+
83
+ 1. Add your files and push:
84
+
85
+ ```bash
59
86
  aipm publish add .
60
- aipm publish status # (optional)
61
- aipm publish preview # (optional)
62
- aipm publish validate # (optional)
63
- aipm publish token --package @team/review-helper # (optional)
87
+ aipm publish preview # optional — see what will be uploaded
88
+ aipm publish validate # optional — check for problems
64
89
  AIPM_TOKEN=<5-minute-token> aipm publish push --yes
65
90
  ```
66
91
 
67
- Create an account on `https://aipm-registry.com`, create an org, reserve the
68
- package name, and generate the 5-minute token from the package dashboard.
92
+ Get the short-lived publish token from your package page on **[aipm-registry.com/dashboard](https://aipm-registry.com/dashboard)**.
93
+
94
+ **Starter templates:** `blank`, `code-review`, `issue-summary`, `release-notes`. They only set the initial `SKILL.md` — edit any file before publishing.
95
+
96
+ **Already have a skill file?** Import it into an AIPM package:
69
97
 
70
- Use `.aipmignore` to exclude local-only files from publishing. AIPM also
71
- rejects common private key, env, Azure connection string, publish profile, and
72
- large package mistakes before upload.
98
+ ```bash
99
+ aipm publish import ~/.codex/skills/review-helper --name @team/review-helper
100
+ cd review-helper
101
+ aipm publish add .
102
+ ```
73
103
 
74
- Helpful publishing commands:
104
+ **Helpful extras:**
75
105
 
76
106
  ```bash
77
- aipm publish init --name @team/issue-summary --template issue-summary
78
- aipm publish init --name @team/release-notes --template release-notes
79
- aipm publish open --docs # (optional)
80
- aipm publish open --package @team/review-helper # (optional)
81
- aipm doctor --publish # (optional)
107
+ aipm publish explain # what publishing does
108
+ aipm publish status # staged files
109
+ aipm publish token --package @team/review-helper
110
+ aipm publish open --docs # open publishing docs
111
+ aipm publish open --package @team/review-helper
112
+ aipm doctor --publish # check publish setup
82
113
  ```
83
114
 
84
- Starter templates are `blank`, `code-review`, `issue-summary`, and
85
- `release-notes`. They only change the starter `SKILL.md`; you can edit every
86
- file before staging and publishing.
115
+ Use `.aipmignore` to skip local-only files. Before upload, AIPM blocks common mistakes such as private keys, `.env` files, and oversized packages.
87
116
 
88
- If your AI tool already generated a skill or rule file, import it into an AIPM package folder:
117
+ Full publishing guide: **[aipm-registry.com/publish](https://aipm-registry.com/publish)**
118
+
119
+ ## Configuration
89
120
 
90
121
  ```bash
91
- aipm publish import ~/.codex/skills/review-helper --name @team/review-helper
92
- cd review-helper
93
- aipm publish add .
94
- aipm publish preview # (optional)
122
+ aipm config
95
123
  ```
96
124
 
97
- The default registry API is `https://api.aipm-registry.com`. Use `--registry <url>`,
98
- `AIPM_REGISTRY_URL=<url>`, or `AIPM_REGISTRY=<url>` for local or private
99
- registries.
125
+ Default registry API: `https://api.aipm-registry.com`
126
+
127
+ For a local or private registry, use `--registry <url>`, or set `AIPM_REGISTRY_URL` / `AIPM_REGISTRY`.
128
+
129
+ ## Links
130
+
131
+ - **Registry & browse skills:** [aipm-registry.com](https://aipm-registry.com)
132
+ - **Publishing guide:** [aipm-registry.com/publish](https://aipm-registry.com/publish)
133
+ - **Docs & examples:** [aipm-registry.com/examples](https://aipm-registry.com/examples)
134
+ - **Source code:** [github.com/aipm-registry/aipm](https://github.com/aipm-registry/aipm)
135
+
package/dist/bin.cjs CHANGED
@@ -4176,7 +4176,8 @@ var init_manifest = __esm({
4176
4176
  description: external_exports.string().min(1),
4177
4177
  entry: external_exports.string().min(1),
4178
4178
  targets: external_exports.array(AiToolSchema).min(1),
4179
- license: external_exports.string().optional()
4179
+ license: external_exports.string().optional(),
4180
+ usage: external_exports.string().min(1).optional()
4180
4181
  });
4181
4182
  }
4182
4183
  });
@@ -8015,6 +8016,10 @@ function registryFetchError(registry, cause) {
8015
8016
  }
8016
8017
  return new Error(`Registry request failed (${registry}): ${msg}`);
8017
8018
  }
8019
+ function registryAuthHeaders(token) {
8020
+ if (!token) return void 0;
8021
+ return { Authorization: `Bearer ${token}` };
8022
+ }
8018
8023
  async function registryFetch(url, registry, init) {
8019
8024
  const timeoutSignal = typeof AbortSignal !== "undefined" && "timeout" in AbortSignal ? AbortSignal.timeout(1e4) : void 0;
8020
8025
  try {
@@ -8023,6 +8028,12 @@ async function registryFetch(url, registry, init) {
8023
8028
  throw registryFetchError(registry, e);
8024
8029
  }
8025
8030
  }
8031
+ function privateInstallHint(name, status, token) {
8032
+ if (status !== 404 || !name.startsWith("@") || token) {
8033
+ return `Package not found: ${name} (${status})`;
8034
+ }
8035
+ return `Package not found: ${name} (${status}). If @org/pkg is private, set AIPM_TOKEN with an install token from the dashboard.`;
8036
+ }
8026
8037
  async function assertRegistryReachable(registry) {
8027
8038
  const base = registry.replace(/\/$/, "");
8028
8039
  const res = await registryFetch(`${base}/health`, base);
@@ -8053,19 +8064,19 @@ async function searchPackages(registry, query, limit = 20) {
8053
8064
  const data = await res.json();
8054
8065
  return data.packages ?? [];
8055
8066
  }
8056
- async function fetchPackageMetadata(registry, name, version) {
8067
+ async function fetchPackageMetadata(registry, name, version, token) {
8057
8068
  const base = registry.replace(/\/$/, "");
8058
8069
  const url = `${base}/v1/packages/${encodePackageName(name)}/versions/${version}`;
8059
- const res = await registryFetch(url, base);
8060
- if (!res.ok) throw new Error(`Package not found: ${name}@${version} (${res.status})`);
8070
+ const res = await registryFetch(url, base, { headers: registryAuthHeaders(token) });
8071
+ if (!res.ok) throw new Error(privateInstallHint(`${name}@${version}`, res.status, token));
8061
8072
  const data = await res.json();
8062
- return { manifest: data.manifest, integrity: data.integrity };
8073
+ return { manifest: data.manifest, integrity: data.integrity, deprecated: data.deprecated ?? null };
8063
8074
  }
8064
- async function fetchPackageTarball(registry, name, version) {
8075
+ async function fetchPackageTarball(registry, name, version, token) {
8065
8076
  const base = registry.replace(/\/$/, "");
8066
8077
  const url = `${base}/v1/packages/${encodePackageName(name)}/versions/${version}/tarball`;
8067
- const res = await registryFetch(url, base);
8068
- if (!res.ok) throw new Error(`Tarball not found: ${name}@${version} (${res.status})`);
8078
+ const res = await registryFetch(url, base, { headers: registryAuthHeaders(token) });
8079
+ if (!res.ok) throw new Error(privateInstallHint(`${name}@${version}`, res.status, token));
8069
8080
  const ab = await res.arrayBuffer();
8070
8081
  return Buffer.from(ab);
8071
8082
  }
@@ -8163,11 +8174,17 @@ async function installOnePackage(options) {
8163
8174
  const configRoot = options.configRoot;
8164
8175
  const installRoot = options.installRoot ?? configRoot;
8165
8176
  await assertRegistryReachable(options.registry);
8166
- const { manifest, integrity: remoteIntegrity } = await fetchPackageMetadata(
8177
+ const { manifest, integrity: remoteIntegrity, deprecated } = await fetchPackageMetadata(
8167
8178
  options.registry,
8168
8179
  options.name,
8169
- options.version
8180
+ options.version,
8181
+ options.token
8170
8182
  );
8183
+ if (deprecated) {
8184
+ console.warn(
8185
+ `Warning: ${options.name} is deprecated${deprecated.message ? `: ${deprecated.message}` : ""}.`
8186
+ );
8187
+ }
8171
8188
  let explicitTarget = options.explicitTarget;
8172
8189
  let preferredTools = options.project.preferredTools;
8173
8190
  const { resolveInstallTools: resolveInstallTools2 } = await Promise.resolve().then(() => (init_dist4(), dist_exports));
@@ -8196,7 +8213,8 @@ async function installOnePackage(options) {
8196
8213
  const tarball = await fetchPackageTarball(
8197
8214
  options.registry,
8198
8215
  options.name,
8199
- options.version
8216
+ options.version,
8217
+ options.token
8200
8218
  );
8201
8219
  const skillMarkdown = await unpackTarballToBuffer(tarball, manifest.entry);
8202
8220
  const result = await installSkillPackage({
@@ -8543,6 +8561,26 @@ async function inferEntryFromSource(source, fallback) {
8543
8561
  if (entries.includes(fallback)) return fallback;
8544
8562
  return entries.find((entry) => entry.endsWith(".md")) ?? entries.find((entry) => entry.endsWith(".mdc")) ?? fallback;
8545
8563
  }
8564
+ async function copySourceIntoSkillRoot(source, root, sourceLabel) {
8565
+ const sourceInfo = await (0, import_promises9.stat)(source);
8566
+ await (0, import_promises9.mkdir)(root, { recursive: true });
8567
+ if (sourceInfo.isDirectory()) {
8568
+ const entries = await (0, import_promises9.readdir)(source);
8569
+ for (const entry of entries) {
8570
+ await (0, import_promises9.cp)((0, import_node_path11.join)(source, entry), (0, import_node_path11.join)(root, entry), {
8571
+ recursive: true,
8572
+ force: false,
8573
+ errorOnExist: true
8574
+ });
8575
+ }
8576
+ return;
8577
+ }
8578
+ if (sourceInfo.isFile()) {
8579
+ await (0, import_promises9.cp)(source, (0, import_node_path11.join)(root, (0, import_node_path11.basename)(source)), { force: false, errorOnExist: true });
8580
+ return;
8581
+ }
8582
+ throw new Error(`Unsupported source path: ${sourceLabel}`);
8583
+ }
8546
8584
  async function latestVersionForPackage(registry, name) {
8547
8585
  const packages = await searchPackages(registry, name, 20);
8548
8586
  const exact = packages.find((pkg) => pkg.name === name);
@@ -8694,7 +8732,6 @@ async function initSkill(opts) {
8694
8732
  if (!isValidScopeName(opts.name)) throw new Error("Invalid @scope/name");
8695
8733
  const root = opts.here ? (0, import_node_process5.cwd)() : (0, import_node_path11.resolve)((0, import_node_process5.cwd)(), opts.dir ?? skillFolderName(opts.name));
8696
8734
  const cdTarget = opts.here ? null : opts.dir ?? skillFolderName(opts.name);
8697
- await (0, import_promises9.mkdir)(root, { recursive: true });
8698
8735
  const source = opts.from ? (0, import_node_path11.resolve)(opts.from) : null;
8699
8736
  const entry = source ? await inferEntryFromSource(source, opts.entry) : opts.entry;
8700
8737
  const manifest = PackageManifestSchema.parse({
@@ -8708,14 +8745,9 @@ async function initSkill(opts) {
8708
8745
  license: "Apache-2.0"
8709
8746
  });
8710
8747
  if (source) {
8711
- const sourceInfo = await (0, import_promises9.stat)(source);
8712
- if (sourceInfo.isDirectory()) {
8713
- await (0, import_promises9.cp)(source, root, { recursive: true, force: false, errorOnExist: true });
8714
- } else if (sourceInfo.isFile()) {
8715
- await (0, import_promises9.cp)(source, (0, import_node_path11.join)(root, (0, import_node_path11.basename)(source)), { force: false, errorOnExist: true });
8716
- } else {
8717
- throw new Error(`Unsupported source path: ${opts.from}`);
8718
- }
8748
+ await copySourceIntoSkillRoot(source, root, source);
8749
+ } else {
8750
+ await (0, import_promises9.mkdir)(root, { recursive: true });
8719
8751
  }
8720
8752
  await writeStarterFile((0, import_node_path11.join)(root, "aipm.manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
8721
8753
  if (!opts.from) {
@@ -8823,7 +8855,7 @@ program2.command("config").description("Show resolved AIPM CLI and project confi
8823
8855
  console.log(`Installed packages: ${data.installedPackages.length}`);
8824
8856
  console.log(`Publish guide: ${data.publishDoc}`);
8825
8857
  });
8826
- program2.command("add <package>").description("Add and install a package @scope/name[@version]").option(GLOBAL_OPTION, GLOBAL_OPTION_DESC).option("--registry <url>", "Registry base URL").option("--target <tool>", "cursor, claude, or *").option("--ci", "Non-interactive; fail if prompt needed").action(async (pkgArg, opts) => {
8858
+ program2.command("add <package>").description("Add and install a package @scope/name[@version]").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) => {
8827
8859
  const { name, version: requestedVersion } = parsePackageArg(pkgArg);
8828
8860
  const scope = { global: opts.global };
8829
8861
  const configRoot = resolveConfigRoot(scope);
@@ -8846,7 +8878,8 @@ program2.command("add <package>").description("Add and install a package @scope/
8846
8878
  version: version.replace(/^\^/, ""),
8847
8879
  project,
8848
8880
  explicitTarget: parseTargetFlag(opts.target),
8849
- ci: opts.ci
8881
+ ci: opts.ci,
8882
+ token: opts.token ?? process.env.AIPM_TOKEN
8850
8883
  });
8851
8884
  if (!opts.ci && !program2.opts().quiet) {
8852
8885
  await notifyCliUpdateIfNeeded(CLI_VERSION);
@@ -8871,7 +8904,7 @@ program2.command("search [query]").description("Search public registry packages"
8871
8904
  console.log(` ${meta}`);
8872
8905
  }
8873
8906
  });
8874
- program2.command("install").description("Install all packages from aipm.package.json").option(GLOBAL_OPTION, GLOBAL_OPTION_DESC).option("--registry <url>", "Registry base URL").option("--target <tool>", "cursor, claude, or *").option("--ci", "Non-interactive").action(async (opts) => {
8907
+ program2.command("install").description("Install all packages from aipm.package.json").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").action(async (opts) => {
8875
8908
  const scope = { global: opts.global };
8876
8909
  const configRoot = resolveConfigRoot(scope);
8877
8910
  const installRoot = resolveInstallRoot(scope);
@@ -8888,7 +8921,8 @@ program2.command("install").description("Install all packages from aipm.package.
8888
8921
  version: version.replace(/^\^/, ""),
8889
8922
  project,
8890
8923
  explicitTarget: target,
8891
- ci: opts.ci
8924
+ ci: opts.ci,
8925
+ token: opts.token ?? process.env.AIPM_TOKEN
8892
8926
  });
8893
8927
  }
8894
8928
  });