promptdock 0.1.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/dist/ui.d.ts ADDED
@@ -0,0 +1,22 @@
1
+ import type { CliIo } from "./context.js";
2
+ /** NO_COLOR (https://no-color.org) honored: any non-empty value disables color. */
3
+ export declare function colors(env: Record<string, string | undefined>, isTTY: boolean): {
4
+ bold: (s: string) => string;
5
+ dim: (s: string) => string;
6
+ green: (s: string) => string;
7
+ red: (s: string) => string;
8
+ yellow: (s: string) => string;
9
+ cyan: (s: string) => string;
10
+ };
11
+ export type Colors = ReturnType<typeof colors>;
12
+ export declare function formatBytes(n: number): string;
13
+ export declare function formatCountdown(totalSecs: number): string;
14
+ /** ~Nh retry copy for the cap verdict (mirrors the app's premium-cap copy). */
15
+ export declare function retryHours(retryAfterSecs: number): string;
16
+ /**
17
+ * Numbered-list picker (TTY only — callers gate). Returns the chosen index.
18
+ * Enter accepts the preselected default.
19
+ */
20
+ export declare function promptSelect(io: CliIo, title: string, options: string[], preselect: number): Promise<number>;
21
+ /** y/N confirm (TTY only — callers gate; -y bypasses upstream). */
22
+ export declare function confirm(io: CliIo, prompt: string, def?: boolean): Promise<boolean>;
package/dist/ui.js ADDED
@@ -0,0 +1,58 @@
1
+ /** NO_COLOR (https://no-color.org) honored: any non-empty value disables color. */
2
+ export function colors(env, isTTY) {
3
+ const on = isTTY && !env.NO_COLOR;
4
+ const wrap = (open, close) => (s) => (on ? `${open}${s}${close}` : s);
5
+ return {
6
+ bold: wrap("\u001b[1m", "\u001b[22m"),
7
+ dim: wrap("\u001b[2m", "\u001b[22m"),
8
+ green: wrap("\u001b[32m", "\u001b[39m"),
9
+ red: wrap("\u001b[31m", "\u001b[39m"),
10
+ yellow: wrap("\u001b[33m", "\u001b[39m"),
11
+ cyan: wrap("\u001b[36m", "\u001b[39m"),
12
+ };
13
+ }
14
+ export function formatBytes(n) {
15
+ if (!Number.isFinite(n) || n < 0)
16
+ return "0 B";
17
+ if (n < 1024)
18
+ return `${n} B`;
19
+ if (n < 1024 * 1024)
20
+ return `${(n / 1024).toFixed(1)} KB`;
21
+ return `${(n / (1024 * 1024)).toFixed(1)} MB`;
22
+ }
23
+ export function formatCountdown(totalSecs) {
24
+ const s = Math.max(0, Math.floor(totalSecs));
25
+ return `${Math.floor(s / 60)}:${String(s % 60).padStart(2, "0")}`;
26
+ }
27
+ /** ~Nh retry copy for the cap verdict (mirrors the app's premium-cap copy). */
28
+ export function retryHours(retryAfterSecs) {
29
+ return `~${Math.max(1, Math.ceil(retryAfterSecs / 3600))}h`;
30
+ }
31
+ /**
32
+ * Numbered-list picker (TTY only — callers gate). Returns the chosen index.
33
+ * Enter accepts the preselected default.
34
+ */
35
+ export async function promptSelect(io, title, options, preselect) {
36
+ io.out(title);
37
+ options.forEach((opt, i) => {
38
+ const marker = i === preselect ? "❯" : " ";
39
+ io.out(` ${marker} ${i + 1}. ${opt}`);
40
+ });
41
+ for (;;) {
42
+ const answer = (await io.question(`Choose [${preselect + 1}]: `)).trim();
43
+ if (answer === "")
44
+ return preselect;
45
+ const n = Number(answer);
46
+ if (Number.isInteger(n) && n >= 1 && n <= options.length)
47
+ return n - 1;
48
+ io.out(`Enter a number between 1 and ${options.length}.`);
49
+ }
50
+ }
51
+ /** y/N confirm (TTY only — callers gate; -y bypasses upstream). */
52
+ export async function confirm(io, prompt, def = false) {
53
+ const suffix = def ? "[Y/n]" : "[y/N]";
54
+ const answer = (await io.question(`${prompt} ${suffix} `)).trim().toLowerCase();
55
+ if (answer === "")
56
+ return def;
57
+ return answer === "y" || answer === "yes";
58
+ }
@@ -0,0 +1,4 @@
1
+ import type { ResolveResponse } from "./contract.js";
2
+ export declare const UPGRADE_URL = "https://promptdock.ai/pricing";
3
+ /** Throws the DX3-copy CliError for a deny verdict; returns for ok/already_entitled. */
4
+ export declare function assertInstallable(resolve: ResolveResponse, refString: string): void;
@@ -0,0 +1,26 @@
1
+ import { CliError, EXIT } from "./errors.js";
2
+ import { retryHours } from "./ui.js";
3
+ export const UPGRADE_URL = "https://promptdock.ai/pricing";
4
+ /** Throws the DX3-copy CliError for a deny verdict; returns for ok/already_entitled. */
5
+ export function assertInstallable(resolve, refString) {
6
+ switch (resolve.verdict) {
7
+ case "ok":
8
+ case "already_entitled":
9
+ return;
10
+ case "not_found":
11
+ throw new CliError(`skill not found: ${refString} — check the ref (the format is handle/slug; a pasted promptdock.ai skill URL also works)`, EXIT.DENIED, { footer: "not_found" });
12
+ case "insufficient_tier": {
13
+ const title = resolve.title ? `"${resolve.title}"` : "This skill";
14
+ throw new CliError(`${title} is a premium skill. Premium skills need the Explorer plan ($10/mo) or higher` +
15
+ (resolve.current ? ` — your account is on the ${resolve.current} tier.` : "."), EXIT.DENIED, { footer: "insufficient_tier", hint: `Upgrade: ${UPGRADE_URL}` });
16
+ }
17
+ case "cap_hit": {
18
+ const wait = retryHours(Number(resolve.retry_after_secs) || 3600);
19
+ throw new CliError(`you've hit the daily premium-skill limit — try again in ${wait}`, EXIT.DENIED, { footer: "cap_hit" });
20
+ }
21
+ default:
22
+ throw new CliError(`unrecognized server verdict — update the CLI and retry`, EXIT.DENIED, {
23
+ footer: "verdict",
24
+ });
25
+ }
26
+ }
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "promptdock",
3
+ "version": "0.1.0",
4
+ "description": "Install AI agent skills from PromptDock — npx promptdock@latest install <handle>/<slug>",
5
+ "keywords": ["promptdock", "skills", "ai", "agents", "claude", "cli"],
6
+ "homepage": "https://promptdock.ai",
7
+ "bugs": { "url": "https://promptdock.ai/docs/cli/errors" },
8
+ "license": "UNLICENSED",
9
+ "type": "module",
10
+ "bin": { "promptdock": "dist/index.js" },
11
+ "files": ["dist", "README.md"],
12
+ "engines": { "node": ">=18" },
13
+ "scripts": {
14
+ "build": "tsc -p tsconfig.json",
15
+ "test": "vitest run",
16
+ "prepublishOnly": "npm run build"
17
+ },
18
+ "devDependencies": {
19
+ "@types/node": "^20",
20
+ "typescript": "^5",
21
+ "vitest": "^4.1.8"
22
+ }
23
+ }