@botdocs/cli 0.6.0 → 0.8.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.
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Parse a BotDoc ref like `@user/slug` or `user/slug` into its parts.
3
+ *
4
+ * Mirrors the inline `parseRef` already in `commands/install.ts` and
5
+ * `commands/edit.ts` — kept here for reuse by `publish`/`unpublish`.
6
+ * The existing duplicates can be folded into this helper in a future
7
+ * refactor without behavior change.
8
+ *
9
+ * Throws on a malformed input so callers can surface a clear message
10
+ * instead of silently dispatching a malformed API request.
11
+ */
12
+ export declare function parseRef(raw: string): {
13
+ username: string;
14
+ slug: string;
15
+ };
16
+ /**
17
+ * True when `source` looks like a BotDoc ref (e.g. `@me/my-skill` or
18
+ * `me/my-skill`), false when it looks like a local filesystem path.
19
+ *
20
+ * Used by `botdocs publish <source>` to overload one verb by argument
21
+ * shape — ref-form dispatches to the publish-toggle API, anything else
22
+ * goes through the existing file/dir/zip upload flow.
23
+ *
24
+ * Heuristic (lean toward "is a ref" only when unambiguous):
25
+ *
26
+ * - `@…` → ref. The leading `@` is unambiguous; no filesystem path
27
+ * starts with `@`.
28
+ * - Starts with `./`, `..`, or `/` → path. Absolute and explicit
29
+ * relative paths are never refs.
30
+ * - Contains a `.` (e.g. `foo.md`, `foo.zip`, `dir/file.md`) → path.
31
+ * File extensions are the strongest signal we're looking at a file.
32
+ * - Contains exactly one `/` with non-empty parts on either side → ref.
33
+ * `user/slug` and `org/repo`-style.
34
+ * - Otherwise → path. Bare names like `my-skill` resolve as directory
35
+ * paths under the existing upload flow.
36
+ *
37
+ * False positives (a real directory named `user/slug` with no `.`) will
38
+ * surface as a clean "BotDoc not found" 404 from the API — not silent
39
+ * data loss, just a clearer error than letting the upload flow choke
40
+ * on a non-existent path.
41
+ */
42
+ export declare function isRefForm(source: string): boolean;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Parse a BotDoc ref like `@user/slug` or `user/slug` into its parts.
3
+ *
4
+ * Mirrors the inline `parseRef` already in `commands/install.ts` and
5
+ * `commands/edit.ts` — kept here for reuse by `publish`/`unpublish`.
6
+ * The existing duplicates can be folded into this helper in a future
7
+ * refactor without behavior change.
8
+ *
9
+ * Throws on a malformed input so callers can surface a clear message
10
+ * instead of silently dispatching a malformed API request.
11
+ */
12
+ export function parseRef(raw) {
13
+ const cleaned = raw.startsWith('@') ? raw.slice(1) : raw;
14
+ const parts = cleaned.split('/');
15
+ if (parts.length !== 2 || !parts[0] || !parts[1]) {
16
+ throw new Error(`Invalid ref: ${raw} (expected @user/slug)`);
17
+ }
18
+ return { username: parts[0], slug: parts[1] };
19
+ }
20
+ /**
21
+ * True when `source` looks like a BotDoc ref (e.g. `@me/my-skill` or
22
+ * `me/my-skill`), false when it looks like a local filesystem path.
23
+ *
24
+ * Used by `botdocs publish <source>` to overload one verb by argument
25
+ * shape — ref-form dispatches to the publish-toggle API, anything else
26
+ * goes through the existing file/dir/zip upload flow.
27
+ *
28
+ * Heuristic (lean toward "is a ref" only when unambiguous):
29
+ *
30
+ * - `@…` → ref. The leading `@` is unambiguous; no filesystem path
31
+ * starts with `@`.
32
+ * - Starts with `./`, `..`, or `/` → path. Absolute and explicit
33
+ * relative paths are never refs.
34
+ * - Contains a `.` (e.g. `foo.md`, `foo.zip`, `dir/file.md`) → path.
35
+ * File extensions are the strongest signal we're looking at a file.
36
+ * - Contains exactly one `/` with non-empty parts on either side → ref.
37
+ * `user/slug` and `org/repo`-style.
38
+ * - Otherwise → path. Bare names like `my-skill` resolve as directory
39
+ * paths under the existing upload flow.
40
+ *
41
+ * False positives (a real directory named `user/slug` with no `.`) will
42
+ * surface as a clean "BotDoc not found" 404 from the API — not silent
43
+ * data loss, just a clearer error than letting the upload flow choke
44
+ * on a non-existent path.
45
+ */
46
+ export function isRefForm(source) {
47
+ if (!source)
48
+ return false;
49
+ if (source.startsWith('@'))
50
+ return true;
51
+ if (source.startsWith('./') || source.startsWith('../') || source.startsWith('/')) {
52
+ return false;
53
+ }
54
+ if (source.includes('.'))
55
+ return false;
56
+ const parts = source.split('/');
57
+ if (parts.length !== 2)
58
+ return false;
59
+ return Boolean(parts[0] && parts[1]);
60
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botdocs/cli",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "description": "CLI for BotDocs — author, publish, install, and sync agent skills across Claude, Claude Code, Cursor, Codex, ChatGPT, Windsurf, Copilot, Gemini, Antigravity, and OpenCode.",
5
5
  "keywords": [
6
6
  "botdocs",
@@ -119,7 +119,8 @@ the user and recommend they set one.
119
119
  | `init [name]` | Scaffold a new skill directory. |
120
120
  | `validate <source>` | Pre-publish structural check. |
121
121
  | `search <query>` | Search the registry. |
122
- | `publish <source>` | Publish from a file, directory, or zip. |
122
+ | `publish <source>` | Publish from a file, directory, or zip — or pass `@user/slug` to mark an existing draft live. |
123
+ | `unpublish <ref>` | Hide a published BotDoc from `/explore` (sets the `draft` flag back; prompts unless `--yes`). |
123
124
  | `install <ref>` | Install a skill or bundle (auto-detects destinations). |
124
125
  | `sync [ref]` | Apply available updates to installed skills. |
125
126
  | `uninstall <ref>` | Remove an installed skill or bundle. |